No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

RenderGraphResourceTexture.cs 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. using System;
  2. using System.Diagnostics;
  3. using System.Runtime.CompilerServices;
  4. using UnityEngine.Experimental.Rendering;
  5. using UnityEngine.Rendering;
  6. using UnityEngine.Scripting.APIUpdating;
  7. namespace UnityEngine.Rendering.RenderGraphModule
  8. {
  9. internal struct TextureAccess
  10. {
  11. public TextureHandle textureHandle;
  12. public int mipLevel;
  13. public int depthSlice;
  14. public AccessFlags flags;
  15. public TextureAccess(TextureHandle handle, AccessFlags flags, int mipLevel, int depthSlice)
  16. {
  17. this.textureHandle = handle;
  18. this.flags = flags;
  19. this.mipLevel = mipLevel;
  20. this.depthSlice = depthSlice;
  21. }
  22. }
  23. /// <summary>
  24. /// An abstract handle representing a texture resource as known by one particular record + execute of the render graph.
  25. /// TextureHandles should not be used outside of the context of a render graph execution.
  26. ///
  27. /// A render graph needs to do additional state tracking on texture resources (lifetime, how is it used,...) to enable
  28. /// this all textures relevant to the render graph need to be make known to it. A texture handle specifies such a texture as
  29. /// known to the render graph.
  30. ///
  31. /// It is important to understand that a render graph texture handle does not necessarily represent an actual texture. For example
  32. /// textures could be created the render graph that are only referenced by passes that are later culled when executing the graph.
  33. /// Such textures would never be allocated as actual RenderTextures.
  34. ///
  35. /// Texture handles are only relevant to one particular record+execute phase of the render graph. After execution all texture
  36. /// handles are invalidated. The system will catch texture handles from a different execution of the render graph but still
  37. /// users should be careful to avoid keeping texture handles around from other render graph executions.
  38. ///
  39. /// Texture handles do not need to be disposed/freed (they are auto-invalidated at the end of graph execution). The RenderTextures they represent
  40. /// are either freed by the render graph internally (when the handle was acquired through RenderGraph.CreateTexture) or explicitly managed by
  41. /// some external system (when acquired through RenderGraph.ImportTexture).
  42. ///
  43. /// </summary>
  44. [DebuggerDisplay("Texture ({handle.index})")]
  45. [MovedFrom(true, "UnityEngine.Experimental.Rendering.RenderGraphModule", "UnityEngine.Rendering.RenderGraphModule")]
  46. public struct TextureHandle
  47. {
  48. private static TextureHandle s_NullHandle = new TextureHandle();
  49. /// <summary>
  50. /// Returns a null texture handle
  51. /// </summary>
  52. /// <value>A null texture handle.</value>
  53. public static TextureHandle nullHandle { get { return s_NullHandle; } }
  54. internal ResourceHandle handle;
  55. private bool builtin;
  56. internal TextureHandle(in ResourceHandle h)
  57. {
  58. handle = h;
  59. builtin = false;
  60. }
  61. internal TextureHandle(int handle, bool shared = false, bool builtin = false)
  62. {
  63. this.handle = new ResourceHandle(handle, RenderGraphResourceType.Texture, shared);
  64. this.builtin = builtin;
  65. }
  66. /// <summary>
  67. /// Cast to RenderTargetIdentifier
  68. /// </summary>
  69. /// <param name="texture">Input TextureHandle.</param>
  70. /// <returns>Resource as a RenderTargetIdentifier.</returns>
  71. public static implicit operator RenderTargetIdentifier(TextureHandle texture) => texture.IsValid() ? RenderGraphResourceRegistry.current.GetTexture(texture) : default(RenderTargetIdentifier);
  72. /// <summary>
  73. /// Cast to Texture
  74. /// </summary>
  75. /// <param name="texture">Input TextureHandle.</param>
  76. /// <returns>Resource as a Texture.</returns>
  77. public static implicit operator Texture(TextureHandle texture) => texture.IsValid() ? RenderGraphResourceRegistry.current.GetTexture(texture) : null;
  78. /// <summary>
  79. /// Cast to RenderTexture
  80. /// </summary>
  81. /// <param name="texture">Input TextureHandle.</param>
  82. /// <returns>Resource as a RenderTexture.</returns>
  83. public static implicit operator RenderTexture(TextureHandle texture) => texture.IsValid() ? RenderGraphResourceRegistry.current.GetTexture(texture) : null;
  84. /// <summary>
  85. /// Cast to RTHandle
  86. /// </summary>
  87. /// <param name="texture">Input TextureHandle.</param>
  88. /// <returns>Resource as a RTHandle.</returns>
  89. public static implicit operator RTHandle(TextureHandle texture) => texture.IsValid() ? RenderGraphResourceRegistry.current.GetTexture(texture) : null;
  90. /// <summary>
  91. /// Return true if the handle is valid.
  92. /// </summary>
  93. /// <returns>True if the handle is valid.</returns>
  94. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  95. public bool IsValid() => handle.IsValid();
  96. /// <summary>
  97. /// Return true if the handle is a builtin handle managed by RenderGraph internally.
  98. /// </summary>
  99. /// <returns>True if the handle is a builtin handle.</returns>
  100. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  101. internal bool IsBuiltin() => this.builtin;
  102. /// <summary>
  103. /// Get the Descriptor of the texture. This simply calls RenderGraph.GetTextureDesc but is more easily discoverable through auto complete.
  104. /// </summary>
  105. /// <param name="renderGraph">The rendergraph instance that was used to create the texture on. Texture handles are a lightweight object, all information is stored on the RenderGraph itself.</param>
  106. /// <returns>The texture descriptor for the given texture handle.</returns>
  107. public TextureDesc GetDescriptor(RenderGraph renderGraph) { return renderGraph.GetTextureDesc(this); }
  108. }
  109. /// <summary>
  110. /// The mode that determines the size of a Texture.
  111. /// </summary>
  112. public enum TextureSizeMode
  113. {
  114. ///<summary>Explicit size.</summary>
  115. Explicit,
  116. ///<summary>Size automatically scaled by a Vector.</summary>
  117. Scale,
  118. ///<summary>Size automatically scaled by a Functor.</summary>
  119. Functor
  120. }
  121. #if UNITY_2020_2_OR_NEWER
  122. /// <summary>
  123. /// Subset of the texture desc containing information for fast memory allocation (when platform supports it)
  124. /// </summary>
  125. public struct FastMemoryDesc
  126. {
  127. ///<summary>Whether the texture will be in fast memory.</summary>
  128. public bool inFastMemory;
  129. ///<summary>Flag to determine what parts of the render target is spilled if not fully resident in fast memory.</summary>
  130. public FastMemoryFlags flags;
  131. ///<summary>How much of the render target is to be switched into fast memory (between 0 and 1).</summary>
  132. public float residencyFraction;
  133. }
  134. #endif
  135. /// <summary>
  136. /// Descriptor used to create texture resources
  137. /// </summary>
  138. public struct TextureDesc
  139. {
  140. ///<summary>Texture sizing mode.</summary>
  141. public TextureSizeMode sizeMode;
  142. ///<summary>Texture width.</summary>
  143. public int width;
  144. ///<summary>Texture height.</summary>
  145. public int height;
  146. ///<summary>Number of texture slices..</summary>
  147. public int slices;
  148. ///<summary>Texture scale.</summary>
  149. public Vector2 scale;
  150. ///<summary>Texture scale function.</summary>
  151. public ScaleFunc func;
  152. ///<summary>Depth buffer bit depth.</summary>
  153. public DepthBits depthBufferBits;
  154. ///<summary>Color format.</summary>
  155. public GraphicsFormat colorFormat;
  156. ///<summary>Filtering mode.</summary>
  157. public FilterMode filterMode;
  158. ///<summary>Addressing mode.</summary>
  159. public TextureWrapMode wrapMode;
  160. ///<summary>Texture dimension.</summary>
  161. public TextureDimension dimension;
  162. ///<summary>Enable random UAV read/write on the texture.</summary>
  163. public bool enableRandomWrite;
  164. ///<summary>Texture needs mip maps.</summary>
  165. public bool useMipMap;
  166. ///<summary>Automatically generate mip maps.</summary>
  167. public bool autoGenerateMips;
  168. ///<summary>Texture is a shadow map.</summary>
  169. public bool isShadowMap;
  170. ///<summary>Anisotropic filtering level.</summary>
  171. public int anisoLevel;
  172. ///<summary>Mip map bias.</summary>
  173. public float mipMapBias;
  174. ///<summary>Number of MSAA samples.</summary>
  175. public MSAASamples msaaSamples;
  176. ///<summary>Bind texture multi sampled.</summary>
  177. public bool bindTextureMS;
  178. ///<summary>[See Dynamic Resolution documentation](https://docs.unity3d.com/Manual/DynamicResolution.html)</summary>
  179. public bool useDynamicScale;
  180. ///<summary>[See Dynamic Resolution documentation](https://docs.unity3d.com/Manual/DynamicResolution.html)</summary>
  181. public bool useDynamicScaleExplicit;
  182. ///<summary>Memory less flag.</summary>
  183. public RenderTextureMemoryless memoryless;
  184. ///<summary>Special treatment of the VR eye texture used in stereoscopic rendering.</summary>
  185. public VRTextureUsage vrUsage;
  186. ///<summary>Texture name.</summary>
  187. public string name;
  188. #if UNITY_2020_2_OR_NEWER
  189. ///<summary>Descriptor to determine how the texture will be in fast memory on platform that supports it.</summary>
  190. public FastMemoryDesc fastMemoryDesc;
  191. #endif
  192. ///<summary>Determines whether the texture will fallback to a black texture if it is read without ever writing to it.</summary>
  193. public bool fallBackToBlackTexture;
  194. ///<summary>
  195. ///If all passes writing to a texture are culled by Dynamic Render Pass Culling, it will automatically fallback to a similar preallocated texture.
  196. ///Set this to true to force the allocation.
  197. ///</summary>
  198. public bool disableFallBackToImportedTexture;
  199. // Initial state. Those should not be used in the hash
  200. ///<summary>Texture needs to be cleared on first use.</summary>
  201. public bool clearBuffer;
  202. ///<summary>Clear color.</summary>
  203. public Color clearColor;
  204. ///<summary>Texture needs to be discarded on last use.</summary>
  205. public bool discardBuffer;
  206. void InitDefaultValues(bool dynamicResolution, bool xrReady)
  207. {
  208. useDynamicScale = dynamicResolution;
  209. vrUsage = VRTextureUsage.None;
  210. // XR Ready
  211. if (xrReady)
  212. {
  213. slices = TextureXR.slices;
  214. dimension = TextureXR.dimension;
  215. }
  216. else
  217. {
  218. slices = 1;
  219. dimension = TextureDimension.Tex2D;
  220. }
  221. discardBuffer = false;
  222. }
  223. /// <summary>
  224. /// TextureDesc constructor for a texture using explicit size
  225. /// </summary>
  226. /// <param name="width">Texture width</param>
  227. /// <param name="height">Texture height</param>
  228. /// <param name="dynamicResolution">Use dynamic resolution</param>
  229. /// <param name="xrReady">Set this to true if the Texture is a render texture in an XR setting.</param>
  230. public TextureDesc(int width, int height, bool dynamicResolution = false, bool xrReady = false)
  231. : this()
  232. {
  233. // Size related init
  234. sizeMode = TextureSizeMode.Explicit;
  235. this.width = width;
  236. this.height = height;
  237. // Important default values not handled by zero construction in this()
  238. msaaSamples = MSAASamples.None;
  239. InitDefaultValues(dynamicResolution, xrReady);
  240. }
  241. /// <summary>
  242. /// TextureDesc constructor for a texture using a fixed scaling
  243. /// </summary>
  244. /// <param name="scale">RTHandle scale used for this texture</param>
  245. /// <param name="dynamicResolution">Use dynamic resolution</param>
  246. /// <param name="xrReady">Set this to true if the Texture is a render texture in an XR setting.</param>
  247. public TextureDesc(Vector2 scale, bool dynamicResolution = false, bool xrReady = false)
  248. : this()
  249. {
  250. // Size related init
  251. sizeMode = TextureSizeMode.Scale;
  252. this.scale = scale;
  253. // Important default values not handled by zero construction in this()
  254. msaaSamples = MSAASamples.None;
  255. dimension = TextureDimension.Tex2D;
  256. InitDefaultValues(dynamicResolution, xrReady);
  257. }
  258. /// <summary>
  259. /// TextureDesc constructor for a texture using a functor for scaling
  260. /// </summary>
  261. /// <param name="func">Function used to determine the texture size</param>
  262. /// <param name="dynamicResolution">Use dynamic resolution</param>
  263. /// <param name="xrReady">Set this to true if the Texture is a render texture in an XR setting.</param>
  264. public TextureDesc(ScaleFunc func, bool dynamicResolution = false, bool xrReady = false)
  265. : this()
  266. {
  267. // Size related init
  268. sizeMode = TextureSizeMode.Functor;
  269. this.func = func;
  270. // Important default values not handled by zero construction in this()
  271. msaaSamples = MSAASamples.None;
  272. dimension = TextureDimension.Tex2D;
  273. InitDefaultValues(dynamicResolution, xrReady);
  274. }
  275. /// <summary>
  276. /// Copy constructor
  277. /// </summary>
  278. /// <param name="input">The TextureDesc instance to copy from.</param>
  279. public TextureDesc(TextureDesc input)
  280. {
  281. this = input;
  282. }
  283. /// <summary>
  284. /// Do a best effort conversion from a RenderTextureDescriptor to a TextureDesc. This tries to initialize a descriptor to be as close as possible to the given render texture descriptor but there might be subtle differences when creating
  285. /// render graph textures using this TextureDesc due to the underlying RTHandle system.
  286. /// Some parameters of the TextureDesc (like name and filtering modes) are not present in the RenderTextureDescriptor for these the returned TextureDesc will contain plausible default values.
  287. /// </summary>
  288. /// <param name="input">The texture descriptor to create a TextureDesc from</param>
  289. public TextureDesc(RenderTextureDescriptor input)
  290. {
  291. sizeMode = TextureSizeMode.Explicit;
  292. width = input.width;
  293. height = input.height;
  294. slices = input.volumeDepth;
  295. scale = Vector2.one;
  296. func = null;
  297. depthBufferBits = (DepthBits)input.depthBufferBits;
  298. colorFormat = input.graphicsFormat;
  299. filterMode = FilterMode.Bilinear;
  300. wrapMode = TextureWrapMode.Clamp;
  301. dimension = input.dimension;
  302. enableRandomWrite = input.enableRandomWrite;
  303. useMipMap = input.useMipMap;
  304. autoGenerateMips = input.autoGenerateMips;
  305. isShadowMap = (input.shadowSamplingMode != ShadowSamplingMode.None);
  306. anisoLevel = 1;
  307. mipMapBias = 0;
  308. msaaSamples = (MSAASamples)input.msaaSamples;
  309. bindTextureMS = input.bindMS;
  310. useDynamicScale = input.useDynamicScale;
  311. useDynamicScaleExplicit = false;
  312. memoryless = input.memoryless;
  313. vrUsage = input.vrUsage;
  314. name = "UnNamedFromRenderTextureDescriptor";
  315. fastMemoryDesc = new FastMemoryDesc();
  316. fastMemoryDesc.inFastMemory = false;
  317. fallBackToBlackTexture = false;
  318. disableFallBackToImportedTexture = true;
  319. clearBuffer = true;
  320. clearColor = Color.black;
  321. discardBuffer = false;
  322. }
  323. /// <summary>
  324. /// Do a best effort conversion from a RenderTexture to a TextureDesc. This tries to initialize a descriptor to be as close as possible to the given render texture but there might be subtle differences when creating
  325. /// render graph textures using this TextureDesc due to the underlying RTHandle system.
  326. /// </summary>
  327. /// <param name="input">The texture to create a TextureDesc from</param>
  328. public TextureDesc(RenderTexture input) : this(input.descriptor)
  329. {
  330. filterMode = input.filterMode;
  331. wrapMode = input.wrapMode;
  332. anisoLevel = input.anisoLevel;
  333. mipMapBias = input.mipMapBias;
  334. this.name = "UnNamedFromRenderTextureDescriptor";
  335. }
  336. /// <summary>
  337. /// Hash function
  338. /// </summary>
  339. /// <returns>The texture descriptor hash.</returns>
  340. public override int GetHashCode()
  341. {
  342. int hashCode = 17;
  343. unchecked
  344. {
  345. switch (sizeMode)
  346. {
  347. case TextureSizeMode.Explicit:
  348. hashCode = hashCode * 23 + width;
  349. hashCode = hashCode * 23 + height;
  350. break;
  351. case TextureSizeMode.Functor:
  352. if (func != null)
  353. hashCode = hashCode * 23 + func.GetHashCode();
  354. break;
  355. case TextureSizeMode.Scale:
  356. hashCode = hashCode * 23 + scale.x.GetHashCode();
  357. hashCode = hashCode * 23 + scale.y.GetHashCode();
  358. break;
  359. }
  360. hashCode = hashCode * 23 + mipMapBias.GetHashCode();
  361. hashCode = hashCode * 23 + slices;
  362. hashCode = hashCode * 23 + (int)depthBufferBits;
  363. hashCode = hashCode * 23 + (int)colorFormat;
  364. hashCode = hashCode * 23 + (int)filterMode;
  365. hashCode = hashCode * 23 + (int)wrapMode;
  366. hashCode = hashCode * 23 + (int)dimension;
  367. hashCode = hashCode * 23 + (int)memoryless;
  368. hashCode = hashCode * 23 + (int)vrUsage;
  369. hashCode = hashCode * 23 + anisoLevel;
  370. hashCode = hashCode * 23 + (enableRandomWrite ? 1 : 0);
  371. hashCode = hashCode * 23 + (useMipMap ? 1 : 0);
  372. hashCode = hashCode * 23 + (autoGenerateMips ? 1 : 0);
  373. hashCode = hashCode * 23 + (isShadowMap ? 1 : 0);
  374. hashCode = hashCode * 23 + (bindTextureMS ? 1 : 0);
  375. hashCode = hashCode * 23 + (useDynamicScale ? 1 : 0);
  376. hashCode = hashCode * 23 + (int)msaaSamples;
  377. #if UNITY_2020_2_OR_NEWER
  378. hashCode = hashCode * 23 + (fastMemoryDesc.inFastMemory ? 1 : 0);
  379. #endif
  380. }
  381. return hashCode;
  382. }
  383. /// <summary>
  384. /// Calculate the final size of the texture descriptor in pixels. This takes into account the sizeMode set for this descriptor.
  385. /// For the automatically scaled sizes the size will be relative to the RTHandle reference size <see cref="RTHandles.SetReferenceSize">SetReferenceSize</see>.
  386. /// </summary>
  387. /// <returns>The calculated size.</returns>
  388. /// <exception cref="ArgumentOutOfRangeException">Thrown if the texture descriptor's size mode falls outside the expected range.</exception>
  389. public Vector2Int CalculateFinalDimensions()
  390. {
  391. return sizeMode switch
  392. {
  393. TextureSizeMode.Explicit => new Vector2Int(width, height),
  394. TextureSizeMode.Scale => RTHandles.CalculateDimensions(scale),
  395. TextureSizeMode.Functor => RTHandles.CalculateDimensions(func),
  396. _ => throw new ArgumentOutOfRangeException()
  397. };
  398. }
  399. }
  400. [DebuggerDisplay("TextureResource ({desc.name})")]
  401. class TextureResource : RenderGraphResource<TextureDesc, RTHandle>
  402. {
  403. static int m_TextureCreationIndex;
  404. public override string GetName()
  405. {
  406. if (imported && !shared)
  407. return graphicsResource != null ? graphicsResource.name : "null resource";
  408. else
  409. return desc.name;
  410. }
  411. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  412. public override int GetDescHashCode() { return desc.GetHashCode(); }
  413. public override void CreateGraphicsResource()
  414. {
  415. var name = GetName();
  416. // Textures are going to be reused under different aliases along the frame so we can't provide a specific name upon creation.
  417. // The name in the desc is going to be used for debugging purpose and render graph visualization.
  418. if (name == "")
  419. name = $"RenderGraphTexture_{m_TextureCreationIndex++}";
  420. switch (desc.sizeMode)
  421. {
  422. case TextureSizeMode.Explicit:
  423. graphicsResource = RTHandles.Alloc(desc.width, desc.height, desc.slices, desc.depthBufferBits, desc.colorFormat, desc.filterMode, desc.wrapMode, desc.dimension, desc.enableRandomWrite,
  424. desc.useMipMap, desc.autoGenerateMips, desc.isShadowMap, desc.anisoLevel, desc.mipMapBias, desc.msaaSamples, desc.bindTextureMS, desc.useDynamicScale, desc.useDynamicScaleExplicit, desc.memoryless, desc.vrUsage, name);
  425. break;
  426. case TextureSizeMode.Scale:
  427. graphicsResource = RTHandles.Alloc(desc.scale, desc.slices, desc.depthBufferBits, desc.colorFormat, desc.filterMode, desc.wrapMode, desc.dimension, desc.enableRandomWrite,
  428. desc.useMipMap, desc.autoGenerateMips, desc.isShadowMap, desc.anisoLevel, desc.mipMapBias, desc.msaaSamples, desc.bindTextureMS, desc.useDynamicScale, desc.useDynamicScaleExplicit, desc.memoryless, desc.vrUsage, name);
  429. break;
  430. case TextureSizeMode.Functor:
  431. graphicsResource = RTHandles.Alloc(desc.func, desc.slices, desc.depthBufferBits, desc.colorFormat, desc.filterMode, desc.wrapMode, desc.dimension, desc.enableRandomWrite,
  432. desc.useMipMap, desc.autoGenerateMips, desc.isShadowMap, desc.anisoLevel, desc.mipMapBias, desc.msaaSamples, desc.bindTextureMS, desc.useDynamicScale, desc.useDynamicScaleExplicit, desc.memoryless, desc.vrUsage, name);
  433. break;
  434. }
  435. }
  436. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  437. public override void UpdateGraphicsResource()
  438. {
  439. if (graphicsResource != null)
  440. graphicsResource.m_Name = GetName();
  441. }
  442. public override void ReleaseGraphicsResource()
  443. {
  444. if (graphicsResource != null)
  445. graphicsResource.Release();
  446. base.ReleaseGraphicsResource();
  447. }
  448. public override void LogCreation(RenderGraphLogger logger)
  449. {
  450. logger.LogLine($"Created Texture: {desc.name} (Cleared: {desc.clearBuffer})");
  451. }
  452. public override void LogRelease(RenderGraphLogger logger)
  453. {
  454. logger.LogLine($"Released Texture: {desc.name}");
  455. }
  456. }
  457. class TexturePool : RenderGraphResourcePool<RTHandle>
  458. {
  459. protected override void ReleaseInternalResource(RTHandle res)
  460. {
  461. res.Release();
  462. }
  463. protected override string GetResourceName(in RTHandle res)
  464. {
  465. return res.rt.name;
  466. }
  467. protected override long GetResourceSize(in RTHandle res)
  468. {
  469. return Profiling.Profiler.GetRuntimeMemorySizeLong(res.rt);
  470. }
  471. override protected string GetResourceTypeName()
  472. {
  473. return "Texture";
  474. }
  475. override protected int GetSortIndex(RTHandle res)
  476. {
  477. return res.GetInstanceID();
  478. }
  479. }
  480. }