Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 2024-05-22 - VRCUrl.Get() Interop Overhead
**Learning:** Calling `VRCUrl.Get()` inside loops in UdonSharp creates significant performance overhead due to interop calls between the Udon VM and the host engine.
**Action:** Cache `VRCUrl` string values into a native `string[]` array at startup (e.g., in `Start()`) and use the cached array for lookups and comparisons in loops.
62 changes: 52 additions & 10 deletions Scripts/ImageLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public class ImageLoader : UdonSharpBehaviour
private string[] _captions = new string[0];
private int _currentIndex = 0;
private string _lastLoadedUrlList = "";
private string[] _cachedUrls;

// Current texture reference
private Texture2D _currentTexture;
Expand All @@ -64,6 +65,8 @@ void Start()
// Initialize core components
_imageDownloader = new VRCImageDownloader();
_udonEventReceiver = (IUdonEventReceiver)this;

CacheUrls();

// Store reference to original material
if (renderer != null)
Expand All @@ -90,6 +93,28 @@ void Start()
}
}

private void CacheUrls()
{
if (predefinedUrls == null)
{
_cachedUrls = new string[0];
return;
}

_cachedUrls = new string[predefinedUrls.Length];
for (int i = 0; i < predefinedUrls.Length; i++)
{
if (predefinedUrls[i] != null)
{
_cachedUrls[i] = predefinedUrls[i].Get();
}
else
{
_cachedUrls[i] = null;
}
}
}

private void InitFromGeneratedUrls()
{
// Make sure predefinedUrls are properly initialized
Expand Down Expand Up @@ -427,11 +452,16 @@ private int FindMatchingUrlIndex(string urlToFind, int additionalCount = 0)
Debug.LogError("No predefined URLs available!");
return -1;
}

if (_cachedUrls == null || _cachedUrls.Length != predefinedUrls.Length)
{
CacheUrls();
}

// First try to find an exact match
for (int i = 0; i < predefinedUrls.Length; i++)
for (int i = 0; i < _cachedUrls.Length; i++)
{
if (predefinedUrls[i] != null && predefinedUrls[i].Get() == urlToFind)
if (_cachedUrls[i] != null && _cachedUrls[i] == urlToFind)
{
return i;
}
Expand All @@ -454,11 +484,11 @@ private int FindMatchingUrlIndex(string urlToFind, int additionalCount = 0)
string filename = ExtractFilenameFromUrl(urlToFind);
if (!string.IsNullOrEmpty(filename))
{
for (int i = 0; i < predefinedUrls.Length; i++)
for (int i = 0; i < _cachedUrls.Length; i++)
{
if (predefinedUrls[i] != null &&
!string.IsNullOrEmpty(predefinedUrls[i].Get()) &&
predefinedUrls[i].Get().Contains(filename))
if (_cachedUrls[i] != null &&
!string.IsNullOrEmpty(_cachedUrls[i]) &&
_cachedUrls[i].Contains(filename))
{
return i;
}
Expand Down Expand Up @@ -505,13 +535,18 @@ private int FindUrlSlotForFilename(string filename)
Debug.LogError("No valid VRCUrl found in predefinedUrls array!");
return -1;
}

if (_cachedUrls == null || _cachedUrls.Length != predefinedUrls.Length)
{
CacheUrls();
}

// Find a predefined URL that matches this filename
for (int i = 0; i < predefinedUrls.Length; i++)
for (int i = 0; i < _cachedUrls.Length; i++)
{
if (predefinedUrls[i] != null && !string.IsNullOrEmpty(predefinedUrls[i].Get()))
if (_cachedUrls[i] != null && !string.IsNullOrEmpty(_cachedUrls[i]))
{
if (predefinedUrls[i].Get().Contains(filename))
if (_cachedUrls[i].Contains(filename))
{
return i;
}
Expand Down Expand Up @@ -598,13 +633,18 @@ public override void OnImageLoadSuccess(IVRCImageDownload result)

string loadedUrl = result.Url.Get();
Debug.Log($"Image loaded successfully: {loadedUrl}");

if (_cachedUrls == null || _cachedUrls.Length != predefinedUrls.Length)
{
CacheUrls();
}

// Find which of our URLs this corresponds to
for (int i = 0; i < _activeUrlIndices.Length; i++)
{
int urlIndex = _activeUrlIndices[i];
if (urlIndex < predefinedUrls.Length && predefinedUrls[urlIndex] != null &&
predefinedUrls[urlIndex].Get() == loadedUrl)
_cachedUrls[urlIndex] == loadedUrl)
{
// Store the downloaded texture
_downloadedTextures[i] = result.Result;
Expand Down Expand Up @@ -700,6 +740,8 @@ public void ClearPredefinedUrls()
}
}

CacheUrls();

// Reset URL count
urlCount = 0;

Expand Down