Применение пиксельных шейдеров HLSL к захвату экрана Win32 - PullRequest
1 голос
/ 30 апреля 2020

Немного предыстории: я пытаюсь создать приложение Windows (10), которое делает экран похожим на старый ЭЛТ-монитор, линии сканирования, размытие и все такое. Я использую эту официальную демонстрацию захвата экрана Microsoft в качестве отправной точки: на этом этапе я могу захватить окно и отобразить его обратно в новом сквозном окне мыши, как если бы это было исходное окно.

Я пытаюсь использовать CRT-Royale CRT-шейдеры , которые обычно считаются лучшими CRT-шейдерами; они доступны в формате .cg. Я передаю их с помощью cg c в hlsl, затем компилирую файлы hlsl в скомпилированный байт-код шейдера с помощью fx c. Я могу успешно загрузить скомпилированные шейдеры и создать пиксельный шейдер. Затем я устанавливаю пиксельный шейдер в контексте d3d. Затем я пытаюсь скопировать кадр поверхности захвата в ресурс пиксельных шейдеров и установить ресурс созданных шейдеров. Все это строит и запускает, но я не вижу никакой разницы в выходном изображении и не уверен, что делать дальше. Ниже приведен соответствующий код. Я не разработчик C ++ и делаю это как личный проект, который я планирую открыть с открытым исходным кодом, как только у меня будет примитивная рабочая версия. Любой совет приветствуется, спасибо.

SimpleCapture::SimpleCapture(
IDirect3DDevice const& device,
GraphicsCaptureItem const& item)
{
m_item = item;
m_device = device;

// Set up 
auto d3dDevice = GetDXGIInterfaceFromObject<ID3D11Device>(m_device);
d3dDevice->GetImmediateContext(m_d3dContext.put());
auto size = m_item.Size();

m_swapChain = CreateDXGISwapChain(
    d3dDevice, 
    static_cast<uint32_t>(size.Width),
    static_cast<uint32_t>(size.Height),
    static_cast<DXGI_FORMAT>(DirectXPixelFormat::B8G8R8A8UIntNormalized),
    2);

// ADDED THIS
HRESULT hr1 = D3DReadFileToBlob(L"crt-royale-first-pass-ps_4_0.fxc", &ps_1_buffer);
HRESULT hr = d3dDevice->CreatePixelShader(
    ps_1_buffer->GetBufferPointer(),
    ps_1_buffer->GetBufferSize(),
    nullptr,
    &ps_1
);
m_d3dContext->PSSetShader(
    ps_1,
    nullptr,
    0
);
// END OF ADDED CHANGES

// Create framepool, define pixel format (DXGI_FORMAT_B8G8R8A8_UNORM), and frame size. 
m_framePool = Direct3D11CaptureFramePool::Create(
    m_device,
    DirectXPixelFormat::B8G8R8A8UIntNormalized,
    2,
    size);
m_session = m_framePool.CreateCaptureSession(m_item);
m_lastSize = size;
m_frameArrived = m_framePool.FrameArrived(auto_revoke, { this, &SimpleCapture::OnFrameArrived });
}

void SimpleCapture::OnFrameArrived(
Direct3D11CaptureFramePool const& sender,
winrt::Windows::Foundation::IInspectable const&)
{
auto newSize = false;

{
    auto frame = sender.TryGetNextFrame();
    auto frameContentSize = frame.ContentSize();

    if (frameContentSize.Width != m_lastSize.Width ||
        frameContentSize.Height != m_lastSize.Height)
    {
        // The thing we have been capturing has changed size.
        // We need to resize our swap chain first, then blit the pixels.
        // After we do that, retire the frame and then recreate our frame pool.
        newSize = true;
        m_lastSize = frameContentSize;
        m_swapChain->ResizeBuffers(
            2, 
            static_cast<uint32_t>(m_lastSize.Width),
            static_cast<uint32_t>(m_lastSize.Height),
            static_cast<DXGI_FORMAT>(DirectXPixelFormat::B8G8R8A8UIntNormalized), 
            0);
    }

    {
        auto frameSurface = GetDXGIInterfaceFromObject<ID3D11Texture2D>(frame.Surface());

        com_ptr<ID3D11Texture2D> backBuffer;
        check_hresult(m_swapChain->GetBuffer(0, guid_of<ID3D11Texture2D>(), backBuffer.put_void()));

        // ADDED THIS
        D3D11_TEXTURE2D_DESC txtDesc = {};
        txtDesc.MipLevels = txtDesc.ArraySize = 1;
        txtDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
        txtDesc.SampleDesc.Count = 1;
        txtDesc.Usage = D3D11_USAGE_IMMUTABLE;
        txtDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;

        auto d3dDevice = GetDXGIInterfaceFromObject<ID3D11Device>(m_device);

        ID3D11Texture2D *tex;
        d3dDevice->CreateTexture2D(&txtDesc, NULL,
            &tex);
        frameSurface.copy_to(&tex);

        d3dDevice->CreateShaderResourceView(
            tex,
            nullptr,
            srv_1
        );

        auto texture = srv_1;
        m_d3dContext->PSSetShaderResources(0, 1, texture);
        // END OF ADDED CHANGES

        m_d3dContext->CopyResource(backBuffer.get(), frameSurface.get());
    }
}

DXGI_PRESENT_PARAMETERS presentParameters = { 0 };
m_swapChain->Present1(1, 0, &presentParameters);
... // Truncated

1 Ответ

0 голосов
/ 03 мая 2020

Шейдеры определяют, как все рисуется. Однако вы ничего не рисуете - вы просто копируете, поэтому шейдер ничего не делает.

Что вам нужно сделать, это удалить вызов CopyResource и вместо этого нарисовать полноэкранный квад в заднем буфере (для этого необходимо создать буфер вершин, который вы можете связать, а затем установить задний буфер в качестве цели рендеринга). и, наконец, вызовите Draw / DrawIndexed для фактической визуализации чего-либо, что затем вызовет шейдер).

Кроме того - поскольку я не уверен, что вы уже сделали это и просто удалили его из показанного кода - такие функции, как CreatePixelShader, не возвращают HRESULT просто для удовольствия - вы должны проверить, что на самом деле возвращается потому что DirectX молча возвращает большинство ошибок и ожидает, что вы их обработаете, а не вылетаете из программы.

...