Compare commits

..

22 Commits

Author SHA1 Message Date
hecomi
f2cad8bf73 check cursor texture size. 2016-11-02 14:59:15 +09:00
hecomi
ad4b49c177 add scenes in build. 2016-11-02 14:13:04 +09:00
hecomi
92b926bcde remove cursor GameObject from Monitor.prefab. 2016-11-02 14:12:56 +09:00
hecomi
3e90889266 add culling option and fix cursor problem on transparent material. 2016-11-02 11:25:43 +09:00
hecomi
6531fe1dd6 create class for duplication. 2016-11-02 02:57:27 +09:00
hecomi
0b62376ac5 fix shader position. 2016-11-02 02:57:08 +09:00
hecomi
9f611d5d34 use release build. 2016-11-01 09:26:45 +09:00
hecomi
dd4e27030d remove unused file. 2016-11-01 02:37:05 +09:00
hecomi
4350ac3953 cache pointer texture. 2016-11-01 02:20:46 +09:00
hecomi
6ba381f5d4 fetch cursor texture. 2016-11-01 02:15:25 +09:00
hecomi
9287ef2f2d simplify duplication code. 2016-10-30 21:20:31 +09:00
hecomi
dc6b8bc204 fix vertical screen bug. 2016-10-30 19:56:22 +09:00
hecomi
14cd67e0c6 check if monitor is vertical or not. 2016-10-30 19:35:23 +09:00
hecomi
73018f671a add error code getter. 2016-10-30 19:35:00 +09:00
hecomi
7a27d86eff consider aspect ratio in Multiple Monitor scene. 2016-10-30 19:03:25 +09:00
hecomi
99e1774388 add Monitor.aspect. 2016-10-30 19:03:06 +09:00
hecomi
e0408d4749 fix line endings. 2016-10-30 18:42:16 +09:00
hecomi
23ceef6502 modify invert method. 2016-10-30 18:41:43 +09:00
hecomi
4317fc06e3 update .gitignore. 2016-10-30 18:41:30 +09:00
hecomi
f8cb71b472 fix typo. 2016-10-28 20:15:10 +09:00
hecomi
f1a7c302bd update .gitignore. 2016-10-28 20:15:03 +09:00
hecomi
ec82c49d7f fix toggle scene. 2016-10-28 19:41:44 +09:00
27 changed files with 881 additions and 185 deletions

3
.gitignore vendored
View File

@@ -4,6 +4,7 @@
/[Oo]bj/
/[Bb]uild/
/[Ww]iki/
/[Mm]isc/
# Autogenerated VS/MD solution and project files
*.csproj
@@ -24,3 +25,5 @@ sysinfo.txt
# Others
.DS_Store
/Assets/AssetStoreTools*
/Assets/Extensions*

View File

@@ -3,17 +3,35 @@
public class MultipleMonitorCreator : MonoBehaviour
{
[SerializeField] GameObject monitorPrefab;
[SerializeField] Vector3 margin = new Vector3(20f, 0f, 0f);
[SerializeField] float scale = 10f;
[SerializeField] float margin = 1f;
[SerializeField] float ratio = 0.001f;
void Start()
{
var monitors = uDesktopDuplication.Manager.monitors;
var n = monitors.Count;
var totalWidth = 0f;
for (int i = 0 ; i < n; ++i) {
var go = Instantiate(monitorPrefab);
go.transform.position = transform.position + margin * (i - n / 2f + 0.5f);
go.name = "Monitor " + i;
var texture = go.GetComponent<uDesktopDuplication.Texture>();
texture.monitorId = i;
go.transform.localScale = new Vector3(texture.monitor.width * ratio, 1f, texture.monitor.height * ratio);
go.transform.SetParent(transform);
totalWidth += texture.monitor.width * ratio * scale;
}
totalWidth += margin * (n - 1);
var x = -totalWidth / 2;
for (int i = 0 ; i < n; ++i) {
var go = transform.FindChild("Monitor " + i);
var texture = go.GetComponent<uDesktopDuplication.Texture>();
var halfWidth = texture.monitor.width * ratio * scale / 2;
x += halfWidth;
go.transform.localPosition = new Vector3(x, 0f, 0f);
x += halfWidth + margin;
}
}
}

View File

