Skip to content

Commit a1dc721

Browse files
committed
Implement non blocking custom texture render system without using compute shader.
1 parent a970fae commit a1dc721

16 files changed

+518
-598
lines changed

Assets/CustomTextureRenderer.Samples/Scripts/Test.cs

Lines changed: 12 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
using System;
2+
using System.Runtime.InteropServices;
23
using UnityEngine;
34

45
namespace UnityCustomTextureRenderer.Samples
56
{
67
public class Test : MonoBehaviour
78
{
8-
[System.Runtime.InteropServices.DllImport("Plasma")]
9+
[System.Runtime.InteropServices.DllImport("Plasma2")]
910
static extern IntPtr UpdateRawTextureData(IntPtr data, int width, int height, uint frameCount);
1011

1112
enum TextureSize
@@ -20,15 +21,12 @@ enum TextureSize
2021
}
2122

2223
[SerializeField] TextureSize _textureSize;
23-
[SerializeField] bool _useNonBlockingVersion;
24-
25-
public event Action<(int TextureWidth, int TextureHeight, bool UseNonBlockingVersion)> OnInitialized;
24+
public event Action<(int TextureWidth, int TextureHeight)> OnInitialized;
2625

2726
uint _frame;
2827

29-
Texture _texture;
30-
CustomTextureRenderer _customTextureRenderer;
31-
NonBlockingCustomTextureRenderer _nonBlockingCustomTextureRenderer;
28+
Texture2D _texture;
29+
PluginTextureRenderer _pluginTextureRenderer;
3230

3331
void Start()
3432
{
@@ -44,44 +42,18 @@ void Start()
4442
_ => 64,
4543
};
4644

47-
var asyncGPUUploadCount = _textureSize switch
48-
{
49-
TextureSize._4096x4096 => 4,
50-
_ => 1,
51-
};
52-
53-
if (_useNonBlockingVersion)
54-
{
55-
var rt = new RenderTexture(size, size, 0, RenderTextureFormat.ARGB32);
56-
rt.enableRandomWrite = true;
57-
rt.Create();
58-
59-
_texture = rt;
45+
_texture = new Texture2D(size, size, TextureFormat.RGBA32, false);
46+
_texture.wrapMode = TextureWrapMode.Clamp;
6047

61-
_nonBlockingCustomTextureRenderer =
62-
new NonBlockingCustomTextureRenderer(
63-
this.UpdateRawTextureDataFunction,
64-
targetTexture: (RenderTexture)_texture,
65-
targetFrameRateOfPluginRenderThread: 60,
66-
asyncGPUUploadCount: asyncGPUUploadCount
67-
);
68-
}
69-
else
70-
{
71-
var tex2d = new Texture2D(size, size, TextureFormat.RGBA32, false);
72-
tex2d.wrapMode = TextureWrapMode.Clamp;
73-
74-
_texture = tex2d;
75-
76-
_customTextureRenderer = new CustomTextureRenderer(UpdateRawTextureDataFunction, (Texture2D)_texture);
77-
}
48+
_pluginTextureRenderer = new PluginTextureRenderer(UpdateRawTextureDataCallback, _texture);
49+
CustomTextureRenderSystem.Instance.AddRenderer(_pluginTextureRenderer);
7850

7951
// Set the texture to the renderer with using a property block.
8052
var prop = new MaterialPropertyBlock();
8153
prop.SetTexture("_MainTex", _texture);
8254
GetComponent<Renderer>().SetPropertyBlock(prop);
8355

84-
OnInitialized?.Invoke((size, size, _useNonBlockingVersion));
56+
OnInitialized?.Invoke((size, size));
8557
}
8658

8759
void OnDestroy()
@@ -92,16 +64,7 @@ void OnDestroy()
9264
void Update()
9365
{
9466
_frame = (uint)(Time.time * 60);
95-
96-
// Update texture
97-
if (_useNonBlockingVersion)
98-
{
99-
_nonBlockingCustomTextureRenderer.Update();
100-
}
101-
else
102-
{
103-
_customTextureRenderer.Update();
104-
}
67+
_pluginTextureRenderer.SetUserData(_frame);
10568

10669
// Rotation
10770
transform.eulerAngles = new Vector3(10, 20, 30) * Time.time;
@@ -114,7 +77,7 @@ void Update()
11477
/// <param name="width"></param>
11578
/// <param name="height"></param>
11679
/// <param name="frameCount"></param>
117-
void UpdateRawTextureDataFunction(IntPtr data, int width, int height, int bytesPerPixel)
80+
void UpdateRawTextureDataCallback(IntPtr data, int width, int height, int bytesPerPixel)
11881
{
11982
UpdateRawTextureData(data, width, height, _frame);
12083
}

Assets/CustomTextureRenderer.Samples/Scripts/TestPresenter.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,7 @@ void Awake()
1515

1616
_test.OnInitialized += (values) =>
1717
{
18-
var title = values.UseNonBlockingVersion ?
19-
nameof(UnityCustomTextureRenderer.NonBlockingCustomTextureRenderer) :
20-
nameof(UnityCustomTextureRenderer.CustomTextureRenderer);
21-
18+
var title = nameof(UnityCustomTextureRenderer.CustomTextureRenderSystem);
2219
_uiView.SetTitle(title);
2320
_uiView.SetTextureSize(values.TextureWidth, values.TextureHeight);
2421
};

Assets/CustomTextureRenderer.Samples/Test.unity

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -955,7 +955,6 @@ MonoBehaviour:
955955
m_Name:
956956
m_EditorClassIdentifier:
957957
_textureSize: 3
958-
_useNonBlockingVersion: 1
959958
--- !u!1 &1703854062
960959
GameObject:
961960
m_ObjectHideFlags: 0
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
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+
}

Assets/CustomTextureRenderer/Runtime/NonBlockingCustomTextureRenderer.cs.meta renamed to Assets/CustomTextureRenderer/Runtime/CustomTextureRenderSystem.cs.meta

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)