1+ using System ;
2+ using System . Collections . Concurrent ;
3+ using System . Runtime . InteropServices ;
4+ using UnityEngine ;
5+ using UnityEngine . Rendering ;
6+
7+ #if DEVELOPMENT_BUILD || UNITY_EDITOR
8+ using UnityEngine . Profiling ;
9+ #endif
10+
11+ namespace UnityCustomTextureRenderer
12+ {
13+ public delegate void RawTextureDataUpdateCallback ( IntPtr data , int width , int height , int bytesPerPixel ) ;
14+ public delegate void IssuePluginCustomTextureUpdateCallback ( int eventID , IntPtr data ) ;
15+
16+ /// <summary>
17+ /// A high performance graphics utility to update textures from native plugins. <br/>
18+ /// The function for updating textures runs on another thread.
19+ /// </summary>
20+ public sealed class CustomTextureRenderSystem : MonoBehaviour , IDisposable
21+ {
22+
23+ #region Singleton class handling
24+
25+ public static CustomTextureRenderSystem Instance
26+ {
27+ get
28+ {
29+ if ( _instance is null )
30+ {
31+ var previous = FindObjectOfType < CustomTextureRenderSystem > ( ) ;
32+ if ( previous )
33+ {
34+ _instance = previous ;
35+ DebugLogWarning ( $ "[{ nameof ( CustomTextureRenderSystem ) } ] The instance attached on \" { previous . gameObject . name } \" is used.") ;
36+ }
37+ else
38+ {
39+ var go = new GameObject ( "__CustomTextureRenderSystem" ) ;
40+ _instance = go . AddComponent < CustomTextureRenderSystem > ( ) ;
41+ DontDestroyOnLoad ( go ) ;
42+ // go.hideFlags = HideFlags.HideInHierarchy;
43+ }
44+ }
45+ return _instance ;
46+ }
47+ }
48+
49+ private static CustomTextureRenderSystem _instance ;
50+
51+ #endregion
52+
53+ private bool _initialized ;
54+ private bool _disposed ;
55+
56+ private CommandBuffer _commandBuffer ;
57+
58+ private ushort _rendererRegisterationCount ;
59+ private static readonly ConcurrentDictionary < ushort , PluginTextureRenderer > s_TextureRenderers = new ConcurrentDictionary < ushort , PluginTextureRenderer > ( ) ;
60+ private static readonly ConcurrentDictionary < ushort , IntPtr > s_TextureBufferPtrs = new ConcurrentDictionary < ushort , IntPtr > ( ) ;
61+
62+ #if DEVELOPMENT_BUILD || UNITY_EDITOR
63+ private static readonly CustomSampler _textureUpdateCallbackSampler = CustomSampler . Create ( "TextureUpdateCallback" ) ;
64+ #endif
65+
66+ #region MonoBehaviour functions
67+
68+ private void Awake ( )
69+ {
70+ Initialize ( ) ;
71+ }
72+
73+ private void LateUpdate ( )
74+ {
75+ SystemUpdate ( ) ;
76+ }
77+
78+ #endregion
79+
80+ public void Initialize ( )
81+ {
82+ if ( _initialized )
83+ {
84+ DebugLog ( $ "[{ nameof ( CustomTextureRenderSystem ) } ] Already initialized") ;
85+ return ;
86+ }
87+
88+ UnityEngine . Application . quitting += Dispose ;
89+ _commandBuffer = new CommandBuffer ( ) ;
90+ _commandBuffer . name = "CustomTextureRenderer.IssuePluginCustomTextureUpdateV2" ;
91+
92+ _initialized = true ;
93+ }
94+
95+ public void Dispose ( )
96+ {
97+ if ( _disposed )
98+ {
99+ DebugLog ( $ "[{ nameof ( CustomTextureRenderSystem ) } ] Already disposed") ;
100+ return ;
101+ }
102+
103+ _disposed = true ;
104+
105+ foreach ( var renderer in s_TextureRenderers . Values )
106+ {
107+ renderer . Dispose ( ) ;
108+ }
109+
110+ s_TextureRenderers . Clear ( ) ;
111+ s_TextureBufferPtrs . Clear ( ) ;
112+
113+ DebugLog ( $ "[{ nameof ( CustomTextureRenderSystem ) } ] Disposed") ;
114+ }
115+
116+ public int AddRenderer ( PluginTextureRenderer renderer )
117+ {
118+ var rendererId = _rendererRegisterationCount ;
119+ if ( s_TextureRenderers . TryAdd ( rendererId , renderer ) )
120+ {
121+ _rendererRegisterationCount ++ ;
122+ return rendererId ;
123+ }
124+ else
125+ {
126+ return - 1 ;
127+ }
128+ }
129+
130+ public void RemoveRenderer ( ushort rendererId )
131+ {
132+ s_TextureRenderers . TryRemove ( rendererId , out PluginTextureRenderer renderer ) ;
133+ }
134+
135+ private void SystemUpdate ( )
136+ {
137+ if ( _disposed ) { return ; }
138+
139+ foreach ( var keyValue in s_TextureRenderers )
140+ {
141+ var rendererId = keyValue . Key ;
142+ var renderer = keyValue . Value ;
143+
144+ s_TextureBufferPtrs [ rendererId ] = renderer . GetTextureBufferPtr ( ) ;
145+ if ( s_TextureBufferPtrs [ rendererId ] != IntPtr . Zero )
146+ {
147+ _commandBuffer . IssuePluginCustomTextureUpdateV2 ( GetTextureUpdateCallback ( ) , renderer . TargetTexture , rendererId ) ;
148+ }
149+ }
150+
151+ Graphics . ExecuteCommandBuffer ( _commandBuffer ) ;
152+ _commandBuffer . Clear ( ) ;
153+ }
154+
155+ #region TextureUpdateCallback
156+
157+ private IntPtr GetTextureUpdateCallback ( )
158+ {
159+ return Marshal . GetFunctionPointerForDelegate ( _callback ) ;
160+ }
161+
162+ private delegate void UnityRenderingEventAndData ( int eventID , IntPtr data ) ;
163+ private static readonly UnityRenderingEventAndData _callback = new UnityRenderingEventAndData ( TextureUpdateCallback ) ;
164+
165+ /// <summary>
166+ /// This function runs on Unity's Render Thread.
167+ /// </summary>
168+ /// <param name="eventID"></param>
169+ /// <param name="data"></param>
170+ [ AOT . MonoPInvokeCallback ( typeof ( IssuePluginCustomTextureUpdateCallback ) ) ]
171+ private static unsafe void TextureUpdateCallback ( int eventID , IntPtr data )
172+ {
173+ var updateParams = ( UnityRenderingExtTextureUpdateParamsV2 * ) data . ToPointer ( ) ;
174+
175+ if ( eventID == ( int ) UnityRenderingExtEventType . kUnityRenderingExtEventUpdateTextureBeginV2 )
176+ {
177+ #if DEVELOPMENT_BUILD || UNITY_EDITOR
178+ _textureUpdateCallbackSampler . Begin ( ) ;
179+ #endif
180+
181+ var rendererId = updateParams ->userData ;
182+ if ( s_TextureBufferPtrs . TryGetValue ( ( ushort ) rendererId , out var textureBufferPtr ) )
183+ {
184+ updateParams ->texData = textureBufferPtr . ToPointer ( ) ;
185+ }
186+
187+ #if DEVELOPMENT_BUILD || UNITY_EDITOR
188+ _textureUpdateCallbackSampler . End ( ) ;
189+ #endif
190+ }
191+ else if ( eventID == ( int ) UnityRenderingExtEventType . kUnityRenderingExtEventUpdateTextureEndV2 )
192+ {
193+ updateParams ->texData = null ;
194+ }
195+ }
196+
197+ #endregion
198+
199+ /// <summary>
200+ /// Logs a message to the Unity Console
201+ /// only when DEVELOPMENT_BUILD or UNITY_EDITOR is defined.
202+ /// </summary>
203+ /// <param name="message"></param>
204+ /// <returns></returns>
205+ [
206+ System . Diagnostics . Conditional ( "DEVELOPMENT_BUILD" ) ,
207+ System . Diagnostics . Conditional ( "UNITY_EDITOR" ) ,
208+ ]
209+ private static void DebugLog ( object message )
210+ {
211+ UnityEngine . Debug . Log ( message ) ;
212+ }
213+
214+ /// <summary>
215+ /// Logs a message to the Unity Console
216+ /// only when DEVELOPMENT_BUILD or UNITY_EDITOR is defined.
217+ /// </summary>
218+ /// <param name="message"></param>
219+ /// <returns></returns>
220+ [
221+ System . Diagnostics . Conditional ( "DEVELOPMENT_BUILD" ) ,
222+ System . Diagnostics . Conditional ( "UNITY_EDITOR" ) ,
223+ ]
224+ private static void DebugLogWarning ( object message )
225+ {
226+ UnityEngine . Debug . LogWarning ( message ) ;
227+ }
228+
229+ /// <summary>
230+ /// Logs a message to the Unity Console
231+ /// only when DEVELOPMENT_BUILD or UNITY_EDITOR is defined.
232+ /// </summary>
233+ /// <param name="message"></param>
234+ /// <returns></returns>
235+ [
236+ System . Diagnostics . Conditional ( "DEVELOPMENT_BUILD" ) ,
237+ System . Diagnostics . Conditional ( "UNITY_EDITOR" ) ,
238+ ]
239+ private static void DebugLogError ( object message )
240+ {
241+ UnityEngine . Debug . LogError ( message ) ;
242+ }
243+ }
244+ }
0 commit comments