circle-loader
1
by
1/ 1166/ /4

1) Questions about the use of Texture Streaming

2) The reasons why Unity 3D scene UI is interrupted and batched

3) The meaning of Asset Provider and Asset Bundle Provider

4) Addressables only load the initial resources when updating resources

5) Stroke algorithm display problem


Editor

Q: Does Unity 2019.2 Texture Streaming not take effect under Editor? According to Unity’s official introduction, you can debug Texture Streaming in SceneView. But in fact, the switch will turn into light blue/dark blue, and there is no other change. Has anyone encountered the same situation?

A:

  1. You need to test the script to know how much memory is saved, the scene is not visible.
using System;
using UnityEditor;
using UnityEngine;

public class ShowTextureStreamingSummary : MonoBehaviour
{
    public float X = 10;
    public float Y = 20;
    public int MemoryBudgetPercentThreshold = 80;
    public int TextureStreamingPercentThreshold = 50;
    private ulong HighestDesiredTextureMemory;
    private Rect TextRect;

    public void Start()
    {
        HighestDesiredTextureMemory = 0;
        Texture.streamingTextureDiscardUnusedMips = false;
      QualitySettings.masterTextureLimit = 2;//The mandatory level is 2 to cooperate with the following test script
    }

    public string HumanReadableSize(ulong size)
    {
        return string.Format("{0:0.0}M", (float)size / (float)(1024 * 1024));
    }

    void ShowText(string text)
    {
        float yInc = GUI.skin.font.lineHeight;
        GUI.Label(TextRect, text);
        TextRect.y += yInc;
    }

    public void OnGUI()
    {
        TextRect = new Rect(X, Y, Screen.width - X, Screen.height - Y);
        GUI.color = Color.red;
        if (!SystemInfo.supportsMipStreaming)
            ShowText("Texture streaming unsupported");
        if (!QualitySettings.streamingMipmapsActive)
            ShowText("Texture streaming disabled");
        else if (QualitySettings.streamingMipmapsMemoryBudget == 0)
            ShowText("No texture streaming budget");
        else if (Texture.totalTextureMemory == 0)
            ShowText("No texture memory needed");
        else
        {
            // Reduced highest memory usage
            if (Texture.desiredTextureMemory > HighestDesiredTextureMemory)
                HighestDesiredTextureMemory = Texture.desiredTextureMemory;

            // Show stats
            ulong budget = (ulong)(1024 * 1024 * QualitySettings.streamingMipmapsMemoryBudget);
            float percentUsed = (float)(100 * Texture.desiredTextureMemory) / (float)budget;
            ShowText(string.Format("Memory budget utilisation {0:0.0}% of {1} texture budget", percentUsed, HumanReadableSize(budget)));

            if (HighestDesiredTextureMemory > budget)
            {
                ulong memoryExcess = HighestDesiredTextureMemory - budget;
                ShowText(string.Format("Memory exceeds budget by {0}", HumanReadableSize(memoryExcess)));
            }
            else
            {
                ulong memorySaving = Texture.totalTextureMemory - HighestDesiredTextureMemory;
                float percentReduction = (float)(100 * HighestDesiredTextureMemory) / (float)Texture.totalTextureMemory;
                ShowText(string.Format("Memory saving at least {0} with streaming enabled ( at {1:0.0}% of original {2}) - ignoring caching", HumanReadableSize(memorySaving), percentReduction, HumanReadableSize(Texture.totalTextureMemory)));
            }

            // Advice section
#if UNITY_EDITOR
            ShowText("Run in standalone app for accurate figures. When run in Play Mode the stats are biased by editor textures");
#endif                
            if (percentUsed < (float)MemoryBudgetPercentThreshold)
                ShowText(string.Format("Reduce the Memory Budget closer to {0}", HumanReadableSize(Texture.desiredTextureMemory)));
            else if (Texture.desiredTextureMemory > budget)
                ShowText(string.Format("Raise Memory Budget above {0}", HumanReadableSize(Texture.desiredTextureMemory)));
           
            float percentStreaming = (float)(100 * (Texture.totalTextureMemory - Texture.nonStreamingTextureMemory)) / (float)Texture.totalTextureMemory;
            if (percentStreaming < (float)TextureStreamingPercentThreshold)
                ShowText(string.Format("Mark more textures streaming to improve savings ({0:0.0}% texture memory marked as streaming)", percentStreaming));

            if (!Texture.streamingTextureDiscardUnusedMips)
                ShowText("Consider turning on Texture.streamingTextureDiscardUnusedMips to analyse without cached textures");

            ShowText(string.Format("desiredTextureMemory {0}", HumanReadableSize(Texture.desiredTextureMemory)));
            ShowText(string.Format("nonStreamingTextureMemory {0}", HumanReadableSize(Texture.nonStreamingTextureMemory)));
            ShowText(string.Format("totalTextureMemory {0}", HumanReadableSize(Texture.totalTextureMemory)));
        }
    }
}

 

  1. In the Game window, check which texture uses Streaming. Green is using Streaming; Blue shows Streaming off; The red one is not using Streaming yet.
