浏览代码

HDTest scene for fast batch rendering

/batch_rendering
Arnaud Carre 8 年前
当前提交
05fd006e
共有 16 个文件被更改,包括 1241 次插入47 次删除
  1. 4
      Assets/BasicRenderBatching/BasicBatching.unity
  2. 2
      Assets/BasicRenderBatching/BasicRenderBatchingShader8.shader
  3. 6
      Assets/BasicRenderBatching/Scripts/MiniProfiler.cs
  4. 54
      Assets/BasicRenderBatching/Scripts/PopulateScene.cs
  5. 27
      Assets/TestScenes/HDTest/GlobalIlluminationTest.unity
  6. 32
      Assets/TestScenes/HDTest/Material/HDRenderLoopMaterials/Green.mat
  7. 34
      Assets/TestScenes/HDTest/Material/HDRenderLoopMaterials/Lit_Emissive_Blue.mat
  8. 32
      Assets/TestScenes/HDTest/Material/HDRenderLoopMaterials/Red.mat
  9. 4
      ProjectSettings/EditorBuildSettings.asset
  10. 4
      ProjectSettings/GraphicsSettings.asset
  11. 50
      Assets/TestScenes/HDTest/FlyCamera.cs
  12. 12
      Assets/TestScenes/HDTest/FlyCamera.cs.meta
  13. 1001
      Assets/TestScenes/HDTest/GlobalIlluminationTest/LightingData.asset
  14. 8
      Assets/TestScenes/HDTest/GlobalIlluminationTest/LightingData.asset.meta
  15. 9
      Assets/TestScenes/HDTest/HDRI.meta
  16. 9
      Assets/TestScenes/HDTest/LayeredLitTest/Mesh/Materials.meta

4
Assets/BasicRenderBatching/BasicBatching.unity


- {fileID: 2800000, guid: 2d69bdda1399802499987e9f68ae88ed, type: 3}
- {fileID: 2800000, guid: 14d974c88ade21747938eb13f6cfc152, type: 3}
- {fileID: 2800000, guid: 8e18cf146b7c0d0449454cdeb8653d94, type: 3}
m_GridWidth: 100
m_GridLayers: 2
m_GridWidth: 32
m_GridLayers: 1
m_Spacing: 0.4
--- !u!114 &870320119
MonoBehaviour:

2
Assets/BasicRenderBatching/BasicRenderBatchingShader8.shader


}
}
CustomEditor "StandardShaderGUI"
// CustomEditor "StandardShaderGUI"
}

6
Assets/BasicRenderBatching/Scripts/MiniProfiler.cs