@@ -1,9 +1,9 @@
using UnityEngine;
public class ToggleMonitors : MonoBehaviour
{
void Update()
{
using UnityEngine;
public class ToggleMonitors : MonoBehaviour
{
void Update()
{
if (Input.GetKeyDown(KeyCode.Tab)) {
var texture = GetComponent<uDesktopDuplication.Texture>();
if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) {
@@ -11,7 +11,7 @@ public class ToggleMonitors : MonoBehaviour
} else {
texture.monitorId++;
}
}
}
}
}
}
}

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 4fad0d0b5018b8649ac41af2fef8dbf2
folderAsset: yes
timeCreated: 1478011152
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,81 @@
Shader "uDesktopDuplication/Cursor"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
[KeywordEnum(Off, Front, Back)] _Cull("Culling", Int) = 2
}
SubShader
{
Tags { "RenderType"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "Queue" = "Transparent+1" }
Cull [_Cull]
ZWrite Off
Offset -0.01, -0.01
CGINCLUDE
#include "UnityCG.cginc"
#include "../../../Shaders/uDD_Common.cginc"
half4 _PositionScale;
#define _PointerX _PositionScale.x
#define _PointerY _PositionScale.y
#define _PointerWidth _PositionScale.z
#define _PointerHeight _PositionScale.w
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
};
sampler2D _MainTex;
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
fixed4 frag(v2f i) : SV_Target
{
uddInvertUV(i.uv);
i.uv.x = (i.uv.x - _PointerX) / _PointerWidth;
i.uv.y = (i.uv.y - _PointerY) / _PointerHeight;
fixed4 color = tex2D(_MainTex, i.uv);
color.a *= step(0, i.uv.x) * step(0, i.uv.y) * step(i.uv.x, 1) * step(i.uv.y, 1);
clip(color.a - 0.01);
uddToLinearIfNeeded(color.rgb);
return color;
}
ENDCG
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile ___ INVERT_X
#pragma multi_compile ___ INVERT_Y
#pragma multi_compile ___ VERTICAL
ENDCG
}
}
Fallback "Unlit/Texture"
}

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: d224cadaa1bfa2d46a6224ce2a1443c7
timeCreated: 1477847636
licenseType: Pro
ShaderImporter:
defaultTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,4 +1,5 @@
using UnityEngine;
using System.Collections.Generic;
namespace uDesktopDuplication
{
@@ -7,35 +8,104 @@ namespace uDesktopDuplication
RequireComponent(typeof(Texture))]
public class Cursor : MonoBehaviour
{
[SerializeField]
Transform cursor;
[SerializeField]
Vector2 modelScale = Vector2.one;
[SerializeField]
Vector2 offset = new Vector2(0.5f, 0.5f);
[SerializeField] Vector2 modelScale = Vector2.one;
private Texture texture_;
Vector3 worldPosition { get; set; }
private Texture uddTexture_;
private Monitor monitor { get { return uddTexture_.monitor; } }
private Texture2D pointerTexture_;
private Material cursorMaterial_;
private Dictionary<Vector2, Texture2D> pointerTextures_ = new Dictionary<Vector2, Texture2D>();
void OnEnable()
{
texture_ = GetComponent<Texture>();
uddTexture_ = GetComponent<Texture>();
}
void Start()
{
var clone = new GameObject(name + " Cursor");
clone.transform.SetParent(transform);
clone.transform.localPosition = Vector3.zero;
clone.transform.localRotation = Quaternion.identity;
clone.transform.localScale = Vector3.one;
var filter = clone.AddComponent<MeshFilter>();
filter.mesh = GetComponent<MeshFilter>().sharedMesh;
var renderer = clone.AddComponent<MeshRenderer>();
var shader = Shader.Find("uDesktopDuplication/Cursor");
cursorMaterial_ = new Material(shader);
renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
renderer.receiveShadows = false;
renderer.motionVectors = false;
renderer.material = cursorMaterial_;
}
void Update()
{
var monitor = texture_.monitor;
if (monitor.isPointerVisible) {
var x = (1f * monitor.pointerX / monitor.width - 0.5f) * modelScale.x;
var y = (0.5f - 1f * monitor.pointerY / monitor.height) * modelScale.y;
var iy = texture_.invertY ? +1 : -1;
var localPos = transform.right * x + iy * transform.up * y;
var worldPos = transform.TransformPoint(localPos);
worldPos += cursor.right * offset.x * cursor.localScale.x;
worldPos += -cursor.up * offset.y * cursor.localScale.y;
worldPos += -cursor.forward * 0.001f;
cursor.position = worldPos;
UpdatePosition();
UpdateTexture();
UpdateMaterial();
}
}
void UpdatePosition()
{
var x = (1f * monitor.pointerX / monitor.width - 0.5f) * modelScale.x;
var y = (1f * monitor.pointerY / monitor.height - 0.5f) * modelScale.y;
var iy = uddTexture_.invertY ? -1 : +1;
var localPos = transform.right * x + iy * transform.up * y;
worldPosition = transform.TransformPoint(localPos);
}
void UpdateTexture()
{
var w = monitor.pointerShapeWidth;
var h = monitor.pointerShapeHeight;
if (w == 0 || h == 0) return;
var scale = new Vector2(w, h);
if (!pointerTextures_.ContainsKey(scale)) {
var texture = new Texture2D(w, h, TextureFormat.BGRA32, false);
texture.wrapMode = TextureWrapMode.Clamp;
pointerTextures_.Add(scale, texture);
}
var pointerTexture = pointerTextures_[scale];
monitor.UpdatePointerTexture(pointerTexture.GetNativeTexturePtr());
cursorMaterial_.SetTexture("_MainTex", pointerTexture);
}
void UpdateMaterial()
{
if (uddTexture_.invertX) {
cursorMaterial_.EnableKeyword("INVERT_X");
} else {
cursorMaterial_.DisableKeyword("INVERT_X");
}
if (uddTexture_.invertY) {
cursorMaterial_.EnableKeyword("INVERT_Y");
} else {
cursorMaterial_.DisableKeyword("INVERT_Y");
}
if (monitor.isVertical) {
cursorMaterial_.EnableKeyword("VERTICAL");
} else {
cursorMaterial_.DisableKeyword("VERTICAL");
}
var x = (float)monitor.pointerX / monitor.width;
var y = (float)monitor.pointerY / monitor.height;
var w = (float)monitor.pointerShapeWidth / monitor.width;
var h = (float)monitor.pointerShapeHeight / monitor.height;
cursorMaterial_.SetVector("_PositionScale", new Vector4(x, y, w, h));
}
}
}

