通过劫持NVIDIA GeForceAMD Software的窗口,即可实现全屏渲染。

Overlay.h

#pragma once
#include <d2d1.h>
#include <dwrite.h>
#include <windows.h>

class Overlay
{

public:
    Overlay(const wchar_t* font, float fontSize);
    ~Overlay();

    bool Init();
    bool InitD2D();
    void UnInitD2D() const;

    void BeginScene() const;
    void EndScene() const;
    void ClearScene() const;

    void DrawTextA(int x, int y, const char* text, D2D1::ColorF color, ...) const;
    void DrawLine(int x, int y, int x2, int y2, D2D1::ColorF color, float thickness) const;

private:
    HWND m_hijackedHwnd;

    ID2D1Factory* m_d2dFactory;
    ID2D1HwndRenderTarget* m_renderTarget;
    IDWriteFactory* m_writeFactory;
    ID2D1SolidColorBrush* m_brush;
    IDWriteTextFormat* m_format;

    const wchar_t* m_font;
    float m_size;

    bool m_isAmdGPU;
};

Overlay.cpp

#include "Overlay.h"
#include <dwmapi.h>
#include <string>

#pragma comment(lib, "d2d1.lib")
#pragma comment(lib, "Dwrite.lib")
#pragma comment(lib, "Dwmapi.lib")

Overlay::Overlay(const wchar_t* font, float fontSize)
{
    m_font = font;
    m_size = fontSize;
}

Overlay::~Overlay()
{
    BeginScene();
    ClearScene();
    EndScene();
    UnInitD2D();
}

bool Overlay::Init()
{
    m_hijackedHwnd = FindWindowA("CEF-OSC-WIDGET", "NVIDIA GeForce Overlay");
    if (NULL != m_hijackedHwnd)
    {
        m_isAmdGPU = false;
    }
    else
    {
        m_hijackedHwnd = FindWindowA("AMDDVROVERLAYWINDOWCLASS", "amd dvr overlay");
        if (NULL != m_hijackedHwnd)
        {
            m_isAmdGPU = true;
        }
        else
        {
            return false;
        }
    }

    ShowWindow(m_hijackedHwnd, SW_SHOW);

    //SetWindowLongA(m_hijackedHwnd, GWL_EXSTYLE, WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW | WS_EX_LAYERED);

    const auto info = GetWindowLongA(m_hijackedHwnd, GWL_EXSTYLE);
    if (!info)
        return false;

    const auto attrChange = SetWindowLongPtrA(m_hijackedHwnd, GWL_EXSTYLE, (info | WS_EX_TRANSPARENT));
    if (!attrChange)
        return false;

    // AMD GPU需要
    if (m_isAmdGPU)
    {
        const auto screenWidth = GetSystemMetrics(SM_CXSCREEN);
        const auto screenHeight = GetSystemMetrics(SM_CYSCREEN);
        MoveWindow(m_hijackedHwnd, 0, 0, screenWidth, screenHeight, false);
    }

    MARGINS margin;
    margin.cyBottomHeight = margin.cyTopHeight = margin.cxLeftWidth = margin.cxRightWidth = -1;
    if (DwmExtendFrameIntoClientArea(m_hijackedHwnd, &margin) != S_OK)
        return false;
    if (!SetLayeredWindowAttributes(m_hijackedHwnd, 0x000000, 0xFF, 0x02))
        return false;
    if (!SetWindowPos(m_hijackedHwnd, HWND_TOPMOST, 0, 0, 0, 0, 0x0002 | 0x0001))
        return false;
    UpdateWindow(m_hijackedHwnd);
    return true;
}

bool Overlay::InitD2D()
{
    RECT rc;
    if (SUCCEEDED(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_d2dFactory)))
    {
        if (SUCCEEDED(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast<IUnknown**>(&m_writeFactory))))
        {
            m_writeFactory->CreateTextFormat(m_font, NULL, DWRITE_FONT_WEIGHT_REGULAR, DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL, m_size, L"", &m_format);
            GetClientRect(m_hijackedHwnd, &rc);

            if (SUCCEEDED(m_d2dFactory->CreateHwndRenderTarget(D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT,
                D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED)),
                D2D1::HwndRenderTargetProperties(m_hijackedHwnd, D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top)), &m_renderTarget)))
            {
                const D2D1_BRUSH_PROPERTIES properties = { 1.0f, D2D1::Matrix3x2F::Identity() };
                m_renderTarget->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), properties, &m_brush);
                return true;
            }
        }
    }
    return false;
}

void Overlay::UnInitD2D() const
{
    m_renderTarget->Release();
    m_writeFactory->Release();
    m_brush->Release();
    m_d2dFactory->Release();
}

void Overlay::BeginScene() const
{
    m_renderTarget->BeginDraw();
}

void Overlay::EndScene() const
{
    m_renderTarget->EndDraw();
}

void Overlay::ClearScene() const
{
    m_renderTarget->Clear();
}

void Overlay::DrawTextA(const int x, const int y, const char* text, const D2D1::ColorF color, ...) const
{
    char buffer[4096]{};
    RECT windowMetrics{};
    if (!GetWindowRect(m_hijackedHwnd, &windowMetrics))
        return;

    va_list arg_list;
    va_start(arg_list, text);
    vsnprintf_s(buffer, sizeof(buffer), text, arg_list);
    va_end(arg_list);

    wchar_t out[256]{};
    size_t length = 0;
    mbstowcs_s(&length, out, buffer, strlen(buffer));
    m_renderTarget->DrawTextA(out, length, m_format, D2D1::RectF(x, y, windowMetrics.right - windowMetrics.left, windowMetrics.bottom - windowMetrics.top), m_brush);
    m_brush->SetColor(color);
}

void Overlay::DrawLine(const int x, const int y, const int x2, const int y2, const D2D1::ColorF color, const float thickness) const
{
    m_renderTarget->DrawLine(D2D1::Point2F(x, y), D2D1::Point2F(x2, y2), m_brush, thickness);
    m_brush->SetColor(color);
}