Change the way to get pixels and finish the implementation of the APIs 🎉

This commit is contained in:
hecomi
2016-12-01 22:29:52 +09:00
parent 7e3df8ec2e
commit 583af4ff46
15 changed files with 185 additions and 159 deletions
@@ -162,16 +162,16 @@ void Monitor::Render(UINT timeout)
return;
}
ID3D11Texture2D* texture;
if (FAILED(resource.CopyTo(&texture)))
{
Debug::Error("Monitor::Render() => resource.As() failed.");
return;
}
// Get texture
if (unityTexture_)
{
ID3D11Texture2D* texture;
if (FAILED(resource.CopyTo(&texture)))
{
Debug::Error("Monitor::Render() => resource.As() failed.");
return;
}
D3D11_TEXTURE2D_DESC srcDesc, dstDesc;
texture->GetDesc(&srcDesc);
unityTexture_->GetDesc(&dstDesc);
@@ -193,22 +193,6 @@ void Monitor::Render(UINT timeout)
GetDevice()->GetImmediateContext(&context);
context->CopyResource(unityTexture_, texture);
}
{
std::lock_guard<std::mutex> lock(mutex_);
ComPtr<ID3D11DeviceContext> context;
if (!ddaTexture_)
{
if (FAILED(GetDevice()->CreateTexture2D(&dstDesc, nullptr, &ddaTexture_)))
{
Debug::Error("Monitor::Render() => GetDevice()->CreateTexture2D() failed.");
return;
}
}
GetDevice()->GetImmediateContext(&context);
context->CopyResource(ddaTexture_.Get(), texture);
}
}
UpdateMetadata(frameInfo);
@@ -223,6 +207,11 @@ void Monitor::Render(UINT timeout)
UpdateCursor(frameInfo);
}
if (UseGetPixels())
{
CopyTextureFromGpuToCpu(texture);
}
hr = deskDupl_->ReleaseFrame();
if (FAILED(hr))
{
@@ -399,6 +388,12 @@ bool Monitor::IsPrimary() const
}
bool Monitor::HasBeenUpdated() const
{
return hasBeenUpdated_;
}
int Monitor::GetLeft() const
{
return static_cast<int>(outputDesc_.DesktopCoordinates.left);
@@ -477,17 +472,94 @@ RECT* Monitor::GetDirtyRects() const
}
bool Monitor::GetPixels(UINT* ptr, int x, int y, int width, int height)
void Monitor::UseGetPixels(bool use)
{
if (!GetMonitorManager()->UseGetPixels())
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 (!ddaTexture_)
if (!bufferForGetPixels_)
{
Debug::Error("Monitor::GetPixels() => texture is not set.");
Debug::Error("Monitor::GetPixels() => CopyTextureFromGpuToCpu() has not been called yet.");
return false;
}
@@ -500,6 +572,7 @@ bool Monitor::GetPixels(UINT* ptr, int x, int y, int width, int height)
const auto desktopImageWidth = !isVertical ? monitorWidth : monitorHeight;
const auto desktopImageHeight = !isVertical ? monitorHeight : monitorWidth;
// check area in destop coorinates.
int left, top, right, bottom;
switch (monitorRot)
@@ -554,75 +627,46 @@ bool Monitor::GetPixels(UINT* ptr, int x, int y, int width, int height)
return false;
}
D3D11_BOX box;
box.left = left;
box.top = top;
box.front = 0;
box.right = right;
box.bottom = bottom;
box.back = 1;
// Create texture for capturing desktop image
ComPtr<ID3D11Texture2D> texture;
D3D11_TEXTURE2D_DESC desc;
desc.Width = width;
desc.Height = height;
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, &texture)))
{
Debug::Error("Monitor::GetPixels() => GetDevice()->CreateTexture2D() failed.");
return false;
}
std::lock_guard<std::mutex> lock(mutex_);
ComPtr<ID3D11DeviceContext> context;
GetDevice()->GetImmediateContext(&context);
context->CopySubresourceRegion(texture.Get(), 0, 0, 0, 0, ddaTexture_.Get(), 0, &box);
ComPtr<IDXGISurface> surface;
if (FAILED(texture.As(&surface)))
{
Debug::Error("Monitor::GetPixels() => texture.As() failed.");
return false;
}
DXGI_MAPPED_RECT mappedSurface;
if (FAILED(surface->Map(&mappedSurface, DXGI_MAP_READ)))
{
Debug::Error("Monitor::GetPixels() => surface->Map() failed.");
return false;
}
const auto colors = reinterpret_cast<UINT*>(mappedSurface.pBits);
std::memcpy(ptr, colors, width * height * sizeof(UINT));
std::reverse(&ptr[0], &ptr[width * height - 1]);
for (int row = 0; row < height; ++row)
{
const auto start = width * row;
const auto end = width * (row + 1) - 1;
std::reverse(&ptr[start], &ptr[end]);
}
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);
if (FAILED(surface->Unmap()))
{
Debug::Error("Monitor::GetPixels() => surface->Unmap() failed.");
return false;
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;
}
bool Monitor::HasBeenUpdated() const
{
return hasBeenUpdated_;
}
@@ -38,6 +38,7 @@ public:
ID3D11Texture2D* GetUnityTexture() const;
void GetName(char* buf, int len) const;
bool IsPrimary() const;
bool HasBeenUpdated() const;
int GetLeft() const;
int GetRight() const;
int GetTop() const;
@@ -52,27 +53,30 @@ public:
DXGI_OUTDUPL_MOVE_RECT* GetMoveRects() const;
int GetDirtyRectCount() const;
RECT* GetDirtyRects() const;
bool GetPixels(UINT* ptr, int x, int y, int width, int height);
bool HasBeenUpdated() const;
void UseGetPixels(bool use);
bool UseGetPixels() const;
bool GetPixels(BYTE* output, int x, int y, int width, int height);
private:
void UpdateCursor(const DXGI_OUTDUPL_FRAME_INFO& frameInfo);
void UpdateMetadata(const DXGI_OUTDUPL_FRAME_INFO& frameInfo);
void UpdateMoveRects(const DXGI_OUTDUPL_FRAME_INFO& frameInfo);
void UpdateDirtyRects(const DXGI_OUTDUPL_FRAME_INFO& frameInfo);
void CopyTextureFromGpuToCpu(ID3D11Texture2D* texture);
int id_ = -1;
UINT dpiX_ = -1, dpiY_ = -1;
int width_ = -1, height_ = -1;
bool hasBeenUpdated_ = false;
bool useGetPixels_ = false;
State state_ = State::NotSet;
IDXGIOutputDuplication* deskDupl_ = nullptr;
ID3D11Texture2D* unityTexture_ = nullptr;
Microsoft::WRL::ComPtr<ID3D11Texture2D> ddaTexture_;
DXGI_OUTPUT_DESC outputDesc_;
MONITORINFOEX monitorInfo_;
Buffer<BYTE> metaData_;
Microsoft::WRL::ComPtr<ID3D11Texture2D> textureForGetPixels_;
Buffer<BYTE> bufferForGetPixels_;
UINT moveRectSize_ = 0;
UINT dirtyRectSize_ = 0;;
mutable std::mutex mutex_;
};
@@ -140,18 +140,6 @@ int MonitorManager::GetTimeout() const
}
void MonitorManager::UseGetPixels(bool use)
{
useGetPixels_ = use;
}
bool MonitorManager::UseGetPixels() const
{
return useGetPixels_;
}
int MonitorManager::GetMonitorCount() const
{
return static_cast<int>(monitors_.size());
@@ -32,8 +32,6 @@ public:
void Update();
void SetTimeout(int timeout);
int GetTimeout() const;
void UseGetPixels(bool use);
bool UseGetPixels() const;
// Getters from Unity
public:
@@ -43,7 +41,7 @@ public:
private:
int timeout_ = 10;
bool useGetPixels_ = false;
bool enableTextureCopyFromGpuToCpu_ = false;
std::vector<std::shared_ptr<Monitor>> monitors_;
std::shared_ptr<Cursor> cursor_ = std::make_shared<Cursor>();
int cursorMonitorId_ = -1;
@@ -21,6 +21,7 @@
IUnityInterfaces* g_unity = nullptr;
std::unique_ptr<MonitorManager> g_manager;
std::queue<Message> g_messages;
ID3D11DeviceContext* g_deviceContextForMainThread = nullptr;
extern "C"
@@ -393,12 +394,12 @@ extern "C"
return nullptr;
}
UNITY_INTERFACE_EXPORT bool UNITY_INTERFACE_API GetPixels(int id, UINT* ptr, int x, int y, int width, int height)
UNITY_INTERFACE_EXPORT bool UNITY_INTERFACE_API GetPixels(int id, BYTE* output, int x, int y, int width, int height)
{
if (!g_manager) return nullptr;
if (auto monitor = g_manager->GetMonitor(id))
{
return monitor->GetPixels(ptr, x, y, width, height);
return monitor->GetPixels(output, x, y, width, height);
}
return false;
}
@@ -413,9 +414,12 @@ extern "C"
return false;
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API UseGetPixels(bool use)
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API UseGetPixels(int id, bool use)
{
if (!g_manager) return;
g_manager->UseGetPixels(use);
if (auto monitor = g_manager->GetMonitor(id))
{
return monitor->UseGetPixels(use);
}
}
}