Files
uDesktopDuplication/Plugins/uDesktopDuplication/uDesktopDuplication/Monitor.cpp
T

470 lines
12 KiB
C++

#include <d3d11.h>
#include <ShellScalingAPI.h>
#include <queue>
#include "Monitor.h"
#include "Duplicator.h"
#include "Debug.h"
#include "Cursor.h"
#include "MonitorManager.h"
#include "Device.h"
using namespace Microsoft::WRL;
Monitor::Monitor(int id)
: id_(id)
{
ZeroMemory(&monitorInfo_, sizeof(monitorInfo_));
}
Monitor::~Monitor()
{
duplicator_->Stop();
}
void Monitor::Initialize(
const ComPtr<IDXGIAdapter> &adapter,
const ComPtr<IDXGIOutput> &output
)
{
adapter_ = adapter;
output_ = output;
if (FAILED(output->GetDesc(&outputDesc_)))
{
Debug::Error("Monitor::Initialize() => IDXGIOutput::GetDesc() failed.");
return;
}
monitorInfo_.cbSize = sizeof(MONITORINFOEX);
if (!GetMonitorInfo(outputDesc_.Monitor, &monitorInfo_))
{
Debug::Error("Monitor::Initialize() => GetMonitorInfo() failed.");
return;
}
else
{
const auto rect = monitorInfo_.rcMonitor;
width_ = rect.right - rect.left;
height_ = rect.bottom - rect.top;
}
if (FAILED(GetDpiForMonitor(outputDesc_.Monitor, MDT_RAW_DPI, &dpiX_, &dpiY_)))
{
Debug::Error("Monitor::Initialize() => GetDpiForMonitor() failed.");
// DPI is set as -1, so the application has to use the appropriate value.
}
duplicator_ = std::make_shared<Duplicator>(this);
}
void Monitor::Finalize()
{
StopCapture();
}
void Monitor::Render()
{
const auto& frame = duplicator_->GetLastFrame();
const auto texture = frame.texture;
const auto frameInfo = frame.info; // clone
if (!texture) return;
// Get texture
if (unityTexture_)
{
D3D11_TEXTURE2D_DESC srcDesc, dstDesc;
texture->GetDesc(&srcDesc);
unityTexture_->GetDesc(&dstDesc);
if (srcDesc.Width != dstDesc.Width ||
srcDesc.Height != dstDesc.Height)
{
Debug::Error("Monitor::Render() => Texture sizes are defferent.");
Debug::Error(" Source : (", srcDesc.Width, ", ", srcDesc.Height, ")");
Debug::Error(" Dest : (", dstDesc.Width, ", ", dstDesc.Height, ")");
//Debug::Log(" => Try modifying width/height using reported value from DDA.");
//width_ = srcDesc.Width;
//height_ = srcDesc.Height;
//state_ = MonitorState::TextureSizeInconsistent;
//SendMessageToUnity(Message::TextureSizeChanged);
return;
}
else
{
ComPtr<ID3D11DeviceContext> context;
GetDevice()->GetImmediateContext(&context);
context->CopyResource(unityTexture_, texture.Get());
}
}
if (UseGetPixels())
{
CopyTextureFromGpuToCpu(unityTexture_);
}
hasBeenUpdated_ = true;
}
void Monitor::StartCapture()
{
if (duplicator_->GetState() == DuplicatorState::Ready)
{
duplicator_->Start();
}
}
void Monitor::StopCapture()
{
duplicator_->Stop();
}
int Monitor::GetId() const
{
return id_;
}
ComPtr<struct IDXGIAdapter> Monitor::GetAdapter()
{
return adapter_;
}
ComPtr<struct IDXGIOutput> Monitor::GetOutput()
{
return output_;
}
DuplicatorState Monitor::GetDuplicatorState() const
{
return duplicator_->GetState();
}
void Monitor::SetUnityTexture(ID3D11Texture2D* texture)
{
unityTexture_ = texture;
}
ID3D11Texture2D* Monitor::GetUnityTexture() const
{
return unityTexture_;
}
ComPtr<IDXGIOutputDuplication> Monitor::GetDeskDupl()
{
return duplicator_->GetDuplication();
}
void Monitor::GetName(char* buf, int len) const
{
strcpy_s(buf, len, monitorInfo_.szDevice);
}
bool Monitor::IsPrimary() const
{
return monitorInfo_.dwFlags == MONITORINFOF_PRIMARY;
}
bool Monitor::HasBeenUpdated() const
{
return hasBeenUpdated_;
}
int Monitor::GetLeft() const
{
return static_cast<int>(outputDesc_.DesktopCoordinates.left);
}
int Monitor::GetRight() const
{
return static_cast<int>(outputDesc_.DesktopCoordinates.right);
}
int Monitor::GetTop() const
{
return static_cast<int>(outputDesc_.DesktopCoordinates.top);
}
int Monitor::GetBottom() const
{
return static_cast<int>(outputDesc_.DesktopCoordinates.bottom);
}
int Monitor::GetRotation() const
{
return static_cast<int>(outputDesc_.Rotation);
}
int Monitor::GetDpiX() const
{
return static_cast<int>(dpiX_);
}
int Monitor::GetDpiY() const
{
return static_cast<int>(dpiY_);
}
int Monitor::GetWidth() const
{
return width_;
}
int Monitor::GetHeight() const
{
return height_;
}
int Monitor::GetMoveRectCount() const
{
const auto& metaData = duplicator_->GetLastFrame().metaData;
return metaData.moveRectSize / sizeof(DXGI_OUTDUPL_MOVE_RECT);
}
DXGI_OUTDUPL_MOVE_RECT* Monitor::GetMoveRects() const
{
const auto& metaData = duplicator_->GetLastFrame().metaData;
return metaData.buffer.As<DXGI_OUTDUPL_MOVE_RECT>();
}
int Monitor::GetDirtyRectCount() const
{
const auto& metaData = duplicator_->GetLastFrame().metaData;
return metaData.dirtyRectSize / sizeof(RECT);
}
RECT* Monitor::GetDirtyRects() const
{
const auto& metaData = duplicator_->GetLastFrame().metaData;
return metaData.buffer.As<RECT>(metaData.moveRectSize);
}
void Monitor::UseGetPixels(bool use)
{
useGetPixels_ = use;
}
bool Monitor::UseGetPixels() const
{
return useGetPixels_;
}
void Monitor::CopyTextureFromGpuToCpu(ID3D11Texture2D* texture)
{
const auto monitorRot = static_cast<DXGI_MODE_ROTATION>(GetRotation());
const auto monitorWidth = GetWidth();
const auto monitorHeight = GetHeight();
const auto isVertical =
monitorRot == DXGI_MODE_ROTATION_ROTATE90 ||
monitorRot == DXGI_MODE_ROTATION_ROTATE270;
const auto desktopImageWidth = !isVertical ? monitorWidth : monitorHeight;
const auto desktopImageHeight = !isVertical ? monitorHeight : monitorWidth;
if (!textureForGetPixels_)
{
D3D11_TEXTURE2D_DESC desc;
desc.Width = desktopImageWidth;
desc.Height = desktopImageHeight;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Usage = D3D11_USAGE_STAGING;
desc.BindFlags = 0;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.MiscFlags = 0;
if (FAILED(GetDevice()->CreateTexture2D(&desc, nullptr, &textureForGetPixels_)))
{
Debug::Error("Monitor::CopyTextureFromGpuToCpu() => GetDevice()->CreateTexture2D() failed.");
return;
}
}
{
ComPtr<ID3D11DeviceContext> context;
GetDevice()->GetImmediateContext(&context);
context->CopyResource(textureForGetPixels_.Get(), texture);
}
ComPtr<IDXGISurface> surface;
if (FAILED(textureForGetPixels_.As(&surface)))
{
Debug::Error("Monitor::CopyTextureFromGpuToCpu() => texture.As() failed.");
return;
}
DXGI_MAPPED_RECT mappedSurface;
if (FAILED(surface->Map(&mappedSurface, DXGI_MAP_READ)))
{
Debug::Error("Monitor::CopyTextureFromGpuToCpu() => surface->Map() failed.");
return;
}
const UINT size = desktopImageWidth * desktopImageHeight * sizeof(UINT);
bufferForGetPixels_.ExpandIfNeeded(size);
std::memcpy(bufferForGetPixels_.Get(), mappedSurface.pBits, size);
if (FAILED(surface->Unmap()))
{
Debug::Error("Monitor::CopyTextureFromGpuToCpu() => surface->Unmap() failed.");
return;
}
}
bool Monitor::GetPixels(BYTE* output, int x, int y, int width, int height)
{
if (!UseGetPixels())
{
Debug::Error("Monitor::GetPixels() => UseGetPixels(true) must have been called when you want to use GetPixels().");
return false;
}
if (!bufferForGetPixels_)
{
Debug::Error("Monitor::GetPixels() => CopyTextureFromGpuToCpu() has not been called yet.");
return false;
}
const auto monitorRot = static_cast<DXGI_MODE_ROTATION>(GetRotation());
const auto monitorWidth = GetWidth();
const auto monitorHeight = GetHeight();
const auto isVertical =
monitorRot == DXGI_MODE_ROTATION_ROTATE90 ||
monitorRot == DXGI_MODE_ROTATION_ROTATE270;
const auto desktopImageWidth = !isVertical ? monitorWidth : monitorHeight;
const auto desktopImageHeight = !isVertical ? monitorHeight : monitorWidth;
// check area in destop coorinates.
int left, top, right, bottom;
switch (monitorRot)
{
case DXGI_MODE_ROTATION_ROTATE90:
{
left = y;
top = monitorWidth - x - width;
right = y + width;
bottom = monitorWidth - x;
break;
}
case DXGI_MODE_ROTATION_ROTATE180:
{
left = monitorWidth - x - width;
top = monitorHeight - y - height;
right = monitorWidth - x;
bottom = monitorHeight - y;
break;
}
case DXGI_MODE_ROTATION_ROTATE270:
{
left = monitorHeight - y - height;
top = x;
right = monitorHeight - y;
bottom = x + width;
break;
}
case DXGI_MODE_ROTATION_IDENTITY:
case DXGI_MODE_ROTATION_UNSPECIFIED:
default:
{
left = x;
top = y;
right = x + width;
bottom = y + height;
break;
}
}
if (left < 0 ||
top < 0 ||
right >= desktopImageWidth ||
bottom >= desktopImageHeight)
{
Debug::Error("Monitor::GetPixels() => is out of area.");
Debug::Error(
" ",
"(", left, ", ", top, ")",
" ~ (", right, ", ", bottom, ") > ",
"(", desktopImageWidth, ", ", desktopImageHeight, ")");
return false;
}
for (int row = 0; row < height; ++row)
{
for (int col = 0; col < width; ++col)
{
int inRow, inCol;
switch (monitorRot)
{
case DXGI_MODE_ROTATION_ROTATE90:
inCol = left + row;
inRow = bottom - 1 - col;
break;
case DXGI_MODE_ROTATION_ROTATE180:
inCol = right - 1 - col;
inRow = bottom - 1 - row;
break;
case DXGI_MODE_ROTATION_ROTATE270:
inCol = right - 1 - row;
inRow = top + col;
break;
case DXGI_MODE_ROTATION_IDENTITY:
case DXGI_MODE_ROTATION_UNSPECIFIED:
default:
inCol = left + col;
inRow = top + row;
break;
}
const auto inIndex = 4 * (inRow * desktopImageWidth + inCol);
const auto outRow = height - 1 - row;
const auto outCol = col;
const auto outIndex = 4 * (outRow * width + outCol);
// BGRA -> RGBA
output[outIndex + 0] = bufferForGetPixels_[inIndex + 2];
output[outIndex + 1] = bufferForGetPixels_[inIndex + 1];
output[outIndex + 2] = bufferForGetPixels_[inIndex + 0];
output[outIndex + 3] = bufferForGetPixels_[inIndex + 3];
}
}
return true;
}