View File

@@ -5,6 +5,13 @@ using System.Runtime.InteropServices;
namespace uDesktopDuplication
{
public enum PointerShapeType
{
MonoChrome = 1,
Color = 2,
MaskedColor = 4,
}
public static class Lib
{
[DllImport("uDesktopDuplication")]
@@ -28,7 +35,21 @@ public static class Lib
[DllImport("uDesktopDuplication")]
public static extern int GetPointerY(int id);
[DllImport("uDesktopDuplication")]
public static extern int GetPointerShapeWidth(int id);
[DllImport("uDesktopDuplication")]
public static extern int GetPointerShapeHeight(int id);
[DllImport("uDesktopDuplication")]
public static extern int GetPointerShapePitch(int id);
[DllImport("uDesktopDuplication")]
public static extern PointerShapeType GetPointerShapeType(int id);
[DllImport("uDesktopDuplication")]
public static extern void UpdatePointerTexture(int id, System.IntPtr ptr);
[DllImport("uDesktopDuplication")]
public static extern int SetTexturePtr(int id, IntPtr ptr);
[DllImport("uDesktopDuplication")]
public static extern int GetErrorCode();
[DllImport("uDesktopDuplication")]
public static extern void GetErrorMessage(StringBuilder buf, int len);
public static string GetName(int id)
{
@@ -36,6 +57,13 @@ public static class Lib
GetName(id, buf, buf.Capacity);
return buf.ToString();
}
public static string GetErrorMessage()
{
var buf = new StringBuilder(64);
GetErrorMessage(buf, buf.Capacity);
return buf.ToString();
}
}
}

View File

@@ -40,6 +40,9 @@ public class Manager : MonoBehaviour
[SerializeField, Tooltip("Set Desktop Duplication API timeout (milliseconds).")]
int timeout = 0;
[SerializeField, Tooltip("Output logs given by the plugin.")]
bool outputDebugLog = false;
private Coroutine renderCoroutine_ = null;
void Awake()
@@ -78,6 +81,10 @@ public class Manager : MonoBehaviour
}
monitor.shouldBeUpdated = false;
}
if (outputDebugLog && Lib.GetErrorCode() != 0)
{
Debug.Log(Lib.GetErrorMessage());
}
}
}
}

View File

@@ -36,6 +36,21 @@ public class Monitor
get { return Lib.GetHeight(id); }
}
public float aspect
{
get { return (float)width / height; }
}
public bool isHorizontal
{
get { return width > height; }
}
public bool isVertical
{
get { return height > width; }
}
public bool isPointerVisible
{
get { return Lib.IsPointerVisible(id); }
@@ -51,6 +66,21 @@ public class Monitor
get { return Lib.GetPointerY(id); }
}
public int pointerShapeWidth
{
get { return Lib.GetPointerShapeWidth(id); }
}
public int pointerShapeHeight
{
get { return Lib.GetPointerShapeHeight(id); }
}
public PointerShapeType pointerShapeType
{
get { return Lib.GetPointerShapeType(id); }
}
public bool shouldBeUpdated
{
get;
@@ -63,7 +93,9 @@ public class Monitor
get
{
if (texture_ == null) {
texture_ = new Texture2D(width, height, TextureFormat.BGRA32, false);
var w = isHorizontal ? width : height;
var h = isHorizontal ? height : width;
texture_ = new Texture2D(w, h, TextureFormat.BGRA32, false);
Lib.SetTexturePtr(id, texture_.GetNativeTexturePtr());
}
return texture_;
@@ -74,6 +106,11 @@ public class Monitor
{
GL.IssuePluginEvent(Lib.GetRenderEventFunc(), id);
}
public void UpdatePointerTexture(System.IntPtr ptr)
{
Lib.UpdatePointerTexture(id, ptr);
}
}
}

View File

@@ -55,6 +55,12 @@ public class Texture : MonoBehaviour
} else {
material_.DisableKeyword("INVERT_Y");
}
if (monitor.isVertical) {
material_.EnableKeyword("VERTICAL");
} else {
material_.DisableKeyword("VERTICAL");
}
}
}

View File