using UnityEngine;

public class TextureStreamingDebug : MonoBehaviour
{
    public Camera m_MainCamera;
    public Shader m_ReplacementShader;
    public bool m_ActivateDebugShader;
    private bool m_DebugShaderActive = false;
    private RenderingPath originalRenderingPath;

    void Start()
    {
        // Grab camera from self if none set
        if (!m_MainCamera)
            m_MainCamera = GetComponent<Camera>();

        if (m_MainCamera)
            originalRenderingPath = m_MainCamera.renderingPath;
    }

    void Update()
    {
        if (!m_MainCamera || !m_ReplacementShader)
            return;

#if UNITY_STANDALONE_WIN || UNITY_EDITOR
        if (Input.GetKeyDown(KeyCode.Space))
            m_ActivateDebugShader ^= true;
#else
        if (Input.GetButtonDown("Fire1"))
            m_ActivateDebugShader ^= true;
#endif

        if (m_ActivateDebugShader != m_DebugShaderActive)
        {
            if (m_ActivateDebugShader)
            {
                m_MainCamera.renderingPath = RenderingPath.Forward;
                m_MainCamera.SetReplacementShader(m_ReplacementShader, "RenderType");
            }
            else
            {
                m_MainCamera.renderingPath = originalRenderingPath;
                m_MainCamera.ResetReplacementShader();
            }

            m_DebugShaderActive = m_ActivateDebugShader;
        }

        if (m_DebugShaderActive)
            Texture.SetStreamingTextureMaterialDebugProperties();
    }
}

 

Corresponding Shader:

