Skip to content

Commit 90dfb1a

Browse files
authored
opt: cache Asset textures in Editor to increase the playback speed (#452)
* add a new constant READ_STATUS_USE_EDITOR_API in StreamingImageSequenceConstants * move the code to load Asset textures to EditorCachedTextureLoader class * unload all cached textures when opening a new scene * cache textures in Editor to increase the playback speed
1 parent 5398a48 commit 90dfb1a

4 files changed

Lines changed: 153 additions & 63 deletions

File tree

Runtime/Scripts/Features/SIS/StreamingImageSequencePlayableAsset.cs

Lines changed: 55 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@
99
using UnityEngine;
1010
using UnityEngine.Experimental.Rendering;
1111
#if UNITY_EDITOR
12-
using UnityEditor.Timeline;
1312
using UnityEditor;
14-
1513
#endif
1614

1715
namespace Unity.StreamingImageSequence {
@@ -90,7 +88,7 @@ public StreamingImageSequencePlayableAsset() {
9088
m_lastCopiedImageIndex = -1;
9189
}
9290

93-
//----------------------------------------------------------------------------------------------------------------------
91+
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
9492

9593
void OnEnable() {
9694
m_texture = null;
@@ -101,16 +99,14 @@ void OnEnable() {
10199
return m_texture.mipmapCount != 1;
102100
},$"Textures should not have mipmap. Folder: ");
103101

104-
m_regularAssetLoadLogger = new OneTimeLogger(() => !m_regularAssetLoaded,
105-
$"Can't load textures. Make sure their import settings are set to Texture2D. Folder: ");
106-
107102
}
108103

104+
109105
private void OnDisable() {
110106
ResetTexture();
111107
}
112108

113-
//----------------------------------------------------------------------------------------------------------------------
109+
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
114110

115111
//[Note-sin: 2020-7-17] This is also called when the TimelineClip in TimelineWindow is deleted, instead of just
116112
//The TimelineClipAsset (on file, for example) is deleted
@@ -184,6 +180,10 @@ private void Reset() {
184180
m_lastCopiedImageIndex = -1;
185181
ResetTexture();
186182
ResetResolution();
183+
184+
#if UNITY_EDITOR
185+
m_editorCachedTextureLoader.UnloadAll();
186+
#endif
187187
}
188188

189189
//----------------------------------------------------------------------------------------------------------------------
@@ -243,26 +243,26 @@ internal void RequestLoadImage(int index) {
243243

244244
m_primaryImageIndex = index;
245245

246-
#if UNITY_EDITOR
247-
if (fullPath.IsRegularAssetPath()) {
248-
UpdateTextureAsRegularAssetInEditor(fullPath, m_primaryImageIndex);
249-
return;
250-
}
251-
#endif
252-
253-
254-
if (QueueImageLoadTask(index, out ImageData readResult)) {
246+
if (QueueImageLoadTask(fullPath, out ImageData readResult, out Texture2D tex)) {
255247
m_forwardPreloadImageIndex = Mathf.Min(m_primaryImageIndex + 1, numImages - 1);
256-
m_backwardPreloadImageIndex = Mathf.Max(m_primaryImageIndex - 1, 0);
248+
m_backwardPreloadImageIndex = Mathf.Max(m_primaryImageIndex - 1, 0);
257249
} else {
258250
//If we can't queue, try from the primary index again
259251
m_forwardPreloadImageIndex = m_backwardPreloadImageIndex = index;
260252
}
261253

262-
if (StreamingImageSequenceConstants.READ_STATUS_SUCCESS == readResult.ReadStatus) {
263-
UpdateTexture(readResult, index);
254+
switch (readResult.ReadStatus) {
255+
case StreamingImageSequenceConstants.READ_STATUS_SUCCESS: {
256+
UpdateTexture(readResult, index);
257+
break;
258+
}
259+
#if UNITY_EDITOR
260+
case StreamingImageSequenceConstants.READ_STATUS_USE_EDITOR_API: {
261+
UpdateTexture(tex, m_primaryImageIndex);
262+
break;
263+
}
264+
#endif
264265
}
265-
266266
}
267267

268268
//----------------------------------------------------------------------------------------------------------------------
@@ -277,8 +277,8 @@ internal bool UpdateTextureWithRequestedImage() {
277277
return false;
278278

279279
#if UNITY_EDITOR
280-
if (fullPath.IsRegularAssetPath()) {
281-
return UpdateTextureAsRegularAssetInEditor(fullPath, m_primaryImageIndex);
280+
if (UpdateTextureAsRegularAssetInEditor(fullPath, m_primaryImageIndex)) {
281+
return true;
282282
}
283283
#endif
284284

@@ -290,24 +290,20 @@ internal bool UpdateTextureWithRequestedImage() {
290290
UpdateTexture(imageData, m_primaryImageIndex);
291291
return true;
292292
}
293-
294-
295-
//----------------------------------------------------------------------------------------------------------------------
293+
294+
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
296295

297296
internal void ContinuePreloadingImages(int numNeighboringImagesToLoad) {
298297

299298
if (null == m_imageFiles || 0== m_imageFiles.Count)
300299
return;
301300

302-
if (m_folder.IsRegularAssetPath())
303-
return;
304-
305301
//forward
306302
int maxForwardPreloadIndex = Mathf.Min(m_forwardPreloadImageIndex + numNeighboringImagesToLoad, m_imageFiles.Count) -1;
307303
int startForwardPreloadIndex = m_forwardPreloadImageIndex;
308304
for (int i = startForwardPreloadIndex; i <= maxForwardPreloadIndex; ++i) {
309-
if (QueueImageLoadTask(i, out _)) {
310-
++m_forwardPreloadImageIndex;
305+
if (QueueImageLoadTask(i, out _, out _)) {
306+
++m_forwardPreloadImageIndex;
311307
} else {
312308
break;
313309
}
@@ -317,24 +313,35 @@ internal void ContinuePreloadingImages(int numNeighboringImagesToLoad) {
317313
int minBackwardPreloadIndex = Mathf.Max((m_backwardPreloadImageIndex - numNeighboringImagesToLoad)+1, 0);
318314
int startBackwardPreloadIndex = m_backwardPreloadImageIndex;
319315
for (int i = startBackwardPreloadIndex; i >=minBackwardPreloadIndex; --i) {
320-
if (QueueImageLoadTask(i, out _)) {
321-
--m_backwardPreloadImageIndex;
316+
if (QueueImageLoadTask(i, out _, out _)) {
317+
--m_backwardPreloadImageIndex;
322318
} else {
323319
break;
324320
}
325321
}
326-
327322
}
328323

329-
330-
//----------------------------------------------------------------------------------------------------------------------
324+
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
331325

332326
//return true if we should continue preloading the next image. False otherwise
333-
private bool QueueImageLoadTask(int index, out ImageData imageData) {
327+
private bool QueueImageLoadTask(int index, out ImageData imageData, out Texture2D tex) {
334328
string fullPath = GetImageFilePath(index);
329+
return QueueImageLoadTask(fullPath, out imageData, out tex);
330+
}
331+
332+
333+
//return true if we should continue preloading the next image. False otherwise
334+
private bool QueueImageLoadTask(string fullPath, out ImageData imageData, out Texture2D tex) {
335335

336+
tex = null;
337+
338+
#if UNITY_EDITOR
339+
if (m_editorCachedTextureLoader.GetOrLoad(fullPath, out imageData, out tex))
340+
return true;
341+
#endif
342+
336343
if (!File.Exists(fullPath)) {
337-
imageData = new ImageData(StreamingImageSequenceConstants.READ_STATUS_FAIL);
344+
imageData = new ImageData(StreamingImageSequenceConstants.READ_STATUS_FAIL);
338345
return true;
339346
}
340347

@@ -354,9 +361,8 @@ private bool QueueImageLoadTask(int index, out ImageData imageData) {
354361

355362
return true;
356363
}
357-
358364

359-
//----------------------------------------------------------------------------------------------------------------------
365+
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
360366
Texture2D UpdateTexture(ImageData imageData, int index) {
361367
bool textureRecreated = false;
362368
if (m_texture.IsNullRef() || !imageData.IsTextureCompatible(m_texture) || m_texture.filterMode != m_textureFilterMode) {
@@ -410,9 +416,6 @@ void ResetTexture() {
410416

411417
}
412418

413-
414-
415-
416419
//----------------------------------------------------------------------------------------------------------------------
417420
#region Observer
418421

@@ -465,6 +468,7 @@ public void OnAfterDeserialize() {
465468
{
466469
m_folder = folder;
467470
m_imageFiles = imageFiles;
471+
m_editorCachedTextureLoader.UnloadAll();
468472
UpdateResolution(res);
469473

470474
if (null!=m_folder && m_folder.StartsWith("Assets")) {
@@ -479,9 +483,9 @@ public void OnAfterDeserialize() {
479483

480484
protected override void ReloadInternalInEditorV() {
481485
m_lastCopiedImageIndex = -1;
486+
m_editorCachedTextureLoader.UnloadAll();
482487
ResetResolution();
483488
RequestLoadImage(m_primaryImageIndex);
484-
485489
}
486490

487491
internal UnityEditor.DefaultAsset GetTimelineDefaultAsset() { return m_timelineDefaultAsset; }
@@ -494,20 +498,12 @@ protected override void ReloadInternalInEditorV() {
494498

495499

496500
private bool UpdateTextureAsRegularAssetInEditor(string fullPath, int imageIndex) {
497-
Assert.IsTrue(fullPath.IsRegularAssetPath());
498-
499-
Texture2D tex = AssetDatabase.LoadAssetAtPath<Texture2D>(fullPath);
500-
m_regularAssetLoaded = (null!=tex);
501-
m_regularAssetLoadLogger.Update("[SIS]", m_folder);
502-
503-
if (null == tex) {
501+
if (!m_editorCachedTextureLoader.GetOrLoad(fullPath, out _, out Texture2D tex)) {
504502
return false;
505503
}
506504

507505
UpdateTexture(tex, imageIndex);
508-
Resources.UnloadAsset(tex);
509506
return true;
510-
511507
}
512508

513509
#endif //end #if UNITY_EDITOR
@@ -552,13 +548,15 @@ private bool UpdateTextureAsRegularAssetInEditor(string fullPath, int imageIndex
552548
Texture2D m_texture = null;
553549

554550
private OneTimeLogger m_regularAssetMipmapCheckLogger;
555-
private OneTimeLogger m_regularAssetLoadLogger;
556-
private bool m_regularAssetLoaded = false;
551+
552+
553+
#if UNITY_EDITOR
554+
private EditorCachedTextureLoader m_editorCachedTextureLoader = new EditorCachedTextureLoader();
555+
#endif
557556

558-
//----------------------------------------------------------------------------------------------------------------------
557+
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
559558

560559
private const int CUR_SIS_PLAYABLE_ASSET_VERSION = (int) SISPlayableAssetVersion.WATCHED_FILE_0_4;
561-
562560

563561
enum SISPlayableAssetVersion {
564562
INITIAL = 1, //initial
@@ -570,7 +568,7 @@ enum SISPlayableAssetVersion {
570568

571569
} //end namespace
572570

573-
//----------------------------------------------------------------------------------------------------------------------
571+
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
574572
//[Note-Sin: 2019-12-23] We need two things, in order to enable folder drag/drop to the timeline Window
575573
//1. Derive this class from PlayableAsset
576574
//2. Declare UnityEditor.DefaultAsset variable
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#if UNITY_EDITOR
2+
3+
using System.IO;
4+
using System.Collections.Generic;
5+
using Unity.FilmInternalUtilities;
6+
using UnityEngine;
7+
using UnityEditor.SceneManagement;
8+
using UnityEditor;
9+
10+
namespace Unity.StreamingImageSequence {
11+
12+
[System.Serializable]
13+
internal class EditorCachedTextureLoader {
14+
15+
internal EditorCachedTextureLoader() {
16+
EditorSceneManager.sceneClosed += OnSceneClosed;
17+
m_regularAssetLoadLogger = new OneTimeLogger(() => !m_regularAssetLoaded,
18+
$"Can't load textures. Make sure their import settings are set to Texture2D. Folder: ");
19+
}
20+
21+
void OnSceneClosed(UnityEngine.SceneManagement.Scene scene) {
22+
UnloadAll();
23+
}
24+
25+
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
26+
27+
internal bool GetOrLoad(string fullPath, out ImageData imageData, out Texture2D tex) {
28+
if (fullPath.IsRegularAssetPath()) {
29+
30+
bool isCached = m_cachedTexturesInEditor.TryGetValue(fullPath, out tex);
31+
bool isTexReady = null != tex;
32+
if (!isCached || !isTexReady) {
33+
m_cachedTexturesInEditor[fullPath] = tex = AssetDatabase.LoadAssetAtPath<Texture2D>(fullPath);
34+
35+
isTexReady = null != tex;
36+
}
37+
38+
//Log
39+
m_regularAssetLoaded = isTexReady;
40+
m_regularAssetLoadLogger.Update("[SIS]", Path.GetDirectoryName(fullPath));
41+
42+
if (!m_regularAssetLoaded) {
43+
imageData = new ImageData(StreamingImageSequenceConstants.READ_STATUS_FAIL);
44+
return false;
45+
}
46+
47+
imageData = new ImageData(StreamingImageSequenceConstants.READ_STATUS_USE_EDITOR_API);
48+
return true;
49+
}
50+
51+
tex = null;
52+
imageData = new ImageData(StreamingImageSequenceConstants.READ_STATUS_FAIL);
53+
return false;
54+
}
55+
56+
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
57+
58+
internal void UnloadAll() {
59+
foreach (KeyValuePair<string, Texture2D> kv in m_cachedTexturesInEditor) {
60+
Resources.UnloadAsset(kv.Value);
61+
}
62+
m_cachedTexturesInEditor.Clear();
63+
}
64+
65+
66+
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
67+
68+
private OneTimeLogger m_regularAssetLoadLogger;
69+
private bool m_regularAssetLoaded = false;
70+
71+
Dictionary<string,Texture2D> m_cachedTexturesInEditor = new Dictionary<string, Texture2D>(); //path -> Texture2D
72+
73+
}
74+
75+
} //end namespace
76+
77+
#endif

Runtime/Scripts/Loader/EditorCachedTextureLoader.cs.meta

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Runtime/Scripts/StreamingImageSequenceConstants.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@ internal static class StreamingImageSequenceConstants {
88

99
public const string DIALOG_HEADER = "StreamingImageSequence";
1010

11-
public const int READ_STATUS_UNAVAILABLE = -1;
12-
public const int READ_STATUS_IDLE = 0;
13-
public const int READ_STATUS_LOADING = 1;
14-
public const int READ_STATUS_SUCCESS = 2;
15-
public const int READ_STATUS_FAIL = 3;
16-
public const int READ_STATUS_OUT_OF_MEMORY = 4;
11+
internal const int READ_STATUS_UNAVAILABLE = -1;
12+
internal const int READ_STATUS_IDLE = 0;
13+
internal const int READ_STATUS_LOADING = 1;
14+
internal const int READ_STATUS_SUCCESS = 2;
15+
internal const int READ_STATUS_FAIL = 3;
16+
internal const int READ_STATUS_OUT_OF_MEMORY = 4;
17+
18+
internal const int READ_STATUS_USE_EDITOR_API = 100;
19+
1720

1821
public const int IMAGE_FORMAT_RGBA32 = 0;
1922
public const int IMAGE_FORMAT_BGRA32 = 1;

0 commit comments

Comments
 (0)