@@ -6,20 +6,30 @@
inline void uddInvertUV(inout float2 uv)
{
#ifdef INVERT_X
uv.x *= -1.0;
uv.x = 1.0 - uv.x;
#endif
#ifdef INVERT_Y
uv.y *= -1.0;
uv.y = 1.0 - uv.y;
#endif
#ifdef VERTICAL
float2 tmp = uv;
uv.x = tmp.y;
uv.y = 1.0 - tmp.x;
#endif
}
inline void uddToLinearIfNeeded(inout fixed3 rgb)
{
if (!IsGammaSpace()) {
rgb = GammaToLinearSpace(rgb);
}
}
inline fixed4 uddGetTexture(sampler2D tex, float2 uv)
{
uddInvertUV(uv);
fixed4 c = tex2D(tex, uv);
if (!IsGammaSpace()) {
c.rgb = GammaToLinearSpace(c.rgb);
}
uddToLinearIfNeeded(c.rgb);
return c;
}

View File

@@ -7,11 +7,14 @@ Properties
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0, 1)) = 0.5
_Metallic ("Metallic", Range(0, 1)) = 0.0
[KeywordEnum(Off, Front, Back)] _Cull("Culling", Int) = 2
}
SubShader
{
Tags { "RenderType"="Opaque" }
Cull [_Cull]
CGPROGRAM
@@ -19,6 +22,7 @@ SubShader
#pragma surface surf Standard fullforwardshadows
#pragma multi_compile ___ INVERT_X
#pragma multi_compile ___ INVERT_Y
#pragma multi_compile ___ VERTICAL
#include "./uDD_Common.cginc"

View File

@@ -5,6 +5,7 @@ Properties
{
_Color ("Color", Color) = (1, 1, 1, 1)
_MainTex ("Texture", 2D) = "white" {}
[KeywordEnum(Off, Front, Back)] _Cull("Culling", Int) = 2
}
SubShader
@@ -12,6 +13,8 @@ SubShader
Tags { "RenderType"="Opaque" }
Cull [_Cull]
CGINCLUDE
#include "UnityCG.cginc"
@@ -55,6 +58,7 @@ Pass
#pragma fragment frag
#pragma multi_compile ___ INVERT_X
#pragma multi_compile ___ INVERT_Y
#pragma multi_compile ___ VERTICAL
ENDCG
}

View File

@@ -6,6 +6,7 @@ Properties
_Color ("Color", Color) = (1, 1, 1, 1)
_MainTex ("Texture", 2D) = "white" {}
_Mask ("Mask", Range(0, 1)) = 0.1
[KeywordEnum(Off, Front, Back)] _Cull("Culling", Int) = 2
}
SubShader
@@ -13,7 +14,8 @@ SubShader
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
ZWrite Off
Cull [_Cull]
ZWrite On
Blend SrcAlpha OneMinusSrcAlpha
CGINCLUDE
@@ -62,6 +64,7 @@ Pass
#pragma fragment frag
#pragma multi_compile ___ INVERT_X
#pragma multi_compile ___ INVERT_Y
#pragma multi_compile ___ VERTICAL
ENDCG
}

View File

@@ -5,6 +5,7 @@ Properties
{
_Color ("Color", Color) = (1, 1, 1, 1)
_MainTex ("Texture", 2D) = "white" {}
[KeywordEnum(Off, Front, Back)] _Cull("Culling", Int) = 2
}
SubShader
@@ -12,7 +13,8 @@ SubShader
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
ZWrite Off
Cull [_Cull]
ZWrite On
Blend SrcAlpha OneMinusSrcAlpha
CGINCLUDE
@@ -58,6 +60,7 @@ Pass
#pragma fragment frag
#pragma multi_compile ___ INVERT_X
#pragma multi_compile ___ INVERT_Y
#pragma multi_compile ___ VERTICAL
ENDCG
}

View File