Shader "Hidden/Scene View Show Texture Streaming" 
{
    Properties 
    {
        _MainTex ("", 2D) = "white" {}
        _Control ("Control (RGBA)", 2D) = "red" {}
        _Splat3 ("Layer 3 (A)", 2D) = "white" {}
        _Splat2 ("Layer 2 (B)", 2D) = "white" {}
        _Splat1 ("Layer 1 (G)", 2D) = "white" {}
        _Splat0 ("Layer 0 (R)", 2D) = "white" {}
        _BaseMap ("", 2D) = "white" {}
        _Cutoff ("Cutoff", float) = 0.5
    }
 
CGINCLUDE
// Common code used by most of the things below
#include "UnityCG.cginc"
struct v2f 
{
    float4 pos : SV_POSITION;
    float2 uv : TEXCOORD0;
};
uniform float4 _MainTex_ST;
uniform float4 _MainTex_TexelSize;
uniform float4 _MainTex_MipInfo;
 
UNITY_DECLARE_TEX2D(_MainTex);
UNITY_DECLARE_TEX2D(_SceneViewMipcolorsTexture);
 
uint GetMipCount(Texture2D tex)
{
#if defined(SHADER_API_D3D11) || defined(SHADER_API_D3D12) || defined(SHADER_API_D3D11_9X) || defined(SHADER_API_XBOXONE) || defined(SHADER_API_PSSL)
    #define MIP_COUNT_SUPPORTED 1
#endif
#if (defined(SHADER_API_OPENGL) || defined(SHADER_API_VULKAN)) && !defined(SHADER_STAGE_COMPUTE)
    // OpenGL only supports textureSize for width, height, depth
    // textureQueryLevels (GL_ARB_texture_query_levels) needs OpenGL 4.3 or above and doesn't compile in compute shaders
    // tex.GetDimensions converted to textureQueryLevels
    #define MIP_COUNT_SUPPORTED 1
#endif
    // Metal doesn't support high enough OpenGL version
 
#if defined(MIP_COUNT_SUPPORTED)
    uint mipLevel, width, height, mipCount;
    mipLevel = width = height = mipCount = 0;
    tex.GetDimensions(mipLevel, width, height, mipCount);
    return mipCount;
#else
    return 0;
#endif
}
 
float4 GetStreamingMipColor(uint mipCount, float4 mipInfo)
{
    // alpha is amount to blend with source color (0.0 = use original, 1.0 = use new color)
 
    // mipInfo :
    // x = quality setings minStreamingMipLevel
    // y = original mip count for texture
    // z = desired on screen mip level
    // w = loaded mip level
    uint originalTextureMipCount = uint(mipInfo.y);
 
    // If material/shader mip info (original mip level) has not been set it’s either not a streamed texture 
    // or no renderer is updating it
    if (originalTextureMipCount == 0)
        return float4(0.0, 0.0, 1.0, 0.5);
 
    uint desiredMipLevel = uint(mipInfo.z);
    uint mipCountDesired = uint(originalTextureMipCount)-uint(desiredMipLevel);
    if (mipCount == 0)
    {
        // Can't calculate, use the passed value
        mipCount = originalTextureMipCount - uint(mipInfo.w);
    }
 
    if (mipCount < mipCountDesired)
    {
        // red tones when not at the desired mip level (reduction due to budget). Brighter is further from original, alpha 0 when at desired
        float ratioToDesired = float(mipCount) / float(mipCountDesired);
            return float4(1.0, 0.0, 0.0, 1.0 - ratioToDesired);
    }
    else if (mipCount >= originalTextureMipCount)
    {
        // original color when at (or beyond) original mip count
        return float4(1.0, 1.0, 1.0, 0.0);
    }
    else
    {
        // green tones when not at the original mip level. Brighter is closer to original, alpha 0 when at original
        float ratioToOriginal = float(mipCount) / float(originalTextureMipCount);
        return float4(0.0, 1.0, 0.0, 1.0 - ratioToOriginal);
    }
}
 
float3 GetDebugStreamingMipColorBlended(float3 originalColor, Texture2D tex, float4 mipInfo)
{
    uint mipCount = GetMipCount(tex);
    float4 mipColor = GetStreamingMipColor(mipCount, mipInfo);
    return lerp(originalColor, mipColor.rgb, mipColor.a);
}
 
v2f vert( appdata_base v ) 
{
    v2f o;
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
  
    return o;
}
 
fixed4 frag(v2f i) : COLOR
{
    fixed4 col = UNITY_SAMPLE_TEX2D(_MainTex, i.uv);
    half4 res;
    res.rgb = GetDebugStreamingMipColorBlended(col.rgb, _MainTex, _MainTex_MipInfo);
    res.a = col.a;
    return res;
}
 
struct v2fGrass 
{
    float4 pos : SV_POSITION;
    fixed4 color : COLOR;
    float2 uv : TEXCOORD0;
};
 
fixed4 fragGrass(v2fGrass i) : COLOR
{
    fixed4 col = UNITY_SAMPLE_TEX2D(_MainTex, i.uv);
    half4 res;
    res.rgb = GetDebugStreamingMipColorBlended(col.rgb, _MainTex, _MainTex_MipInfo);
    res.a = col.a * i.color.a;
    return res;
}
ENDCG
 
SubShader 
{
    Tags { "ForceSupported" = "True" "RenderType"="Opaque" }
    Pass 
    {
CGPROGRAM
 
// As both normal opaque shaders and terrain splat shaders
// have "Opaque" render type, we need to do some voodoo
// to make both work.
 
#pragma vertex vertWTerrain
#pragma fragment fragWTerrain
#pragma target 2.0
#pragma exclude_renderers gles
 
struct v2fterr 
{
    float4 pos : SV_POSITION;
    float2 uvnormal : TEXCOORD0;
    float4 uv[3] : TEXCOORD2;
    float nonterrain  : TEXCOORD5;
};
 
uniform float4 _Splat0_ST,_Splat1_ST,_Splat2_ST,_Splat3_ST,_Splat4_ST;
uniform float4 _Splat0_TexelSize,_Splat1_TexelSize,_Splat2_TexelSize,_Splat3_TexelSize,_Splat4_TexelSize;
uniform float4 _BaseMap_TexelSize;
 
v2fterr vertWTerrain( appdata_base v ) 
{
    v2fterr o;
    o.pos = UnityObjectToClipPos(v.vertex);
    // assume it's not a terrain if _Splat0_TexelSize is not set up.
    float nonterrain = _Splat0_TexelSize.z==0.0 ? 1:0;
    // collapse/don't draw terrain's add pass in this mode, since it looks really bad if first pass
    // and add pass blink depending on which gets drawn first with this replacement shader
    // TODO: make it display mips properly even for two-pass terrains. 
    o.pos *= _MainTex_TexelSize.z==0.0 && _Splat0_TexelSize.z!=0.0 ? 0 : 1;
    // normal texture UV
    o.uvnormal = TRANSFORM_TEX(v.texcoord,_MainTex);
    // terrain splat UVs
    float2 baseUV = v.texcoord.xy;
    o.uv[0].xy = baseUV;
    o.uv[0].zw = half2(0,0);
    o.uv[1].xy = TRANSFORM_TEX (baseUV, _Splat0);
    o.uv[1].zw = TRANSFORM_TEX (baseUV, _Splat1);
    o.uv[2].xy = TRANSFORM_TEX (baseUV, _Splat2);
    o.uv[2].zw = TRANSFORM_TEX (baseUV, _Splat3);
  
    o.nonterrain = nonterrain;
    return o;
}
UNITY_DECLARE_TEX2D(_Control);
UNITY_DECLARE_TEX2D(_Splat0);
UNITY_DECLARE_TEX2D(_Splat1);
UNITY_DECLARE_TEX2D(_Splat2);
UNITY_DECLARE_TEX2D(_Splat3);
UNITY_DECLARE_TEX2D(_BaseMap);
fixed4 fragWTerrain(v2fterr i) : COLOR
{
    // sample regular texture
    fixed4 colnormal = UNITY_SAMPLE_TEX2D(_MainTex, i.uvnormal);
  
    // sample splatmaps
    half4 splat_control = UNITY_SAMPLE_TEX2D(_Control, i.uv[0].xy);
    half3 splat_color = splat_control.r * UNITY_SAMPLE_TEX2D(_Splat0, i.uv[1].xy).rgb;
    splat_color += splat_control.g * UNITY_SAMPLE_TEX2D(_Splat1, i.uv[1].zw).rgb;
    splat_color += splat_control.b * UNITY_SAMPLE_TEX2D(_Splat2, i.uv[2].xy).rgb;
    splat_color += splat_control.a * UNITY_SAMPLE_TEX2D(_Splat3, i.uv[2].zw).rgb;
  
    // lerp between normal and splatmaps
    half3 col = lerp(splat_color, colnormal.rgb, (half)i.nonterrain);
 
    half4 res;
    // TODO: Take splat mips into account
    res.rgb = GetDebugStreamingMipColorBlended(col.rgb, _MainTex, _MainTex_MipInfo);
    res.a = colnormal.a;
  
    return res;
}
ENDCG
    }
}
 
SubShader 
{
    Tags { "ForceSupported" = "True" "RenderType"="Transparent" }
    Pass 
    {
        Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#pragma exclude_renderers gles
ENDCG
    }
}
 
SubShader 
{
    Tags { "ForceSupported" = "True" "RenderType"="TransparentCutout" }
    Pass 
    {
        AlphaTest Greater [_Cutoff]
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#pragma exclude_renderers gles
ENDCG
    }
}
 
SubShader 
{
    Tags { "ForceSupported" = "True" "RenderType"="TreeBark" }
    Pass 
    {
CGPROGRAM
#pragma vertex vertTreeBark
#pragma fragment frag
#pragma target 2.0
#pragma exclude_renderers gles
#include "UnityCG.cginc"
#include "UnityBuiltin3xTreeLibrary.cginc"
v2f vertTreeBark (appdata_full v) 
{
    v2f o;
    TreeVertBark(v);
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv = v.texcoord;
    return o;
}
ENDCG
  }
}
 
SubShader 
{
    Tags { "ForceSupported" = "True" "RenderType"="TreeLeaf" }
    Pass 
    {
CGPROGRAM
#pragma vertex vertTreeLeaf
#pragma fragment frag
#pragma target 2.0
#pragma exclude_renderers gles
#include "UnityCG.cginc"
#include "UnityBuiltin3xTreeLibrary.cginc"
v2f vertTreeLeaf (appdata_full v) 
{
    v2f o;
    TreeVertLeaf (v);
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv = v.texcoord;
    return o;
}
ENDCG
        AlphaTest GEqual [_Cutoff]
    }
}
 
SubShader 
{
    Tags { "ForceSupported" = "True" "RenderType"="TreeOpaque" }
    Pass 
    {
CGPROGRAM
#pragma vertex vertTree
#pragma fragment frag
#pragma target 2.0
#pragma exclude_renderers gles
#include "TerrainEngine.cginc"
struct appdata 
{
    float4 vertex : POSITION;
    fixed4 color : COLOR;
    float2 texcoord : TEXCOORD0;
};
v2f vertTree( appdata v ) 
{
    v2f o;
    TerrainAnimateTree(v.vertex, v.color.w);
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv = v.texcoord;
    return o;
}
ENDCG
    }
} 
 
SubShader 
{
    Tags { "ForceSupported" = "True" "RenderType"="TreeTransparentCutout" }
    Pass 
    {
        Cull Off
CGPROGRAM
#pragma vertex vertTree
#pragma fragment frag
#pragma target 2.0
#pragma exclude_renderers gles
#include "TerrainEngine.cginc"
struct appdata 
{
    float4 vertex : POSITION;
    fixed4 color : COLOR;
    float4 texcoord : TEXCOORD0;
};
v2f vertTree( appdata v ) 
{
    v2f o;
    TerrainAnimateTree(v.vertex, v.color.w);
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv = v.texcoord;
    return o;
}
ENDCG
        AlphaTest GEqual [_Cutoff]
    }
}
 
SubShader 
{
    Tags { "ForceSupported" = "True" "RenderType"="TreeBillboard" }
    Pass 
    {
        Cull Off
        ZWrite Off
CGPROGRAM
#pragma vertex vertTree
#pragma fragment frag
#pragma target 2.0
#pragma exclude_renderers gles
#include "TerrainEngine.cginc"
v2f vertTree (appdata_tree_billboard v) 
{
    v2f o;
    TerrainBillboardTree(v.vertex, v.texcoord1.xy, v.texcoord.y);
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv.x = v.texcoord.x;
    o.uv.y = v.texcoord.y > 0;
    return o;
}
ENDCG
      
        SetTexture [_MainTex] { combine primary, texture }
    }
}

SubShader 
{
    Tags { "ForceSupported" = "True" "RenderType"="GrassBillboard" }
    Pass 
    {
        Cull Off
CGPROGRAM
#pragma vertex vertGrass
#pragma fragment fragGrass
#pragma target 2.0
#pragma exclude_renderers gles
#include "TerrainEngine.cginc"
v2fGrass vertGrass (appdata_full v) 
{
    v2fGrass o;
    WavingGrassBillboardVert (v);
    o.color = v.color;
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv = v.texcoord;
    return o;
}
ENDCG
        AlphaTest Greater [_Cutoff]
    }
}
 
SubShader 
{
    Tags { "ForceSupported" = "True" "RenderType"="Grass" }
    Pass 
    {
        Cull Off
CGPROGRAM
#pragma vertex vertGrass
#pragma fragment fragGrass
#pragma target 2.0
#pragma exclude_renderers gles
#include "TerrainEngine.cginc"
v2fGrass vertGrass (appdata_full v) 
{
    v2fGrass o;
    WavingGrassVert (v);
    o.color = v.color;
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv = v.texcoord;
    return o;
}
ENDCG
        AlphaTest Greater [_Cutoff]
    }
}
 
Fallback Off
}