RecorderEntry[] recordersList =
{
new RecorderEntry() { name="RenderLoop.Draw" },
// new RecorderEntry() { name="BatchRenderer.Flush" },
new RecorderEntry() { name="BatchRenderer.RenderBatch" },
new RecorderEntry() { name="BatchRenderer.ApplyShaderPass" },
new RecorderEntry() { name="PerformFlushProperties" },
/*
new RecorderEntry() { name="Camera.Render" },
new RecorderEntry() { name="GUI.Repaint" },

54
Assets/BasicRenderBatching/Scripts/PopulateScene.cs


public int m_GridLayers = 1;
public float m_Spacing = 0.6f;
public static bool m_DifferentColors = true;
public static bool m_ChangeTexture = false;
private Material[] m_Materials; //
// Use this for initialization
void Start()

m_Objects = new GameObject[m_GridWidth * m_GridWidth * m_GridLayers];
m_Materials = new Material[m_GridWidth * m_GridWidth * m_GridLayers];
int iIndex = 0;

m_Objects[iIndex] = Instantiate(m_ObjectsPrefab[r], vPos, m_CenterPoint.rotation) as GameObject;
if (m_DifferentColors)
{
Renderer renderer = m_Objects[iIndex].GetComponent<Renderer>();
Material mat = Instantiate(m_Material);
Color oColor = new Color(Random.value, Random.value, Random.value, 1.0f);
mat.SetColor("myColor", oColor);
Renderer renderer = m_Objects[iIndex].GetComponent<Renderer>();
Material mat = Instantiate(m_Material);
Color oColor = new Color(Random.value, Random.value, Random.value, 1.0f);
mat.SetColor("myColor", oColor);
mat.SetTexture("myTexture1", m_Textures[Random.Range(0, 7 + 1)]);
mat.SetTexture("myTexture2", m_Textures[Random.Range(0, 7 + 1)]);
mat.SetTexture("myTexture3", m_Textures[Random.Range(0, 7 + 1)]);
mat.SetTexture("myTexture4", m_Textures[Random.Range(0, 7 + 1)]);
mat.SetTexture("myTexture5", m_Textures[Random.Range(0, 7 + 1)]);
mat.SetTexture("myTexture6", m_Textures[Random.Range(0, 7 + 1)]);
mat.SetTexture("myTexture7", m_Textures[Random.Range(0, 7 + 1)]);
mat.SetTexture("myTexture8", m_Textures[Random.Range(0, 7 + 1)]);
mat.SetTexture("myTexture1", m_Textures[Random.Range(0, 7 + 1)]);
mat.SetTexture("myTexture2", m_Textures[Random.Range(0, 7 + 1)]);
mat.SetTexture("myTexture3", m_Textures[Random.Range(0, 7 + 1)]);
mat.SetTexture("myTexture4", m_Textures[Random.Range(0, 7 + 1)]);
mat.SetTexture("myTexture5", m_Textures[Random.Range(0, 7 + 1)]);
mat.SetTexture("myTexture6", m_Textures[Random.Range(0, 7 + 1)]);
mat.SetTexture("myTexture7", m_Textures[Random.Range(0, 7 + 1)]);
mat.SetTexture("myTexture8", m_Textures[Random.Range(0, 7 + 1)]);
mat.InitUniformBuffers();
renderer.material = mat;
renderer.material.InitUniformBuffers();
m_Materials[iIndex] = renderer.material;
renderer.material = mat;
}
iIndex++;
}
}

// Update is called once per frame
void Update ()
{
}
if ( m_ChangeTexture )
{
int r = Random.Range(0, m_GridWidth * m_GridWidth * m_GridLayers);
m_Materials[r].SetTexture("myTexture1", m_Textures[Random.Range(0, 7 + 1)]);
m_Materials[r].SetTexture("myTexture2", m_Textures[Random.Range(0, 7 + 1)]);
m_Materials[r].SetTexture("myTexture3", m_Textures[Random.Range(0, 7 + 1)]);
m_Materials[r].SetTexture("myTexture4", m_Textures[Random.Range(0, 7 + 1)]);
m_Materials[r].SetTexture("myTexture5", m_Textures[Random.Range(0, 7 + 1)]);
m_Materials[r].SetTexture("myTexture6", m_Textures[Random.Range(0, 7 + 1)]);
m_Materials[r].SetTexture("myTexture7", m_Textures[Random.Range(0, 7 + 1)]);
m_Materials[r].SetTexture("myTexture8", m_Textures[Random.Range(0, 7 + 1)]);
}
}
m_DifferentColors = GUI.Toggle(new Rect(10, 10, 200, 20), m_DifferentColors, "One color per object");
m_ChangeTexture = GUI.Toggle(new Rect(10, 10, 200, 20), m_ChangeTexture, "change rand texture");
if (GUI.changed)
{

27
Assets/TestScenes/HDTest/GlobalIlluminationTest.unity


m_ReflectionIntensity: 1
m_CustomReflection: {fileID: 0}
m_Sun: {fileID: 0}
m_IndirectSpecularColor: {r: 0.5456157, g: 0.39781958, b: 0.24038762, a: 1}
m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1}
--- !u!157 &3
LightmapSettings:
m_ObjectHideFlags: 0

m_FinalGatherRayCount: 256
m_ReflectionCompression: 2
m_StationaryBakeMode: 1
m_LightingDataAsset: {fileID: 0}
m_LightingDataAsset: {fileID: 112000009, guid: 294c7f8b138fe394b907bd5382401e46,
type: 2}
m_RuntimeCPUUsage: 50
m_ShadowMaskMode: 2
--- !u!196 &4

m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 921723278}
m_Enabled: 1
m_Enabled: 0
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2fab13ca2ee45bc4e9a52a716666fbe1, type: 3}
m_Name:

m_SpotAngle: 85
m_CookieSize: 10
m_Shadows:
m_Type: 2
m_Type: 0
m_Resolution: -1
m_CustomResolution: -1
m_Strength: 1

m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
m_IsActive: 0
--- !u!4 &1271727142
Transform:
m_ObjectHideFlags: 0

- component: {fileID: 1879932840}
- component: {fileID: 1879932839}
- component: {fileID: 1879932838}
- component: {fileID: 1879932843}
- component: {fileID: 1879932844}
m_Layer: 0
m_Name: Main Camera
m_TagString: MainCamera

m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 1879932837}
m_LocalRotation: {x: 0.120674305, y: -0, z: -0, w: 0.9926922}
m_LocalPosition: {x: -0.56, y: 2.81, z: -12.59}
m_LocalRotation: {x: 0.2170331, y: -0.7017431, z: 0.2399485, w: 0.6347267}
m_LocalPosition: {x: 6.789028, y: 3.5850766, z: -3.7886026}
--- !u!114 &1879932843
--- !u!114 &1879932844
MonoBehaviour:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}

m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 92bb16b4ee20841929b24d6bd771738d, type: 3}
m_Script: {fileID: 11500000, guid: dd3f3fa5c40a66f44a9444b42845b28f, type: 3}
m_Enable: 1
--- !u!1 &1910068096
GameObject:
m_ObjectHideFlags: 0

m_Name:
m_EditorClassIdentifier:
m_SkyRendererTypeName: UnityEngine.Experimental.ScriptableRenderLoop.HDRISkyRenderer
m_ShadowMaxDistance: 1000
m_ShadowCascadeCount: 4
m_ShadowCascadeSplit0: 0.05
m_ShadowCascadeSplit1: 0.2
m_ShadowCascadeSplit2: 0.3
--- !u!4 &1944423448
Transform:
m_ObjectHideFlags: 0

32
Assets/TestScenes/HDTest/Material/HDRenderLoopMaterials/Green.mat


m_PrefabInternal: {fileID: 0}
m_Name: Green
m_Shader: {fileID: 4800000, guid: 6e4ae4064600d784cac1e41a9e6f2e59, type: 3}
m_ShaderKeywords: _ALPHACUTOFFENABLE_OFF _DISTORTIONDEPTHTEST_OFF _DISTORTIONONLY_OFF
_EMISSION _NORMALMAP_TANGENT_SPACE _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
m_ShaderKeywords: _ALPHACUTOFFENABLE_OFF _DETAIL_MAP_WITH_NORMAL _DISTORTIONDEPTHTEST_OFF
_DISTORTIONONLY_OFF _EMISSION _NORMALMAP_TANGENT_SPACE _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
disabledShaderPasses:
- DistortionVectors
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:

m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BaseColorMap:
m_Texture: {fileID: 0}
m_Texture: {fileID: 2800000, guid: ff3ba92a36048274ab6eb2ec4567fa44, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:

m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}

m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DiffuseLightingMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DistortionVectorMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}

- _BumpScale: 1
- _CullMode: 2
- _Cutoff: 0.5
- _DepthOffsetEnable: 0
- _DetailAOScale: 1
- _DetailAlbedoScale: 1
- _DetailHeightScale: 1
- _DetailMapMode: 0
- _DetailNormalScale: 1
- _DetailSmoothnessScale: 1
- _DistortionEnable: 0
- _DistortionOnly: 0
- _DoubleSided: 1
- _DoubleSidedLigthing: 1

- _Mettalic: 0
- _Mode: 0
- _NormalMapSpace: 0
- _NormalScale: 1
- _OcclusionStrength: 1
- _Parallax: 0.02
- _Smoothness: 0.5

- _SubSurfaceRadius: 0
- _SurfaceType: 0
- _TexWorldScale: 1
- _UVBase: 0
- _UVDetail: 0
- _UVMappingPlanar: 0
- _ZTestMode: 8
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 0, g: 1, b: 0, a: 1}

- _UVDetailsMappingMask: {r: 1, g: 0, b: 0, a: 0}
- _UVMappingMask: {r: 1, g: 0, b: 0, a: 0}

34
Assets/TestScenes/HDTest/Material/HDRenderLoopMaterials/Lit_Emissive_Blue.mat


m_PrefabInternal: {fileID: 0}
m_Name: Lit_Emissive_Blue
m_Shader: {fileID: 4800000, guid: 6e4ae4064600d784cac1e41a9e6f2e59, type: 3}
m_ShaderKeywords: _ALPHACUTOFFENABLE_OFF _DISTORTIONDEPTHTEST_OFF _DISTORTIONONLY_OFF
_EMISSION _EMISSIVE_COLOR _NORMALMAP_TANGENT_SPACE _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
m_ShaderKeywords: _ALPHACUTOFFENABLE_OFF _DETAIL_MAP_WITH_NORMAL _DISTORTIONDEPTHTEST_OFF
_DISTORTIONONLY_OFF _EMISSION _EMISSIVE_COLOR _NORMALMAP_TANGENT_SPACE _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
disabledShaderPasses:
- DistortionVectors
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:

m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BaseColorMap:
m_Texture: {fileID: 0}
m_Texture: {fileID: 2800000, guid: 04b429f47f1e0d54ca799a2a31986801, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:

m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}

m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DiffuseLightingMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DistortionVectorMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}

- _BumpScale: 1
- _CullMode: 2
- _Cutoff: 0.5
- _DepthOffsetEnable: 0
- _DetailAOScale: 1
- _DetailAlbedoScale: 1
- _DetailHeightScale: 1
- _DetailMapMode: 0
- _DetailNormalScale: 1
- _DetailSmoothnessScale: 1
- _DistortionEnable: 0
- _DistortionOnly: 0
- _DoubleSided: 1
- _DoubleSidedLigthing: 1

- _Mettalic: 0
- _Mode: 0
- _NormalMapSpace: 0
- _NormalScale: 1
- _OcclusionStrength: 1
- _Parallax: 0.02
- _Smoothness: 0.5

- _SubSurfaceRadius: 0
- _SurfaceType: 0
- _TexWorldScale: 1
- _UVBase: 0
- _UVDetail: 0
- _UVMappingPlanar: 0
- _ZTestMode: 8
- _BaseColor: {r: 0, g: 0, b: 0, a: 1}
- _BaseColor: {r: 1, g: 1, b: 1, a: 1}
- _UVDetailsMappingMask: {r: 1, g: 0, b: 0, a: 0}
- _UVMappingMask: {r: 1, g: 0, b: 0, a: 0}

32
Assets/TestScenes/HDTest/Material/HDRenderLoopMaterials/Red.mat


m_PrefabInternal: {fileID: 0}
m_Name: Red
m_Shader: {fileID: 4800000, guid: 6e4ae4064600d784cac1e41a9e6f2e59, type: 3}
m_ShaderKeywords: _ALPHACUTOFFENABLE_OFF _DISTORTIONDEPTHTEST_OFF _DISTORTIONONLY_OFF
_EMISSION _MASKMAP _NORMALMAP _NORMALMAP_TANGENT_SPACE
m_ShaderKeywords: _ALPHACUTOFFENABLE_OFF _DETAIL_MAP_WITH_NORMAL _DISTORTIONDEPTHTEST_OFF
_DISTORTIONONLY_OFF _EMISSION _NORMALMAP_TANGENT_SPACE
disabledShaderPasses:
- DistortionVectors
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:

m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BaseColorMap:
m_Texture: {fileID: 0}
m_Texture: {fileID: 2800000, guid: 4425fca2a65e78c49b2415476d1cc5ec, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:

m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}

m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DiffuseLightingMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DistortionVectorMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}

- _BumpScale: 1
- _CullMode: 2
- _Cutoff: 0.5
- _DepthOffsetEnable: 0
- _DetailAOScale: 1
- _DetailAlbedoScale: 1
- _DetailHeightScale: 1
- _DetailMapMode: 0
- _DetailNormalScale: 1
- _DetailSmoothnessScale: 1
- _DistortionEnable: 0
- _DistortionOnly: 0
- _DoubleSided: 1
- _DoubleSidedLigthing: 1

- _Mettalic: 0
- _Mode: 0
- _NormalMapSpace: 0
- _NormalScale: 1
- _OcclusionStrength: 1
- _Parallax: 0.02
- _Smoothness: 0.486

- _SubSurfaceRadius: 0
- _SurfaceType: 0
- _TexWorldScale: 1
- _UVBase: 0
- _UVDetail: 0
- _UVMappingPlanar: 0
- _ZTestMode: 8
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 1, g: 0, b: 0, a: 1}

- _UVDetailsMappingMask: {r: 1, g: 0, b: 0, a: 0}
- _UVMappingMask: {r: 1, g: 0, b: 0, a: 0}

4
ProjectSettings/EditorBuildSettings.asset


m_ObjectHideFlags: 0
serializedVersion: 2
m_Scenes:
- enabled: 1
- enabled: 0
- enabled: 1
path: Assets/TestScenes/HDTest/GlobalIlluminationTest.unity

4
ProjectSettings/GraphicsSettings.asset


- {fileID: 15106, guid: 0000000000000000f000000000000000, type: 0}
- {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0}
- {fileID: 10782, guid: 0000000000000000f000000000000000, type: 0}
- {fileID: 4800000, guid: 36c23fe83f8e7a54d8fb168fa6cf2a3d, type: 3}
- {fileID: 103, guid: 0000000000000000f000000000000000, type: 0}
m_CustomRenderPipeline: {fileID: 11400000, guid: 6bec8f7a5722d5b47a3fd9a0c0b7bbf4,
m_CustomRenderPipeline: {fileID: 11400000, guid: e185fecca3c73cd47a09f1092663ef32,
type: 2}
m_TransparencySortMode: 0
m_TransparencySortAxis: {x: 0, y: 0, z: 1}

50
Assets/TestScenes/HDTest/FlyCamera.cs


using System;
using UnityEngine;
public class FlyCamera : MonoBehaviour
{
public float LookSpeed = 2f;
public float MoveSpeed = 0.1f;
protected float m_RotationX = 0f;
protected float m_RotationY = 0f;
private bool m_Active = true;
void Update()
{
if (Input.GetMouseButtonDown(1))
m_Active = !m_Active;
if (!m_Active)
return;
float joyX = 0f;
float joyY = 0f;
try
{
joyX = Input.GetAxis("Joystick Mouse X");
joyY = Input.GetAxis("Joystick Mouse Y");
}
catch
{
// ignored
}
float mx = Input.GetAxis("Mouse X") + joyX;
float my = Input.GetAxis("Mouse Y") + joyY;
m_RotationX += mx * LookSpeed;
m_RotationY += my * LookSpeed;
m_RotationY = Mathf.Clamp(m_RotationY, -90f, 90f);
transform.localRotation = Quaternion.AngleAxis(m_RotationX, Vector3.up);
transform.localRotation *= Quaternion.AngleAxis(m_RotationY, Vector3.left);
float moveSpeed = Input.GetKey(KeyCode.LeftShift) || Input.GetButton("Fire1")
? MoveSpeed * 2f
: MoveSpeed;
transform.position += transform.forward * moveSpeed * Input.GetAxis("Vertical") * Time.deltaTime;
transform.position += transform.right * moveSpeed * Input.GetAxis("Horizontal") * Time.deltaTime;
}
}

12
Assets/TestScenes/HDTest/FlyCamera.cs.meta


fileFormatVersion: 2
guid: 79d9c15c92bb0084d80fb50abc1c2e98
timeCreated: 1483973718
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

1001
Assets/TestScenes/HDTest/GlobalIlluminationTest/LightingData.asset
文件差异内容过多而无法显示
查看文件

8
Assets/TestScenes/HDTest/GlobalIlluminationTest/LightingData.asset.meta


fileFormatVersion: 2
guid: 294c7f8b138fe394b907bd5382401e46
timeCreated: 1483971268
licenseType: Pro
NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:

9
Assets/TestScenes/HDTest/HDRI.meta


fileFormatVersion: 2
guid: eb8a3d63ad4e60a4dbf178b7f7bc0404
folderAsset: yes
timeCreated: 1483636323
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

9
Assets/TestScenes/HDTest/LayeredLitTest/Mesh/Materials.meta


fileFormatVersion: 2
guid: b4945bd06b6687e408c75d27ba1fc309
folderAsset: yes
timeCreated: 1483636569
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:
正在加载...
取消
保存