Skip to content

Instantly share code, notes, and snippets.

@NanyiJiang
Created February 12, 2023 02:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save NanyiJiang/d11828eb09e3cf00155deceef7f6e986 to your computer and use it in GitHub Desktop.
Save NanyiJiang/d11828eb09e3cf00155deceef7f6e986 to your computer and use it in GitHub Desktop.
Unity Streaming Virtual Texture Request improvement
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
#if ENABLE_VIRTUALTEXTURES
using VT = UnityEngine.Rendering.VirtualTexturing;
#endif
// [ExecuteInEditMode]
public class VTRequester : MonoBehaviour
{
#if ENABLE_VIRTUALTEXTURES
List<VTProperty> m_Stacks = new List<VTProperty>();
//The standard texture tile size is 128 pixels.
const int k_TileSizeInPxl = 128;
public struct VTProperty
{
public Material mat;
public int stackId;
public int topMip;
}
public void CacheVTProperties()
{
Debug.Log("caching VT properties");
m_Stacks.Clear();
Renderer[] meshRenderers = FindObjectsOfType<MeshRenderer>(true);
var skinnedMeshRenderers = FindObjectsOfType<SkinnedMeshRenderer>(true);
var renderers = meshRenderers.Concat(skinnedMeshRenderers);
Dictionary<int, Material> materials = new Dictionary<int, Material>();
//find all unique materials
foreach (Renderer r in renderers)
{
var materialsOfRenderer = r.sharedMaterials;
foreach(var mat in materialsOfRenderer) {
if (mat == null) continue;
if( !materials.ContainsKey(mat.GetInstanceID()) )
{
materials.Add(mat.GetInstanceID(), mat);
}
}
}
foreach (var m in materials)
{
//We use the stack ID to request a mip map of this stack to be streamed
//We use the max Dimension to calculate the top mip
var stackIDandMaxDimensions = GetAllStackIDAndMaxDimensions(m.Value);
var tailMips = (int)Mathf.Log(k_TileSizeInPxl, 2);
if (stackIDandMaxDimensions.Count != 0)
{
foreach (var item in stackIDandMaxDimensions)
{
var stackId = item.Key;
var stackSize = item.Value;
if (stackSize != 0) {
//Calculate the lowest resolution mip available. A stack does not have mips smaller than the tile size.
//a texture of 128x128 has 1 mip level, 256x256 has 2 mip levels, etc...
int topMip = (int)Mathf.Log(stackSize, 2) - tailMips ;
VTProperty s;
s.mat = m.Value;
s.stackId = stackId;
s.topMip = topMip;
m_Stacks.Add(s);
}
}
}
}
//Debug.Log("found materials " + materials.Count);
}
void Update()
{
var i = Random.Range(0, m_Stacks.Count);
var s = m_Stacks[i];
VT.Streaming.RequestRegion(s.mat, s.stackId, new Rect(0, 0, 1, 1), s.topMip, 1);
}
//In the future there might be better ways to do this.
Dictionary<int, int> GetAllStackIDAndMaxDimensions(Material mat)
{
Shader shader = mat.shader;
var stackIDandMaxDimensions = new Dictionary<int, int>();
int count = shader.GetPropertyCount();
for (int i = 0; i < count; i++)
{
if (shader.GetPropertyType(i) == UnityEngine.Rendering.ShaderPropertyType.Texture)
{
string stackName;
int layerIndex;
//check if textures on a material are part of a stack.
if (shader.FindTextureStack(i, out stackName, out layerIndex))
{
var stackId = Shader.PropertyToID(stackName);
if (!stackIDandMaxDimensions.ContainsKey(stackId))
{
int w = 0, h = 0;
VT.Streaming.GetTextureStackSize(mat, stackId, out w, out h);
if (w == 0 || h == 0) {
// TODO: resolve invalid streaming requests
// Debug.Log($"Invalid stack {mat.name}");
}
stackIDandMaxDimensions[stackId] = Mathf.Max(w, h);
}
}
}
}
return stackIDandMaxDimensions;
}
#endif
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment