protected override async Task OnReferencedRNameChangedOverride(ResourceInfo referencedResInfo, EngineNS.RName newRName, EngineNS.RName oldRName)
        {
            if (referencedResInfo.ResourceType == EngineNS.Editor.Editor_RNameTypeAttribute.Texture)
            {
                ReferenceRNameList.Remove(oldRName);
                ReferenceRNameList.Add(referencedResInfo.ResourceName);
                await Save();

                // mat
                bool bFinded = false;
                var  mat     = await EngineNS.CEngine.Instance.MaterialManager.GetMaterialAsync(EngineNS.CEngine.Instance.RenderContext, ResourceName);

                foreach (var param in mat.ParamList)
                {
                    if (param.VarType == EngineNS.EShaderVarType.SVT_Texture && param.TextureRName.Equals(oldRName))
                    {
                        param.SetValueStr(referencedResInfo.ResourceName);
                        bFinded = true;
                    }
                }
                mat.SaveMaterial();

                // link
                if (bFinded)
                {
                    var xndHolder = await EngineNS.IO.XndHolder.LoadXND(ResourceName.Address + EngineNS.Graphics.CGfxMaterial.ShaderLinkExtension);

                    if (xndHolder != null)
                    {
                        var container = new CodeGenerateSystem.Base.NodesContainer();
                        await container.Load(xndHolder.Node);

                        xndHolder.Node.TryReleaseHolder();

                        foreach (var node in container.CtrlNodeList)
                        {
                            if (node is Controls.TextureControl)
                            {
                                var tc = node as Controls.TextureControl;
                                if (tc.TextureRName == oldRName)
                                {
                                    tc.TextureRName = referencedResInfo.ResourceName;
                                }
                            }
                        }

                        var saveHolder = EngineNS.IO.XndHolder.NewXNDHolder();
                        container.Save(saveHolder.Node);
                        EngineNS.IO.XndHolder.SaveXND(ResourceName.Address + EngineNS.Graphics.CGfxMaterial.ShaderLinkExtension, saveHolder);
                    }
                }
            }
        }
        protected override async Task <bool> InitializeContextMenuOverride(ContextMenu contextMenu)
        {
            await EngineNS.Thread.AsyncDummyClass.DummyFunc();

            var textSeparatorStyle = Application.Current.MainWindow.TryFindResource(new ComponentResourceKey(typeof(ResourceLibrary.CustomResources), "TextMenuSeparatorStyle")) as System.Windows.Style;
            var menuItemStyle      = Application.Current.MainWindow.TryFindResource(new ComponentResourceKey(typeof(ResourceLibrary.CustomResources), "MenuItem_Default")) as System.Windows.Style;

            contextMenu.Items.Add(new ResourceLibrary.Controls.Menu.TextSeparator()
            {
                Text  = "Common",
                Style = textSeparatorStyle,
            });

            var ciMenuItem = new MenuItem()
            {
                Name   = "MatResInfo_CreateMatIns",
                Header = "创建材质实例",
                Style  = menuItemStyle,
            };

            ciMenuItem.Click += async(object sender, RoutedEventArgs e) =>
            {
                var resInfo = new MaterialInstanceResourceInfo();

                var win = new EditorCommon.Controls.ResourceBrowser.CreateResDialog();
                win.Title        = $"创建材质实例";
                win.ResourceName = EditorCommon.Program.GetValidName(ResourceName.GetDirectory(), ResourceName.PureName() + "_ins", EngineNS.CEngineDesc.MaterialInstanceExtension);
                var data = new MaterialInstanceResourceCreateData()
                {
                    ResourceName   = win.ResourceName,
                    ParentMaterial = this.ResourceName,
                };
                win.ResCreateData = data;
                data.HostDialog   = win;
                if (win.ShowDialog((value, cultureInfo) =>
                {
                    if (value == null)
                    {
                        return(new ValidationResult(false, "内容不合法"));
                    }
                    return(resInfo.ResourceNameAvailable(ResourceName.GetDirectory(), value.ToString()));
                }) == false)
                {
                    return;
                }

                var createData = win.GetCreateData();
                createData.RNameType = ResourceName.RNameType;
                var resourceInfo = await resInfo.CreateEmptyResource(ResourceName.GetDirectory(), ResourceName.GetRootFolder(), createData);

                var pro = resourceInfo.GetType().GetProperty("ResourceType");
                pro?.SetValue(resourceInfo, EngineNS.Editor.Editor_RNameTypeAttribute.MaterialInstance);
                resourceInfo.ParentBrowser = this.ParentBrowser;
                await resourceInfo.Save(true);

                this.ParentBrowser.AddResourceInfo(resourceInfo);
                this.ParentBrowser.SelectResourceInfo(resourceInfo);
                await resourceInfo.InitializeContextMenu();
            };
            ResourceLibrary.Controls.Menu.MenuAssist.SetIcon(ciMenuItem, new BitmapImage(new System.Uri("pack://application:,,,/ResourceLibrary;component/Icons/Icons/AssetIcons/MaterialInstanceConstant_64x.png", UriKind.Absolute)));
            contextMenu.Items.Add(ciMenuItem);

            var duplicateMenuItem = new MenuItem()
            {
                Name   = "MatResInfo_Duplicate",
                Header = $"复制",
                Style  = menuItemStyle,
            };

            duplicateMenuItem.Click += async(object sender, RoutedEventArgs e) =>
            {
                var dir      = ResourceName.GetDirectory();
                var inputWin = new InputWindow.InputWindow();
                inputWin.Title       = $"复制{ResourceName.PureName()}";
                inputWin.Description = "输入材质名称";
                inputWin.Value       = EditorCommon.Program.GetValidName(dir, ResourceName.PureName(), EngineNS.CEngineDesc.MaterialExtension);
                if (inputWin.ShowDialog((value, cultureInfo) =>
                {
                    if (value == null)
                    {
                        return(new ValidationResult(false, "内容不合法"));
                    }
                    return(ResourceNameAvailable(dir, value.ToString()));
                }) == false)
                {
                    return;
                }

                var rc       = EngineNS.CEngine.Instance.RenderContext;
                var tagRName = EngineNS.RName.EditorOnly_GetRNameFromAbsFile(dir + inputWin.Value + EngineNS.CEngineDesc.MaterialExtension);
                // mat
                var mat = await EngineNS.CEngine.Instance.MaterialManager.GetMaterialAsync(rc, ResourceName);

                var matCopy = EngineNS.CEngine.Instance.MaterialManager.NewMaterial(tagRName);
                mat.CopyTo(matCopy);
                matCopy.SaveMaterial();
                // link
                var xndHolder = await EngineNS.IO.XndHolder.LoadXND(ResourceName.Address + EngineNS.Graphics.CGfxMaterial.ShaderLinkExtension);

                if (xndHolder != null)
                {
                    var container = new CodeGenerateSystem.Base.NodesContainer();
                    await container.Load(xndHolder.Node);

                    xndHolder.Node.TryReleaseHolder();

                    Controls.MaterialControl matCtrl = null;
                    foreach (var node in container.OrigionNodeControls)
                    {
                        if (node is Controls.MaterialControl)
                        {
                            node.Id = Guid.NewGuid();
                            matCtrl = node as Controls.MaterialControl;
                        }
                    }

                    System.IO.TextWriter codeFile, varFile;
                    CodeGenerator.GenerateCode(container, matCtrl, out codeFile, out varFile);

                    // var
                    System.IO.File.WriteAllText(tagRName.Address + EngineNS.Graphics.CGfxMaterial.ShaderDefineExtension, varFile.ToString());
                    // code
                    System.IO.File.WriteAllText(tagRName.Address + EngineNS.Graphics.CGfxMaterial.ShaderIncludeExtension, codeFile.ToString());
                    // link
                    var tagHolder = EngineNS.IO.XndHolder.NewXNDHolder();
                    container.Save(tagHolder.Node);
                    EngineNS.IO.XndHolder.SaveXND(tagRName.Address + EngineNS.Graphics.CGfxMaterial.ShaderLinkExtension, tagHolder);
                }
                // resInfo
                var copyedRInfo = new MaterialResourceInfo();
                copyedRInfo.ResourceName = tagRName;
                await CopyResource(copyedRInfo);

                await copyedRInfo.Save();

                // snapshot
                EngineNS.CEngine.Instance.FileManager.CopyFile(ResourceName.Address + EditorCommon.Program.SnapshotExt, tagRName.Address + EditorCommon.Program.SnapshotExt, true);
                await copyedRInfo.InitializeContextMenu();

                ParentBrowser.AddResourceInfo(copyedRInfo);
                ParentBrowser.SelectResourceInfo(copyedRInfo);
            };
            ResourceLibrary.Controls.Menu.MenuAssist.SetIcon(duplicateMenuItem, new BitmapImage(new System.Uri("pack://application:,,,/ResourceLibrary;component/Icons/Icons/icon_Edit_Duplicate_40x.png", UriKind.Absolute)));
            contextMenu.Items.Add(duplicateMenuItem);

            var menuItem = new MenuItem()
            {
                Name   = "MatResInfo_Delete",
                Header = "删除",
                Style  = menuItemStyle,
            };

            menuItem.Click += async(object sender, RoutedEventArgs e) =>
            {
                await DeleteResource();

                EngineNS.CEngine.Instance.MaterialManager.Materials.Remove(ResourceName);
            };
            ResourceLibrary.Controls.Menu.MenuAssist.SetIcon(menuItem, new BitmapImage(new System.Uri("pack://application:,,,/ResourceLibrary;component/Icons/Icons/icon_delete_16px.png", UriKind.Absolute)));
            contextMenu.Items.Add(menuItem);

            return(true);
        }
        public static bool GenerateCode(CodeGenerateSystem.Base.NodesContainer container, Controls.MaterialControl setValueCtrl, out System.IO.TextWriter codeFile, out System.IO.TextWriter varFile)
        {
            codeFile = new System.IO.StringWriter();
            varFile  = new System.IO.StringWriter();

            // #include...
            //string includeString = "";
            //foreach (var node in container.CtrlNodeList)
            //{
            //    if (node is Controls.Operation.Function)
            //    {
            //        var funcCtrl = node as Controls.Operation.Function;
            //        if (!includeString.Contains(funcCtrl.Include))
            //            includeString += "#include \"" + funcCtrl.Include + "\"\r\n";
            //        //tw.WriteLine("#include \"" + funcCtrl.Include + "\"");
            //    }
            //}

            //codeFile.WriteLine(includeString);
            codeFile.WriteLine("");

            // value
            Dictionary <EngineNS.EShaderVarType, List <Controls.BaseNodeControl_ShaderVar> > varNodeDic = new Dictionary <EngineNS.EShaderVarType, List <Controls.BaseNodeControl_ShaderVar> >();

            foreach (var node in container.CtrlNodeList)
            {
                if (node is Controls.BaseNodeControl_ShaderVar)
                {
                    var ctrl = node as Controls.BaseNodeControl_ShaderVar;
                    if (ctrl.IsInConstantBuffer)
                    {
                        EngineNS.EShaderVarType varType = EngineNS.EShaderVarType.SVT_Unknown;
                        var valueTypeStr = ctrl.GCode_GetTypeString(null, null);
                        switch (valueTypeStr)
                        {
                        case "int":
                            varType = EngineNS.EShaderVarType.SVT_Int1;
                            break;

                        case "int2":
                            varType = EngineNS.EShaderVarType.SVT_Int2;
                            break;

                        case "int3":
                            varType = EngineNS.EShaderVarType.SVT_Int3;
                            break;

                        case "int4":
                            varType = EngineNS.EShaderVarType.SVT_Int4;
                            break;

                        case "float":
                        case "float1":
                            varType = EngineNS.EShaderVarType.SVT_Float1;
                            break;

                        case "float2":
                            varType = EngineNS.EShaderVarType.SVT_Float2;
                            break;

                        case "float3":
                            varType = EngineNS.EShaderVarType.SVT_Float3;
                            break;

                        case "float4":
                            varType = EngineNS.EShaderVarType.SVT_Float4;
                            break;
                        }
                        List <Controls.BaseNodeControl_ShaderVar> varList;
                        if (!varNodeDic.TryGetValue(varType, out varList))
                        {
                            varList             = new List <Controls.BaseNodeControl_ShaderVar>();
                            varNodeDic[varType] = varList;
                        }
                        varList.Add(ctrl);
                        //varFile.Write(ctrl.GetValueDefine());
                    }
                    else
                    {
                        codeFile.Write(ctrl.GetValueDefine());
                    }
                }
            }
            codeFile.WriteLine("");

            // 字节对齐
            List <Controls.BaseNodeControl_ShaderVar> tempList;

            if (varNodeDic.TryGetValue(EngineNS.EShaderVarType.SVT_Float4, out tempList))
            {
                foreach (var ctrl in tempList)
                {
                    varFile.Write(ctrl.GetValueDefine());
                }
            }
            if (varNodeDic.TryGetValue(EngineNS.EShaderVarType.SVT_Int4, out tempList))
            {
                foreach (var ctrl in tempList)
                {
                    varFile.Write(ctrl.GetValueDefine());
                }
            }
            List <Controls.BaseNodeControl_ShaderVar> float1List;

            varNodeDic.TryGetValue(EngineNS.EShaderVarType.SVT_Float1, out float1List);
            if (float1List == null)
            {
                float1List = new List <Controls.BaseNodeControl_ShaderVar>();
            }
            List <Controls.BaseNodeControl_ShaderVar> int1List;

            varNodeDic.TryGetValue(EngineNS.EShaderVarType.SVT_Int1, out int1List);
            if (int1List == null)
            {
                int1List = new List <Controls.BaseNodeControl_ShaderVar>();
            }
            if (varNodeDic.TryGetValue(EngineNS.EShaderVarType.SVT_Float3, out tempList))
            {
                foreach (var ctrl in tempList)
                {
                    varFile.Write(ctrl.GetValueDefine());
                    if (float1List.Count > 0)
                    {
                        var floatVar = float1List[0];
                        varFile.Write(floatVar.GetValueDefine());
                        float1List.RemoveAt(0);
                    }
                    else if (int1List.Count > 0)
                    {
                        var intVar = int1List[0];
                        varFile.Write(intVar.GetValueDefine());
                        int1List.RemoveAt(0);
                    }
                    else
                    {
                        // 补位
                        var varName = ctrl.GCode_GetValueName(null, null);
                        varFile.Write("float " + varName + "_fill;\r\n");
                    }
                }
            }
            if (varNodeDic.TryGetValue(EngineNS.EShaderVarType.SVT_Int3, out tempList))
            {
                foreach (var ctrl in tempList)
                {
                    varFile.Write(ctrl.GetValueDefine());
                    if (float1List.Count > 0)
                    {
                        var floatVar = float1List[0];
                        varFile.Write(floatVar.GetValueDefine());
                        float1List.RemoveAt(0);
                    }
                    else if (int1List.Count > 0)
                    {
                        var intVar = int1List[0];
                        varFile.Write(intVar.GetValueDefine());
                        int1List.RemoveAt(0);
                    }
                    else
                    {
                        // 补位
                        var varName = ctrl.GCode_GetValueName(null, null);
                        varFile.Write("int1 " + varName + "_fill;\r\n");
                    }
                }
            }
            if (varNodeDic.TryGetValue(EngineNS.EShaderVarType.SVT_Float2, out tempList))
            {
                foreach (var ctrl in tempList)
                {
                    varFile.Write(ctrl.GetValueDefine());
                }
                if ((tempList.Count % 2) != 0)
                {
                    int float2fillCount = 2;
                    while (float2fillCount > 0)
                    {
                        if (float1List.Count > 0)
                        {
                            var floatVar = float1List[0];
                            varFile.Write(floatVar.GetValueDefine());
                            float1List.RemoveAt(0);
                            float2fillCount--;
                        }
                        else
                        {
                            break;
                        }
                    }
                    while (float2fillCount > 0)
                    {
                        if (int1List.Count > 0)
                        {
                            var intVar = int1List[0];
                            varFile.Write(intVar.GetValueDefine());
                            int1List.RemoveAt(0);
                            float2fillCount--;
                        }
                        else
                        {
                            break;
                        }
                    }
                    if (float2fillCount > 0)
                    {
                        varFile.Write($"float{float2fillCount} SV_{EngineNS.Editor.Assist.GetValuedGUIDString(System.Guid.NewGuid())}_Fill;\r\n");
                    }
                }
            }
            if (varNodeDic.TryGetValue(EngineNS.EShaderVarType.SVT_Int2, out tempList))
            {
                foreach (var ctrl in tempList)
                {
                    varFile.Write(ctrl.GetValueDefine());
                }
                if ((tempList.Count % 2) != 0)
                {
                    int int2fillCount = 2;
                    while (int2fillCount > 0)
                    {
                        if (float1List.Count > 0)
                        {
                            var floatVar = float1List[0];
                            varFile.Write(floatVar.GetValueDefine());
                            float1List.RemoveAt(0);
                            int2fillCount--;
                        }
                        else
                        {
                            break;
                        }
                    }
                    while (int2fillCount > 0)
                    {
                        if (int1List.Count > 0)
                        {
                            var intVar = int1List[0];
                            varFile.Write(intVar.GetValueDefine());
                            int1List.RemoveAt(0);
                            int2fillCount--;
                        }
                        else
                        {
                            break;
                        }
                    }
                    if (int2fillCount > 0)
                    {
                        varFile.Write($"int{int2fillCount} SV_{EngineNS.Editor.Assist.GetValuedGUIDString(System.Guid.NewGuid())}_Fill;\r\n");
                    }
                }
            }
            var fillCount = (float1List.Count + int1List.Count) % 4;
            var idxCount  = float1List.Count + int1List.Count - fillCount;
            int idx       = 0;

            if (idx < idxCount)
            {
                for (int i = float1List.Count - 1; i >= 0; i--)
                {
                    var ctrl = float1List[i];
                    varFile.Write(ctrl.GetValueDefine());
                    float1List.RemoveAt(i);
                    idx++;
                    if (idx >= idxCount)
                    {
                        break;
                    }
                }
            }
            if (idx < idxCount)
            {
                for (int i = int1List.Count - 1; i >= 0; i--)
                {
                    var ctrl = int1List[i];
                    varFile.Write(ctrl.GetValueDefine());
                    int1List.RemoveAt(i);
                    idx++;
                    if (idx >= idxCount)
                    {
                        break;
                    }
                }
            }
            if (fillCount > 0)
            {
                varFile.Write($"float{4 - fillCount} SV_{EngineNS.Editor.Assist.GetValuedGUIDString(System.Guid.NewGuid())}_FinalFill;\r\n");
            }
            foreach (var ctrl in float1List)
            {
                varFile.Write(ctrl.GetValueDefine());
            }
            foreach (var ctrl in int1List)
            {
                varFile.Write(ctrl.GetValueDefine());
            }

            var idStr = EngineNS.Editor.Assist.GetValuedGUIDString(setValueCtrl.Id);

            // VS
            codeFile.WriteLine($"void DoVSMaterial_{idStr}(in PS_INPUT input, inout MTL_OUTPUT mtl)");
            codeFile.WriteLine("{");
            string strSegment           = "";
            string strDefinitionSegment = "";

            setValueCtrl.GCode_GenerateCode_VS(ref strDefinitionSegment, ref strSegment, 1, null);
            codeFile.WriteLine(strDefinitionSegment + "\r\n");
            codeFile.WriteLine(strSegment);
            codeFile.WriteLine("}");

            // PS
            codeFile.WriteLine($"void DoPSMaterial_{idStr}(in PS_INPUT input, inout MTL_OUTPUT mtl)");
            codeFile.WriteLine("{");
            strSegment           = "";
            strDefinitionSegment = "";
            setValueCtrl.GCode_GenerateCode_PS(ref strDefinitionSegment, ref strSegment, 1, null);
            codeFile.WriteLine(strDefinitionSegment + "\r\n");
            codeFile.WriteLine(strSegment);
            codeFile.WriteLine("}");

            codeFile.WriteLine($"#define DO_VS_MATERIAL DoVSMaterial_{idStr}");
            codeFile.WriteLine($"#define DO_PS_MATERIAL DoPSMaterial_{idStr}");

            return(true);
        }