@@ -0,0 +1,424 @@
#include <d3d11.h>
#include <dxgi1_2.h>
#include <vector>
#include <string>
#include "IUnityInterface.h"
#include "IUnityGraphicsD3D11.h"
#include "Duplication.h"
Duplication::Duplication(IUnityInterfaces* unity)
: unity_(unity)
, device_(unity->Get<IUnityGraphicsD3D11>()->GetDevice())
{
Initialize();
}
Duplication::~Duplication()
{
Finalize();
unity_ = nullptr;
}
void Duplication::Initialize()
{
Finalize();
// Get factory
IDXGIFactory1* factory;
CreateDXGIFactory1(__uuidof(IDXGIFactory1), reinterpret_cast<void**>(&factory));
// Check all display adapters
int id = 0;
IDXGIAdapter1* adapter;
for (int i = 0; (factory->EnumAdapters1(i, &adapter) != DXGI_ERROR_NOT_FOUND); ++i)
{
// Search the main monitor from all outputs
IDXGIOutput* output;
for (int j = 0; (adapter->EnumOutputs(j, &output) != DXGI_ERROR_NOT_FOUND); ++j)
{
Monitor monitor;
monitor.id = id++;
output->GetDesc(&monitor.outputDesc);
monitor.monitorInfo.cbSize = sizeof(MONITORINFOEX);
GetMonitorInfo(monitor.outputDesc.Monitor, &monitor.monitorInfo);
auto output1 = reinterpret_cast<IDXGIOutput1*>(output);
output1->DuplicateOutput(device_, &monitor.deskDupl);
output->Release();
monitors_.push_back(monitor);
}
adapter->Release();
}
factory->Release();
}
void Duplication::Finalize()
{
// Release all duplicaitons
for (auto& monitor : monitors_)
{
monitor.deskDupl->Release();
}
monitors_.clear();
}
bool Duplication::IsValidId(int id) const
{
return id >= 0 && id < monitors_.size();
}
void Duplication::OnRender(int id)
{
errorCode_ = 0;
errorMessage_ = "";
if (!IsValidId(id)) return;
auto& monitor = monitors_[id];
if (monitor.deskDupl == nullptr || monitor.texture == nullptr) return;
IDXGIResource* resource = nullptr;
DXGI_OUTDUPL_FRAME_INFO frameInfo;
errorCode_ = monitor.deskDupl->AcquireNextFrame(timeout_, &frameInfo, &resource);
if (FAILED(errorCode_))
{
if (errorCode_ == DXGI_ERROR_ACCESS_LOST)
{
Initialize();
errorMessage_ = "[IDXGIOutputDuplication::AcquireNextFrame()] Access lost.";
}
else
{
errorMessage_ = "[IDXGIOutputDuplication::AcquireNextFrame()] Maybe timeout.";
}
return;
}
ID3D11Texture2D* texture;
resource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&texture));
ID3D11DeviceContext* context;
device_->GetImmediateContext(&context);
context->CopyResource(monitor.texture, texture);
if (!UpdateMouse(frameInfo, monitor))
{
errorCode_ = -999;
errorMessage_ = "[UpdateMouse()] failed.";
}
resource->Release();
monitor.deskDupl->ReleaseFrame();
}
bool Duplication::UpdateMouse(const DXGI_OUTDUPL_FRAME_INFO& frameInfo, Monitor& monitor)
{
auto& pointer = monitor.pointer;
pointer.isVisible = frameInfo.PointerPosition.Visible != 0;
pointer.x = frameInfo.PointerPosition.Position.x;
pointer.y = frameInfo.PointerPosition.Position.y;
// Pointer type
const auto pointerType = pointer.shapeInfo.Type;
const bool isMono = pointerType == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME;
const bool isColorMask = pointerType == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR;
if (pointer.isVisible)
{
mouseMonitor_ = monitor.id;
}
if (mouseMonitor_ != monitor.id) {
return true;
}
// Increase the buffer size if needed
if (frameInfo.PointerShapeBufferSize > pointer.apiBufferSize)
{
if (pointer.apiBuffer) delete[] pointer.apiBuffer;
pointer.apiBuffer = new BYTE[frameInfo.PointerShapeBufferSize];
pointer.apiBufferSize = frameInfo.PointerShapeBufferSize;
}
if (!pointer.apiBuffer) return true;
// Get information about the mouse pointer if needed
if (frameInfo.PointerShapeBufferSize != 0)
{
UINT bufferSize;
monitor.deskDupl->GetFramePointerShape(
frameInfo.PointerShapeBufferSize,
reinterpret_cast<void*>(pointer.apiBuffer),
&bufferSize,
&pointer.shapeInfo);
}
// Size
const auto w = pointer.shapeInfo.Width;
const auto h = pointer.shapeInfo.Height / (isMono ? 2 : 1);
const auto p = pointer.shapeInfo.Pitch;
// Convert the buffer given by API into BGRA32
const auto bgraBufferSize = w * h * 4;
if (bgraBufferSize > pointer.bgra32BufferSize)
{
if (pointer.bgra32Buffer) delete[] pointer.bgra32Buffer;
pointer.bgra32Buffer = new BYTE[bgraBufferSize];
pointer.bgra32BufferSize = bgraBufferSize;
}
if (!pointer.bgra32Buffer) return true;
// If masked, copy the desktop image and merge it with masked image.
if (isMono || isColorMask)
{
HRESULT hr;
D3D11_TEXTURE2D_DESC desc;
desc.Width = w;
desc.Height = h;
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;
ID3D11DeviceContext* context;
device_->GetImmediateContext(&context);
ID3D11Texture2D* texture = nullptr;
hr = device_->CreateTexture2D(&desc, nullptr, &texture);
if (FAILED(hr))
{
return false;
}
D3D11_BOX box;
box.front = 0;
box.back = 1;
box.left = pointer.x;
box.top = pointer.y;
box.right = pointer.x + w;
box.bottom = pointer.y + h;
context->CopySubresourceRegion(texture, 0, 0, 0, 0, monitor.texture, 0, &box);
IDXGISurface* surface = nullptr;
hr = texture->QueryInterface(__uuidof(IDXGISurface), (void**)&surface);
texture->Release();
if (FAILED(hr))
{
return false;
}
DXGI_MAPPED_RECT mappedSurface;
hr = surface->Map(&mappedSurface, DXGI_MAP_READ);
if (FAILED(hr))
{
surface->Release();
return false;
}
// Finally, get the texture behind the mouse pointer.
const auto desktop32 = reinterpret_cast<UINT*>(mappedSurface.pBits);
const UINT desktopPitch = mappedSurface.Pitch / sizeof(UINT);
// Access RGBA values at the same time
auto output32 = reinterpret_cast<UINT*>(pointer.bgra32Buffer);
if (isMono)
{
for (UINT row = 0; row < h; ++row)
{
BYTE mask = 0x80;
for (UINT col = 0; col < w; ++col)
{
const int i = row * w + col;
const BYTE andMask = pointer.apiBuffer[col / 8 + row * p] & mask;
const BYTE xorMask = pointer.apiBuffer[col / 8 + (row + h) * p] & mask;
const UINT andMask32 = andMask ? 0xFFFFFFFF : 0xFF000000;
const UINT xorMask32 = xorMask ? 0x00FFFFFF : 0x00000000;
output32[i] = (desktop32[row * desktopPitch + col] & andMask32) ^ xorMask32;
mask = (mask == 0x01) ? 0x80 : (mask >> 1);
}
}
}
else // DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR
{
const auto buffer32 = reinterpret_cast<UINT*>(pointer.apiBuffer);
for (UINT row = 0; row < h; ++row)
{
for (UINT col = 0; col < w; ++col)
{
const int i = row * w + col;
const int j = row * p / sizeof(UINT) + col;
UINT mask = 0xFF000000 & buffer32[j];
if (mask)
{
output32[i] = (desktop32[row * desktopPitch + col] ^ buffer32[j]) | 0xFF000000;
}
else
{
output32[i] = buffer32[j] | 0xFF000000;
}
}
}
}
hr = surface->Unmap();
surface->Release();
if (FAILED(hr))
{
return false;
}
}
else // DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR
{
auto output32 = reinterpret_cast<UINT*>(pointer.bgra32Buffer);
const auto buffer32 = reinterpret_cast<UINT*>(pointer.apiBuffer);
for (UINT i = 0; i < w * h; ++i)
{
output32[i] = buffer32[i];
}
}
return true;
}
void Duplication::UpdatePointerTexture(int id, ID3D11Texture2D* ptr)
{
if (!IsValidId(id)) return;
const auto& pointer = monitors_[id].pointer;
if (!pointer.bgra32Buffer) return;
ID3D11DeviceContext* context;
device_->GetImmediateContext(&context);
context->UpdateSubresource(ptr, 0, nullptr, pointer.bgra32Buffer, pointer.shapeInfo.Width * 4, 0);
}
void Duplication::SetTimeout(int timeout)
{
timeout_ = timeout;
}
void Duplication::SetTexturePtr(int id, void* texture)
{
if (!IsValidId(id)) return;
monitors_[id].texture = reinterpret_cast<ID3D11Texture2D*>(texture);
}
int Duplication::GetMonitorCount() const
{
return static_cast<int>(monitors_.size());
}
void Duplication::GetName(int id, char* buf, int len) const
{
if (!IsValidId(id)) return;
strcpy_s(buf, len, monitors_[id].monitorInfo.szDevice);
}
bool Duplication::IsPrimary(int id) const
{
if (!IsValidId(id)) return false;
return monitors_[id].monitorInfo.dwFlags == MONITORINFOF_PRIMARY;
}
int Duplication::GetWidth(int id) const
{
if (!IsValidId(id)) return -1;
const auto rect = monitors_[id].monitorInfo.rcMonitor;
return rect.right - rect.left;
}
int Duplication::GetHeight(int id) const
{
if (!IsValidId(id)) return -1;
const auto rect = monitors_[id].monitorInfo.rcMonitor;
return rect.bottom - rect.top;
}
int Duplication::IsPointerVisible(int id) const
{
if (!IsValidId(id)) return false;
return monitors_[id].pointer.isVisible;
}
int Duplication::GetPointerX(int id) const
{
if (!IsValidId(id)) return -1;
return monitors_[id].pointer.x;
}
int Duplication::GetPointerY(int id) const
{
if (!IsValidId(id)) return -1;
return monitors_[id].pointer.y;
}
int Duplication::GetPointerShapeWidth(int id) const
{
if (!IsValidId(id)) return -1;
return monitors_[id].pointer.shapeInfo.Width;
}
int Duplication::GetPointerShapeHeight(int id) const
{
if (!IsValidId(id)) return -1;
const auto& info = monitors_[id].pointer.shapeInfo;
return (info.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME) ? info.Height / 2 : info.Height;
}
int Duplication::GetPointerShapePitch(int id) const
{
if (!IsValidId(id)) return -1;
return monitors_[id].pointer.shapeInfo.Pitch;
}
int Duplication::GetPointerShapeType(int id) const
{
if (!IsValidId(id)) return -1;
return monitors_[id].pointer.shapeInfo.Type;
}
int Duplication::GetErrorCode() const
{
return errorCode_;
}
void Duplication::GetErrorMessage(char* buf, int len) const
{
strcpy_s(buf, len, errorMessage_.c_str());
}