Rendering

Q: As shown in the figure below, using Sprite Renderer for pictures and TextMeshPro for texts, and using Unity frame debugger to view, and found that the three pictures in the first row are dynamically batched, the three pictures in the second row are dynamically batched, and the three pictures in the third row are dynamically batched, but the text cannot be batched. The reason is that the dynamic batching is turned off in the Player Settings or temporarily disabled in the current environment to avoid Z-Fighting. Is it possible to have a batch of all pictures and a batch of all texts?

A1: My method is to set Sorting Layer and Order in Layer:

SpriteRenderer -> Additional Settings;
TextMeshPro Text -> Extra Settings.

But the disadvantage of this is that the characters will always be on the top of the picture, and there will be problems in displaying when they overlap.

 

A2: 1. For the drawing of ordinary Mesh objects, that is, the Mesh drawn using Mesh Renderer: When dynamic batching is enabled, two objects of the same Material are used and drawn in adjacent order, If the conditions of other dynamic batches are met, they can be batched together.

The text part of the subject is not batched because the dynamic batching is not turned on. Looking at Frame Debugger, the rendering pipeline of the subject uses SRP. Dynamic Batching can be enabled by selecting Dynamic Batching under Advanced in the Inspector panel of RenderPipelineAsset.

As for why the pictures appear in batching, this is because the pictures are drawn using Sprite Renderer, and the mesh is dynamically generated by Unity. The batching behavior is also processed internally by Unity, it is not affected by whether Dynamic Batching is enabled or not.

