The continuation of my previous question, I am able to find a way to capture a live screen without own window with help of WinRT's Windows.Graphics.Capture. I can concentrate directly on a particular window handle to get live capture. now, the problem with this approach is I am not able to apply pixel shader. The question Applying HLSL Pixel Shaders to Win32 Screen Capture having the same requirement but the answer to that question is not solving my problem.
Code with more information:
#include "pch.h"
#include "SimpleCapture.h"
#include <windows.h>
#include <d3d11.h>
#include <dxgi1_2.h>
#include <sal.h>
#include <new>
#include <warning.h>
#include <DirectXMath.h>
#include "PixelShader.h"
using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::System;
using namespace Windows::Graphics::Capture;
using namespace Windows::Graphics::DirectX;
using namespace Windows::Graphics::DirectX::Direct3D11;
using namespace Windows::Foundation::Numerics;
using namespace Windows::UI;
using namespace Windows::UI::Composition;
using namespace DirectX;
typedef struct _VERTEX
{
DirectX::XMFLOAT3 Pos;
DirectX::XMFLOAT2 TexCoord;
} VERTEX;
//
// A vertex with a position and texture coordinate
//
SimpleCapture::SimpleCapture(
IDirect3DDevice const& device,
GraphicsCaptureItem const& item)
{
m_item = item;
m_device = device;
// Set up
m_d3ddevice = GetDXGIInterfaceFromObject<ID3D11Device>(m_device);
m_d3ddevice->GetImmediateContext(m_d3dContext.put());
auto size = m_item.Size();
m_swapChain = CreateDXGISwapChain(
m_d3ddevice,
static_cast<uint32_t>(size.Width),
static_cast<uint32_t>(size.Height),
static_cast<DXGI_FORMAT>(DirectXPixelFormat::B8G8R8A8UIntNormalized),
2);
UINT Size = ARRAYSIZE(g_VS1);
m_d3ddevice->CreateVertexShader(g_VS1, Size, nullptr, &m_VertexShader);
D3D11_INPUT_ELEMENT_DESC Layout[] =
{
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0}
};
UINT NumElements = ARRAYSIZE(Layout);
m_d3ddevice->CreateInputLayout(Layout, NumElements, g_VS1, Size, &m_InputLayout);
m_d3dContext->IASetInputLayout(m_InputLayout);
Size = ARRAYSIZE(g_main);
// ADDED THIS
Size = ARRAYSIZE(g_main);
HRESULT hr = m_d3ddevice->CreatePixelShader(g_main, Size, nullptr, &m_PixelShader);
// 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_frameArrived = m_framePool.FrameArrived(auto_revoke, { this, &SimpleCapture::OnFrameArrived });
}
// Start sending capture frames
void SimpleCapture::StartCapture()
{
CheckClosed();
m_session.StartCapture();
}
ICompositionSurface SimpleCapture::CreateSurface(
Compositor const& compositor)
{
CheckClosed();
return CreateCompositionSurfaceForSwapChain(compositor, m_swapChain.get());
}
// Process captured frames
void SimpleCapture::Close()
{
auto expected = false;
if (m_closed.compare_exchange_strong(expected, true))
{
m_frameArrived.revoke();
m_framePool.Close();
m_session.Close();
m_swapChain = nullptr;
m_framePool = nullptr;
m_session = nullptr;
m_item = nullptr;
}
}
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 d3dDevice = GetDXGIInterfaceFromObject<ID3D11Device>(m_device);
d3dDevice->GetImmediateContext(m_d3dContext.put());*/
{
auto frameSurface = GetDXGIInterfaceFromObject<ID3D11Texture2D>(frame.Surface());
com_ptr<ID3D11Texture2D> backBuffer;
check_hresult(m_swapChain->GetBuffer(0, guid_of<ID3D11Texture2D>(), backBuffer.put_void()));
m_d3ddevice->CreateRenderTargetView(backBuffer.get(), NULL, &m_RTV);
// 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;
m_d3ddevice->CreateTexture2D(&txtDesc, NULL,
&tex);
frameSurface.copy_to(&tex);
ID3D11ShaderResourceView* srv_1;
m_d3ddevice->CreateShaderResourceView(
tex,
nullptr,
&srv_1
);
auto texture = srv_1;
m_d3dContext->PSSetShaderResources(0, 1, &texture);
// END OF ADDED CHANGES
// Create the sample state
D3D11_SAMPLER_DESC SampDesc;
RtlZeroMemory(&SampDesc, sizeof(SampDesc));
SampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
SampDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
SampDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
SampDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
SampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
SampDesc.MinLOD = 0;
SampDesc.MaxLOD = D3D11_FLOAT32_MAX;
m_d3ddevice->CreateSamplerState(&SampDesc, &m_SamplerLinear);
D3D11_TEXTURE2D_DESC FrameDesc;
tex->GetDesc(&FrameDesc);
D3D11_SHADER_RESOURCE_VIEW_DESC ShaderDesc;
ShaderDesc.Format = FrameDesc.Format;
ShaderDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
ShaderDesc.Texture2D.MostDetailedMip = FrameDesc.MipLevels - 1;
ShaderDesc.Texture2D.MipLevels = FrameDesc.MipLevels;
// Vertices for drawing whole texture
VERTEX Vertices[NUMVERTICES] =
{
{XMFLOAT3(-1.0f, -1.0f, 0), XMFLOAT2(0.0f, 1.0f)},
{XMFLOAT3(-1.0f, 1.0f, 0), XMFLOAT2(0.0f, 0.0f)},
{XMFLOAT3(1.0f, -1.0f, 0), XMFLOAT2(1.0f, 1.0f)},
{XMFLOAT3(1.0f, -1.0f, 0), XMFLOAT2(1.0f, 1.0f)},
{XMFLOAT3(-1.0f, 1.0f, 0), XMFLOAT2(0.0f, 0.0f)},
{XMFLOAT3(1.0f, 1.0f, 0), XMFLOAT2(1.0f, 0.0f)},
};
// Create new shader resource view
ID3D11ShaderResourceView* ShaderResource = nullptr;
m_d3ddevice->CreateShaderResourceView(tex, &ShaderDesc, &ShaderResource);
// Set resources
UINT Stride = sizeof(VERTEX);
UINT Offset = 0;
m_d3dContext->OMSetRenderTargets(1, &m_RTV, nullptr);
m_d3dContext->VSSetShader(m_VertexShader, nullptr, 0);
m_d3dContext->PSSetShader(m_PixelShader, nullptr, 0);
m_d3dContext->PSSetShaderResources(0, 1, &ShaderResource);
m_d3dContext->PSSetSamplers(0, 1, &m_SamplerLinear);
m_d3dContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
D3D11_BUFFER_DESC BufferDesc;
RtlZeroMemory(&BufferDesc, sizeof(BufferDesc));
BufferDesc.Usage = D3D11_USAGE_DEFAULT;
BufferDesc.ByteWidth = sizeof(VERTEX) * NUMVERTICES;
BufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
BufferDesc.CPUAccessFlags = 0;
D3D11_SUBRESOURCE_DATA InitData;
RtlZeroMemory(&InitData, sizeof(InitData));
InitData.pSysMem = Vertices;
ID3D11Buffer* VertexBuffer = nullptr;
// Create vertex buffer
m_d3ddevice->CreateBuffer(&BufferDesc, &InitData, &VertexBuffer);
m_d3dContext->IASetVertexBuffers(0, 1, &VertexBuffer, &Stride, &Offset);
// Draw textured quad onto render target
//m_d3dContext->CopyResource(backBuffer.get(), tex);
m_d3dContext->Draw(NUMVERTICES, 0);
//
//m_d3dContext->CopyResource(backBuffer.get(), frameSurface.get());
//m_d3dContext->DrawAuto();
}
}
DXGI_PRESENT_PARAMETERS presentParameters = { 0 };
m_swapChain->Present1(1, 0, &presentParameters);
}
few additional links:
- Here is complete code Screen capture for HWND
- Ready to move to c# in case its easy in C# WPF Screen Capture
Please let me know any changes required in above code.