View File

@@ -0,0 +1,72 @@
#include <d3d11.h>
#include <dxgi1_2.h>
#include <vector>
#include <string>
struct IUnityInterfaces;
struct Pointer
{
bool isVisible = false;
int x = -1;
int y = -1;
BYTE* apiBuffer = nullptr;
UINT apiBufferSize = 0;
BYTE* bgra32Buffer = nullptr;
UINT bgra32BufferSize = 0;
DXGI_OUTDUPL_POINTER_SHAPE_INFO shapeInfo;
};
struct Monitor
{
int id = -1;
IDXGIOutputDuplication* deskDupl = nullptr;
ID3D11Texture2D* texture = nullptr;
DXGI_OUTPUT_DESC outputDesc;
MONITORINFOEX monitorInfo;
Pointer pointer;
};
class Duplication
{
public:
explicit Duplication(IUnityInterfaces* unity);
~Duplication();
void OnRender(int id);
public:
void UpdatePointerTexture(int id, ID3D11Texture2D* ptr);
void SetTimeout(int timeout);
void SetTexturePtr(int id, void* texture);
public:
int GetMonitorCount() const;
void GetName(int id, char* buf, int len) const;
bool IsPrimary(int id) const;
int GetWidth(int id) const;
int GetHeight(int id) const;
int IsPointerVisible(int id) const;
int GetPointerX(int id) const;
int GetPointerY(int id) const;
int GetPointerShapeWidth(int id) const;
int GetPointerShapeHeight(int id) const;
int GetPointerShapePitch(int id) const;
int GetPointerShapeType(int id) const;
int GetErrorCode() const;
void GetErrorMessage(char* buf, int len) const;
private:
void Initialize();
void Finalize();
bool UpdateMouse(const DXGI_OUTDUPL_FRAME_INFO& frameInfo, Monitor& monitor);
bool IsValidId(int id) const;
IUnityInterfaces* unity_ = nullptr;
ID3D11Device* device_ = nullptr;
int mouseMonitor_ = 0;
int timeout_ = 10;
HRESULT errorCode_ = 0;
std::string errorMessage_ = "";
std::vector<Monitor> monitors_;
};