2. The second is to control the rendering order, the objects in the batch should be rendered in adjacent order, drawing all pictures first, and then drawing all text.

  • Method 1: According to the above, set the Order in Layer, the Order in Layer of the picture is set to 0, and the text is 1.

  • Method 2: Set all pictures’ Render Queue to 3000, and set all texts’ Render Queue to 3001.

The effect is as follows:

 


Addressable

Q1: Does anyone know the meaning of Asset Provider and Asset Bundle Provider two options in Addressable 1.17.13?

A1: You can search the original code of Addressables. You can extend the default implementation to customize your own download and load process.

[DisplayName(“Assets from Bundles Provider”)]
public class BundledAssetProvider : ResourceProviderBase
[DisplayName(“AssetBundle Provider”)]
public class AssetBundleProvider : ResourceProviderBase

 

A2: This is to customize the way to load AssetBundle and AssetBundle resources. You can look at the implementation of AssetBundleProvider.cs in the Addressables.CN version. In this version, a method for decrypting AssetBundle is added, which is another way of loading AssetBundle.

 

Q2: The problem is described as follows:
Usage scenario: I am using Addressables to implement a hot update without restarting the game.

Problem recurring:

  • Part1: The currently released resource is version A.
  • Part2: Then I released the assets of version B. After opening the game, the update has not yet started, so the loaded assets are version A. I started to check the catalog and assets updates. After detecting the need to update, I released all previously loaded handles and started the hot update. Load the assets again, which is version B at this time. Restart the game and load the assets again is also the B version. (Everything looks normal here) 
  • Part3: I released the version C assets. At this time, I opened the game and there was a problem. The assets I loaded is version A, not version B. (This step, I think there is a problem) I still release all the previously loaded handles, start the hot update, and load the assets again. C version assets can also be used.
  • Part4: And then, I start to wonder whether Addressable would automatically request the Catalog when it was connected to the Internet (I have checked Disable Catalog Update on Startup). If an asset update is detected, the initial version will be used instead of the latest version of the asset source after the hot update. So after my experiment, I turned off my asset server, and it automatically used the latest version after the hot update. After starting the asset server, the loaded asset will be the initial version, and only after I update it will it return to normal.

