Compare commits

..

68 Commits

Author SHA1 Message Date
hecomi
808a71ed6d remove unnecessary codes. 2017-01-08 00:09:24 +09:00
hecomi
da072aa9f3 draw cursor texture to unity texture instead of cached texture. 2017-01-08 00:02:41 +09:00
hecomi
7237d283ef add error messages. 2017-01-07 23:10:13 +09:00
hecomi
4a28b5f341 tweak shared texture. 2017-01-07 23:02:48 +09:00
hecomi
10091be4b3 lock shared texture. 2017-01-07 15:28:13 +09:00
hecomi
c41fe42aa3 check frame id. 2017-01-07 13:48:38 +09:00
hecomi
c0e1967e2e modify duplicator state handling. 2017-01-07 13:26:28 +09:00
hecomi
f5a8a83c7e change member name. 2017-01-07 13:18:11 +09:00
hecomi
c251de3a30 add debug timer macro. 2017-01-07 01:15:33 +09:00
hecomi
b0877a49ed tweak code. 2017-01-07 00:53:37 +09:00
hecomi
68d9e30816 allow buffer copy. 2017-01-06 18:44:43 +09:00
hecomi
b8ae1d6c4a remove unused code. 2017-01-06 18:11:18 +09:00
hecomi
15624dc0f4 add frame rate setter and calc more accurate sleep duration. 2017-01-06 18:07:35 +09:00
hecomi
06d4edbcb0 add timeout to avoid unnecessarily high cpu usage. 2017-01-06 17:53:16 +09:00
hecomi
9ee2d35175 replace code to copy texture with old one #11 2017-01-06 17:32:10 +09:00
hecomi
0634993bb0 prepare multiple texture buffer for each device to avoid flickering cursor #11 2017-01-06 17:29:09 +09:00
hecomi
6bd892bc4b remove unused code. 2017-01-06 16:34:02 +09:00
hecomi
2a631a0a89 add mutex for debug logs. 2017-01-06 16:17:08 +09:00
hecomi
0833b965e9 rename GetFrame() to GetLastFrame(). 2017-01-06 16:14:10 +09:00
hecomi
12726d85dc fix cursor bugs #11 2017-01-06 13:52:23 +09:00
hecomi
29920b6b94 fix bug that blocked stopping scene #12 2017-01-06 04:02:24 +09:00
hecomi
4de8f0ba51 remove unused code. 2017-01-06 03:47:48 +09:00
hecomi
476be8243a remove unused code. 2017-01-06 03:42:48 +09:00
hecomi
c681e43d65 Merge branch 'ousttrue-AcquireNextFrame_on_thread' 2017-01-05 21:21:32 +09:00
hecomi
ed24ca2fb2 add x86 dll and remove pdb file #10 2017-01-05 21:20:35 +09:00
hecomi
791fa42478 fix crash bug when stopping scene #10 2017-01-05 21:13:25 +09:00
hecomi
11dbb7be57 update cursor in each thread #10 2017-01-05 19:38:30 +09:00
hecomi
9ed24dd3b1 modify timing to fetch metadata and add it to frame data #10 2017-01-05 19:03:29 +09:00
hecomi
cfbd218374 fix styles. 2017-01-05 19:03:28 +09:00
hecomi
bf7174f406 initialize at first #10 2017-01-05 19:03:28 +09:00
hecomi
4a761a06f3 remove const_cast #10 2017-01-05 19:03:28 +09:00
hecomi
1effd65aeb add Duplicator class to modularize duplication function #10 2017-01-05 19:03:28 +09:00
hecomi
46cc789be5 spearate IsolatedD3D11Device into different files #10 2017-01-05 19:03:28 +09:00
hecomi
1053b30d7e remove ThreadedDesktopDuplicator.cpp #10 2017-01-05 19:03:28 +09:00
hecomi
d58855ff8a check ReleaseFrame result #10 2017-01-05 19:03:18 +09:00
hecomi
741e2c0bc1 remove useThread flag #10 2017-01-05 12:14:03 +09:00
ousttrue
f6ce875463 call AcquireNextFrame on isolated thread 2016-12-28 02:12:03 +09:00
hecomi
e6407d1c3b modify raycast. 2016-12-18 14:57:31 +09:00
hecomi
620a0f3150 calculate coordinates of raycast hit point. 2016-12-15 22:17:12 +09:00
hecomi
d519dba120 add raycast api. 2016-12-15 02:30:04 +09:00
hecomi
295b41f57b fix standard shader normal bug. 2016-12-04 11:59:08 +09:00
hecomi
6a8bd86d2b fix thickness bug. 2016-12-03 23:27:31 +09:00
hecomi
22bc306732 fix zoom bug. 2016-12-03 23:15:37 +09:00
hecomi
3ed37d1436 tweak scenes. 2016-12-03 23:15:32 +09:00
hecomi
03419f2918 make monitor rotation setter private. 2016-12-03 22:34:49 +09:00
hecomi
2ee8ba35b7 add monitor rotation to editor. 2016-12-03 21:24:32 +09:00
hecomi
2a536ce018 fix scene loop bug. 2016-12-03 21:22:01 +09:00
hecomi
54531a90f3 add texture editor. 2016-12-03 21:18:12 +09:00
hecomi
ae8323fe29 add 32-bit dll. 2016-12-03 01:05:45 +09:00
hecomi
d23fe2fc3d fix indent. 2016-12-01 22:42:17 +09:00
hecomi
81bc23d0bc fix bug that destroyed texture in non-main thread. 2016-12-01 22:34:35 +09:00
hecomi
583af4ff46 Change the way to get pixels and finish the implementation of the APIs 🎉 2016-12-01 22:29:52 +09:00
hecomi
7e3df8ec2e add GetPixels() and GetPixel() APIs (wip). 2016-11-30 02:40:16 +09:00
hecomi
32adf6c2d0 fix out-of-area bug. 2016-11-29 20:10:05 +09:00
hecomi
a24472bd92 fix cursor capture area bug. 2016-11-29 20:02:02 +09:00
hecomi
cfee22832a fix indent. 2016-11-28 02:31:55 +09:00
hecomi
9c467a0e6f fix indent. 2016-11-28 02:31:17 +09:00
hecomi
87557ed5ff refactoring. 2016-11-28 02:30:09 +09:00
hecomi
67c90073d9 fix unity log call from native. 2016-11-27 16:22:03 +09:00
hecomi
ce9c148282 remove unused code and fix minor bug. 2016-11-27 15:48:09 +09:00
hecomi
8201a22523 move cursor owner from monitor to manager. 2016-11-27 14:44:23 +09:00
hecomi
5cfbd70e0b output monitor orientation in log. 2016-11-27 13:54:42 +09:00
hecomi
f2b6a99af3 modify the mothod to check monitor orientation. 2016-11-26 18:11:02 +09:00
hecomi
c48bf7fecf add displacement mapping example. 2016-11-26 17:50:03 +09:00
hecomi
6673734b9c remove Cursor-related codes because they were handled directly in the native code. 2016-11-26 16:31:34 +09:00
hecomi
3364c19adf add displacement shader. 2016-11-26 16:14:00 +09:00
hecomi
f7eba2434f fix bug #2 that causes crash after returning from UAC. 2016-11-24 23:38:07 +09:00
hecomi
ddbfad6e95 upload cursor texture in native plugin. 2016-11-20 22:35:52 +09:00
84 changed files with 2965 additions and 997 deletions

View File

@@ -0,0 +1,119 @@
using UnityEngine;
using UnityEditor;
using System.Linq;
namespace uDesktopDuplication
{
[CustomEditor(typeof(Texture))]
public class TextureEditor : Editor
{
Texture texture
{
get { return target as Texture; }
}
Monitor monitor
{
get { return texture.monitor; }
}
bool isAvailable
{
get { return monitor == null || !Application.isPlaying; }
}
bool basicsFolded_ = true;
bool invertFolded_ = true;
bool clipFolded_ = true;
bool matFolded_ = true;
SerializedProperty invertX_;
SerializedProperty invertY_;
SerializedProperty useClip_;
SerializedProperty clipPos_;
SerializedProperty clipScale_;
void OnEnable()
{
invertX_ = serializedObject.FindProperty("invertX_");
invertY_ = serializedObject.FindProperty("invertY_");
useClip_ = serializedObject.FindProperty("useClip_");
clipPos_ = serializedObject.FindProperty("clipPos");
clipScale_ = serializedObject.FindProperty("clipScale");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.Space();
DrawMonitor();
DrawInvert();
DrawClip();
DrawMaterial();
serializedObject.ApplyModifiedProperties();
}
void Fold(string name, ref bool folded, System.Action func)
{
folded = Utils.Foldout(name, folded);
if (folded) {
++EditorGUI.indentLevel;
func();
--EditorGUI.indentLevel;
}
}
void DrawMonitor()
{
Fold("Monitor", ref basicsFolded_, () => {
if (isAvailable) {
EditorGUILayout.HelpBox("Monitor information is available only in runtime.", MessageType.Info);
return;
}
var id = EditorGUILayout.Popup("Monitor", monitor.id, Manager.monitors.Select(x => x.name).ToArray());
if (id != monitor.id) { texture.monitorId = id; }
EditorGUILayout.IntField("ID", monitor.id);
EditorGUILayout.Toggle("Is Primary", monitor.isPrimary);
EditorGUILayout.EnumPopup("Rotation", monitor.rotation);
EditorGUILayout.Vector2Field("Resolution", new Vector2(monitor.width, monitor.height));
EditorGUILayout.Vector2Field("DPI", new Vector2(monitor.dpiX, monitor.dpiY));
});
}
void DrawInvert()
{
Fold("Invert", ref invertFolded_, () => {
texture.invertX = EditorGUILayout.Toggle("Invert X", invertX_.boolValue);
texture.invertY = EditorGUILayout.Toggle("Invert Y", invertY_.boolValue);
});
}
void DrawClip()
{
Fold("Clip", ref clipFolded_, () => {
texture.useClip = EditorGUILayout.Toggle("Use Clip", useClip_.boolValue);
EditorGUILayout.PropertyField(clipPos_);
EditorGUILayout.PropertyField(clipScale_);
});
}
void DrawMaterial()
{
Fold("Material", ref matFolded_, () => {
if (!Application.isPlaying) {
EditorGUILayout.HelpBox("These parameters are applied to the shared material when not playing.", MessageType.Info);
}
texture.meshForwardDirection = (Texture.MeshForwardDirection)EditorGUILayout.EnumPopup("Mesh Forward Direction", texture.meshForwardDirection);
texture.bend = EditorGUILayout.Toggle("Use Bend", texture.bend);
texture.width = EditorGUILayout.FloatField("Bend Width", texture.width);
texture.radius = EditorGUILayout.Slider("Bend Radius", texture.radius, texture.worldWidth / (2 * Mathf.PI), 100f);
texture.thickness = EditorGUILayout.Slider("Thickness", texture.thickness, 0f, 30f);
texture.culling = (Texture.Culling)EditorGUILayout.EnumPopup("Culling", texture.culling);
});
}
}
}

View File

@@ -1,12 +1,12 @@
fileFormatVersion: 2
guid: d2d9e4ca459ecb44da8815bafe9655c5
timeCreated: 1477635630
guid: 1202a4540abf0ad4781c025339772c4a
timeCreated: 1480758898
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 89616e59e1cccb346bd4e959257990ca, type: 3}
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,46 @@
using UnityEngine;
using UnityEditor;
namespace uDesktopDuplication
{
public static class Utils
{
public static bool Foldout(string title, bool display)
{
var style = new GUIStyle("ShurikenModuleTitle");
style.font = new GUIStyle(EditorStyles.label).font;
style.border = new RectOffset(15, 7, 4, 4);
style.fixedHeight = 22;
style.contentOffset = new Vector2(20f, -2f);
var rect = GUILayoutUtility.GetRect(16f, 22f, style);
GUI.Box(rect, title, style);
var e = Event.current;
var toggleRect = new Rect(rect.x + 4f, rect.y + 2f, 13f, 13f);
if (e.type == EventType.Repaint) {
EditorStyles.foldout.Draw(toggleRect, false, false, display, false);
}
if (e.type == EventType.MouseDown && rect.Contains(e.mousePosition)) {
display = !display;
e.Use();
}
return display;
}
public static void ReadOnlyTextField(string label, string text)
{
EditorGUILayout.BeginHorizontal();
{
EditorGUILayout.LabelField(label, GUILayout.Width(EditorGUIUtility.labelWidth - 4));
EditorGUILayout.SelectableLabel(text, EditorStyles.textField, GUILayout.Height(EditorGUIUtility.singleLineHeight));
}
EditorGUILayout.EndHorizontal();
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 278117152a6252546a219a17ef6f21f4
timeCreated: 1480759393
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 57d4a6e606848364c9880f2adec16d90
timeCreated: 1480074774
licenseType: Pro
NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4e1efec2c71c5b94e8522880dd9636c8
timeCreated: 1480074355
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 38a5db360d5147c46ae925104162b881
timeCreated: 1480420198
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f68bd0626da87264cb4daca57d3c1594
timeCreated: 1481736541
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,58 @@
using UnityEngine;
using UnityEngine.Assertions;
[RequireComponent(typeof(uDesktopDuplication.Texture))]
public class DisplacementMapping : MonoBehaviour
{
public enum TargetMonitor
{
Self,
Next,
Prev,
}
public TargetMonitor target = TargetMonitor.Self;
uDesktopDuplication.Texture uddTexture_;
int dispTexId_;
int dispFactorId_;
int tessMinDistId_;
int tessMaxDistId_;
int tessFactorId_;
[Range(0.0f, 10f)] public float displacementFactor = 0.1f;
[Range(0.1f, 10f)] public float tessellationMinDist = 0.5f;
[Range(1.0f, 50f)] public float tessellationMaxDist = 25f;
[Range(1.0f, 50f)] public float tessellationFactor = 25f;
void Start()
{
uddTexture_ = GetComponent<uDesktopDuplication.Texture>();
Assert.IsNotNull(uddTexture_);
dispTexId_ = Shader.PropertyToID("_DispTex");
dispFactorId_ = Shader.PropertyToID("_DispFactor");
tessMinDistId_ = Shader.PropertyToID("_TessMinDist");
tessMaxDistId_ = Shader.PropertyToID("_TessMaxDist");
tessFactorId_ = Shader.PropertyToID("_TessFactor");
}
void Update()
{
var id = uddTexture_.monitor.id;
switch (target) {
case TargetMonitor.Self: break;
case TargetMonitor.Next: ++id; break;
case TargetMonitor.Prev: --id; break;
}
id = Mathf.Clamp(id, 0, uDesktopDuplication.Manager.monitorCount - 1);
var monitor = uDesktopDuplication.Manager.GetMonitor(id);
monitor.shouldBeUpdated = true;
uddTexture_.material.SetTexture(dispTexId_, monitor.texture);
uddTexture_.material.SetFloat(dispFactorId_, displacementFactor);
uddTexture_.material.SetFloat(tessMinDistId_, tessellationMinDist);
uddTexture_.material.SetFloat(tessMaxDistId_, tessellationMaxDist);
uddTexture_.material.SetFloat(tessFactorId_, tessellationFactor);
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 859a2446b86e55c43932212872c82748
timeCreated: 1480146517
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -15,6 +15,11 @@ public class GazePointAnalyzer : MonoBehaviour
{
get { return GetWorldPositionFromCoord((int)averageCoord_.x, (int)averageCoord_.y); }
}
public Vector3 cursorPos
{
get { return GetWorldPositionFromCoord(uddTexture_.monitor.cursorX, uddTexture_.monitor.cursorY); }
}
private Vector2 preCursorCoord_ = Vector2.zero;
[Header("Filters")]
@@ -26,6 +31,7 @@ public class GazePointAnalyzer : MonoBehaviour
[Header("Debug")]
public bool drawAveragePos;
public bool drawCursorPos;
public bool drawMoveRects;
public bool drawDirtyRects;
@@ -37,32 +43,7 @@ public class GazePointAnalyzer : MonoBehaviour
public Vector3 GetWorldPositionFromCoord(int u, int v)
{
var monitor = uddTexture_.monitor;
// Mesh & Scale information
var mesh = GetComponent<MeshFilter>().sharedMesh;
var width = transform.localScale.x * (mesh.bounds.extents.x * 2f);
var height = transform.localScale.y * (mesh.bounds.extents.y * 2f);
// Local position (scale included).
var x = (float)(u - monitor.width / 2) / monitor.width;
var y = -(float)(v - monitor.height / 2) / monitor.height;
var localPos = new Vector3(width * x, height * y, 0f);
// Bending
if (uddTexture_.bend) {
var radius = uddTexture_.radius;
var angle = localPos.x / radius;
if (uddTexture_.meshForwardDirection == MeshForwardDirection.Y) {
localPos.y -= radius * (1f - Mathf.Cos(angle));
} else {
localPos.z -= radius * (1f - Mathf.Cos(angle));
}
localPos.x = radius * Mathf.Sin(angle);
}
// To world position
return transform.position + (transform.rotation * localPos);
return uddTexture_.GetWorldPositionFromCoord(u, v);
}
void CalcAveragePos()
@@ -142,6 +123,7 @@ public class GazePointAnalyzer : MonoBehaviour
void DebugDraw()
{
if (drawAveragePos) DrawAveragePos();
if (drawCursorPos) DrawCursorPos();
if (drawDirtyRects) DrawDirtyRects();
if (drawMoveRects) DrawMoveRects();
}
@@ -164,6 +146,11 @@ public class GazePointAnalyzer : MonoBehaviour
Debug.DrawLine(transform.position, averagePos, Color.yellow);
}
void DrawCursorPos()
{
Debug.DrawLine(transform.position, cursorPos, Color.grey);
}
void DrawMoveRects()
{
foreach (var rect in uddTexture_.monitor.moveRects) {

View File

@@ -0,0 +1,36 @@
using UnityEngine;
public class GetPixelsExample : MonoBehaviour
{
[SerializeField] uDesktopDuplication.Texture uddTexture;
[SerializeField] int x = 100;
[SerializeField] int y = 100;
const int width = 64;
const int height = 32;
public Texture2D texture;
Color32[] colors = new Color32[width * height];
void Start()
{
texture = new Texture2D(width, height, TextureFormat.ARGB32, false);
GetComponent<Renderer>().material.mainTexture = texture;
}
void Update()
{
// must be called (performance will be slightly down).
uDesktopDuplication.Manager.primary.useGetPixels = true;
var monitor = uddTexture.monitor;
if (!monitor.hasBeenUpdated) return;
if (monitor.GetPixels(colors, x, y, width, height)) {
texture.SetPixels32(colors);
texture.Apply();
}
Debug.Log(monitor.GetPixel(monitor.cursorX, monitor.cursorY));
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: fd3f838ff57152e44a1810274a9df49b
timeCreated: 1480420430
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,18 +1,15 @@
using UnityEngine;
[RequireComponent(typeof(uDesktopDuplication.Texture)),
RequireComponent(typeof(uDesktopDuplication.Cursor))]
[RequireComponent(typeof(uDesktopDuplication.Texture))]
public class Loupe : MonoBehaviour
{
private uDesktopDuplication.Texture uddTexture_;
private uDesktopDuplication.Cursor uddCursor_;
public float zoom = 3f;
public float aspect = 1f;
void Start()
{
uddTexture_ = GetComponent<uDesktopDuplication.Texture>();
uddCursor_ = GetComponent<uDesktopDuplication.Cursor>();
uddTexture_.useClip = true;
}
@@ -31,10 +28,10 @@ public class Loupe : MonoBehaviour
var monitor = uddTexture_.monitor;
var x = monitor.isCursorVisible ?
uddCursor_.coord.x :
(float)monitor.cursorX / monitor.width :
(float)monitor.systemCursorX / monitor.width;
var y = monitor.isCursorVisible ?
uddCursor_.coord.y :
(float)monitor.cursorY / monitor.height :
(float)monitor.systemCursorY / monitor.height;
var w = 1f / zoom;
var h = w / aspect * monitor.aspect;

View File

@@ -2,7 +2,7 @@
using UnityEngine.Assertions;
using System.Collections.Generic;
using MeshForwardDirection = uDesktopDuplication.Texture.MeshForwardDirection;
using MonitorState = uDesktopDuplication.MonitorState;
using DuplicatorState = uDesktopDuplication.DuplicatorState;
public class MultipleMonitorCreator : MonoBehaviour
{
@@ -92,11 +92,11 @@ public class MultipleMonitorCreator : MonoBehaviour
if (removeWaitTimer_ > removeWaitDuration) {
hasMonitorUnsupportStateChecked_ = true;
foreach (var info in monitors) {
if (info.uddTexture.monitor.state == MonitorState.Unsupported) {
if (info.uddTexture.monitor.state == DuplicatorState.Unsupported) {
Destroy(info.gameObject);
}
}
monitors.RemoveAll(info => info.uddTexture.monitor.state == MonitorState.Unsupported);
monitors.RemoveAll(info => info.uddTexture.monitor.state == DuplicatorState.Unsupported);
}
}
}
@@ -116,7 +116,7 @@ public class MultipleMonitorCreator : MonoBehaviour
for (int i = 0; i < n; ++i) {
// Create monitor obeject
var go = Instantiate(monitorPrefab);
go.name = "Monitor " + i;
go.name = uDesktopDuplication.Manager.monitors[i].name;
// Saved infomation
if (savedInfoList.Count == i) {

View File

@@ -0,0 +1,23 @@
using UnityEngine;
using System.Collections;
public class RaycastTest : MonoBehaviour
{
public Transform from;
public Transform to;
void Update()
{
if (!from || !to) return;
Debug.DrawLine(from.position, to.position, Color.red);
foreach (var uddTexture in GameObject.FindObjectsOfType<uDesktopDuplication.Texture>()) {
var result = uddTexture.RayCast(from.position, to.position - from.position);
if (result.hit) {
Debug.DrawLine(result.position, result.position + result.normal, Color.yellow);
Debug.Log("COORD: " + result.coords + ", DESKTOP: " + result.desktopCoord);
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 810a4ad59c7d8d34e9b6014e8191fffe
timeCreated: 1481722966
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -47,7 +47,7 @@ public class UddSceneManager : MonoBehaviour
void Prev()
{
sceneNo = (sceneNo - 1) % scenes.Length;
sceneNo = (sceneNo + scenes.Length - 1) % scenes.Length;
Load();
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 596fd740aeeee0d4cb4f962d4072954e
timeCreated: 1480074277
licenseType: Pro
NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -39,11 +39,11 @@ ModelImporter:
globalScale: 0.1
meshCompression: 0
addColliders: 0
importBlendShapes: 1
importBlendShapes: 0
swapUVChannels: 0
generateSecondaryUV: 0
useFileUnits: 1
optimizeMeshForGPU: 1
optimizeMeshForGPU: 0
keepQuads: 0
weldVertices: 1
secondaryUVAngleDistortion: 8

Binary file not shown.

View File

@@ -0,0 +1,87 @@
fileFormatVersion: 2
guid: f30f6cfd6aa37554ca886424cada7cbb
timeCreated: 1480072865
licenseType: Pro
ModelImporter:
serializedVersion: 19
fileIDToRecycleName:
100000: //RootNode
100002: uDD_Plane_MeshPart0
100004: uDD_Plane_MeshPart1
400000: //RootNode
400002: uDD_Plane_MeshPart0
400004: uDD_Plane_MeshPart1
2300000: //RootNode
2300002: uDD_Plane_MeshPart0
2300004: uDD_Plane_MeshPart1
3300000: //RootNode
3300002: uDD_Plane_MeshPart0
3300004: uDD_Plane_MeshPart1
4300000: uDD_Plane
4300002: uDD_Plane_MeshPart0
4300004: uDD_Plane_MeshPart1
9500000: //RootNode
materials:
importMaterials: 0
materialName: 0
materialSearch: 1
animations:
legacyGenerateAnimations: 4
bakeSimulation: 0
resampleCurves: 1
optimizeGameObjects: 0
motionNodeName:
animationImportErrors:
animationImportWarnings:
animationRetargetingWarnings:
animationDoRetargetingWarnings: 0
animationCompression: 1
animationRotationError: 0.5
animationPositionError: 0.5
animationScaleError: 0.5
animationWrapMode: 0
extraExposedTransformPaths: []
clipAnimations: []
isReadable: 0
meshes:
lODScreenPercentages: []
globalScale: 0.1
meshCompression: 0
addColliders: 0
importBlendShapes: 0
swapUVChannels: 0
generateSecondaryUV: 0
useFileUnits: 1
optimizeMeshForGPU: 1
keepQuads: 0
weldVertices: 1
secondaryUVAngleDistortion: 8
secondaryUVAreaDistortion: 15.000001
secondaryUVHardAngle: 88
secondaryUVPackMargin: 4
useFileScale: 1
tangentSpace:
normalSmoothAngle: 60
normalImportMode: 0
tangentImportMode: 2
importAnimation: 0
copyAvatar: 0
humanDescription:
human: []
skeleton: []
armTwist: 0.5
foreArmTwist: 0.5
upperLegTwist: 0.5
legTwist: 0.5
armStretch: 0.05
legStretch: 0.05
feetSpacing: 0
rootMotionBoneName:
hasTranslationDoF: 0
lastHumanDescriptionAvatarSource: {instanceID: 0}
animationType: 0
humanoidOversampling: 1
additionalBone: 0
userData:
assetBundleName:
assetBundleVariant:

View File

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

View File

@@ -0,0 +1,53 @@
fileFormatVersion: 2
guid: 19d4c78204afead44b6de11427d7f2f6
timeCreated: 1480693943
licenseType: Pro
PluginImporter:
serializedVersion: 1
iconMap: {}
executionOrder: {}
isPreloaded: 0
platformData:
Any:
enabled: 1
settings: {}
Editor:
enabled: 0
settings:
CPU: x86
DefaultValueInitialized: true
Linux:
enabled: 1
settings:
CPU: x86
Linux64:
enabled: 0
settings:
CPU: None
LinuxUniversal:
enabled: 1
settings:
CPU: x86
OSXIntel:
enabled: 1
settings:
CPU: AnyCPU
OSXIntel64:
enabled: 0
settings:
CPU: None
OSXUniversal:
enabled: 0
settings:
CPU: x86
Win:
enabled: 1
settings:
CPU: AnyCPU
Win64:
enabled: 0
settings:
CPU: None
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -14,7 +14,41 @@ PluginImporter:
Editor:
enabled: 0
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
Linux:
enabled: 0
settings:
CPU: x86
Linux64:
enabled: 0
settings:
CPU: x86_64
LinuxUniversal:
enabled: 0
settings:
CPU: None
OSXIntel:
enabled: 0
settings:
CPU: AnyCPU
OSXIntel64:
enabled: 0
settings:
CPU: AnyCPU
OSXUniversal:
enabled: 0
settings:
CPU: None
Win:
enabled: 0
settings:
CPU: AnyCPU
Win64:
enabled: 1
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,85 +0,0 @@
using UnityEngine;
using UnityEngine.Assertions;
using System.Collections.Generic;
namespace uDesktopDuplication
{
[AddComponentMenu("uDesktopDuplication/Cursor"), RequireComponent(typeof(Texture))]
public class Cursor : MonoBehaviour
{
[SerializeField] Vector2 modelScale = Vector2.one;
public Vector3 worldPosition { get; set; }
public Vector2 coord { get; set; }
private Texture uddTexture_;
private Monitor monitor { get { return uddTexture_.monitor; } }
struct TextureInfo
{
public Texture2D texture;
public System.IntPtr ptr;
public TextureInfo(Texture2D texture)
{
this.texture = texture;
this.ptr = texture.GetNativeTexturePtr();
}
}
private Dictionary<int, TextureInfo> textures_ = new Dictionary<int, TextureInfo>();
void Start()
{
uddTexture_ = GetComponent<Texture>();
}
void Update()
{
if (monitor.isCursorVisible) {
UpdatePosition();
UpdateTexture();
}
UpdateMaterial();
}
void UpdatePosition()
{
var x = (1f * monitor.cursorX / monitor.width - 0.5f) * modelScale.x;
var y = (1f * monitor.cursorY / 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.cursorShapeWidth;
var h = monitor.cursorShapeHeight;
if (w == 0 || h == 0) return;
var key = w + h * 100;
if (!textures_.ContainsKey(key)) {
var texture = new Texture2D(w, h, TextureFormat.BGRA32, false);
texture.wrapMode = TextureWrapMode.Clamp;
textures_.Add(key, new TextureInfo(texture));
}
var cursorTexture = textures_[key];
Assert.IsNotNull(cursorTexture.texture);
monitor.GetCursorTexture(cursorTexture.ptr);
uddTexture_.material.SetTexture("_CursorTex", cursorTexture.texture);
}
void UpdateMaterial()
{
var x = monitor.isCursorVisible ? (float)monitor.cursorX / monitor.width : -9999f;
var y = monitor.isCursorVisible ? (float)monitor.cursorY / monitor.height : -9999f;
var w = (float)monitor.cursorShapeWidth / monitor.width;
var h = (float)monitor.cursorShapeHeight / monitor.height;
uddTexture_.material.SetVector("_CursorPositionScale", new Vector4(x, y, w, h));
coord = new Vector2(x, y);
}
}
}

View File

@@ -1,7 +1,10 @@
using System;
using UnityEngine;
using System;
using System.Text;
using System.Runtime.InteropServices;
#pragma warning disable 114, 465
namespace uDesktopDuplication
{
@@ -29,17 +32,18 @@ public enum MonitorRotation
Rotate270 = 4
}
public enum MonitorState
public enum DuplicatorState
{
NotSet = -1,
Available = 0,
InvalidArg = 1,
AccessDenied = 2,
Unsupported = 3,
CurrentlyNotAvailable = 4,
SessionDisconnected = 5,
AccessLost = 6,
TextureSizeInconsistent = 7,
Ready = 0,
Running = 1,
InvalidArg = 2,
AccessDenied = 3,
Unsupported = 4,
CurrentlyNotAvailable = 5,
SessionDisconnected = 6,
AccessLost = 7,
TextureSizeInconsistent = 8,
Unknown = 999,
}
@@ -77,9 +81,11 @@ public static class Lib
public delegate void DebugLogDelegate(string str);
[DllImport("uDesktopDuplication")]
public static extern void InitializeUDD();
public static extern bool IsInitialized();
[DllImport("uDesktopDuplication")]
public static extern void FinalizeUDD();
public static extern void Initialize();
[DllImport("uDesktopDuplication")]
public static extern void Finalize();
[DllImport("uDesktopDuplication")]
public static extern void Reinitialize();
[DllImport("uDesktopDuplication")]
@@ -93,9 +99,9 @@ public static class Lib
[DllImport("uDesktopDuplication")]
public static extern void SetDebugMode(DebugMode mode);
[DllImport("uDesktopDuplication")]
public static extern void SetLogFunc(IntPtr func);
public static extern void SetLogFunc(DebugLogDelegate func);
[DllImport("uDesktopDuplication")]
public static extern void SetErrorFunc(IntPtr func);
public static extern void SetErrorFunc(DebugLogDelegate func);
[DllImport("uDesktopDuplication")]
public static extern int GetMonitorCount();
[DllImport("uDesktopDuplication")]
@@ -107,13 +113,11 @@ public static class Lib
[DllImport("uDesktopDuplication")]
public static extern int GetTotalHeight();
[DllImport("uDesktopDuplication")]
public static extern void SetTimeout(int timeout);
[DllImport("uDesktopDuplication")]
public static extern IntPtr GetRenderEventFunc();
[DllImport("uDesktopDuplication")]
public static extern int GetId(int id);
[DllImport("uDesktopDuplication")]
public static extern MonitorState GetState(int id);
public static extern DuplicatorState GetState(int id);
[DllImport("uDesktopDuplication")]
public static extern void GetName(int id, StringBuilder buf, int len);
[DllImport("uDesktopDuplication")]
@@ -139,17 +143,17 @@ public static class Lib
[DllImport("uDesktopDuplication")]
public static extern bool IsCursorVisible(int id);
[DllImport("uDesktopDuplication")]
public static extern int GetCursorX(int id);
public static extern int GetCursorX();
[DllImport("uDesktopDuplication")]
public static extern int GetCursorY(int id);
public static extern int GetCursorY();
[DllImport("uDesktopDuplication")]
public static extern int GetCursorShapeWidth(int id);
public static extern int GetCursorShapeWidth();
[DllImport("uDesktopDuplication")]
public static extern int GetCursorShapeHeight(int id);
public static extern int GetCursorShapeHeight();
[DllImport("uDesktopDuplication")]
public static extern int GetCursorShapePitch(int id);
public static extern int GetCursorShapePitch();
[DllImport("uDesktopDuplication")]
public static extern CursorShapeType GetCursorShapeType(int id);
public static extern CursorShapeType GetCursorShapeType();
[DllImport("uDesktopDuplication")]
public static extern void GetCursorTexture(int id, System.IntPtr ptr);
[DllImport("uDesktopDuplication")]
@@ -162,6 +166,14 @@ public static class Lib
public static extern int GetDirtyRectCount(int id);
[DllImport("uDesktopDuplication", EntryPoint = "GetDirtyRects")]
private static extern IntPtr GetDirtyRects_Internal(int id);
[DllImport("uDesktopDuplication", EntryPoint = "GetPixels")]
private static extern bool GetPixels_Internal(int id, System.IntPtr ptr, int x, int y, int width, int height);
[DllImport("uDesktopDuplication")]
public static extern bool HasBeenUpdated(int id);
[DllImport("uDesktopDuplication")]
public static extern bool UseGetPixels(int id, bool use);
[DllImport("uDesktopDuplication")]
public static extern void SetFrameRate(uint frameRate);
public static string GetName(int id)
{
@@ -195,6 +207,34 @@ public static class Lib
}
return rects;
}
public static Color32[] GetPixels(int id, int x, int y, int width, int height)
{
var color = new Color32[width * height];
GetPixels(id, color, x, y, width, height);
return color;
}
public static bool GetPixels(int id, Color32[] colors, int x, int y, int width, int height)
{
if (colors.Length < width * height) {
Debug.LogErrorFormat("colors is small.", id, x, y, width, height);
return false;
}
var handle = GCHandle.Alloc(colors, GCHandleType.Pinned);
var ptr = handle.AddrOfPinnedObject();
if (!GetPixels_Internal(id, ptr, x, y, width, height)) {
Debug.LogErrorFormat("GetPixels({0}, {1}, {2}, {3}, {4}) failed.", id, x, y, width, height);
return false;
}
handle.Free();
return true;
}
public static Color32 GetPixel(int id, int x, int y)
{
return GetPixels(id, x, y, 1, 1)[0];
}
}
}

View File

@@ -52,8 +52,9 @@ public class Manager : MonoBehaviour
}
}
[Tooltip("Debug mode is not applied while running.")]
[SerializeField] DebugMode debugMode = DebugMode.File;
[SerializeField] int desktopDuplicationApiTimeout = 0;
[SerializeField] float retryReinitializationDuration = 1f;
private Coroutine renderCoroutine_ = null;
@@ -61,6 +62,9 @@ public class Manager : MonoBehaviour
private float reinitializationTimer_ = 0f;
private bool isFirstFrame_ = true;
public static event Lib.DebugLogDelegate onDebugLog = msg => Debug.Log(msg);
public static event Lib.DebugLogDelegate onDebugErr = msg => Debug.LogError(msg);
public delegate void ReinitializeHandler();
public static event ReinitializeHandler onReinitialized;
@@ -89,15 +93,17 @@ public class Manager : MonoBehaviour
instance_ = this;
Lib.SetDebugMode(debugMode);
Lib.SetTimeout(desktopDuplicationApiTimeout);
Lib.InitializeUDD();
Lib.SetLogFunc(onDebugLog);
Lib.SetErrorFunc(onDebugErr);
Lib.Initialize();
CreateMonitors();
}
void OnApplicationQuit()
{
Lib.FinalizeUDD();
Lib.Finalize();
DestroyMonitors();
}
@@ -107,6 +113,9 @@ public class Manager : MonoBehaviour
if (!isFirstFrame_) {
Reinitialize();
}
Lib.SetDebugMode(debugMode);
Lib.SetLogFunc(onDebugLog);
}
void OnDisable()
@@ -115,6 +124,9 @@ public class Manager : MonoBehaviour
StopCoroutine(renderCoroutine_);
renderCoroutine_ = null;
}
Lib.SetLogFunc(null);
Lib.SetErrorFunc(null);
}
void Update()
@@ -144,10 +156,11 @@ public class Manager : MonoBehaviour
var monitor = monitors[i];
var state = monitor.state;
if (
state == MonitorState.NotSet ||
state == MonitorState.AccessLost ||
state == MonitorState.AccessDenied ||
state == MonitorState.SessionDisconnected
state == DuplicatorState.NotSet ||
state == DuplicatorState.AccessLost ||
state == DuplicatorState.AccessDenied ||
state == DuplicatorState.SessionDisconnected ||
state == DuplicatorState.Unknown
) {
reinitializeNeeded = true;
break;

View File

@@ -11,21 +11,23 @@ public class Monitor
switch (state)
{
case MonitorState.Available:
case DuplicatorState.Ready:
break;
case MonitorState.InvalidArg:
case DuplicatorState.Running:
break;
case DuplicatorState.InvalidArg:
Debug.LogErrorFormat("[uDD] {0}:{1} => Invalid.", id, name);
break;
case MonitorState.AccessDenied:
case DuplicatorState.AccessDenied:
Debug.LogWarningFormat("[uDD] {0}:{1} => Access Denied.", id, name);
break;
case MonitorState.Unsupported:
case DuplicatorState.Unsupported:
Debug.LogWarningFormat("[uDD] {0}:{1} => Unsupported.", id, name);
break;
case MonitorState.SessionDisconnected:
case DuplicatorState.SessionDisconnected:
Debug.LogWarningFormat("[uDD] {0}:{1} => Disconnected.", id, name);
break;
case MonitorState.NotSet:
case DuplicatorState.NotSet:
Debug.LogErrorFormat("[uDD] {0}:{1} => Something wrong.", id, name);
break;
default:
@@ -40,7 +42,6 @@ public class Monitor
~Monitor()
{
DestroyTexture();
}
public int id
@@ -54,14 +55,19 @@ public class Monitor
get { return id < Manager.monitorCount; }
}
public MonitorState state
public DuplicatorState state
{
get { return Lib.GetState(id); }
}
public bool available
{
get { return state == MonitorState.Available; }
get
{
return
state == DuplicatorState.Ready ||
state == DuplicatorState.Running;
}
}
public string name
@@ -146,12 +152,22 @@ public class Monitor
public bool isHorizontal
{
get { return width > height; }
get
{
return
(rotation == MonitorRotation.Identity) ||
(rotation == MonitorRotation.Rotate180);
}
}
public bool isVertical
{
get { return height > width; }
get
{
return
(rotation == MonitorRotation.Rotate90) ||
(rotation == MonitorRotation.Rotate270);
}
}
public bool isCursorVisible
@@ -161,12 +177,12 @@ public class Monitor
public int cursorX
{
get { return Lib.GetCursorX(id); }
get { return Lib.GetCursorMonitorId() == id ? Lib.GetCursorX() : -1; }
}
public int cursorY
{
get { return Lib.GetCursorY(id); }
get { return Lib.GetCursorMonitorId() == id ? Lib.GetCursorY() : -1; }
}
public int systemCursorX
@@ -189,17 +205,17 @@ public class Monitor
public int cursorShapeWidth
{
get { return Lib.GetCursorShapeWidth(id); }
get { return Lib.GetCursorShapeWidth(); }
}
public int cursorShapeHeight
{
get { return Lib.GetCursorShapeHeight(id); }
get { return Lib.GetCursorShapeHeight(); }
}
public CursorShapeType cursorShapeType
{
get { return Lib.GetCursorShapeType(id); }
get { return Lib.GetCursorShapeType(); }
}
public int moveRectCount
@@ -222,6 +238,25 @@ public class Monitor
get { return Lib.GetDirtyRects(id); }
}
public bool hasBeenUpdated
{
get { return Lib.HasBeenUpdated(id); }
}
bool useGetPixels_ = false;
public bool useGetPixels
{
get
{
return useGetPixels_;
}
set
{
useGetPixels_ = value;
Lib.UseGetPixels(id, value);
}
}
public bool shouldBeUpdated
{
get;
@@ -253,7 +288,7 @@ public class Monitor
public void Render()
{
if (texture_ && available) {
if (texture_ && available && texturePtr_ != System.IntPtr.Zero) {
Lib.SetTexturePtr(id, texturePtr_);
GL.IssuePluginEvent(Lib.GetRenderEventFunc(), id);
}
@@ -307,6 +342,33 @@ public class Monitor
{
CreateTextureIfNeeded();
}
public Color32[] GetPixels(int x, int y, int width, int height)
{
if (!useGetPixels_) {
Debug.LogErrorFormat("Please set Monitor[{0}].useGetPixels as true.", id);
return null;
}
return Lib.GetPixels(id, x, y, width, height);
}
public bool GetPixels(Color32[] colors, int x, int y, int width, int height)
{
if (!useGetPixels_) {
Debug.LogErrorFormat("Please set Monitor[{0}].useGetPixels as true.", id);
return false;
}
return Lib.GetPixels(id, colors, x, y, width, height);
}
public Color32 GetPixel(int x, int y)
{
if (!useGetPixels_) {
Debug.LogErrorFormat("Please set Monitor[{0}].useGetPixels as true.", id);
return Color.black;
}
return Lib.GetPixel(id, x, y);
}
}
}

View File

@@ -3,10 +3,10 @@
namespace uDesktopDuplication
{
[AddComponentMenu("uDesktopDuplication/Texture"), RequireComponent(typeof(Cursor))]
[AddComponentMenu("uDesktopDuplication/Texture")]
public class Texture : MonoBehaviour
{
private Monitor monitor_;
Monitor monitor_;
public Monitor monitor
{
get { return monitor_; }
@@ -14,33 +14,113 @@ public class Texture : MonoBehaviour
{
monitor_ = value;
if (monitor_ != null) {
material = GetComponent<Renderer>().material; // clone
material.mainTexture = monitor_.texture;
material.SetFloat("_Width", transform.localScale.x);
width = transform.localScale.x;
rotation = monitor.rotation;
invertX = invertX_;
invertY = invertY_;
useClip = useClip_;
}
}
}
private int lastMonitorId_ = 0;
int lastMonitorId_ = 0;
public int monitorId
{
get { return monitor.id; }
set { monitor = Manager.GetMonitor(value); }
}
[Header("Invert UVs")]
public bool invertX = false;
public bool invertY = false;
[SerializeField] bool invertX_ = false;
public bool invertX
{
get
{
return invertX_;
}
set
{
invertX_ = value;
if (invertX_) {
material.EnableKeyword("INVERT_X");
} else {
material.DisableKeyword("INVERT_X");
}
}
}
[Header("Clip")]
public bool useClip = false;
[SerializeField] bool invertY_ = false;
public bool invertY
{
get
{
return invertY_;
}
set
{
invertY_ = value;
if (invertY_) {
material.EnableKeyword("INVERT_Y");
} else {
material.DisableKeyword("INVERT_Y");
}
}
}
public MonitorRotation rotation
{
get
{
return monitor.rotation;
}
private set
{
switch (value)
{
case MonitorRotation.Identity:
material.DisableKeyword("ROTATE90");
material.DisableKeyword("ROTATE180");
material.DisableKeyword("ROTATE270");
break;
case MonitorRotation.Rotate90:
material.EnableKeyword("ROTATE90");
material.DisableKeyword("ROTATE180");
material.DisableKeyword("ROTATE270");
break;
case MonitorRotation.Rotate180:
material.DisableKeyword("ROTATE90");
material.EnableKeyword("ROTATE180");
material.DisableKeyword("ROTATE270");
break;
case MonitorRotation.Rotate270:
material.DisableKeyword("ROTATE90");
material.DisableKeyword("ROTATE180");
material.EnableKeyword("ROTATE270");
break;
default:
break;
}
}
}
[SerializeField] bool useClip_ = false;
public Vector2 clipPos = Vector2.zero;
public Vector2 clipScale = new Vector2(0.2f, 0.2f);
public enum MeshForwardDirection
public bool useClip
{
Y = 0,
Z = 1,
get
{
return useClip_;
}
set
{
useClip_ = value;
if (useClip_) {
material.EnableKeyword("USE_CLIP");
} else {
material.DisableKeyword("USE_CLIP");
}
}
}
public bool bend
@@ -61,6 +141,11 @@ public class Texture : MonoBehaviour
}
}
public enum MeshForwardDirection
{
Y = 0,
Z = 1,
}
public MeshForwardDirection meshForwardDirection
{
get
@@ -84,6 +169,24 @@ public class Texture : MonoBehaviour
}
}
public enum Culling
{
Off = 0,
Front = 1,
Back = 2,
}
public Culling culling
{
get
{
return (Culling)material.GetInt("_Cull");
}
set
{
material.SetInt("_Cull", (int)value);
}
}
public float radius
{
get { return material.GetFloat("_Radius"); }
@@ -102,15 +205,39 @@ public class Texture : MonoBehaviour
set { material.SetFloat("_Thickness", value); }
}
Material material_;
public Material material
{
get;
private set;
get
{
if (Application.isPlaying) {
return material_ ?? (material_ = GetComponent<Renderer>().material); // clone
} else {
return GetComponent<Renderer>().sharedMaterial;
}
}
}
void Awake()
Mesh mesh
{
AddCursorIfNotAttached();
get
{
return GetComponent<MeshFilter>().sharedMesh;
}
}
public float worldWidth
{
get
{
return transform.localScale.x * (mesh.bounds.extents.x * 2f);
}
}
public float worldHeight
{
get
{
return transform.localScale.y * (mesh.bounds.extents.y * 2f);
}
}
void OnEnable()
@@ -129,7 +256,7 @@ public class Texture : MonoBehaviour
void Update()
{
KeepMonitor();
monitor.shouldBeUpdated = true;
RequireUpdate();
UpdateMaterial();
}
@@ -142,12 +269,9 @@ public class Texture : MonoBehaviour
}
}
void AddCursorIfNotAttached()
void RequireUpdate()
{
if (!GetComponent<Cursor>())
{
gameObject.AddComponent<Cursor>();
}
monitor.shouldBeUpdated = true;
}
void Reinitialize()
@@ -158,63 +282,149 @@ public class Texture : MonoBehaviour
void UpdateMaterial()
{
Invert();
Rotate();
Clip();
width = transform.localScale.x;
rotation = monitor.rotation;
material.SetVector("_ClipPositionScale", new Vector4(clipPos.x, clipPos.y, clipScale.x, clipScale.y));
}
void Invert()
public Vector3 GetWorldPositionFromCoord(Vector2 coord)
{
if (invertX) {
material.EnableKeyword("INVERT_X");
} else {
material.DisableKeyword("INVERT_X");
}
if (invertY) {
material.EnableKeyword("INVERT_Y");
} else {
material.DisableKeyword("INVERT_Y");
}
return GetWorldPositionFromCoord((int)coord.x, (int)coord.y);
}
void Rotate()
public Vector3 GetWorldPositionFromCoord(int u, int v)
{
switch (monitor.rotation)
{
case MonitorRotation.Identity:
material.DisableKeyword("ROTATE90");
material.DisableKeyword("ROTATE180");
material.DisableKeyword("ROTATE270");
break;
case MonitorRotation.Rotate90:
material.EnableKeyword("ROTATE90");
material.DisableKeyword("ROTATE180");
material.DisableKeyword("ROTATE270");
break;
case MonitorRotation.Rotate180:
material.DisableKeyword("ROTATE90");
material.EnableKeyword("ROTATE180");
material.DisableKeyword("ROTATE270");
break;
case MonitorRotation.Rotate270:
material.DisableKeyword("ROTATE90");
material.DisableKeyword("ROTATE180");
material.EnableKeyword("ROTATE270");
break;
default:
break;
// Local position (scale included).
var x = (float)(u - monitor.width / 2) / monitor.width;
var y = -(float)(v - monitor.height / 2) / monitor.height;
var localPos = new Vector3(worldWidth * x, worldHeight * y, 0f);
// Bending
if (bend) {
var angle = localPos.x / radius;
if (meshForwardDirection == MeshForwardDirection.Y) {
localPos.y -= radius * (1f - Mathf.Cos(angle));
} else {
localPos.z -= radius * (1f - Mathf.Cos(angle));
}
localPos.x = radius * Mathf.Sin(angle);
}
// To world position
return transform.position + (transform.rotation * localPos);
}
void Clip()
public struct RayCastResult
{
if (useClip) {
material.EnableKeyword("USE_CLIP");
material.SetVector("_ClipPositionScale", new Vector4(clipPos.x, clipPos.y, clipScale.x, clipScale.y));
} else {
material.DisableKeyword("USE_CLIP");
public bool hit;
public Texture texture;
public Vector3 position;
public Vector3 normal;
public Vector2 coords;
public Vector2 desktopCoord;
}
static readonly RayCastResult raycastFailedResult = new RayCastResult {
hit = false,
texture = null,
position = Vector3.zero,
normal = Vector3.forward,
coords = Vector2.zero,
desktopCoord = Vector2.zero,
};
// This function can be used only for vertical (= MeshForwardDirection.Z) plane.
public RayCastResult RayCast(Vector3 from, Vector3 dir)
{
var r = radius;
var center = transform.position - transform.forward * r;
// Localize the start point of the ray and the direction.
var trs = Matrix4x4.TRS(center, transform.rotation, Vector3.one);
var invTrs = trs.inverse;
Vector3 localFrom = invTrs.MultiplyPoint3x4(from);
Vector3 localDir = invTrs.MultiplyVector(dir).normalized;
// Calculate the intersection points of circle and line on the X-Z plane.
var a = localDir.z / localDir.x;
var b = localFrom.z - a * localFrom.x;
var aa = a * a;
var bb = b * b;
var ab = a * b;
var rr = r * r;
var s = aa * rr - bb + rr;
if (s < 0f) {
return raycastFailedResult;
}
s = Mathf.Sqrt(s);
var t = aa + 1;
var lx0 = (-s - ab) / t;
var lz0 = (b - a * s) / t;
var to0 = new Vector3(lx0, 0f, lz0);
var lx1 = (s - ab) / t;
var lz1 = (a * s + b) / t;
var to1 = new Vector3(lx1, 0f, lz1);
var to = (Vector3.Dot(localDir, to0) > 0f) ? to0 : to1;
// Check if the point is inner angle of mesh width.
var toAngle = Mathf.Atan2(to.x, to.z);
var halfWidthAngle = (worldWidth / radius) * 0.5f;
if (Mathf.Abs(toAngle) > halfWidthAngle) {
return raycastFailedResult;
}
// Calculate the intersection points on XZ-Y plane.
var v = to - localFrom;
var l = Mathf.Sqrt(Mathf.Pow(v.x, 2f) + Mathf.Pow(v.z, 2f));
var ly = localFrom.y + l * localDir.y / Mathf.Sqrt(Mathf.Pow(localDir.x, 2f) + Mathf.Pow(localDir.z, 2f));
// Check if the point is inner mesh height.
var halfHeight = worldHeight * 0.5f;
if (Mathf.Abs(ly) > halfHeight) {
return raycastFailedResult;
}
// Check hit position is in the range of the ray.
to.y = ly;
var hitPos = trs.MultiplyPoint(to);
if ((hitPos - from).magnitude > dir.magnitude) {
return raycastFailedResult;
}
// Calculate coordinates.
var coordX = toAngle / halfWidthAngle * 0.5f;
var coordY = ly / halfHeight * 0.5f;
int desktopX = monitor.left + (int)((coordX + 0.5f) * monitor.width);
int desktopY = monitor.top + (int)((0.5f - coordY) * monitor.height);
// Calculate normal.
var normal = new Vector3(-to.x, 0f, -to.z);
// Result
return new RayCastResult {
hit = true,
texture = this,
position = trs.MultiplyPoint(to),
normal = trs.MultiplyVector(normal).normalized,
coords = new Vector2(coordX, coordY),
desktopCoord = new Vector2(desktopX, desktopY)
};
}
public static RayCastResult RayCastAll(Vector3 from, Vector3 dir)
{
foreach (var uddTexture in GameObject.FindObjectsOfType<uDesktopDuplication.Texture>()) {
var result = uddTexture.RayCast(from, dir);
if (result.hit) return result;
}
return raycastFailedResult;
}
}

View File

@@ -63,36 +63,23 @@ inline void uddConvertToLinearIfNeeded(inout fixed3 rgb)
}
}
inline fixed4 uddGetScreenTexture(float2 uv)
{
fixed4 c = tex2D(_MainTex, uv);
return c;
}
inline fixed4 uddGetCursorTexture(float2 uv)
{
uv.x = (uv.x - _CursorX) / _CursorWidth;
uv.y = (uv.y - _CursorY) / _CursorHeight;
fixed4 c = tex2D(_CursorTex, uv);
fixed a = step(0, uv.x) * step(0, uv.y) * step(uv.x, 1) * step(uv.y, 1);
c.a *= step(0.01, a);
return c;
}
inline fixed4 uddGetScreenTextureWithCursor(float2 uv)
inline fixed4 uddGetTexture(sampler2D tex, float2 uv)
{
uv = uddInvertUV(uv);
#ifdef USE_CLIP
uv = uddClipUV(uv);
#endif
fixed4 screen = uddGetScreenTexture(uddRotateUV(uv));
fixed4 cursor = uddGetCursorTexture(uv);
fixed4 color = lerp(screen, cursor, cursor.a);
uddConvertToLinearIfNeeded(color.rgb);
return color;
fixed4 c = tex2D(tex, uddRotateUV(uv));
uddConvertToLinearIfNeeded(c.rgb);
return c;
}
inline void uddBendVertex(inout float4 v, half radius, half width, half thickness)
inline fixed4 uddGetScreenTexture(float2 uv)
{
return uddGetTexture(_MainTex, uv);
}
inline void uddBendVertex(inout float3 v, half radius, half width, half thickness)
{
#ifdef BEND_ON
half angle = width * v.x / radius;
@@ -108,9 +95,35 @@ inline void uddBendVertex(inout float4 v, half radius, half width, half thicknes
v.x = radius * sin(angle) / width;
#else
#ifdef _FORWARD_Z
v.y *= thickness;
#elif _FORWARD_Y
v.z *= thickness;
#elif _FORWARD_Y
v.y *= thickness;
#endif
#endif
}
inline float3 uddRotateY(float3 n, float angle)
{
float c = cos(angle);
float s = sin(angle);
return float3(c * n.x - s * n.z, n.y, s * n.x + c * n.z);
}
inline float3 uddRotateX(float3 n, float angle)
{
float c = cos(angle);
float s = sin(angle);
return float3(n.x, c * n.y + s * n.z, -s * n.y + c * n.z);
}
inline void uddBendNormal(float4 x, inout float3 n, half radius, half width)
{
#ifdef BEND_ON
half angle = width * x / radius;
#ifdef _FORWARD_Z
n = uddRotateY(n, -angle);
#elif _FORWARD_Y
n = uddRotateX(n, -angle);
#endif
#endif
}

View File

@@ -7,6 +7,11 @@ Properties
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0, 1)) = 0.5
_Metallic ("Metallic", Range(0, 1)) = 0.0
[KeywordEnum(Y, Z)] _Forward("Mesh Forward Direction", Int) = 0
[Toggle(BEND_ON)] _Bend("Use Bend", Int) = 0
[PowerSlider(10.0)] _Radius("Bend Radius", Range(1, 100)) = 30
[PowerSlider(10.0)] _Thickness("Thickness", Range(0.01, 10)) = 1
_Width("Width", Range(0.0, 10.0)) = 1.0
[KeywordEnum(Off, Front, Back)] _Cull("Culling", Int) = 2
}
@@ -19,22 +24,32 @@ SubShader
CGPROGRAM
#pragma target 3.0
#pragma surface surf Standard fullforwardshadows
#pragma surface surf Standard fullforwardshadows vertex:vert
#pragma multi_compile ___ INVERT_X
#pragma multi_compile ___ INVERT_Y
#pragma multi_compile ___ ROTATE90 ROTATE180 ROTATE270
#pragma multi_compile ___ USE_BEND
#pragma multi_compile ___ USE_CLIP
#pragma multi_compile ___ BEND_ON
#pragma multi_compile _FORWARD_Y _FORWARD_Z
#define SURFACE_SHADER
#include "./uDD_Common.cginc"
half _Glossiness;
half _Metallic;
half _Radius;
half _Width;
half _Thickness;
void vert(inout appdata_full v)
{
uddBendNormal(v.vertex.x, v.normal, _Radius, _Width);
uddBendVertex(v.vertex.xyz, _Radius, _Width, _Thickness);
}
void surf(Input IN, inout SurfaceOutputStandard o)
{
fixed4 c = uddGetScreenTextureWithCursor(IN.uv_MainTex) * _Color;
fixed4 c = uddGetScreenTexture(IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;

View File

@@ -5,11 +5,11 @@ Properties
{
_Color ("Color", Color) = (1, 1, 1, 1)
_MainTex ("Texture", 2D) = "white" {}
_CursorTex ("Cursor Texture", 2D) = "white" {}
[KeywordEnum(Y, Z)] _Forward("Mesh Forward Direction", Int) = 0
[Toggle(BEND_ON)] _Bend("Use Bend", Int) = 0
[PowerSlider(10.0)] _Radius("Bend Radius", Range(1, 100)) = 30
[PowerSlider(10.0)] _Thickness("Thickness", Range(0.01, 10)) = 1
_Width("Width", Range(0.0, 10.0)) = 1.0
[KeywordEnum(Off, Front, Back)] _Cull("Culling", Int) = 2
}
@@ -31,7 +31,7 @@ half _Thickness;
v2f vert(appdata v)
{
v2f o;
uddBendVertex(v.vertex, _Radius, _Width, _Thickness);
uddBendVertex(v.vertex.xyz, _Radius, _Width, _Thickness);
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
@@ -39,7 +39,7 @@ v2f vert(appdata v)
fixed4 frag(v2f i) : SV_Target
{
return uddGetScreenTextureWithCursor(i.uv);
return uddGetScreenTexture(i.uv);
}
ENDCG

View File

@@ -6,11 +6,11 @@ Properties
_Color ("Color", Color) = (1, 1, 1, 1)
_MainTex ("Texture", 2D) = "white" {}
_Mask ("Mask", Range(0, 1)) = 0.1
_CursorTex ("Cursor Texture", 2D) = "white" {}
[KeywordEnum(Y, Z)] _Forward("Mesh Forward Direction", Int) = 0
[Toggle(BEND_ON)] _Bend("Use Bend", Int) = 0
[PowerSlider(10.0)] _Radius("Bend Radius", Range(1, 100)) = 30
[PowerSlider(10.0)] _Thickness("Thickness", Range(0.01, 10)) = 1
_Width("Width", Range(0.0, 10.0)) = 1.0
[KeywordEnum(Off, Front, Back)] _Cull("Culling", Int) = 2
}
@@ -35,7 +35,7 @@ half _Thickness;
v2f vert(appdata v)
{
v2f o;
uddBendVertex(v.vertex, _Radius, _Width, _Thickness);
uddBendVertex(v.vertex.xyz, _Radius, _Width, _Thickness);
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
@@ -43,7 +43,7 @@ v2f vert(appdata v)
fixed4 frag(v2f i) : SV_Target
{
fixed4 tex = uddGetScreenTextureWithCursor(i.uv);
fixed4 tex = uddGetScreenTexture(i.uv);
fixed alpha = pow((tex.r + tex.g + tex.b) / 3.0, _Mask);
return fixed4(tex.rgb * _Color.rgb, alpha * _Color.a);
}
@@ -58,9 +58,9 @@ Pass
#pragma multi_compile ___ INVERT_X
#pragma multi_compile ___ INVERT_Y
#pragma multi_compile ___ ROTATE90 ROTATE180 ROTATE270
#pragma multi_compile ___ USE_BEND
#pragma multi_compile ___ USE_CLIP
#pragma multi_compile _BEND_OFF _BEND_Y _BEND_Z
#pragma multi_compile ___ BEND_ON
#pragma multi_compile _FORWARD_Y _FORWARD_Z
ENDCG
}

View File

@@ -0,0 +1,178 @@
Shader "uDesktopDuplication/Unlit_Displacement"
{
Properties
{
_Color ("Color", Color) = (1, 1, 1, 1)
_MainTex ("Texture", 2D) = "white" {}
[KeywordEnum(Y, Z)] _Forward("Mesh Forward Direction", Int) = 0
[Toggle(BEND_ON)] _Bend("Use Bend", Int) = 0
[PowerSlider(10.0)] _Radius("Bend Radius", Range(1, 100)) = 30
_Width ("Width", Range(0.0, 10.0)) = 1.92
[PowerSlider(10.0)] _Thickness("Thickness", Range(0.01, 10)) = 1
[KeywordEnum(Off, Front, Back)] _Cull("Culling", Int) = 2
_DispTex ("Displacement Map", 2D) = "black" {}
_DispFactor("Displacement Factor", Range(0, 5.0)) = 1
_TessMinDist("Tessellation Min Distance", Range(0.1, 100.0)) = 1.0
_TessMaxDist("Tessellation Max Distance", Range(0.1, 100.0)) = 5.0
_TessFactor("Tessellation Factor", Range(0.1, 50.0)) = 10.0
}
SubShader
{
Tags { "RenderType"="Opaque" }
Cull [_Cull]
CGINCLUDE
#include "./uDD_Common.cginc"
#include "Tessellation.cginc"
half _Radius;
half _Width;
half _Thickness;
Texture2D _DispTex;
SamplerState sampler_DispTex;
half _DispFactor;
half _TessMinDist;
half _TessMaxDist;
half _TessFactor;
struct VsInput
{
float3 vertex : POSITION;
float3 normal : NORMAL;
float2 texcoord : TEXCOORD0;
};
struct HsInput
{
float4 f4Position : POS;
float3 f3Normal : NORMAL;
float2 f2TexCoord : TEXCOORD;
};
struct HsControlPointOutput
{
float3 f3Position : POS;
float3 f3Normal : NORMAL;
float2 f2TexCoord : TEXCOORD;
};
struct HsConstantOutput
{
float fTessFactor[3] : SV_TessFactor;
float fInsideTessFactor : SV_InsideTessFactor;
};
struct DsOutput
{
float4 f4Position : SV_Position;
float2 f2TexCoord : TEXCOORD0;
};
HsInput vert(VsInput i)
{
HsInput o;
o.f4Position = float4(i.vertex, 1.0);
o.f3Normal = i.normal;
o.f2TexCoord = i.texcoord;
return o;
}
[domain("tri")]
[partitioning("integer")]
[outputtopology("triangle_cw")]
[patchconstantfunc("hullConst")]
[outputcontrolpoints(3)]
HsControlPointOutput hull(InputPatch<HsInput, 3> i, uint id : SV_OutputControlPointID)
{
HsControlPointOutput o = (HsControlPointOutput)0;
o.f3Position = i[id].f4Position.xyz;
o.f3Normal = i[id].f3Normal;
o.f2TexCoord = i[id].f2TexCoord;
return o;
}
HsConstantOutput hullConst(InputPatch<HsInput, 3> i)
{
HsConstantOutput o = (HsConstantOutput)0;
float4 p0 = i[0].f4Position;
float4 p1 = i[1].f4Position;
float4 p2 = i[2].f4Position;
float4 tessFactor = UnityDistanceBasedTess(p0, p1, p2, _TessMinDist, _TessMaxDist, _TessFactor);
o.fTessFactor[0] = tessFactor.x;
o.fTessFactor[1] = tessFactor.y;
o.fTessFactor[2] = tessFactor.z;
o.fInsideTessFactor = tessFactor.w;
return o;
}
[domain("tri")]
DsOutput domain(
HsConstantOutput hsConst,
const OutputPatch<HsControlPointOutput, 3> i,
float3 bary : SV_DomainLocation)
{
DsOutput o = (DsOutput)0;
float3 f3Position =
bary.x * i[0].f3Position +
bary.y * i[1].f3Position +
bary.z * i[2].f3Position;
float3 f3Normal = normalize(
bary.x * i[0].f3Normal +
bary.y * i[1].f3Normal +
bary.z * i[2].f3Normal);
o.f2TexCoord =
bary.x * i[0].f2TexCoord +
bary.y * i[1].f2TexCoord +
bary.z * i[2].f2TexCoord;
uddBendNormal(f3Position.x, f3Normal, _Radius, _Width);
uddBendVertex(f3Position, _Radius, _Width, _Thickness);
float disp = length(_DispTex.SampleLevel(sampler_DispTex, o.f2TexCoord, 0)) * _DispFactor;
f3Position.xyz += f3Normal * disp;
o.f4Position = mul(UNITY_MATRIX_MVP, float4(f3Position.xyz, 1.0));
return o;
}
fixed4 frag(DsOutput i) : SV_Target
{
return uddGetScreenTexture(i.f2TexCoord);
}
ENDCG
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma hull hull
#pragma domain domain
#pragma multi_compile ___ INVERT_X
#pragma multi_compile ___ INVERT_Y
#pragma multi_compile ___ ROTATE90 ROTATE180 ROTATE270
#pragma multi_compile ___ USE_CLIP
#pragma multi_compile ___ BEND_ON
#pragma multi_compile _FORWARD_Y _FORWARD_Z
ENDCG
}
}
Fallback "Unlit/Texture"
}

View File

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

View File

@@ -5,11 +5,11 @@ Properties
{
_Color ("Color", Color) = (1, 1, 1, 1)
_MainTex ("Texture", 2D) = "white" {}
_CursorTex ("Cursor Texture", 2D) = "white" {}
[KeywordEnum(Y, Z)] _Forward("Mesh Forward Direction", Int) = 0
[Toggle(BEND_ON)] _Bend("Use Bend", Int) = 0
[PowerSlider(10.0)] _Radius("Bend Radius", Range(1, 100)) = 30
[PowerSlider(10.0)] _Thickness("Thickness", Range(0.01, 10)) = 1
_Width("Width", Range(0.0, 10.0)) = 1.0
[KeywordEnum(Off, Front, Back)] _Cull("Culling", Int) = 2
}
@@ -33,7 +33,7 @@ half _Thickness;
v2f vert(appdata v)
{
v2f o;
uddBendVertex(v.vertex, _Radius, _Width, _Thickness);
uddBendVertex(v.vertex.xyz, _Radius, _Width, _Thickness);
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
@@ -41,7 +41,7 @@ v2f vert(appdata v)
fixed4 frag(v2f i) : SV_Target
{
return fixed4(uddGetScreenTextureWithCursor(i.uv).rgb, 1.0) * _Color;
return fixed4(uddGetScreenTexture(i.uv).rgb, 1.0) * _Color;
}
ENDCG
@@ -54,9 +54,9 @@ Pass
#pragma multi_compile ___ INVERT_X
#pragma multi_compile ___ INVERT_Y
#pragma multi_compile ___ ROTATE90 ROTATE180 ROTATE270
#pragma multi_compile ___ USE_BEND
#pragma multi_compile ___ USE_CLIP
#pragma multi_compile _BEND_OFF _BEND_Y _BEND_Z
#pragma multi_compile ___ BEND_ON
#pragma multi_compile _FORWARD_Y _FORWARD_Z
ENDCG
}

View File

@@ -6,10 +6,12 @@
#include "IUnityInterface.h"
#include "IUnityGraphicsD3D11.h"
#include "Common.h"
#include "Debug.h"
using namespace Microsoft::WRL;
extern IUnityInterfaces* g_unity;
extern std::unique_ptr<MonitorManager> g_manager;
extern std::queue<Message> g_messages;
@@ -33,7 +35,45 @@ const std::unique_ptr<MonitorManager>& GetMonitorManager()
}
LUID GetUnityAdapterLuid()
{
const auto device = GetUnity()->Get<IUnityGraphicsD3D11>()->GetDevice();
Microsoft::WRL::ComPtr<IDXGIDevice1> dxgiDevice;
if (FAILED(device->QueryInterface(IID_PPV_ARGS(&dxgiDevice)))){
Debug::Error("QueryInterface from IUnityGraphicsD3D11 to IDXGIDevice1 failed.");
return LUID();
}
Microsoft::WRL::ComPtr<IDXGIAdapter> dxgiAdapter;
if (FAILED(dxgiDevice->GetAdapter(&dxgiAdapter))) {
Debug::Error("QueryInterface from IDXGIDevice1 to IDXGIAdapter failed.");
return LUID();
}
DXGI_ADAPTER_DESC adapterDesc;
dxgiAdapter->GetDesc(&adapterDesc);
return adapterDesc.AdapterLuid;
}
void SendMessageToUnity(Message message)
{
g_messages.push(message);
}
ScopedTimer::ScopedTimer(TimerFuncType&& func)
: func_(func)
, start_(std::chrono::high_resolution_clock::now())
{
}
ScopedTimer::~ScopedTimer()
{
const auto end = std::chrono::high_resolution_clock::now();
const auto time = std::chrono::duration_cast<microseconds>(end - start_);
func_(time);
}

View File

@@ -1,21 +1,58 @@
#pragma once
#include <functional>
#include <memory>
#include <chrono>
#include <wrl/client.h>
// Unity interface and ID3D11Device getters
// Unity interface getter
struct IUnityInterfaces;
IUnityInterfaces* GetUnity();
// ID3D11Device (in Unity) getter
struct ID3D11Device;
Microsoft::WRL::ComPtr<ID3D11Device> GetDevice();
// Manager getter
class MonitorManager;
const std::unique_ptr<MonitorManager>& GetMonitorManager();
// Get adapter LUID to check the adapter of the monitor is same as Unity one.
LUID GetUnityAdapterLuid();
// Releaser
class ScopedReleaser
{
public:
using ReleaseFuncType = std::function<void()>;
ScopedReleaser(ReleaseFuncType&& func) : func_(func) {}
~ScopedReleaser() { func_(); }
private:
const ReleaseFuncType func_;
};
// Timer
class ScopedTimer
{
public:
using microseconds = std::chrono::microseconds;
using TimerFuncType = std::function<void(microseconds)>;
ScopedTimer(TimerFuncType&& func);
~ScopedTimer();
private:
const TimerFuncType func_;
const std::chrono::time_point<std::chrono::steady_clock> start_;
};
// Message is pooled and fetch from Unity.
enum class Message
@@ -28,13 +65,44 @@ enum class Message
void SendMessageToUnity(Message message);
// Buffer
template <class T>
class Buffer
{
public:
Buffer() {}
~Buffer() {}
Buffer()
{
}
Buffer(const Buffer& other)
{
value_.reset();
size_ = 0;
ExpandIfNeeded(other.size_);
memcpy_s(value_.get(), size_, other.value_.get(), other.size_);
}
Buffer<T>& operator=(const Buffer& other)
{
if (&other == this) return *this;
value_.reset();
size_ = 0;
ExpandIfNeeded(other.size_);
memcpy_s(value_.get(), size_, other.value_.get(), other.size_);
return *this;
}
~Buffer()
{
}
bool Empty() const
{
return !value_;
}
void ExpandIfNeeded(UINT size)
{
@@ -83,7 +151,7 @@ public:
return value_ != nullptr;
}
T operator [](UINT index) const
const T operator [](UINT index) const
{
if (index >= size_)
{
@@ -93,6 +161,16 @@ public:
return value_[index];
}
T& operator [](UINT index)
{
if (index >= size_)
{
Debug::Error("Array index out of range: ", index, size_);
return value_[0];
}
return value_[index];
}
private:
std::unique_ptr<T[]> value_;
UINT size_ = 0;

View File

@@ -1,15 +1,15 @@
#include <d3d11.h>
#include <wrl/client.h>
#include "Debug.h"
#include "MonitorManager.h"
#include "Monitor.h"
#include "Cursor.h"
#include "Debug.h"
#include "Monitor.h"
#include "Duplicator.h"
using namespace Microsoft::WRL;
Cursor::Cursor(Monitor* monitor)
: monitor_(monitor)
Cursor::Cursor()
{
}
@@ -19,7 +19,7 @@ Cursor::~Cursor()
}
void Cursor::UpdateBuffer(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
void Cursor::UpdateBuffer(Duplicator* duplicator, const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
{
if (frameInfo.LastMouseUpdateTime.QuadPart == 0)
{
@@ -27,27 +27,17 @@ void Cursor::UpdateBuffer(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
}
isVisible_ = frameInfo.PointerPosition.Visible != 0;
if (isVisible_)
{
GetMonitorManager()->SetCursorMonitorId(monitor_->GetId());
}
x_ = frameInfo.PointerPosition.Position.x;
y_ = frameInfo.PointerPosition.Position.y;
timestamp_ = frameInfo.LastMouseUpdateTime;
if (!IsCursorOnParentMonitor())
{
return;
}
if (frameInfo.PointerShapeBufferSize == 0)
{
return;
}
apiBuffer_.ExpandIfNeeded(frameInfo.PointerShapeBufferSize);
if (!apiBuffer_)
buffer_.ExpandIfNeeded(frameInfo.PointerShapeBufferSize);
if (!buffer_)
{
return;
}
@@ -55,16 +45,16 @@ void Cursor::UpdateBuffer(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
// Get mouse pointer information
UINT bufferSize;
DXGI_OUTDUPL_POINTER_SHAPE_INFO shapeInfo;
const auto hr = monitor_->GetDeskDupl()->GetFramePointerShape(
apiBuffer_.Size(),
apiBuffer_.Get(),
const auto hr = duplicator->GetDuplication()->GetFramePointerShape(
buffer_.Size(),
buffer_.Get(),
&bufferSize,
&shapeInfo);
if (FAILED(hr))
{
Debug::Error("Cursor::UpdateBuffer() => GetFramePointerShape() failed.");
apiBuffer_.Reset();
buffer_.Reset();
return;
}
@@ -72,264 +62,329 @@ void Cursor::UpdateBuffer(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
}
void Cursor::UpdateTexture()
void Cursor::UpdateTexture(
Duplicator* duplicator,
const ComPtr<ID3D11Texture2D>& desktopTexture)
{
if (!IsCursorOnParentMonitor())
auto monitor = duplicator->GetMonitor();
// Check desktop texure
if (desktopTexture == nullptr)
{
Debug::Error("Cursor::UpdateTexture() => Desktop texture is null.");
return;
}
// Cursor information
const bool isMono = GetType() == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME;
const bool isColorMask = GetType() == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR;
const auto cursorImageWidth = GetWidth();
const auto cursorImageWidth = GetWidth();
const auto cursorImageHeight = GetHeight();
const auto cursorImagePitch = GetPitch();
const auto cursorImagePitch = GetPitch();
// Captured area
auto capturedImageWidth = cursorImageWidth;
auto capturedImageHeight = cursorImageHeight;
// Monitor orientation
const auto monitorRot = static_cast<DXGI_MODE_ROTATION>(monitor->GetRotation());
const auto isMonitorPortrait =
monitorRot == DXGI_MODE_ROTATION_ROTATE90 ||
monitorRot == DXGI_MODE_ROTATION_ROTATE270;
// Captured size (desktop cooridinates).
auto capturedImageWidth = !isMonitorPortrait ? cursorImageWidth : cursorImageHeight;
auto capturedImageHeight = !isMonitorPortrait ? cursorImageHeight : cursorImageWidth;
// Convert the buffer given by API into BGRA32
const UINT bgraBufferSize = cursorImageWidth * cursorImageHeight * 4;
bgra32Buffer_.ExpandIfNeeded(bgraBufferSize);
bgraBuffer_.ExpandIfNeeded(bgraBufferSize);
// Check buffers
if (!bgra32Buffer_ || !apiBuffer_)
if (!bgraBuffer_ || !buffer_)
{
Debug::Error("Cursor::UpdateTexture() => no buffer.");
return;
}
// Desktop size
const int monitorWidth = monitor->GetWidth();
const int monitorHeight = monitor->GetHeight();
const int desktopImageWidth = !isMonitorPortrait ? monitorWidth : monitorHeight;
const int desktopImageHeight = !isMonitorPortrait ? monitorHeight : monitorWidth;
// x_, y_ are cooridinates in rotated monitor.
// desktopX, desktopY are coordinates in captured desktop image (always landscape).
int desktopX, desktopY;
switch (monitorRot)
{
case DXGI_MODE_ROTATION_ROTATE90:
{
desktopX = y_;
desktopY = (desktopImageHeight - 1) - x_ - cursorImageWidth;
break;
}
case DXGI_MODE_ROTATION_ROTATE180:
{
desktopX = (desktopImageWidth - 1) - x_ - cursorImageWidth;
desktopY = (desktopImageHeight - 1) - y_ - cursorImageHeight;
break;
}
case DXGI_MODE_ROTATION_ROTATE270:
{
desktopX = (desktopImageWidth - 1) - y_ - cursorImageHeight;
desktopY = x_;
break;
}
case DXGI_MODE_ROTATION_IDENTITY:
case DXGI_MODE_ROTATION_UNSPECIFIED:
default:
{
desktopX = x_;
desktopY = y_;
break;
}
}
// Calculate information to capture desktop image under cursor.
int cursorOffsetX = 0;
int cursorOffsetY = 0;
int capturedImageLeft = desktopX;
int capturedImageTop = desktopY;
int capturedImageRight = desktopX + capturedImageWidth;
int capturedImageBottom = desktopY + capturedImageHeight;
if (capturedImageLeft < 0)
{
capturedImageWidth -= -desktopX;
cursorOffsetX = -desktopX;
capturedImageLeft = 0;
}
if (capturedImageRight >= desktopImageWidth)
{
capturedImageWidth -= capturedImageRight - desktopImageWidth;
capturedImageRight = desktopImageWidth - 1;
}
if (capturedImageTop < 0)
{
capturedImageHeight -= -desktopY;
cursorOffsetY = -desktopY;
capturedImageTop = 0;
}
if (capturedImageBottom >= desktopImageHeight)
{
capturedImageHeight -= capturedImageBottom - desktopImageHeight;
capturedImageBottom = desktopImageHeight - 1;
}
// Check if box is inner desktop area
if (capturedImageLeft < 0 ||
capturedImageTop < 0 ||
capturedImageRight >= desktopImageWidth ||
capturedImageBottom >= desktopImageHeight)
{
Debug::Error("Cursor::UpdateTexture() => box is out of area.");
Debug::Error(
" ",
"(", capturedImageLeft, ", ", capturedImageTop, ")",
" ~ (", capturedImageRight, ", ", capturedImageBottom, ") > ",
"(", desktopImageWidth, ", ", desktopImageHeight, ")");
return;
}
if (capturedImageWidth == 0 || capturedImageHeight == 0)
{
return;
}
// If masked, copy the desktop image and merge it with masked image.
if (isMono || isColorMask)
capturedImageArea_ = D3D11_BOX
{
static_cast<UINT>(capturedImageLeft),
static_cast<UINT>(capturedImageTop),
0,
static_cast<UINT>(capturedImageRight),
static_cast<UINT>(capturedImageBottom),
1
};
// Create texture for capturing desktop image
ComPtr<ID3D11Texture2D> texture;
{
const auto monitorWidth = monitor_->GetWidth();
const auto monitorHeight = monitor_->GetHeight();
const auto monitorRot = static_cast<DXGI_MODE_ROTATION>(monitor_->GetRotation());
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;
auto x = x_;
auto y = y_;
auto colMin = 0;
auto colMax = cursorImageWidth;
auto rowMin = 0;
auto rowMax = cursorImageHeight;
if (x < 0)
{
x = 0;
capturedImageWidth = cursorImageWidth + x_;
colMin = cursorImageWidth - capturedImageWidth;
}
if (x + capturedImageWidth >= monitorWidth)
{
capturedImageWidth = monitorWidth - x_;
colMax = capturedImageWidth;
}
if (y < 0)
{
y = 0;
capturedImageHeight = cursorImageHeight + y_;
rowMin = cursorImageHeight - capturedImageHeight;
}
if (y + capturedImageHeight >= monitorHeight)
{
capturedImageHeight = monitorHeight - y_;
rowMax = capturedImageHeight;
}
D3D11_BOX box;
box.front = 0;
box.back = 1;
switch (monitorRot)
{
case DXGI_MODE_ROTATION_ROTATE90:
{
box.left = y;
box.top = monitorWidth - x - capturedImageWidth;
box.right = y + capturedImageWidth;
box.bottom = monitorWidth - x;
break;
}
case DXGI_MODE_ROTATION_ROTATE180:
{
box.left = monitorWidth - x - capturedImageWidth;
box.top = monitorHeight - y - capturedImageHeight;
box.right = monitorWidth - x;
box.bottom = monitorHeight - y;
break;
}
case DXGI_MODE_ROTATION_ROTATE270:
{
box.left = monitorHeight - y - capturedImageHeight;
box.top = x;
box.right = monitorHeight - y;
box.bottom = x + capturedImageWidth;
break;
}
case DXGI_MODE_ROTATION_IDENTITY:
case DXGI_MODE_ROTATION_UNSPECIFIED:
{
box.left = x;
box.top = y;
box.right = x + capturedImageWidth;
box.bottom = y + capturedImageHeight;
break;
}
}
if (box.left < 0 ||
box.top < 0 ||
box.right > static_cast<UINT>(desktopImageWidth) ||
box.bottom > static_cast<UINT>(desktopImageHeight))
{
Debug::Error("Cursor::UpdateTexture() => box is out of area.");
Debug::Error(
" ",
"(", box.left, ", ", box.top, ")",
" ~ (", box.right, ", ", box.bottom, ") > ",
"(", desktopImageWidth, ", ", desktopImageHeight, ")");
return;
}
if (monitor_->GetUnityTexture() == nullptr)
{
Debug::Error("Cursor::UpdateTexture() => Monitor::GetUnityTexture() is null.");
return;
}
D3D11_TEXTURE2D_DESC desc;
desc.Width = capturedImageWidth;
desc.Height = capturedImageHeight;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.Width = capturedImageWidth;
desc.Height = capturedImageHeight;
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;
desc.Usage = D3D11_USAGE_STAGING;
desc.BindFlags = 0;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
ComPtr<ID3D11Texture2D> texture;
if (FAILED(GetDevice()->CreateTexture2D(&desc, nullptr, &texture)))
if (FAILED(duplicator->GetDevice()->CreateTexture2D(&desc, nullptr, &texture)))
{
Debug::Error("Cursor::UpdateTexture() => GetDevice()->CreateTexture2D() failed.");
return;
}
}
// Copy desktop image to the texture
{
ComPtr<ID3D11DeviceContext> context;
duplicator->GetDevice()->GetImmediateContext(&context);
context->CopySubresourceRegion(texture.Get(), 0, 0, 0, 0, desktopTexture.Get(), 0, &capturedImageArea_);
}
// Get mapped surface to access pixels in CPU
ComPtr<IDXGISurface> surface;
if (FAILED(texture.As(&surface)))
{
Debug::Error("Cursor::UpdateTexture() => texture.As() failed.");
return;
}
DXGI_MAPPED_RECT mappedSurface;
if (FAILED(surface->Map(&mappedSurface, DXGI_MAP_READ)))
{
Debug::Error("Cursor::UpdateTexture() => surface->Map() failed.");
return;
}
// Finally, get the desktop texture under the mouse cursor.
const auto desktop32 = reinterpret_cast<UINT*>(mappedSurface.pBits);
const UINT desktopPitch = mappedSurface.Pitch / sizeof(UINT);
// Rotate cursor image to match the monitor orientation
Buffer<BYTE> rotatedBuffer_;
rotatedBuffer_.ExpandIfNeeded(buffer_.Size());
for (int y = 0; y < capturedImageHeight; ++y)
{
for (int x = 0; x < capturedImageWidth; ++x)
{
ComPtr<ID3D11DeviceContext> context;
GetDevice()->GetImmediateContext(&context);
context->CopySubresourceRegion(texture.Get(), 0, 0, 0, 0, monitor_->GetUnityTexture(), 0, &box);
}
// Cursor coordinates
int cursorX, cursorY;
ComPtr<IDXGISurface> surface;
if (FAILED(texture.As(&surface)))
{
Debug::Error("Cursor::UpdateTexture() => texture->QueryInterface() failed.");
return;
}
DXGI_MAPPED_RECT mappedSurface;
if (FAILED(surface->Map(&mappedSurface, DXGI_MAP_READ)))
{
Debug::Error("Cursor::UpdateTexture() => surface->Map() failed.");
return;
}
// Finally, get the texture behind the mouse cursor.
const auto desktop32 = reinterpret_cast<UINT*>(mappedSurface.pBits);
const UINT desktopPitch = mappedSurface.Pitch / sizeof(UINT);
// Take the monitor orientation into consideration.
const auto getDesktop32 = [&](int col, int row)
{
switch (monitorRot)
{
case DXGI_MODE_ROTATION_ROTATE90:
return desktop32[(capturedImageWidth - 1 - col) * desktopPitch + row];
{
cursorX = (cursorImageWidth - 1) - (y + cursorOffsetY);
cursorY = (x + cursorOffsetX);
break;
}
case DXGI_MODE_ROTATION_ROTATE180:
return desktop32[(capturedImageHeight - 1 - row) * desktopPitch + (capturedImageWidth - 1 - col)];
{
cursorX = (cursorImageWidth - 1) - (x + cursorOffsetX);
cursorY = (cursorImageHeight - 1) - (y + cursorOffsetY);
break;
}
case DXGI_MODE_ROTATION_ROTATE270:
return desktop32[col * desktopPitch + (capturedImageHeight - 1 - row)];
{
cursorX = (y + cursorOffsetY);
cursorY = (cursorImageHeight - 1) - (x + cursorOffsetX);
break;
}
case DXGI_MODE_ROTATION_IDENTITY:
case DXGI_MODE_ROTATION_UNSPECIFIED:
return desktop32[row * desktopPitch + col];
break;
}
};
// Access RGBA values at the same time
auto output32 = reinterpret_cast<UINT*>(bgra32Buffer_.Get());
if (isMono)
{
for (int row = rowMin, y = 0; row < rowMax; ++row, ++y)
{
for (int col = colMin, x = 0; col < colMax; ++col, ++x)
default:
{
BYTE mask = 0b10000000 >> (col % 8);
const BYTE andMask = apiBuffer_[col / 8 + row * cursorImagePitch] & mask;
const BYTE xorMask = apiBuffer_[col / 8 + (row + capturedImageHeight) * cursorImagePitch] & mask;
const UINT andMask32 = andMask ? 0xFFFFFFFF : 0x00000000;
const UINT xorMask32 = xorMask ? 0xFFFFFFFF : 0x00000000;
output32[row * cursorImageWidth + col] = (getDesktop32(x, y) & andMask32) ^ xorMask32;
cursorX = (x + cursorOffsetX);
cursorY = (y + cursorOffsetY);
break;
}
}
}
else // DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR
{
const auto buffer32 = reinterpret_cast<UINT*>(apiBuffer_.Get());
for (int row = rowMin, y = 0; row < rowMax; ++row, ++y)
const auto outputIndex = y * capturedImageWidth + x;
const auto desktopIndex = y * desktopPitch + x;
const auto cursorIndex = cursorY * cursorImageWidth + cursorX;
const auto buffer32 = buffer_.As<UINT>();
auto output32 = bgraBuffer_.As<UINT>();
switch (GetType())
{
for (int col = colMin, x = 0; col < colMax; ++col, ++x)
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME:
{
const int i = col + row * cursorImageWidth;
const int j = col + row * cursorImagePitch / sizeof(UINT);
UINT mask = 0xFF000000 & buffer32[j];
BYTE mask = 0b10000000 >> (cursorX % 8);
const BYTE andMask = buffer_[cursorX / 8 + cursorY * cursorImagePitch] & mask;
const BYTE xorMask = buffer_[cursorX / 8 + (cursorY + cursorImageHeight) * cursorImagePitch] & mask;
const UINT andMask32 = andMask ? 0xFFFFFFFF : 0x00000000;
const UINT xorMask32 = xorMask ? 0xFFFFFFFF : 0x00000000;
output32[outputIndex] = (desktop32[desktopIndex] & andMask32) ^ xorMask32;
break;
}
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR:
{
UINT mask = 0xFF000000 & buffer32[cursorIndex];
if (mask)
{
output32[i] = (getDesktop32(x, y) ^ buffer32[j]) | 0xFF000000;
output32[outputIndex] = (desktop32[desktopIndex] ^ buffer32[cursorIndex]) | 0xFF000000;
}
else
{
output32[i] = buffer32[j] | 0xFF000000;
output32[outputIndex] = buffer32[cursorIndex] | 0xFF000000;
}
break;
}
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR:
{
const auto desktop = reinterpret_cast<BYTE*>(&desktop32[desktopIndex]);
const auto desktopB = desktop[0];
const auto desktopG = desktop[1];
const auto desktopR = desktop[2];
const auto desktopA = desktop[3];
const auto cursor = buffer_.Get(cursorIndex * 4);
const auto cursorB = cursor[0];
const auto cursorG = cursor[1];
const auto cursorR = cursor[2];
const auto cursorA = cursor[3];
const auto a0 = cursorA / 255.f;
const auto a1 = 1.f - a0;
auto output = reinterpret_cast<BYTE*>(&output32[outputIndex]);
output[0] = static_cast<BYTE>(cursorB * a0 + desktopB * a1);
output[1] = static_cast<BYTE>(cursorG * a0 + desktopG * a1);
output[2] = static_cast<BYTE>(cursorR * a0 + desktopR * a1);
output[3] = desktopA;
break;
}
default:
{
Debug::Error("Cursor::UpdateTexture() => Unknown cursor type");
return;
}
}
}
}
if (FAILED(surface->Unmap()))
{
return;
}
}
else // DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR
if (FAILED(surface->Unmap()))
{
auto output32 = reinterpret_cast<UINT*>(bgra32Buffer_.Get());
const auto buffer32 = reinterpret_cast<UINT*>(apiBuffer_.Get());
for (int i = 0; i < cursorImageWidth * cursorImageHeight; ++i)
{
output32[i] = buffer32[i];
}
Debug::Error("Cursor::UpdateTexture() => surface->Unmap() failed.");
return;
}
}
void Cursor::Draw(const Microsoft::WRL::ComPtr<ID3D11Texture2D>& texture)
{
if (texture == nullptr)
{
Debug::Error("Cursor::UpdateTexture() => Desktop texture is null.");
return;
}
const auto capturedImageWidth = capturedImageArea_.right - capturedImageArea_.left;
ComPtr<ID3D11DeviceContext> context;
GetDevice()->GetImmediateContext(&context);
context->UpdateSubresource(texture.Get(), 0, &capturedImageArea_, bgraBuffer_.Get(), capturedImageWidth * 4, 0);
}
void Cursor::GetTexture(ID3D11Texture2D* texture)
{
if (!bgra32Buffer_)
if (!bgraBuffer_)
{
Debug::Error("Cursor::GetTexture() => bgra32Buffer is null.");
return;
@@ -356,7 +411,7 @@ void Cursor::GetTexture(ID3D11Texture2D* texture)
ComPtr<ID3D11DeviceContext> context;
GetDevice()->GetImmediateContext(&context);
context->UpdateSubresource(texture, 0, nullptr, bgra32Buffer_.Get(), GetWidth() * 4, 0);
context->UpdateSubresource(texture, 0, nullptr, bgraBuffer_.Get(), GetWidth() * 4, 0);
}
@@ -401,10 +456,4 @@ int Cursor::GetPitch() const
int Cursor::GetType() const
{
return shapeInfo_.Type;
}
bool Cursor::IsCursorOnParentMonitor() const
{
return GetMonitorManager()->GetCursorMonitorId() == monitor_->GetId();
}

View File

@@ -2,18 +2,25 @@
#include <d3d11.h>
#include <dxgi1_2.h>
#include <wrl/client.h>
#include <memory>
#include "Common.h"
class Monitor;
class Duplicator;
class Cursor
{
public:
explicit Cursor(Monitor* monitor);
explicit Cursor();
~Cursor();
void UpdateBuffer(const DXGI_OUTDUPL_FRAME_INFO& frameInfo);
void UpdateTexture();
void UpdateBuffer(
Duplicator* duplicator,
const DXGI_OUTDUPL_FRAME_INFO& frameInfo);
void UpdateTexture(
Duplicator* duplicator,
const Microsoft::WRL::ComPtr<ID3D11Texture2D>& desktopTexture);
void Draw(const Microsoft::WRL::ComPtr<ID3D11Texture2D>& texture);
void GetTexture(ID3D11Texture2D* texture);
bool IsVisible() const;
@@ -25,14 +32,12 @@ public:
int GetType() const;
private:
bool IsCursorOnParentMonitor() const;
Monitor* monitor_;
bool isVisible_ = false;
int x_ = -1;
int y_ = -1;
Buffer<BYTE> apiBuffer_;
Buffer<BYTE> bgra32Buffer_;
Buffer<BYTE> buffer_;
Buffer<BYTE> bgraBuffer_;
DXGI_OUTDUPL_POINTER_SHAPE_INFO shapeInfo_;
LARGE_INTEGER timestamp_;
D3D11_BOX capturedImageArea_;
};

View File

@@ -4,15 +4,21 @@
#include "Debug.h"
decltype(Debug::mode_) Debug::mode_ = Debug::Mode::File;
decltype(Debug::logFunc_) Debug::logFunc_ = nullptr;
decltype(Debug::errFunc_) Debug::errFunc_ = nullptr;
decltype(Debug::fs_) Debug::fs_;
decltype(Debug::ss_) Debug::ss_;
decltype(Debug::isInitialized_) Debug::isInitialized_ = false;
decltype(Debug::mode_) Debug::mode_ = Debug::Mode::File;
decltype(Debug::logFunc_) Debug::logFunc_ = nullptr;
decltype(Debug::errFunc_) Debug::errFunc_ = nullptr;
decltype(Debug::fs_) Debug::fs_;
decltype(Debug::ss_) Debug::ss_;
decltype(Debug::mutex_) Debug::mutex_;
void Debug::Initialize()
{
if (isInitialized_) return;
isInitialized_ = true;
if (mode_ == Mode::File)
{
fs_.open("uDesktopDuplication.log");
@@ -23,6 +29,9 @@ void Debug::Initialize()
void Debug::Finalize()
{
if (!isInitialized_) return;
isInitialized_ = false;
if (mode_ == Mode::File)
{
Debug::Log("Stop");

View File

@@ -1,9 +1,18 @@
#pragma once
#include <time.h>
#include <fstream>
#include <sstream>
#include <mutex>
#include "Common.h"
#include "IUnityInterface.h"
// Debug flag
#define UDD_DEBUG_ON
// Logging
class Debug
{
@@ -64,8 +73,12 @@ private:
{
switch (level)
{
case Level::Log : logFunc_(ss_.str().c_str()); break;
case Level::Error : errFunc_(ss_.str().c_str()); break;
case Level::Log :
if (logFunc_) logFunc_(ss_.str().c_str());
break;
case Level::Error :
if (errFunc_) errFunc_(ss_.str().c_str());
break;
}
}
break;
@@ -103,6 +116,7 @@ public:
template <class Arg, class... RestArgs>
static void Log(Arg&& arg, RestArgs&&... restArgs)
{
std::lock_guard<std::mutex> lock(mutex_);
Output("[uDD::Log]");
OutputTime();
Output(" ");
@@ -112,6 +126,7 @@ public:
template <class Arg, class... RestArgs>
static void Error(Arg&& arg, RestArgs&&... restArgs)
{
std::lock_guard<std::mutex> lock(mutex_);
Output("[uDD::Err]");
OutputTime();
Output(" ");
@@ -119,9 +134,27 @@ public:
}
private:
static bool isInitialized_;
static Mode mode_;
static std::ofstream fs_;
static std::ostringstream ss_;
static DebugLogFuncPtr logFunc_;
static DebugLogFuncPtr errFunc_;
};
static std::mutex mutex_;
};
#ifdef UDD_DEBUG_ON
#define UDD_FUNCTION_SCOPE_TIMER \
ScopedTimer _timer_##__COUNTER__([](std::chrono::microseconds us) \
{ \
Debug::Log(__FUNCTION__, "@", __FILE__, ":", __LINE__, " => ", us.count(), " [us]"); \
});
#define UDD_SCOPE_TIMER(Name) \
ScopedTimer _timer_##__COUNTER__([](std::chrono::microseconds us) \
{ \
Debug::Log(#Name, " => ", us.count(), " [us]"); \
});
#else
#define UDD_FUNCTION_SCOPE_TIMER
#endif

View File

@@ -0,0 +1,97 @@
#pragma once
#include <queue>
#include <d3d11.h>
#include "IUnityInterface.h"
#include "IUnityGraphicsD3D11.h"
#include "Device.h"
#include "Debug.h"
#include "Common.h"
#pragma comment(lib, "d3d11.lib")
using namespace Microsoft::WRL;
IsolatedD3D11Device::IsolatedD3D11Device()
{
}
IsolatedD3D11Device::~IsolatedD3D11Device()
{
}
HRESULT IsolatedD3D11Device::Create(const ComPtr<IDXGIAdapter>& adapter)
{
const auto driverType = adapter ?
D3D_DRIVER_TYPE_UNKNOWN :
D3D_DRIVER_TYPE_HARDWARE;
const auto flags =
D3D11_CREATE_DEVICE_BGRA_SUPPORT; // D2D Compatible
// | D3D11_CREATE_DEVICE_VIDEO_SUPPORT // MediaFoundation
const D3D_FEATURE_LEVEL featureLevelsRequested[] =
{
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_3,
D3D_FEATURE_LEVEL_9_2,
D3D_FEATURE_LEVEL_9_1
};
const UINT numLevelsRequested = sizeof(featureLevelsRequested) / sizeof(D3D_FEATURE_LEVEL);
D3D_FEATURE_LEVEL featureLevelsSupported;
return D3D11CreateDevice(
adapter.Get(),
driverType,
nullptr,
flags,
featureLevelsRequested,
numLevelsRequested,
D3D11_SDK_VERSION,
&device_,
&featureLevelsSupported,
nullptr);
}
ComPtr<ID3D11Device> IsolatedD3D11Device::GetDevice()
{
return device_;
}
Microsoft::WRL::ComPtr<ID3D11Texture2D> IsolatedD3D11Device::GetCompatibleSharedTexture(
const ComPtr<ID3D11Texture2D>& src)
{
D3D11_TEXTURE2D_DESC srcDesc;
src->GetDesc(&srcDesc);
// check if the format and size of the current texture are same as the source one
if (cachedSharedTexture_)
{
D3D11_TEXTURE2D_DESC targetDesc;
cachedSharedTexture_->GetDesc(&targetDesc);
if (targetDesc.Format == srcDesc.Format &&
targetDesc.Width == srcDesc.Width &&
targetDesc.Height == srcDesc.Height)
{
return cachedSharedTexture_;
}
}
// for sharing this texture with unity device
srcDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
if (FAILED(device_->CreateTexture2D(&srcDesc, nullptr, &cachedSharedTexture_)))
{
Debug::Error("IsolatedD3D11Device::GetCompatibleSharedTexture() => Creating shared texture failed.");
return nullptr;
}
return cachedSharedTexture_;
}

View File

@@ -0,0 +1,25 @@
#pragma once
#include <atomic>
#include <vector>
#include <memory>
#include <d3d11.h>
#include <wrl/client.h>
// Thraed safe self created ID3D11Device from specified adapter
class IsolatedD3D11Device
{
public:
IsolatedD3D11Device();
~IsolatedD3D11Device();
HRESULT Create(const Microsoft::WRL::ComPtr<IDXGIAdapter>& adapter);
Microsoft::WRL::ComPtr<ID3D11Device> GetDevice();
Microsoft::WRL::ComPtr<ID3D11Texture2D> GetCompatibleSharedTexture(
const Microsoft::WRL::ComPtr<ID3D11Texture2D>& src);
private:
Microsoft::WRL::ComPtr<ID3D11Device> device_;
Microsoft::WRL::ComPtr<ID3D11Texture2D> cachedSharedTexture_;
};

View File

@@ -0,0 +1,459 @@
#pragma once
#include <chrono>
#include "Duplicator.h"
#include "Monitor.h"
#include "MonitorManager.h"
#include "Cursor.h"
#include "Device.h"
#include "Debug.h"
#include "IUnityInterface.h"
#include "IUnityGraphicsD3D11.h"
using namespace Microsoft::WRL;
Duplicator::Duplicator(Monitor* monitor)
: monitor_(monitor)
{
InitializeDevice();
InitializeDuplication();
CheckUnityAdapter();
}
Duplicator::~Duplicator()
{
Stop();
}
void Duplicator::InitializeDevice()
{
device_ = std::make_shared<IsolatedD3D11Device>();
if (FAILED(device_->Create(monitor_->GetAdapter())))
{
Debug::Error("Monitor::Initialize() => IsolatedD3D11Device::Create() failed.");
state_ = State::Unknown;
}
}
void Duplicator::InitializeDuplication()
{
ComPtr<IDXGIOutput1> output1;
if (FAILED(monitor_->GetOutput().As(&output1))) {
return;
}
auto hr = output1->DuplicateOutput(device_->GetDevice().Get(), &dupl_);
switch (hr)
{
case S_OK:
{
state_ = State::Ready;
const auto rot = static_cast<DXGI_MODE_ROTATION>(monitor_->GetRotation());
Debug::Log("Duplicator::Initialize() => OK.");
Debug::Log(" ID : ", monitor_->GetId());
Debug::Log(" Size : (", monitor_->GetWidth(), ", ", monitor_->GetHeight(), ")");
Debug::Log(" DPI : (", monitor_->GetDpiX(), ", ", monitor_->GetDpiY(), ")");
Debug::Log(" Rot : ",
rot == DXGI_MODE_ROTATION_IDENTITY ? "Landscape" :
rot == DXGI_MODE_ROTATION_ROTATE90 ? "Portrait" :
rot == DXGI_MODE_ROTATION_ROTATE180 ? "Landscape (flipped)" :
rot == DXGI_MODE_ROTATION_ROTATE270 ? "Portrait (flipped)" :
"Unspecified");
break;
}
case E_INVALIDARG:
{
state_ = State::InvalidArg;
Debug::Error("Duplicator::Initialize() => Invalid arguments.");
break;
}
case E_ACCESSDENIED:
{
// For example, when the user presses Ctrl + Alt + Delete and the screen
// switches to admin screen, this error occurs.
state_ = State::AccessDenied;
Debug::Error("Duplicator::Initialize() => Access denied.");
break;
}
case DXGI_ERROR_UNSUPPORTED:
{
// If the display adapter on the computer is running under the Microsoft Hybrid system,
// this error occurs.
state_ = State::Unsupported;
Debug::Error("Duplicator::Initialize() => Unsupported display.");
break;
}
case DXGI_ERROR_NOT_CURRENTLY_AVAILABLE:
{
// When other application use Desktop Duplication API, this error occurs.
state_ = State::CurrentlyNotAvailable;
Debug::Error("Duplicator::Initialize() => Currently not available.");
break;
}
case DXGI_ERROR_SESSION_DISCONNECTED:
{
state_ = State::SessionDisconnected;
Debug::Error("Duplicator::Initialize() => Session disconnected.");
break;
}
default:
{
state_ = State::Unknown;
Debug::Error("Duplicator::Render() => Unknown Error.");
break;
}
}
}
void Duplicator::CheckUnityAdapter()
{
DXGI_ADAPTER_DESC desc;
monitor_->GetAdapter()->GetDesc(&desc);
const auto unityAdapterLuid = GetUnityAdapterLuid();
const auto isUnityAdapter =
(desc.AdapterLuid.LowPart == unityAdapterLuid.LowPart) &&
(desc.AdapterLuid.HighPart == unityAdapterLuid.HighPart);
if (!isUnityAdapter)
{
Debug::Error("Duplicator::CheckUnityAdapter() => The adapter is not same as Unity, and now this case is not supported.");
state_ = State::Unsupported;
}
}
void Duplicator::Start()
{
if (state_ != State::Ready) return;
Stop();
thread_ = std::thread([this]
{
using namespace std::chrono;
state_ = State::Running;
shouldRun_ = true;
while (shouldRun_)
{
const auto frameRate = GetMonitorManager()->GetFrameRate();
const UINT frameMicroSeconds = 1000000 / frameRate;
const UINT frameMilliSeconds = 1000 / frameRate;
ScopedTimer timer([frameMicroSeconds] (microseconds us)
{
const auto waitTime = microseconds(frameMicroSeconds) - us;
if (waitTime > microseconds::zero())
{
std::this_thread::sleep_for(waitTime);
}
});
const auto timeout = static_cast<UINT>(frameMilliSeconds);
Duplicate(timeout);
if (state_ != State::Running)
{
break;
}
}
if (state_ == State::Running)
{
state_ = State::Ready;
}
});
}
void Duplicator::Stop()
{
shouldRun_ = false;
if (thread_.joinable())
{
thread_.join();
}
}
bool Duplicator::IsRunning() const
{
return state_ == State::Running;
}
bool Duplicator::IsError() const
{
return
state_ != State::Ready &&
state_ != State::Running;
}
Duplicator::State Duplicator::GetState() const
{
return state_;
}
Monitor* Duplicator::GetMonitor() const
{
return monitor_;
}
Microsoft::WRL::ComPtr<ID3D11Device> Duplicator::GetDevice()
{
return device_->GetDevice();
}
ComPtr<IDXGIOutputDuplication> Duplicator::GetDuplication()
{
return dupl_;
}
const Duplicator::Frame& Duplicator::GetLastFrame() const
{
std::lock_guard<std::mutex> lock(mutex_);
return lastFrame_;
}
void Duplicator::Duplicate(UINT timeout)
{
if (!dupl_ || !device_) return;
ComPtr<IDXGIResource> resource;
DXGI_OUTDUPL_FRAME_INFO frameInfo;
const auto hr = dupl_->AcquireNextFrame(timeout, &frameInfo, &resource);
if (FAILED(hr))
{
switch (hr)
{
case DXGI_ERROR_ACCESS_LOST:
{
// If any monitor setting has changed (e.g. monitor size has changed),
// it is necessary to re-initialize monitors.
Debug::Log("Duplicator::Duplicate() => DXGI_ERROR_ACCESS_LOST.");
state_ = State::AccessLost;
break;
}
case DXGI_ERROR_WAIT_TIMEOUT:
{
// This often occurs when timeout value is small and it is not problem.
// Debug::Log("Duplicator::Duplicate() => DXGI_ERROR_WAIT_TIMEOUT.");
break;
}
case DXGI_ERROR_INVALID_CALL:
{
Debug::Error("Duplicator::Duplicate() => DXGI_ERROR_INVALID_CALL.");
break;
}
case E_INVALIDARG:
{
Debug::Error("Duplicator::Duplicate() => E_INVALIDARG.");
break;
}
default:
{
state_ = State::Unknown;
Debug::Error("Duplicator::Duplicate() => Unknown Error.");
break;
}
}
return;
}
ScopedReleaser releaser([this]
{
const auto hr = dupl_->ReleaseFrame();
if (FAILED(hr))
{
switch (hr)
{
case DXGI_ERROR_ACCESS_LOST:
{
Debug::Log("Duplicator::Duplicate() => DXGI_ERROR_ACCESS_LOST.");
state_ = State::AccessLost;
break;
}
case DXGI_ERROR_INVALID_CALL:
{
Debug::Error("Duplicator::Duplicate() => DXGI_ERROR_INVALID_CALL.");
break;
}
default:
{
state_ = State::Unknown;
Debug::Error("Duplicator::Duplicate() => Unknown Error.");
break;
}
}
}
});
ComPtr<ID3D11Texture2D> texture;
if (FAILED(resource.As(&texture)))
{
Debug::Error("Duplicator::Duplicate() => IDXGIResource could not be converted to ID3D11Texture2D.");
return;
}
auto sharedTexture = device_->GetCompatibleSharedTexture(texture);
if (!sharedTexture)
{
Debug::Error("Duplicator::Duplicate() => Shared texture is null.");
return;
}
{
UDD_SCOPE_TIMER(CopyResource);
ComPtr<ID3D11DeviceContext> context;
device_->GetDevice()->GetImmediateContext(&context);
context->CopyResource(sharedTexture.Get(), texture.Get());
}
UpdateCursor(sharedTexture, frameInfo);
UpdateMetadata(frameInfo.TotalMetadataBufferSize);
{
std::lock_guard<std::mutex> lock(mutex_);
lastFrame_ = Frame
{
lastFrameId_++,
sharedTexture,
frameInfo,
metaData_
};
}
}
void Duplicator::UpdateCursor(
const ComPtr<ID3D11Texture2D>& texture,
const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
{
auto& manager = GetMonitorManager();
if (frameInfo.PointerPosition.Visible)
{
manager->SetCursorMonitorId(monitor_->GetId());
}
if (monitor_->GetId() == manager->GetCursorMonitorId())
{
auto cursor = manager->GetCursor();
cursor->UpdateBuffer(this, frameInfo);
cursor->UpdateTexture(this, texture);
}
}
void Duplicator::UpdateMetadata(UINT totalBufferSize)
{
metaData_.buffer.ExpandIfNeeded(totalBufferSize);
if (!metaData_.buffer.Empty())
{
UpdateMoveRects();
UpdateDirtyRects();
}
}
void Duplicator::UpdateMoveRects()
{
const auto hr = dupl_->GetFrameMoveRects(
metaData_.buffer.Size(),
metaData_.buffer.As<DXGI_OUTDUPL_MOVE_RECT>(),
&metaData_.moveRectSize);
if (FAILED(hr))
{
switch (hr)
{
case DXGI_ERROR_ACCESS_LOST:
{
Debug::Log("Duplicator::UpdateMoveRects() => DXGI_ERROR_ACCESS_LOST.");
break;
}
case DXGI_ERROR_MORE_DATA:
{
Debug::Error("Duplicator::UpdateMoveRects() => DXGI_ERROR_MORE_DATA.");
break;
}
case DXGI_ERROR_INVALID_CALL:
{
Debug::Error("Duplicator::UpdateMoveRects() => DXGI_ERROR_INVALID_CALL.");
break;
}
case E_INVALIDARG:
{
Debug::Error("Duplicator::UpdateMoveRects() => E_INVALIDARG.");
break;
}
default:
{
Debug::Error("Duplicator::UpdateMoveRects() => Unknown Error.");
break;
}
}
return;
}
}
void Duplicator::UpdateDirtyRects()
{
const auto hr = dupl_->GetFrameDirtyRects(
metaData_.buffer.Size() - metaData_.moveRectSize,
metaData_.buffer.As<RECT>(metaData_.moveRectSize /* offset */),
&metaData_.dirtyRectSize);
if (FAILED(hr))
{
switch (hr)
{
case DXGI_ERROR_ACCESS_LOST:
{
Debug::Log("Duplicator::UpdateDirtyRects() => DXGI_ERROR_ACCESS_LOST.");
break;
}
case DXGI_ERROR_MORE_DATA:
{
Debug::Error("Duplicator::UpdateDirtyRects() => DXGI_ERROR_MORE_DATA.");
break;
}
case DXGI_ERROR_INVALID_CALL:
{
Debug::Error("Duplicator::UpdateDirtyRects() => DXGI_ERROR_INVALID_CALL.");
break;
}
case E_INVALIDARG:
{
Debug::Error("Duplicator::UpdateDirtyRects() => E_INVALIDARG.");
break;
}
default:
{
Debug::Error("Duplicator::UpdateDirtyRects() => Unknown Error.");
break;
}
}
return;
}
}

View File

@@ -0,0 +1,92 @@
#pragma once
#include <d3d11.h>
#include <dxgi1_2.h>
#include <memory>
#include <atomic>
#include <thread>
#include <mutex>
#include <wrl/client.h>
#include "Common.h"
class Monitor;
enum class DuplicatorState
{
NotSet = -1,
Ready = 0,
Running = 1,
InvalidArg = 2,
AccessDenied = 3,
Unsupported = 4,
CurrentlyNotAvailable = 5,
SessionDisconnected = 6,
AccessLost = 7,
TextureSizeInconsistent = 8,
Unknown = 999,
};
class Duplicator
{
public:
using State = DuplicatorState;
struct Metadata
{
Buffer<BYTE> buffer;
UINT moveRectSize = 0;
UINT dirtyRectSize = 0;
};
struct Frame
{
UINT id;
Microsoft::WRL::ComPtr<ID3D11Texture2D> texture;
DXGI_OUTDUPL_FRAME_INFO info;
Metadata metaData;
};
explicit Duplicator(Monitor* monitor);
~Duplicator();
void Start();
void Stop();
bool IsRunning() const;
bool IsError() const;
State GetState() const;
Monitor* GetMonitor() const;
Microsoft::WRL::ComPtr<ID3D11Device> GetDevice();
Microsoft::WRL::ComPtr<IDXGIOutputDuplication> GetDuplication();
const Frame& GetLastFrame() const;
private:
void InitializeDevice();
void InitializeDuplication();
void CheckUnityAdapter();
void Duplicate(UINT timeout);
void UpdateCursor(
const Microsoft::WRL::ComPtr<ID3D11Texture2D>& texture,
const DXGI_OUTDUPL_FRAME_INFO& frameInfo);
void UpdateMetadata(UINT totalBufferSize);
void UpdateMoveRects();
void UpdateDirtyRects();
Monitor* monitor_ = nullptr;
std::atomic<State> state_ = State::Ready;
std::shared_ptr<class IsolatedD3D11Device> device_;
Microsoft::WRL::ComPtr<IDXGIOutputDuplication> dupl_;
Frame lastFrame_;
UINT lastFrameId_ = 0;
volatile bool shouldRun_ = false;
std::thread thread_;
mutable std::mutex mutex_;
Metadata metaData_;
};

View File

@@ -1,303 +1,125 @@
#include <d3d11.h>
#include <ShellScalingAPI.h>
#include <queue>
#include "Monitor.h"
#include "Duplicator.h"
#include "Debug.h"
#include "Cursor.h"
#include "MonitorManager.h"
#include "Monitor.h"
#include "Device.h"
using namespace Microsoft::WRL;
Monitor::Monitor(int id)
: id_(id)
, cursor_(std::make_unique<Cursor>(this))
{
ZeroMemory(&monitorInfo_, sizeof(monitorInfo_));
}
Monitor::~Monitor()
{
if (deskDupl_)
{
deskDupl_->Release();
deskDupl_ = nullptr;
}
duplicator_->Stop();
}
void Monitor::Initialize(IDXGIOutput* output)
void Monitor::Initialize(
const ComPtr<IDXGIAdapter> &adapter,
const ComPtr<IDXGIOutput> &output
)
{
if (FAILED(output->GetDesc(&outputDesc_)))
{
Debug::Error("Monitor::Initialize() => IDXGIOutput::GetDesc() failed.");
return;
}
adapter_ = adapter;
output_ = output;
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(output->GetDesc(&outputDesc_)))
{
Debug::Error("Monitor::Initialize() => IDXGIOutput::GetDesc() failed.");
return;
}
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.
}
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;
}
auto output1 = reinterpret_cast<IDXGIOutput1*>(output);
switch (output1->DuplicateOutput(GetDevice().Get(), &deskDupl_))
{
case S_OK:
{
state_ = State::Available;
Debug::Log("Monitor::Initialize() => OK.");
Debug::Log(" ID : ", GetId());
Debug::Log(" Size : (", GetWidth(), ", ", GetHeight(), ")");
Debug::Log(" DPI : (", GetDpiX(), ", ", GetDpiY(), ")");
break;
}
case E_INVALIDARG:
{
state_ = State::InvalidArg;
Debug::Error("Monitor::Initialize() => Invalid arguments.");
break;
}
case E_ACCESSDENIED:
{
// For example, when the user presses Ctrl + Alt + Delete and the screen
// switches to admin screen, this error occurs.
state_ = State::AccessDenied;
Debug::Error("Monitor::Initialize() => Access denied.");
break;
}
case DXGI_ERROR_UNSUPPORTED:
{
// If the display adapter on the computer is running under the Microsoft Hybrid system,
// this error occurs.
state_ = State::Unsupported;
Debug::Error("Monitor::Initialize() => Unsupported display.");
break;
}
case DXGI_ERROR_NOT_CURRENTLY_AVAILABLE:
{
// When other application use Desktop Duplication API, this error occurs.
state_ = State::CurrentlyNotAvailable;
Debug::Error("Monitor::Initialize() => Currently not available.");
break;
}
case DXGI_ERROR_SESSION_DISCONNECTED:
{
state_ = State::SessionDisconnected;
Debug::Error("Monitor::Initialize() => Session disconnected.");
break;
}
default:
{
state_ = State::Unknown;
Debug::Error("Monitor::Render() => Unknown Error.");
break;
}
}
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::Render(UINT timeout)
void Monitor::Finalize()
{
if (!deskDupl_) return;
StopCapture();
}
ComPtr<IDXGIResource> resource;
DXGI_OUTDUPL_FRAME_INFO frameInfo;
const auto hr = deskDupl_->AcquireNextFrame(timeout, &frameInfo, &resource);
if (FAILED(hr))
{
switch (hr)
{
case DXGI_ERROR_ACCESS_LOST:
{
// If any monitor setting has changed (e.g. monitor size has changed),
// it is necessary to re-initialize monitors.
Debug::Log("Monitor::Render() => DXGI_ERROR_ACCESS_LOST.");
state_ = State::AccessLost;
break;
}
case DXGI_ERROR_WAIT_TIMEOUT:
{
// This often occurs when timeout value is small and it is not problem.
// Debug::Log("Monitor::Render() => DXGI_ERROR_WAIT_TIMEOUT.");
break;
}
case DXGI_ERROR_INVALID_CALL:
{
Debug::Error("Monitor::Render() => DXGI_ERROR_INVALID_CALL.");
break;
}
case E_INVALIDARG:
{
Debug::Error("Monitor::Render() => E_INVALIDARG.");
break;
}
default:
{
state_ = State::Unknown;
Debug::Error("Monitor::Render() => Unknown Error.");
break;
}
}
return;
}
void Monitor::Render()
{
const auto& frame = duplicator_->GetLastFrame();
// Get texture
if (unityTexture_)
{
ID3D11Texture2D* texture;
if (FAILED(resource.CopyTo(&texture)))
{
Debug::Error("Monitor::Render() => resource.As() failed.");
if (frame.id == lastFrameId_) return;
lastFrameId_ = frame.id;
if (unityTexture_)
{
D3D11_TEXTURE2D_DESC srcDesc, dstDesc;
frame.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, ")");
return;
}
}
else
{
ComPtr<ID3D11DeviceContext> context;
GetDevice()->GetImmediateContext(&context);
context->CopyResource(unityTexture_, frame.texture.Get());
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);
}
else
{
ComPtr<ID3D11DeviceContext> context;
GetDevice()->GetImmediateContext(&context);
context->CopyResource(unityTexture_, texture);
}
}
auto cursor = GetMonitorManager()->GetCursor();
cursor->Draw(unityTexture_);
}
}
UpdateMetadata(frameInfo);
UpdateCursor(frameInfo);
if (UseGetPixels())
{
CopyTextureFromGpuToCpu(unityTexture_);
}
if (FAILED(deskDupl_->ReleaseFrame()))
hasBeenUpdated_ = true;
}
void Monitor::StartCapture()
{
if (duplicator_->GetState() == DuplicatorState::Ready)
{
Debug::Error("Monitor::Render() => ReleaseFrame() failed.");
duplicator_->Start();
}
}
void Monitor::UpdateCursor(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
void Monitor::StopCapture()
{
cursor_->UpdateBuffer(frameInfo);
cursor_->UpdateTexture();
}
void Monitor::UpdateMetadata(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
{
metaData_.ExpandIfNeeded(frameInfo.TotalMetadataBufferSize);
UpdateMoveRects(frameInfo);
UpdateDirtyRects(frameInfo);
}
void Monitor::UpdateMoveRects(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
{
moveRectSize_ = metaData_.Size();
const auto hr = deskDupl_->GetFrameMoveRects(
moveRectSize_,
metaData_.As<DXGI_OUTDUPL_MOVE_RECT>(),
&moveRectSize_);
if (FAILED(hr))
{
switch (hr)
{
case DXGI_ERROR_ACCESS_LOST:
{
Debug::Log("Monitor::Render() => DXGI_ERROR_ACCESS_LOST (GetFrameMoveRects()).");
break;
}
case DXGI_ERROR_MORE_DATA:
{
Debug::Error("Monitor::Render() => DXGI_ERROR_MORE_DATA (GetFrameMoveRects()).");
break;
}
case DXGI_ERROR_INVALID_CALL:
{
Debug::Error("Monitor::Render() => DXGI_ERROR_INVALID_CALL (GetFrameMoveRects()).");
break;
}
case E_INVALIDARG:
{
Debug::Error("Monitor::Render() => E_INVALIDARG (GetFrameMoveRects()).");
break;
}
default:
{
Debug::Error("Monitor::Render() => Unknown Error (GetFrameMoveRects()).");
break;
}
}
return;
}
}
void Monitor::UpdateDirtyRects(const DXGI_OUTDUPL_FRAME_INFO& frameInfo)
{
dirtyRectSize_ = metaData_.Size() - moveRectSize_;
const auto hr = deskDupl_->GetFrameDirtyRects(
dirtyRectSize_,
metaData_.As<RECT>(moveRectSize_ /* offset */),
&dirtyRectSize_);
if (FAILED(hr))
{
switch (hr)
{
case DXGI_ERROR_ACCESS_LOST:
{
Debug::Log("Monitor::Render() => DXGI_ERROR_ACCESS_LOST (GetFrameDirtyRects()).");
break;
}
case DXGI_ERROR_MORE_DATA:
{
Debug::Error("Monitor::Render() => DXGI_ERROR_MORE_DATA (GetFrameDirtyRects()).");
break;
}
case DXGI_ERROR_INVALID_CALL:
{
Debug::Error("Monitor::Render() => DXGI_ERROR_INVALID_CALL (GetFrameDirtyRects()).");
break;
}
case E_INVALIDARG:
{
Debug::Error("Monitor::Render() => E_INVALIDARG (GetFrameDirtyRects()).");
break;
}
default:
{
Debug::Error("Monitor::Render() => Unknown Error (GetFrameDirtyRects()).");
break;
}
}
return;
}
duplicator_->Stop();
}
@@ -307,9 +129,21 @@ int Monitor::GetId() const
}
MonitorState Monitor::GetState() const
ComPtr<struct IDXGIAdapter> Monitor::GetAdapter()
{
return state_;
return adapter_;
}
ComPtr<struct IDXGIOutput> Monitor::GetOutput()
{
return output_;
}
DuplicatorState Monitor::GetDuplicatorState() const
{
return duplicator_->GetState();
}
@@ -325,21 +159,9 @@ ID3D11Texture2D* Monitor::GetUnityTexture() const
}
IDXGIOutputDuplication* Monitor::GetDeskDupl()
ComPtr<IDXGIOutputDuplication> Monitor::GetDeskDupl()
{
return deskDupl_;
}
const std::unique_ptr<Cursor>& Monitor::GetCursor()
{
return cursor_;
}
void Monitor::GetCursorTexture(ID3D11Texture2D* texture)
{
cursor_->GetTexture(texture);
return duplicator_->GetDuplication();
}
@@ -355,6 +177,12 @@ bool Monitor::IsPrimary() const
}
bool Monitor::HasBeenUpdated() const
{
return hasBeenUpdated_;
}
int Monitor::GetLeft() const
{
return static_cast<int>(outputDesc_.DesktopCoordinates.left);
@@ -411,23 +239,227 @@ int Monitor::GetHeight() const
int Monitor::GetMoveRectCount() const
{
return moveRectSize_ / sizeof(DXGI_OUTDUPL_MOVE_RECT);
const auto& metaData = duplicator_->GetLastFrame().metaData;
return metaData.moveRectSize / sizeof(DXGI_OUTDUPL_MOVE_RECT);
}
DXGI_OUTDUPL_MOVE_RECT* Monitor::GetMoveRects() const
{
return metaData_.As<DXGI_OUTDUPL_MOVE_RECT>();
const auto& metaData = duplicator_->GetLastFrame().metaData;
return metaData.buffer.As<DXGI_OUTDUPL_MOVE_RECT>();
}
int Monitor::GetDirtyRectCount() const
{
return dirtyRectSize_ / sizeof(RECT);
const auto& metaData = duplicator_->GetLastFrame().metaData;
return metaData.dirtyRectSize / sizeof(RECT);
}
RECT* Monitor::GetDirtyRects() const
{
return metaData_.As<RECT>(moveRectSize_);
}
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;
}

View File

@@ -4,42 +4,37 @@
#include <dxgi1_2.h>
#include <wrl/client.h>
#include <memory>
#include <mutex>
#include <thread>
#include "Common.h"
class Cursor;
enum class MonitorState
{
NotSet = -1,
Available = 0,
InvalidArg = 1,
AccessDenied = 2,
Unsupported = 3,
CurrentlyNotAvailable = 4,
SessionDisconnected = 5,
AccessLost = 6,
TextureSizeInconsistent = 7,
Unknown = 999,
};
enum class DuplicatorState;
class Monitor
{
public:
using State = MonitorState;
explicit Monitor(int id);
~Monitor();
void Initialize(IDXGIOutput* output);
void Render(UINT timeout = 0);
void GetCursorTexture(ID3D11Texture2D* texture);
void Initialize(
const Microsoft::WRL::ComPtr<struct IDXGIAdapter> &adapter,
const Microsoft::WRL::ComPtr<struct IDXGIOutput> &output);
void Finalize();
void Render();
void StartCapture();
void StopCapture();
public:
int GetId() const;
State GetState() const;
Microsoft::WRL::ComPtr<struct IDXGIAdapter> GetAdapter();
Microsoft::WRL::ComPtr<struct IDXGIOutput> GetOutput();
DuplicatorState GetDuplicatorState() const;
void SetUnityTexture(ID3D11Texture2D* texture);
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;
@@ -49,29 +44,35 @@ public:
int GetRotation() const;
int GetDpiX() const;
int GetDpiY() const;
IDXGIOutputDuplication* GetDeskDupl();
const std::unique_ptr<Cursor>& GetCursor();
int GetMoveRectCount() const;
DXGI_OUTDUPL_MOVE_RECT* GetMoveRects() const;
int GetDirtyRectCount() const;
RECT* GetDirtyRects() const;
Microsoft::WRL::ComPtr<IDXGIOutputDuplication> GetDeskDupl();
int GetMoveRectCount() const;
DXGI_OUTDUPL_MOVE_RECT* GetMoveRects() const;
int GetDirtyRectCount() const;
RECT* GetDirtyRects() 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;
State state_ = State::NotSet;
std::unique_ptr<Cursor> cursor_;
IDXGIOutputDuplication* deskDupl_ = nullptr;
ID3D11Texture2D* unityTexture_ = nullptr;
bool hasBeenUpdated_ = false;
bool useGetPixels_ = false;
Microsoft::WRL::ComPtr<IDXGIOutput> output_;
Microsoft::WRL::ComPtr<IDXGIAdapter> adapter_;
DXGI_OUTPUT_DESC outputDesc_;
MONITORINFOEX monitorInfo_;
Buffer<BYTE> metaData_;
UINT moveRectSize_ = 0;
UINT dirtyRectSize_ = 0;;
};
std::shared_ptr<class Duplicator> duplicator_;
UINT lastFrameId_ = -1;
ID3D11Texture2D* unityTexture_ = nullptr;
Microsoft::WRL::ComPtr<ID3D11Texture2D> textureForGetPixels_;
Buffer<BYTE> bufferForGetPixels_;
};

View File

@@ -17,9 +17,10 @@
using namespace Microsoft::WRL;
MonitorManager::MonitorManager()
MonitorManager::MonitorManager(LUID unityAdapterLuid)
: unityAdapterLuid_(unityAdapterLuid)
{
Initialize();
}
@@ -51,7 +52,9 @@ void MonitorManager::Initialize()
for (int j = 0; (adapter->EnumOutputs(j, &output) != DXGI_ERROR_NOT_FOUND); ++j)
{
auto monitor = std::make_shared<Monitor>(id++);
monitor->Initialize(output.Get());
const auto unityAdapterLuid = GetUnityAdapterLuid();
monitor->Initialize(adapter, output);
monitor->StartCapture();
monitors_.push_back(monitor);
}
}
@@ -60,10 +63,25 @@ void MonitorManager::Initialize()
void MonitorManager::Finalize()
{
for (const auto& monitor : monitors_)
{
monitor->Finalize();
}
monitors_.clear();
}
void MonitorManager::Update()
{
if (isReinitializationRequired_)
{
Reinitialize();
isReinitializationRequired_ = false;
}
}
void MonitorManager::RequireReinitilization()
{
isReinitializationRequired_ = true;
@@ -104,7 +122,7 @@ bool MonitorManager::HasMonitorCountChanged() const
std::shared_ptr<Monitor> MonitorManager::GetMonitor(int id) const
{
if (id >= 0 && id < monitors_.size())
if (id >= 0 && id < static_cast<int>(monitors_.size()))
{
return monitors_[id];
}
@@ -112,25 +130,9 @@ std::shared_ptr<Monitor> MonitorManager::GetMonitor(int id) const
}
void MonitorManager::Update()
std::shared_ptr<Cursor> MonitorManager::GetCursor() const
{
if (isReinitializationRequired_)
{
Reinitialize();
isReinitializationRequired_ = false;
}
}
void MonitorManager::SetTimeout(int timeout)
{
timeout_ = timeout;
}
int MonitorManager::GetTimeout() const
{
return timeout_;
return cursor_;
}
@@ -165,4 +167,16 @@ int MonitorManager::GetTotalHeight() const
const auto minTop = *std::min_element(tops.begin(), tops.end());
const auto maxBottom = *std::max_element(bottoms.begin(), bottoms.end());
return maxBottom - minTop;
}
}
void MonitorManager::SetFrameRate(UINT frameRate)
{
frameRate_ = frameRate;
}
UINT MonitorManager::GetFrameRate() const
{
return frameRate_;
}

View File

@@ -13,35 +13,32 @@ class Cursor;
class MonitorManager
{
public:
explicit MonitorManager();
explicit MonitorManager(LUID unityAdapterLuid_);
~MonitorManager();
void Initialize();
void Finalize();
void Reinitialize();
void Update();
bool HasMonitorCountChanged() const;
void RequireReinitilization();
void SetCursorMonitorId(int id) { cursorMonitorId_ = id; }
int GetCursorMonitorId() const { return cursorMonitorId_; }
std::shared_ptr<Monitor> GetMonitor(int id) const;
std::shared_ptr<Cursor> GetCursor() const;
void SetFrameRate(UINT frameRate);
UINT GetFrameRate() const;
private:
void Initialize();
void Finalize();
// Setters from Unity
public:
void Update();
void SetTimeout(int timeout);
int GetTimeout() const;
// Getters from Unity
public:
int GetMonitorCount() const;
int GetTotalWidth() const;
int GetTotalHeight() const;
private:
int timeout_ = 10;
LUID unityAdapterLuid_;
UINT frameRate_ = 60;
bool enableTextureCopyFromGpuToCpu_ = false;
std::vector<std::shared_ptr<Monitor>> monitors_;
std::shared_ptr<Cursor> cursor_;
std::shared_ptr<Cursor> cursor_ = std::make_shared<Cursor>();
int cursorMonitorId_ = -1;
bool isReinitializationRequired_ = false;
};
};

View File

@@ -7,10 +7,12 @@
#include "IUnityInterface.h"
#include "IUnityGraphics.h"
#include "include/IUnityGraphicsD3D11.h"
#include "Common.h"
#include "Debug.h"
#include "Monitor.h"
#include "Duplicator.h"
#include "Cursor.h"
#include "MonitorManager.h"
@@ -21,23 +23,49 @@
IUnityInterfaces* g_unity = nullptr;
std::unique_ptr<MonitorManager> g_manager;
std::queue<Message> g_messages;
ID3D11DeviceContext* g_deviceContextForMainThread = nullptr;
extern "C"
{
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API InitializeUDD()
UNITY_INTERFACE_EXPORT bool UNITY_INTERFACE_API IsInitialized()
{
return g_unity && g_manager;
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API Initialize()
{
if (g_unity && !g_manager)
{
Debug::Initialize();
g_manager = std::make_unique<MonitorManager>();
auto device = g_unity->Get<IUnityGraphicsD3D11>()->GetDevice();
Microsoft::WRL::ComPtr<IDXGIDevice1> dxgiDevice;
if (FAILED(device->QueryInterface(IID_PPV_ARGS(&dxgiDevice)))){
Debug::Error("fatal");
return;
}
Microsoft::WRL::ComPtr<IDXGIAdapter> dxgiAdapter;
if (FAILED(dxgiDevice->GetAdapter(&dxgiAdapter))) {
Debug::Error("fatal");
return;
}
DXGI_ADAPTER_DESC desc;
dxgiAdapter->GetDesc(&desc);
g_manager = std::make_unique<MonitorManager>(desc.AdapterLuid);
g_manager->Initialize();
}
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API FinalizeUDD()
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API Finalize()
{
if (!g_manager) return;
g_manager->Finalize();
g_manager.reset();
std::queue<Message> empty;
@@ -52,12 +80,12 @@ extern "C"
{
case kUnityGfxDeviceEventInitialize:
{
InitializeUDD();
Initialize();
break;
}
case kUnityGfxDeviceEventShutdown:
{
FinalizeUDD();
Finalize();
break;
}
}
@@ -66,8 +94,6 @@ extern "C"
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API UnityPluginLoad(IUnityInterfaces* unityInterfaces)
{
g_unity = unityInterfaces;
InitializeUDD();
auto unityGraphics = g_unity->Get<IUnityGraphics>();
unityGraphics->RegisterDeviceEventCallback(OnGraphicsDeviceEvent);
}
@@ -76,8 +102,6 @@ extern "C"
{
auto unityGraphics = g_unity->Get<IUnityGraphics>();
unityGraphics->UnregisterDeviceEventCallback(OnGraphicsDeviceEvent);
FinalizeUDD();
g_unity = nullptr;
}
@@ -86,7 +110,7 @@ extern "C"
if (!g_manager) return;
if (auto monitor = g_manager->GetMonitor(id))
{
monitor->Render(g_manager->GetTimeout());
monitor->Render();
}
}
@@ -161,12 +185,6 @@ extern "C"
return g_manager->GetTotalHeight();
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API SetTimeout(int timeout)
{
if (!g_manager) return;
g_manager->SetTimeout(timeout);
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API GetId(int id)
{
if (!g_manager) return;
@@ -176,14 +194,14 @@ extern "C"
}
}
UNITY_INTERFACE_EXPORT MonitorState UNITY_INTERFACE_API GetState(int id)
UNITY_INTERFACE_EXPORT DuplicatorState UNITY_INTERFACE_API GetState(int id)
{
if (!g_manager) return MonitorState::NotSet;
if (!g_manager) return DuplicatorState::NotSet;
if (auto monitor = g_manager->GetMonitor(id))
{
return monitor->GetState();
return monitor->GetDuplicatorState();
}
return MonitorState::NotSet;
return DuplicatorState::NotSet;
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API GetName(int id, char* buf, int len)
@@ -295,83 +313,52 @@ extern "C"
return -1;
}
UNITY_INTERFACE_EXPORT bool UNITY_INTERFACE_API IsCursorVisible(int id)
UNITY_INTERFACE_EXPORT bool UNITY_INTERFACE_API IsCursorVisible()
{
if (!g_manager) return false;
if (auto monitor = g_manager->GetMonitor(id))
{
return monitor->GetCursor()->IsVisible();
}
return false;
return g_manager->GetCursor()->IsVisible();
}
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorX(int id)
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorX()
{
if (!g_manager) return -1;
if (auto monitor = g_manager->GetMonitor(id))
{
return monitor->GetCursor()->GetX();
}
return -1;
return g_manager->GetCursor()->GetX();
}
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorY(int id)
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorY()
{
if (!g_manager) return -1;
if (auto monitor = g_manager->GetMonitor(id))
{
return monitor->GetCursor()->GetY();
}
return -1;
return g_manager->GetCursor()->GetY();
}
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorShapeWidth(int id)
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorShapeWidth()
{
if (!g_manager) return -1;
if (auto monitor = g_manager->GetMonitor(id))
{
return monitor->GetCursor()->GetWidth();
}
return -1;
return g_manager->GetCursor()->GetWidth();
}
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorShapeHeight(int id)
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorShapeHeight()
{
if (!g_manager) return -1;
if (auto monitor = g_manager->GetMonitor(id))
{
return monitor->GetCursor()->GetHeight();
}
return -1;
return g_manager->GetCursor()->GetHeight();
}
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorShapePitch(int id)
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorShapePitch()
{
if (!g_manager) return -1;
if (auto monitor = g_manager->GetMonitor(id))
{
return monitor->GetCursor()->GetPitch();
}
return -1;
return g_manager->GetCursor()->GetPitch();
}
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorShapeType(int id)
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetCursorShapeType()
{
if (!g_manager) return -1;
if (auto monitor = g_manager->GetMonitor(id))
{
return monitor->GetCursor()->GetType();
}
return -1;
return g_manager->GetCursor()->GetType();
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API GetCursorTexture(int id, ID3D11Texture2D* texture)
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API GetCursorTexture(ID3D11Texture2D* texture)
{
if (!g_manager) return;
if (auto monitor = g_manager->GetMonitor(id))
{
monitor->GetCursorTexture(texture);
}
return g_manager->GetCursor()->GetTexture(texture);
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API SetTexturePtr(int id, void* texture)
@@ -391,6 +378,7 @@ extern "C"
{
return monitor->GetMoveRectCount();
}
return 0;
}
UNITY_INTERFACE_EXPORT void* UNITY_INTERFACE_API GetMoveRects(int id)
@@ -400,6 +388,7 @@ extern "C"
{
return monitor->GetMoveRects();
}
return nullptr;
}
UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetDirtyRectCount(int id)
@@ -409,6 +398,7 @@ extern "C"
{
return monitor->GetDirtyRectCount();
}
return 0;
}
UNITY_INTERFACE_EXPORT void* UNITY_INTERFACE_API GetDirtyRects(int id)
@@ -418,5 +408,41 @@ extern "C"
{
return monitor->GetDirtyRects();
}
return nullptr;
}
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(output, x, y, width, height);
}
return false;
}
UNITY_INTERFACE_EXPORT bool UNITY_INTERFACE_API HasBeenUpdated(int id)
{
if (!g_manager) return nullptr;
if (auto monitor = g_manager->GetMonitor(id))
{
return monitor->HasBeenUpdated();
}
return false;
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API UseGetPixels(int id, bool use)
{
if (!g_manager) return;
if (auto monitor = g_manager->GetMonitor(id))
{
return monitor->UseGetPixels(use);
}
}
UNITY_INTERFACE_EXPORT void UNITY_INTERFACE_API SetFrameRate(UINT frameRate)
{
if (!g_manager) return;
g_manager->SetFrameRate(frameRate);
}
}

View File

@@ -5,14 +5,14 @@
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
@@ -25,20 +25,13 @@
<ProjectName>uDesktopDuplication</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
@@ -51,37 +44,45 @@
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<IncludePath>$(VC_IncludePath);$(WindowsSDK_IncludePath);include</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<IncludePath>$(VC_IncludePath);$(WindowsSDK_IncludePath);include</IncludePath>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<IncludePath>$(VC_IncludePath);$(WindowsSDK_IncludePath);include</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
</ClCompile>
</ItemDefinitionGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<IncludePath>$(VC_IncludePath);$(WindowsSDK_IncludePath);include</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
@@ -92,30 +93,24 @@
<Command>copy /Y "$(SolutionDir)$(Platform)\$(Configuration)\$(TargetFileName)" "$(SolutionDir)..\..\Assets\$(ProjectName)\Plugins\x86_64"</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
<PostBuildEvent>
<Command>
</Command>
<Command>copy /Y "$(SolutionDir)$(Platform)\$(Configuration)\$(TargetFileName)" "$(SolutionDir)..\..\Assets\$(ProjectName)\Plugins\x86_64"</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<Optimization>Full</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
@@ -125,9 +120,28 @@
<Command>copy /Y "$(SolutionDir)$(Platform)\$(Configuration)\$(TargetFileName)" "$(SolutionDir)..\..\Assets\$(ProjectName)\Plugins\x86_64"</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Full</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
<PostBuildEvent>
<Command>copy /Y "$(SolutionDir)$(Platform)\$(Configuration)\$(TargetFileName)" "$(SolutionDir)..\..\Assets\$(ProjectName)\Plugins\x86"</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="Common.cpp" />
<ClCompile Include="Debug.cpp" />
<ClCompile Include="Device.cpp" />
<ClCompile Include="Duplicator.cpp" />
<ClCompile Include="MonitorManager.cpp" />
<ClCompile Include="Main.cpp" />
<ClCompile Include="Monitor.cpp" />
@@ -136,6 +150,8 @@
<ItemGroup>
<ClInclude Include="Common.h" />
<ClInclude Include="Debug.h" />
<ClInclude Include="Device.h" />
<ClInclude Include="Duplicator.h" />
<ClInclude Include="MonitorManager.h" />
<ClInclude Include="include\IUnityGraphics.h" />
<ClInclude Include="include\IUnityGraphicsD3D11.h" />

View File

@@ -20,6 +20,8 @@
<ClInclude Include="Common.h" />
<ClInclude Include="MonitorManager.h" />
<ClInclude Include="Debug.h" />
<ClInclude Include="Device.h" />
<ClInclude Include="Duplicator.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="Monitor.cpp" />
@@ -28,5 +30,7 @@
<ClCompile Include="MonitorManager.cpp" />
<ClCompile Include="Common.cpp" />
<ClCompile Include="Debug.cpp" />
<ClCompile Include="Device.cpp" />
<ClCompile Include="Duplicator.cpp" />
</ItemGroup>
</Project>

View File

@@ -1,2 +1 @@
m_EditorVersion: 5.4.2f2
m_StandardAssetsVersion: 0
m_EditorVersion: 5.5.0f3