From 72376b9e9145b79b7362ea1e3ea34670fc899ebf Mon Sep 17 00:00:00 2001 From: hecomi Date: Sat, 19 Nov 2016 21:45:32 +0900 Subject: [PATCH] implement dirty rects and move rects. --- .../Examples/Prefabs/Monitor Board.prefab | Bin 7964 -> 7964 bytes .../Examples/Scenes/Meta Data.unity | Bin 0 -> 19208 bytes .../Examples/Scenes/Meta Data.unity.meta | 8 ++ .../Examples/Scripts/MetadataVisualizer.cs | 88 ++++++++++++ .../Scripts/MetadataVisualizer.cs.meta | 12 ++ .../Scripts/MultipleMonitorRoundLayouter.cs | 7 +- .../Examples/Scripts/ToggleMonitors.cs | 6 +- .../Materials/uDD_Screen_Unlit.mat | Bin 5252 -> 5296 bytes .../uDesktopDuplication/Models/uDD_Board.fbx | Bin 32288 -> 32288 bytes .../Plugins/x86_64/uDesktopDuplication.dll | Bin 72192 -> 76288 bytes Assets/uDesktopDuplication/Scripts/Lib.cs | 54 +++++++ Assets/uDesktopDuplication/Scripts/Monitor.cs | 20 +++ Assets/uDesktopDuplication/Scripts/Texture.cs | 8 +- .../Shaders/uDD_Common.cginc | 8 +- .../uDesktopDuplication/Common.h | 93 +++++++----- .../uDesktopDuplication/Monitor.cpp | 136 +++++++++++++++++- .../uDesktopDuplication/Monitor.h | 13 ++ .../uDesktopDuplication/main.cpp | 36 +++++ 18 files changed, 435 insertions(+), 54 deletions(-) create mode 100644 Assets/uDesktopDuplication/Examples/Scenes/Meta Data.unity create mode 100644 Assets/uDesktopDuplication/Examples/Scenes/Meta Data.unity.meta create mode 100644 Assets/uDesktopDuplication/Examples/Scripts/MetadataVisualizer.cs create mode 100644 Assets/uDesktopDuplication/Examples/Scripts/MetadataVisualizer.cs.meta diff --git a/Assets/uDesktopDuplication/Examples/Prefabs/Monitor Board.prefab b/Assets/uDesktopDuplication/Examples/Prefabs/Monitor Board.prefab index 8054f247eec29a427bcddf609d33385eb8fbd9f7..6fee75727bbf5e034ae728719d0179528d39f353 100644 GIT binary patch delta 49 ycmbPZH^***1hY|^(MD-+SuO@BXt1ALAS*XHTt;egfQ-oGJ+eP0#{uaRav=aaPYvn- delta 52 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 literal 0 HcmV?d00001 diff --git a/Assets/uDesktopDuplication/Examples/Scenes/Meta Data.unity.meta b/Assets/uDesktopDuplication/Examples/Scenes/Meta Data.unity.meta new file mode 100644 index 0000000..116cee8 --- /dev/null +++ b/Assets/uDesktopDuplication/Examples/Scenes/Meta Data.unity.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: aac40d1b58f3f8940be815779d89b5a4 +timeCreated: 1479544409 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uDesktopDuplication/Examples/Scripts/MetadataVisualizer.cs b/Assets/uDesktopDuplication/Examples/Scripts/MetadataVisualizer.cs new file mode 100644 index 0000000..5d4954f --- /dev/null +++ b/Assets/uDesktopDuplication/Examples/Scripts/MetadataVisualizer.cs @@ -0,0 +1,88 @@ +using UnityEngine; +using MeshForwardDirection = uDesktopDuplication.Texture.MeshForwardDirection; + +public class MetadataVisualizer : MonoBehaviour +{ + uDesktopDuplication.Texture uddTexture_; + + [SerializeField] bool drawMoveRectDest; + [SerializeField] bool drawMoveRectSource; + [SerializeField] bool drawDirtyRect; + + void Start() + { + uddTexture_ = GetComponent(); + } + + public Vector3 GetWorldPositionFromCoord(int u, int v) + { + var monitor = uddTexture_.monitor; + + // mesh & scale information + var mesh = GetComponent().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); + + // bend + 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); + } + + void Update() + { + UpdateMoveRects(); + UpdateDirtyRects(); + } + + void DrawRect(uDesktopDuplication.RECT rect, Color color) + { + var p0 = GetWorldPositionFromCoord(rect.left, rect.top); + var p1 = GetWorldPositionFromCoord(rect.right, rect.top); + var p2 = GetWorldPositionFromCoord(rect.right, rect.bottom); + var p3 = GetWorldPositionFromCoord(rect.left, rect.bottom); + + Debug.DrawLine(p0, p1, color); + Debug.DrawLine(p1, p2, color); + Debug.DrawLine(p2, p3, color); + Debug.DrawLine(p3, p0, color); + } + + void UpdateMoveRects() + { + if (drawMoveRectSource) { + foreach (var rect in uddTexture_.monitor.moveRects) { + DrawRect(rect.source, Color.blue); + } + } + if (drawMoveRectDest) { + foreach (var rect in uddTexture_.monitor.moveRects) { + DrawRect(rect.destination, Color.green); + } + } + } + + void UpdateDirtyRects() + { + if (drawDirtyRect) { + foreach (var rect in uddTexture_.monitor.dirtyRects) { + DrawRect(rect, Color.red); + } + } + } +} diff --git a/Assets/uDesktopDuplication/Examples/Scripts/MetadataVisualizer.cs.meta b/Assets/uDesktopDuplication/Examples/Scripts/MetadataVisualizer.cs.meta new file mode 100644 index 0000000..686a09b --- /dev/null +++ b/Assets/uDesktopDuplication/Examples/Scripts/MetadataVisualizer.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 52d402bdca823cc42925c9d65993ee59 +timeCreated: 1479546123 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/uDesktopDuplication/Examples/Scripts/MultipleMonitorRoundLayouter.cs b/Assets/uDesktopDuplication/Examples/Scripts/MultipleMonitorRoundLayouter.cs index 7abb214..29376b8 100644 --- a/Assets/uDesktopDuplication/Examples/Scripts/MultipleMonitorRoundLayouter.cs +++ b/Assets/uDesktopDuplication/Examples/Scripts/MultipleMonitorRoundLayouter.cs @@ -1,4 +1,5 @@ using UnityEngine; +using MeshForwardDirection = uDesktopDuplication.Texture.MeshForwardDirection; public class MultipleMonitorRoundLayouter : MultipleMonitorLayouter { @@ -20,7 +21,11 @@ public class MultipleMonitorRoundLayouter : MultipleMonitorLayouter // keep the local scale z of monitors as 1 to bend them correctly. foreach (var info in monitors) { var scale = info.gameObject.transform.localScale; - scale.z = 1f; + if (creator_.meshForwardDirection == MeshForwardDirection.Y) { + scale.y = 1f; + } else { + scale.z = 1f; + } info.gameObject.transform.localScale = scale; } diff --git a/Assets/uDesktopDuplication/Examples/Scripts/ToggleMonitors.cs b/Assets/uDesktopDuplication/Examples/Scripts/ToggleMonitors.cs index 9db2619..f262eed 100644 --- a/Assets/uDesktopDuplication/Examples/Scripts/ToggleMonitors.cs +++ b/Assets/uDesktopDuplication/Examples/Scripts/ToggleMonitors.cs @@ -6,10 +6,12 @@ public class ToggleMonitors : MonoBehaviour { if (Input.GetKeyDown(KeyCode.Tab)) { var texture = GetComponent(); + var id = texture.monitorId; + var n = uDesktopDuplication.Manager.monitorCount; if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift)) { - texture.monitorId--; + texture.monitorId = (id - 1 < 0) ? 0 : (id - 1); } else { - texture.monitorId++; + texture.monitorId = (id + 1 >= n) ? (n - 1) : (id + 1); } } } diff --git a/Assets/uDesktopDuplication/Materials/uDD_Screen_Unlit.mat b/Assets/uDesktopDuplication/Materials/uDD_Screen_Unlit.mat index 79bce7a7085390c6be93f3515d379deb484ac40d..71c7f59ddcca1df3dc41a320df300fb5c20c8519 100644 GIT binary patch delta 123 zcmZqC+@LAVz`(JDfk9*g0|Ns;1A_pNVqh@UGto0jGukL!$icW_b1%nwc1DxQwgO6` zZvH{xjzKQ*Q3~;)!LClOelGF;ew%Xyt}rr6Pj(dQ7vKQuj(5v1Do-p*VPI&m-+V#H Xfsv03BpQ;DnVg-MT3kGtOQZ?_&0QZ8 delta 83 zcmdm>*`g`Uz`(JDfkC8&fq{Xafk6OBF)*0wndlj&8E%v=|^ol$eLt$-45 lXt1l3tDj4}zaLQ5<`RJ`jEs_#J%#!=zYua@oNOaf0sy>r6MX;x diff --git a/Assets/uDesktopDuplication/Models/uDD_Board.fbx b/Assets/uDesktopDuplication/Models/uDD_Board.fbx index 035338acb60d1c9c86d373dba2ce5dbbbd64b772..db1cf0cffc09d3a34236a69bd8d0261e35db847c 100644 GIT binary patch delta 324 zcmZ4RhjGCl#toH>tilWo4D%;1U=(L(@yIVN@)Vgohf$f)a`FL2Wi~CKoB-40g^Xg8 z|1EwiJJwC7)BLjmESl#3~)vA18BSAu711F!aR^th@{i4D%;1U=(L(@yIVN@)Vyuhf$ePYw`g`Wj0lyoB-40g^Xg8 z|1ub#(c&KM#d1MtSn5;HfxQLbYz+8^(;>{l_&N8wl zWc$SyPd-#N6QXo-LbVA+Ujy&sVGRgUy2rF@ilTSChcV Yw}SWD$ty`)OFnkV{Z}rZ+*3OR08iU$x&QzG diff --git a/Assets/uDesktopDuplication/Plugins/x86_64/uDesktopDuplication.dll b/Assets/uDesktopDuplication/Plugins/x86_64/uDesktopDuplication.dll index ceee205749124852fcf2bc7364609ca20f583c94..8ff08b9f1016bbe845b02509da7f04f60c69d3f2 100644 GIT binary patch delta 27397 zcmeIbd0bV+|37~2k&CQyS-l|Q1rcyV+$}{fu17_~om_Fp)I@{CB`<}RcqP%X)Lg=? zEKM<7aP7L}o}yVSlw=WAvzT+lv`_wTRYKcDk>%ze(h=J|TfYnC%J zXU-9G_PFI-bt^LHqUweuq$={NP@+uUY8^!>f?$CVns%~Qy3uVbbUG!@O;L>QI>k+g ztQ4J6Mwp;ee0&6z|EB4b3y2har6?J!Wvy`Cb@r{UUAt#?LTErB)3D=unG_{0e%6Gs znPU~D{|MyR5Jn>GVg1$E#s(^;>M|ogK0~m1$?!md&q@f}pvLN=*(vS$3klT zQ#bw{MX}fNRunDY_wEir?#a0F7H_epZlN*%Y(-wfkm`AkHHvcK!UfRZ`#@33E7q{* z+&ZZbo@3kGn)sZ1PEig}MeLbxu$ykixKl9cH_YbeuV_|%Qm9VR@{B0LG@@LC9T@Q> zBc0Y1N9b$o)TS$oW8ItS)W27-A+;C#P28j?{j8@o>nC^m*(a@FcWd`kJvXtA9@*-b z%h_I!3u?VLigP{FRdwU*>}T(2wchK+fj)Yj8kAog=Bv}GGnW?EuXC%G8o&6%0AHQj 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 PjP2Rm_ir-lM(h3`&$m|Z delta 24822 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 class Buffer { public: - Buffer() {} - ~Buffer() {} + Buffer() {} + ~Buffer() {} - void ExpandIfNeeded(UINT size) - { - if (size > size_) - { - size_ = size; - value_ = std::make_unique(size); - } - } + void ExpandIfNeeded(UINT size) + { + if (size > size_) + { + size_ = size; + value_ = std::make_unique(size); + } + } - void Reset() - { - value_.reset(); - size_ = 0; - } + void Reset() + { + value_.reset(); + size_ = 0; + } - UINT Size() const - { - return size_; - } + UINT Size() const + { + return size_; + } - T* Get() const - { - return value_.get(); - } + T* Get() const + { + return value_.get(); + } - operator bool() const - { - return value_ != nullptr; - } + T* Get(UINT offset) const + { + return (value_.get() + offset); + } - T operator [](UINT index) const - { - if (index >= size_) - { - Debug::Error("Array index out of range: ", index, size_); - return T(0); - } - return value_[index]; - } + template + U* As() const + { + return reinterpret_cast(Get()); + } + + template + U* As(UINT offset) const + { + return reinterpret_cast(Get(offset)); + } + + operator bool() const + { + return value_ != nullptr; + } + + T operator [](UINT index) const + { + if (index >= size_) + { + Debug::Error("Array index out of range: ", index, size_); + return T(0); + } + return value_[index]; + } private: - std::unique_ptr value_; - UINT size_ = 0; + std::unique_ptr value_; + UINT size_ = 0; }; \ No newline at end of file diff --git a/Plugins/uDesktopDuplication/uDesktopDuplication/Monitor.cpp b/Plugins/uDesktopDuplication/uDesktopDuplication/Monitor.cpp index 496609b..26c0d47 100644 --- a/Plugins/uDesktopDuplication/uDesktopDuplication/Monitor.cpp +++ b/Plugins/uDesktopDuplication/uDesktopDuplication/Monitor.cpp @@ -1,6 +1,5 @@ #include #include -#include "Common.h" #include "Debug.h" #include "Cursor.h" #include "MonitorManager.h" @@ -50,7 +49,7 @@ void Monitor::Initialize(IDXGIOutput* output) 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. + // DPI is set as -1, so the application has to use the appropriate value. } auto output1 = reinterpret_cast(output); @@ -156,7 +155,7 @@ void Monitor::Render(UINT timeout) return; } - // Get texture + // Get texture if (unityTexture_) { ID3D11Texture2D* texture; @@ -189,8 +188,8 @@ void Monitor::Render(UINT timeout) } } - cursor_->UpdateBuffer(frameInfo); - cursor_->UpdateTexture(); + UpdateMetadata(frameInfo); + UpdateCursor(frameInfo); if (FAILED(deskDupl_->ReleaseFrame())) { @@ -199,6 +198,109 @@ void Monitor::Render(UINT timeout) } +void Monitor::UpdateCursor(const DXGI_OUTDUPL_FRAME_INFO& frameInfo) +{ + 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(), + &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(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; + } +} + + int Monitor::GetId() const { return id_; @@ -304,4 +406,28 @@ int Monitor::GetWidth() const int Monitor::GetHeight() const { return height_; +} + + +int Monitor::GetMoveRectCount() const +{ + return moveRectSize_ / sizeof(DXGI_OUTDUPL_MOVE_RECT); +} + + +DXGI_OUTDUPL_MOVE_RECT* Monitor::GetMoveRects() const +{ + return metaData_.As(); +} + + +int Monitor::GetDirtyRectCount() const +{ + return dirtyRectSize_ / sizeof(RECT); +} + + +RECT* Monitor::GetDirtyRects() const +{ + return metaData_.As(moveRectSize_); } \ No newline at end of file diff --git a/Plugins/uDesktopDuplication/uDesktopDuplication/Monitor.h b/Plugins/uDesktopDuplication/uDesktopDuplication/Monitor.h index 159de5f..e1bc4d0 100644 --- a/Plugins/uDesktopDuplication/uDesktopDuplication/Monitor.h +++ b/Plugins/uDesktopDuplication/uDesktopDuplication/Monitor.h @@ -4,6 +4,7 @@ #include #include #include +#include "Common.h" class Cursor; @@ -50,8 +51,17 @@ public: int GetDpiY() const; IDXGIOutputDuplication* GetDeskDupl(); const std::unique_ptr& GetCursor(); + int GetMoveRectCount() const; + DXGI_OUTDUPL_MOVE_RECT* GetMoveRects() const; + int GetDirtyRectCount() const; + RECT* GetDirtyRects() const; 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); + int id_ = -1; UINT dpiX_ = -1, dpiY_ = -1; int width_ = -1, height_ = -1; @@ -61,4 +71,7 @@ private: ID3D11Texture2D* unityTexture_ = nullptr; DXGI_OUTPUT_DESC outputDesc_; MONITORINFOEX monitorInfo_; + Buffer metaData_; + UINT moveRectSize_ = 0; + UINT dirtyRectSize_ = 0;; }; \ No newline at end of file diff --git a/Plugins/uDesktopDuplication/uDesktopDuplication/main.cpp b/Plugins/uDesktopDuplication/uDesktopDuplication/main.cpp index 0252a6d..d039fbb 100644 --- a/Plugins/uDesktopDuplication/uDesktopDuplication/main.cpp +++ b/Plugins/uDesktopDuplication/uDesktopDuplication/main.cpp @@ -383,4 +383,40 @@ extern "C" monitor->SetUnityTexture(d3d11Texture); } } + + UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetMoveRectCount(int id) + { + if (!g_manager) return -1; + if (auto monitor = g_manager->GetMonitor(id)) + { + return monitor->GetMoveRectCount(); + } + } + + UNITY_INTERFACE_EXPORT void* UNITY_INTERFACE_API GetMoveRects(int id) + { + if (!g_manager) return nullptr; + if (auto monitor = g_manager->GetMonitor(id)) + { + return monitor->GetMoveRects(); + } + } + + UNITY_INTERFACE_EXPORT int UNITY_INTERFACE_API GetDirtyRectCount(int id) + { + if (!g_manager) return -1; + if (auto monitor = g_manager->GetMonitor(id)) + { + return monitor->GetDirtyRectCount(); + } + } + + UNITY_INTERFACE_EXPORT void* UNITY_INTERFACE_API GetDirtyRects(int id) + { + if (!g_manager) return nullptr; + if (auto monitor = g_manager->GetMonitor(id)) + { + return monitor->GetDirtyRects(); + } + } } \ No newline at end of file