Expected effect: Since I have updated the asset, this resource should be the latest no matter what the situation is. Rather than inexplicably appearing in the initial version of the asset.

Unity version: 2020.1.9f1c1
Platform: Windows
Related code: Test Addressables project
https://gitee.com/dreamCirno/addressable-hotfix-test/tree/master

 

A1: I don’t know which version you are using. It is recommended to refer to the information in UWA Q&A:
https://answer.uwa4d.com/question/5e3b7fe686a39548919837e1

Or see if there is a problem with the order of calls. I call them in this order:
InitializeAsync->CheckForCatalogUpdates->UpdateCatalogs->GetDownloadSizeAsync->DownloadDependenciesAsync

 

A2: According to your description, it seems that you have not updated to the B version of the Catalog.

 

A3: Refer to the following code:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.UI;
using static UnityEngine.AddressableAssets.Addressables;

public class GameLaunch : MonoBehaviour 
{

    public Action<string> OnStatusTipHasChanged;
    public Action<float> OnDownloadPercentHasChanged;
    public Action<bool> OnDownloadHasResult;

    AsyncOperationHandle<GameObject> handle;
    GameObject obj;

    /// <summary>
    /// Customize the specific collection of labels to be updated and detected
    /// </summary>
    private List<string> mKeys = new List<string>() { "default" };