View File

@@ -2,158 +2,36 @@
#include <dxgi1_2.h>
#include <vector>
#include <string>
#include <memory>
#include "IUnityInterface.h"
#include "IUnityGraphics.h"
#include "IUnityGraphicsD3D11.h"
#include "Duplication.h"
#pragma comment(lib, "dxgi.lib")
namespace
{
struct Monitor
{
IDXGIOutputDuplication* output = nullptr;
ID3D11Texture2D* texture = nullptr;
std::string name = "";
bool isPrimary = false;
bool isPointerVisible = false;
int pointerX = -1;
int pointerY = -1;
int width = -1;
int height = -1;
};
IUnityInterfaces* g_unity = nullptr;
int g_timeout = 10;
std::vector<Monitor> g_monitors;
std::unique_ptr<Duplication> g_dupl;
}
extern "C"
{
void FinalizeDuplication()
{
for (auto& monitor : g_monitors)
{
monitor.output->Release();
}
g_monitors.clear();
}
void InitializeDuplication()
{
FinalizeDuplication();
IDXGIFactory1* factory;
CreateDXGIFactory1(__uuidof(IDXGIFactory1), reinterpret_cast<void**>(&factory));
// Check all display adapters.
IDXGIAdapter1* adapter;
for (int i = 0; (factory->EnumAdapters1(i, &adapter) != DXGI_ERROR_NOT_FOUND); ++i)
{
// Search the main monitor from all outputs.
IDXGIOutput* output;
for (int j = 0; (adapter->EnumOutputs(j, &output) != DXGI_ERROR_NOT_FOUND); ++j)
{
DXGI_OUTPUT_DESC outputDesc;
output->GetDesc(&outputDesc);
MONITORINFOEX monitorInfo;
monitorInfo.cbSize = sizeof(MONITORINFOEX);
GetMonitorInfo(outputDesc.Monitor, &monitorInfo);
Monitor monitor;
monitor.name = monitorInfo.szDevice;
monitor.isPrimary = (monitorInfo.dwFlags == MONITORINFOF_PRIMARY);
monitor.width = monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left;
monitor.height = monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top;
auto device = g_unity->Get<IUnityGraphicsD3D11>()->GetDevice();
IDXGIOutput1* output1;
output1 = reinterpret_cast<IDXGIOutput1*>(output);
output1->DuplicateOutput(device, &monitor.output);
g_monitors.push_back(monitor);
output->Release();
}
adapter->Release();
}
factory->Release();
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API UnityPluginLoad(IUnityInterfaces* unityInterfaces)
{
g_unity = unityInterfaces;
InitializeDuplication();
g_dupl = std::make_unique<Duplication>(unityInterfaces);
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API UnityPluginUnload()
{
g_unity = nullptr;
FinalizeDuplication();
}
bool DoesMonitorExist(int id)
{
return id >= 0 && id < g_monitors.size();
g_dupl.reset();
}
void UNITY_INTERFACE_API OnRenderEvent(int id)
{
if (!DoesMonitorExist(id)) return;
auto& monitor = g_monitors[id];
if (monitor.output == nullptr || monitor.texture == nullptr) return;
HRESULT hr;
IDXGIResource* resource = nullptr;
DXGI_OUTDUPL_FRAME_INFO frameInfo;
hr = monitor.output->AcquireNextFrame(g_timeout, &frameInfo, &resource);
switch (hr)
{
case S_OK:
{
break;
}
case DXGI_ERROR_ACCESS_LOST:
{
resource->Release();
InitializeDuplication();
return;
}
default:
{
return;
}
}
monitor.isPointerVisible = frameInfo.PointerPosition.Visible == TRUE;
monitor.pointerX = frameInfo.PointerPosition.Position.x;
monitor.pointerY = frameInfo.PointerPosition.Position.y;
ID3D11Texture2D* texture;
hr = resource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&texture));
if (hr != S_OK)
{
resource->Release();
monitor.output->ReleaseFrame();
return;
}
ID3D11DeviceContext* context;
auto device = g_unity->Get<IUnityGraphicsD3D11>()->GetDevice();
device->GetImmediateContext(&context);
context->CopyResource(monitor.texture, texture);
resource->Release();
texture->Release();
monitor.output->ReleaseFrame();
g_dupl->OnRender(id);
}
UNITY_INTERFACE_EXPORT UnityRenderingEvent UNITY_INTERFACE_API GetRenderEventFunc()
@@ -163,59 +41,87 @@ extern "C"
UNITY_INTERFACE_EXPORT size_t UNITY_INTERFACE_API GetMonitorCount()
{
return g_monitors.size();
return g_dupl->GetMonitorCount();
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API SetTimeout(int timeout)
{
g_timeout = timeout;
g_dupl->SetTimeout(timeout);
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API GetName(int id, char* buf, int len)
{
if (!DoesMonitorExist(id)) return;
strcpy_s(buf, len, g_monitors[id].name.c_str());
g_dupl->GetName(id, buf, len);
}
UNITY_INTERFACE_EXPORT bool UNITY_INTERFACE_API IsPrimary(int id)
{
if (!DoesMonitorExist(id)) return false;
return g_monitors[id].isPrimary;
return g_dupl->IsPrimary(id);
}
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetWidth(int id)
{
if (!DoesMonitorExist(id)) return -1;
return g_monitors[id].width;
return g_dupl->GetWidth(id);
}
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetHeight(int id)
{
if (!DoesMonitorExist(id)) return -1;
return g_monitors[id].height;
return g_dupl->GetHeight(id);
}
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API IsPointerVisible(int id)
{
if (!DoesMonitorExist(id)) return false;
return g_monitors[id].isPointerVisible;
return g_dupl->IsPointerVisible(id);
}
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetPointerX(int id)
{
if (!DoesMonitorExist(id)) return -1;
return g_monitors[id].pointerX;
return g_dupl->GetPointerX(id);
}
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetPointerY(int id)
{
if (!DoesMonitorExist(id)) return -1;
return g_monitors[id].pointerY;
return g_dupl->GetPointerY(id);
}
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetPointerShapeWidth(int id)
{
return g_dupl->GetPointerShapeWidth(id);
}
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetPointerShapeHeight(int id)
{
return g_dupl->GetPointerShapeHeight(id);
}
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetPointerShapePitch(int id)
{
return g_dupl->GetPointerShapePitch(id);
}
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetPointerShapeType(int id)
{
return g_dupl->GetPointerShapeType(id);
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API UpdatePointerTexture(int id, ID3D11Texture2D* ptr)
{
g_dupl->UpdatePointerTexture(id, ptr);
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API SetTexturePtr(int id, void* texture)
{
if (!DoesMonitorExist(id)) return;
g_monitors[id].texture = reinterpret_cast<ID3D11Texture2D*>(texture);
g_dupl->SetTexturePtr(id, texture);
}
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetErrorCode()
{
return g_dupl->GetErrorCode();
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API GetErrorMessage(char* buf, int len)
{
g_dupl->GetErrorMessage(buf, len);
}
}

View File

@@ -122,9 +122,11 @@
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="Duplication.cpp" />
<ClCompile Include="main.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="Duplication.h" />
<ClInclude Include="IUnityGraphics.h" />
<ClInclude Include="IUnityGraphicsD3D11.h" />
<ClInclude Include="IUnityInterface.h" />

View File

@@ -1,7 +1,7 @@
uDesktopDuplication
===================
**uDesktopDuplication** is a Unity asset to use the realtime screen capture as `Texture2D` using Desktop Duplication API.
**uDesktopDuplication** is an Unity asset to use the realtime screen capture as `Texture2D` using Desktop Duplication API.
ScreenShot