    public IEnumerator CheckUpdate() 
    {
        bool isNeedUpdateCatalog = false;
        bool isNeedUpdateResources = false;

        // Initialize the Addressable
        var initHandle = Addressables.InitializeAsync();
        yield return initHandle;
        OnStatusTipHasChanged?.Invoke("Start checking for updates");
        Debug.LogError("Start checking for updates");

        // Check whether the local catalog is the latest version
        var checkHandle = Addressables.CheckForCatalogUpdates(false);
        yield return checkHandle;
        if (checkHandle.Status == AsyncOperationStatus.Succeeded) {
            OnDownloadPercentHasChanged?.Invoke(1);
            OnStatusTipHasChanged?.Invoke("Catalog check completed");
            Debug.LogError("Catalog check completed");
        }

        List<string> catalogs = checkHandle.Result;
        if (catalogs != null && catalogs.Count > 0) 
        {
            OnStatusTipHasChanged?.Invoke("The catalog needs to be updated after detected");
            Debug.LogError("The catalog needs to be updated after detected");
            isNeedUpdateCatalog = true;
        } 
        else 
        {
            OnStatusTipHasChanged?.Invoke("The detected catalog is up to date");
            Debug.LogError("The detected catalogs are up to date");
        }

        var sizeHandle = Addressables.GetDownloadSizeAsync(mKeys);
        if (sizeHandle.Result > 0) 
        {
            Debug.LogError("An update resource package was detected");
            OnStatusTipHasChanged?.Invoke("An update resource package was detected");
            isNeedUpdateResources = true;
        } 
        else 
        {
            Debug.LogError("No update resource package detected");
            OnStatusTipHasChanged?.Invoke("No update resource package was detected");
        }

        OnStatusTipHasChanged?.Invoke("Ready for the next step");

        if (isNeedUpdateCatalog || isNeedUpdateResources) 
        {
            if (isNeedUpdateCatalog) 
            {
                yield return UpdateCatalog(catalogs);
            }
            if (isNeedUpdateResources) 
            {
                yield return UpdateResources();
            }
            OnDownloadHasResult?.Invoke(true);
        } 
        else 
        {
            //StartGame();
            Debug.LogError("start game");
        }
    }

    private void Update() 
    {
        if (Input.GetKeyDown(KeyCode.C)) 
        {
            StartCoroutine(CheckUpdate());
        }
        if (Input.GetKeyDown(KeyCode.L)) 
        {
            handle = Addressables.LoadAssetAsync<GameObject>("Image");
            handle.Completed += param => 
            {
                if (param.Status == AsyncOperationStatus.Succeeded) 
                {
                    Debug.LogError("Preloading succeeded");
                } 
                else 
                {
                    Debug.LogError("Preloading failure");
                }
                obj = param.Result;
            };
        }
        if (Input.GetKeyDown(KeyCode.R)) 
        {
            ReleaseCache();
        }
        if (Input.GetKeyDown(KeyCode.Space)) 
        {
            Instantiate(obj, new Vector2(UnityEngine.Random.Range(0, 400), UnityEngine.Random.Range(0, 400)), Quaternion.identity, GameObject.Find("Canvas").transform);
        }
    }

    private IEnumerator UpdateCatalog(List<string> catalogs) 
    {
        var updateHandle = Addressables.UpdateCatalogs(catalogs, false);
        Debug.LogError("Start Updating Catalogs");
        yield return updateHandle;

        Addressables.Release(updateHandle);
    }

    private IEnumerator UpdateResources() 
    {
        ReleaseCache();
        // Need to update the size> 0, indicating that the update needs to be downloaded
        // Clean up old assets
        //var clearHandle = Addressables.ClearDependencyCacheAsync(mKeys, false);
        //yield return clearHandle;

        // Download the asset to be updated
        var downloadHandle = Addressables.DownloadDependenciesAsync(mKeys, MergeMode.Union, false);
        Debug.LogError("Start updating asset");
        while (!downloadHandle.IsDone) 
        {
            DownloadStatus downloadStatus = downloadHandle.GetDownloadStatus();
            OnDownloadPercentHasChanged?.Invoke(downloadStatus.Percent);
            OnStatusTipHasChanged?.Invoke($"Download progress: {downloadStatus.Percent * 100} %");
            Debug.LogError($"Download progress: {downloadStatus.Percent * 100} %");
            yield return null;
        }
        if (downloadHandle.Status == AsyncOperationStatus.Succeeded) 
        {
            OnStatusTipHasChanged?.Invoke($"Download status: {(downloadHandle.IsDone ? "Completed" : "Uncompleted")}");
            Debug.LogError($"Download status: {(downloadHandle.IsDone ? "Completed" : "Uncompleted")}");
        } 
        else 
        {
            OnStatusTipHasChanged?.Invoke($"Failed to download update package");
            Debug.LogError($"Failed to download update package, error content: {downloadHandle.OperationException.Message}");
        }
        Addressables.Release(downloadHandle);
    }

    private void ReleaseCache() 
    {
        try 
        {
            Addressables.Release(handle);
            Debug.LogError("Release assets successfully");
        } 
        catch (Exception) 
        {
            Debug.LogError("Failed to release assets");
        }
    }

}

Rendering

Q: Using the normal line stroke algorithm, there is a phenomenon of punching on some thin mesh. Is this an algorithm problem or a mesh problem? How should it be solved?

Shader is shown in the figure:

A1: There are two doubts:
1. Do you want Cull Front instead of Cull Back?
2. If it is RenderTexture rendered by yourself, is the depth accuracy is not enough?

 

A2: It is an inherent problem caused by this method. The rendered Backfaces conflict with the original model in depth, occlude the model and cause penetration problems.

Solution: One method is to set Z-offset for Backfaces so that the contour line is buried in the adjacent surface. Another method is to modify the normals of the expansion of Backfaces to flatten the contours (Backfaces).


That’s all for today’s sharing. Of course, life is boundless but knowing is boundless. In the long development cycle, these problems you see may be just the tip of the iceberg. We have already prepared more technical topics on the UWA Q&A website, waiting for you to explore and share together. You are welcome to join us, who love progress. Maybe your method can solve the urgent needs of others; and the “stone” of other mountains can also attack your “jade”.

YOU MAY ALSO LIKE!!!

UWA Website: https://en.uwa4d.com

UWA Blogs: https://blog.en.uwa4d.com

LinkedIn: https://www.linkedin.com/company/uwahub

Facebook: https://www.facebook.com/uwa4d

Twitter: https://twitter.com/uwa4d

Related Topics

1 Visitor Comment

Post a Reply

Your email address will not be published.