Repository: keijiro/KlakHap Branch: master Commit: 7212ffe5e402 Files: 187 Total size: 582.0 KB Directory structure: gitextract_73gfcozr/ ├── .gitattributes ├── .gitignore ├── AGENTS.md ├── Assets/ │ ├── Demo/ │ │ ├── Hap.mat │ │ ├── Hap.mat.meta │ │ ├── Hap.renderTexture │ │ ├── Hap.renderTexture.meta │ │ ├── HapAlpha.mat │ │ ├── HapAlpha.mat.meta │ │ ├── HapQ.mat │ │ ├── HapQ.mat.meta │ │ ├── Manifest.asset │ │ ├── Manifest.asset.meta │ │ ├── ScriptingTest.cs │ │ ├── ScriptingTest.cs.meta │ │ ├── ScriptingTest.renderTexture │ │ ├── ScriptingTest.renderTexture.meta │ │ ├── SeekBarBind.cs │ │ ├── SeekBarBind.cs.meta │ │ ├── UITK/ │ │ │ ├── DefaultTheme.tss │ │ │ ├── DefaultTheme.tss.meta │ │ │ ├── Demo.uxml │ │ │ ├── Demo.uxml.meta │ │ │ ├── PanelSettings.asset │ │ │ └── PanelSettings.asset.meta │ │ ├── UITK.meta │ │ ├── URP/ │ │ │ ├── DefaultRenderer.asset │ │ │ ├── DefaultRenderer.asset.meta │ │ │ ├── DefaultVolume.asset │ │ │ ├── DefaultVolume.asset.meta │ │ │ ├── GlobalSettings.asset │ │ │ ├── GlobalSettings.asset.meta │ │ │ ├── URP.asset │ │ │ └── URP.asset.meta │ │ └── URP.meta │ ├── Demo.meta │ ├── Demo.unity │ ├── Demo.unity.meta │ ├── StreamingAssets/ │ │ ├── Tests/ │ │ │ ├── Hap/ │ │ │ │ ├── 000001.png.meta │ │ │ │ ├── 000002.png.meta │ │ │ │ ├── 000003.png.meta │ │ │ │ ├── 000004.png.meta │ │ │ │ ├── 000005.png.meta │ │ │ │ └── TestCards.mov.meta │ │ │ ├── Hap.meta │ │ │ ├── HapAlpha/ │ │ │ │ ├── 000001.png.meta │ │ │ │ └── HapAlpha.mov.meta │ │ │ ├── HapAlpha.meta │ │ │ ├── HapQ/ │ │ │ │ ├── 000001.png.meta │ │ │ │ ├── 000002.png.meta │ │ │ │ ├── 000003.png.meta │ │ │ │ ├── 000004.png.meta │ │ │ │ ├── 000005.png.meta │ │ │ │ └── TestCards.mov.meta │ │ │ ├── HapQ.meta │ │ │ ├── RGBCycle/ │ │ │ │ ├── RGBCycle24.mov.meta │ │ │ │ ├── RGBCycle24000-1001.mov.meta │ │ │ │ ├── RGBCycle25.mov.meta │ │ │ │ ├── RGBCycle30.mov.meta │ │ │ │ ├── RGBCycle30000-1001.mov.meta │ │ │ │ ├── RGBCycle50.mov.meta │ │ │ │ ├── RGBCycle60.mov.meta │ │ │ │ └── RGBCycle60000-1001.mov.meta │ │ │ ├── RGBCycle.meta │ │ │ ├── 日本語/ │ │ │ │ └── Test.mov.meta │ │ │ └── 日本語.meta │ │ └── Tests.meta │ ├── StreamingAssets.meta │ ├── Tests/ │ │ ├── Runtime/ │ │ │ ├── HapAlphaPlaybackTest.cs │ │ │ ├── HapAlphaPlaybackTest.cs.meta │ │ │ ├── JapanesePathPlaybackTest.cs │ │ │ ├── JapanesePathPlaybackTest.cs.meta │ │ │ ├── Klak.Hap.Runtime.Tests.asmdef │ │ │ ├── Klak.Hap.Runtime.Tests.asmdef.meta │ │ │ ├── RgbCycleFrameMappingTest.cs │ │ │ ├── RgbCycleFrameMappingTest.cs.meta │ │ │ ├── TestCardsPlaybackTest.cs │ │ │ └── TestCardsPlaybackTest.cs.meta │ │ └── Runtime.meta │ └── Tests.meta ├── CHANGELOG.md ├── LICENSE ├── Packages/ │ ├── jp.keijiro.klak.hap/ │ │ ├── CHANGELOG.md │ │ ├── CHANGELOG.md.meta │ │ ├── Editor/ │ │ │ ├── HapPlayerEditor.cs │ │ │ ├── HapPlayerEditor.cs.meta │ │ │ ├── Klak.Hap.Editor.asmdef │ │ │ ├── Klak.Hap.Editor.asmdef.meta │ │ │ ├── MaterialPropertySelector.cs │ │ │ └── MaterialPropertySelector.cs.meta │ │ ├── Editor.meta │ │ ├── LICENSE │ │ ├── LICENSE.meta │ │ ├── Plugin/ │ │ │ ├── Linux/ │ │ │ │ └── libKlakHap.so.meta │ │ │ ├── Linux.meta │ │ │ ├── MacOS/ │ │ │ │ ├── KlakHap.bundle │ │ │ │ └── KlakHap.bundle.meta │ │ │ ├── MacOS.meta │ │ │ ├── Windows/ │ │ │ │ └── KlakHap.dll.meta │ │ │ └── Windows.meta │ │ ├── Plugin.meta │ │ ├── README.md │ │ ├── README.md.meta │ │ ├── Resources/ │ │ │ ├── Hap1.shader │ │ │ ├── Hap1.shader.meta │ │ │ ├── Hap5.shader │ │ │ ├── Hap5.shader.meta │ │ │ ├── HapY.shader │ │ │ └── HapY.shader.meta │ │ ├── Resources.meta │ │ ├── Runtime/ │ │ │ ├── Common.cs │ │ │ ├── Common.cs.meta │ │ │ ├── HapPlayer.cs │ │ │ ├── HapPlayer.cs.meta │ │ │ ├── Internal/ │ │ │ │ ├── Decoder.cs │ │ │ │ ├── Decoder.cs.meta │ │ │ │ ├── Demuxer.cs │ │ │ │ ├── Demuxer.cs.meta │ │ │ │ ├── ReadBuffer.cs │ │ │ │ ├── ReadBuffer.cs.meta │ │ │ │ ├── StreamReader.cs │ │ │ │ ├── StreamReader.cs.meta │ │ │ │ ├── TextureUpdater.cs │ │ │ │ ├── TextureUpdater.cs.meta │ │ │ │ ├── Utility.cs │ │ │ │ └── Utility.cs.meta │ │ │ ├── Internal.meta │ │ │ ├── Klak.Hap.asmdef │ │ │ └── Klak.Hap.asmdef.meta │ │ ├── Runtime.meta │ │ ├── package.json │ │ └── package.json.meta │ ├── manifest.json │ └── packages-lock.json ├── Plugin/ │ ├── Common.mk │ ├── Hap/ │ │ ├── hap.c │ │ └── hap.h │ ├── MP4/ │ │ ├── mp4defs.h │ │ ├── mp4demux.c │ │ └── mp4demux.h │ ├── Makefile.linux │ ├── Makefile.macos │ ├── Makefile.windows │ ├── Snappy/ │ │ ├── snappy-c.cc │ │ ├── snappy-c.h │ │ ├── snappy-internal.h │ │ ├── snappy-sinksource.cc │ │ ├── snappy-sinksource.h │ │ ├── snappy-stubs-internal.cc │ │ ├── snappy-stubs-internal.h │ │ ├── snappy-stubs-public.h │ │ ├── snappy.cc │ │ └── snappy.h │ ├── Source/ │ │ ├── Decoder.h │ │ ├── Demuxer.h │ │ ├── KlakHap.cpp │ │ └── ReadBuffer.h │ ├── Unity/ │ │ ├── IUnityGraphics.h │ │ ├── IUnityInterface.h │ │ └── IUnityRenderingExtensions.h │ └── build.macos.sh ├── ProjectSettings/ │ ├── AudioManager.asset │ ├── ClusterInputManager.asset │ ├── DynamicsManager.asset │ ├── EditorBuildSettings.asset │ ├── EditorSettings.asset │ ├── GraphicsSettings.asset │ ├── InputManager.asset │ ├── MemorySettings.asset │ ├── MultiplayerManager.asset │ ├── NavMeshAreas.asset │ ├── PackageManagerSettings.asset │ ├── Packages/ │ │ └── com.unity.dedicated-server/ │ │ └── MultiplayerRolesSettings.asset │ ├── Physics2DSettings.asset │ ├── PresetManager.asset │ ├── ProjectSettings.asset │ ├── ProjectVersion.txt │ ├── QualitySettings.asset │ ├── SceneTemplateSettings.json │ ├── ShaderGraphSettings.asset │ ├── TagManager.asset │ ├── TimeManager.asset │ ├── URPProjectSettings.asset │ ├── UnityConnectSettings.asset │ ├── VFXManager.asset │ └── VersionControlSettings.asset └── README.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ * -text *.cs text eol=lf diff=csharp *.shader text eol=lf *.cginc text eol=lf *.hlsl text eol=lf *.compute text eol=lf *.meta text eol=lf *.h text eol=lf *.cpp text eol=lf *.cc text eol=lf *.c text eol=lf *.plist text eol=lf *.sln text eol=crlf *.vcxproj text eol=crlf *.vcxproj.filters text eol=crlf ================================================ FILE: .gitignore ================================================ # Windows Thumbs.db Desktop.ini # macOS .DS_Store # Code Editors /.idea /.vscode /*.csproj /*.sln *.swp *.vcxproj.user # Unity /Library /Logs /Recordings /Temp /UIElementsSchema /UserSettings # Visual Studio /Plugin/Windows/.vs /Plugin/Windows/x64 # Xcode xcuserdata xcshareddata /Plugin/build-* /Plugin/MacOS/arm64 /Plugin/MacOS/x86_64 /Plugin/MacOS/*.bundle /Plugin/*.bundle /*.tgz /Assets/StreamingAssets/*.mov /Assets/StreamingAssets/*.mov.meta ================================================ FILE: AGENTS.md ================================================ This repository contains a UPM Unity package project. Refer to the following document for the workflow: https://raw.githubusercontent.com/keijiro/AgentWorkflows/refs/heads/main/upm-workflow.md ================================================ FILE: Assets/Demo/Hap.mat ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!114 &-8186347925687542841 MonoBehaviour: m_ObjectHideFlags: 11 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3} m_Name: m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion version: 10 --- !u!21 &2100000 Material: serializedVersion: 8 m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_Name: Hap m_Shader: {fileID: 4800000, guid: 650dd9526735d5b46b79224bc6e94025, type: 3} m_Parent: {fileID: 0} m_ModifiedSerializedProperties: 0 m_ValidKeywords: [] m_InvalidKeywords: [] m_LightmapFlags: 4 m_EnableInstancingVariants: 0 m_DoubleSidedGI: 0 m_CustomRenderQueue: -1 stringTagMap: RenderType: Opaque disabledShaderPasses: - MOTIONVECTORS m_LockedProperties: m_SavedProperties: serializedVersion: 3 m_TexEnvs: - _BaseMap: m_Texture: {fileID: 8400000, guid: a4f22813f74e84455bd3d9e41d54ced6, type: 2} 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} - _DetailAlbedoMap: 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_Offset: {x: 0, y: 0} - _DetailNormalMap: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - _EmissionMap: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - _MainTex: m_Texture: {fileID: 8400000, guid: a4f22813f74e84455bd3d9e41d54ced6, type: 2} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - _MetallicGlossMap: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - _OcclusionMap: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - _ParallaxMap: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - _SpecGlossMap: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - unity_Lightmaps: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - unity_LightmapsInd: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - unity_ShadowMasks: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} m_Ints: [] m_Floats: - _AddPrecomputedVelocity: 0 - _AlphaClip: 0 - _AlphaToMask: 0 - _Blend: 0 - _BlendModePreserveSpecular: 1 - _BlendOp: 0 - _BumpScale: 1 - _ClearCoatMask: 0 - _ClearCoatSmoothness: 0 - _Cull: 2 - _Cutoff: 0.5 - _DetailAlbedoMapScale: 1 - _DetailNormalMapScale: 1 - _DstBlend: 0 - _DstBlendAlpha: 0 - _EnvironmentReflections: 1 - _GlossMapScale: 0 - _Glossiness: 0 - _GlossyReflections: 0 - _Metallic: 0 - _OcclusionStrength: 1 - _Parallax: 0.005 - _QueueOffset: 0 - _ReceiveShadows: 1 - _SampleGI: 0 - _Smoothness: 0.5 - _SmoothnessTextureChannel: 0 - _SpecularHighlights: 1 - _SrcBlend: 1 - _SrcBlendAlpha: 1 - _Surface: 0 - _WorkflowMode: 1 - _XRMotionVectorsPass: 1 - _ZWrite: 1 m_Colors: - _BaseColor: {r: 1, g: 1, b: 1, a: 1} - _Color: {r: 1, g: 1, b: 1, a: 1} - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} - _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1} m_BuildTextureStacks: [] m_AllowLocking: 1 ================================================ FILE: Assets/Demo/Hap.mat.meta ================================================ fileFormatVersion: 2 guid: e127fa53264f341caab6ab276ff39b77 NativeFormatImporter: externalObjects: {} mainObjectFileID: 2100000 userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Demo/Hap.renderTexture ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!84 &8400000 RenderTexture: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_Name: Hap m_ImageContentsHash: serializedVersion: 2 Hash: 00000000000000000000000000000000 m_IsAlphaChannelOptional: 0 serializedVersion: 6 m_Width: 1920 m_Height: 1080 m_AntiAliasing: 1 m_MipCount: -1 m_DepthStencilFormat: 0 m_ColorFormat: 4 m_MipMap: 0 m_GenerateMips: 1 m_SRGB: 1 m_UseDynamicScale: 0 m_UseDynamicScaleExplicit: 0 m_BindMS: 0 m_EnableCompatibleFormat: 1 m_EnableRandomWrite: 0 m_TextureSettings: serializedVersion: 2 m_FilterMode: 2 m_Aniso: 8 m_MipBias: 0 m_WrapU: 1 m_WrapV: 1 m_WrapW: 1 m_Dimension: 2 m_VolumeDepth: 1 m_ShadowSamplingMode: 2 ================================================ FILE: Assets/Demo/Hap.renderTexture.meta ================================================ fileFormatVersion: 2 guid: a4f22813f74e84455bd3d9e41d54ced6 NativeFormatImporter: externalObjects: {} mainObjectFileID: 8400000 userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Demo/HapAlpha.mat ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!21 &2100000 Material: serializedVersion: 8 m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_Name: HapAlpha m_Shader: {fileID: 4800000, guid: 650dd9526735d5b46b79224bc6e94025, type: 3} m_Parent: {fileID: 0} m_ModifiedSerializedProperties: 0 m_ValidKeywords: - _SURFACE_TYPE_TRANSPARENT m_InvalidKeywords: [] m_LightmapFlags: 4 m_EnableInstancingVariants: 0 m_DoubleSidedGI: 0 m_CustomRenderQueue: 3000 stringTagMap: RenderType: Transparent disabledShaderPasses: - MOTIONVECTORS - DepthOnly - SHADOWCASTER m_LockedProperties: m_SavedProperties: serializedVersion: 3 m_TexEnvs: - _BaseMap: m_Texture: {fileID: 0} m_Scale: {x: 1, y: -1} m_Offset: {x: 0, y: 1} - _BumpMap: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - _DetailAlbedoMap: 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_Offset: {x: 0, y: 0} - _DetailNormalMap: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - _EmissionMap: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - _MainTex: m_Texture: {fileID: 0} m_Scale: {x: 1, y: -1} m_Offset: {x: 0, y: 1} - _MetallicGlossMap: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - _OcclusionMap: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - _ParallaxMap: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - _SpecGlossMap: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - unity_Lightmaps: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - unity_LightmapsInd: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - unity_ShadowMasks: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} m_Ints: [] m_Floats: - _AddPrecomputedVelocity: 0 - _AlphaClip: 0 - _AlphaToMask: 0 - _Blend: 0 - _BlendModePreserveSpecular: 1 - _BlendOp: 0 - _BumpScale: 1 - _ClearCoatMask: 0 - _ClearCoatSmoothness: 0 - _Cull: 2 - _Cutoff: 0.5 - _DetailAlbedoMapScale: 1 - _DetailNormalMapScale: 1 - _DstBlend: 10 - _DstBlendAlpha: 10 - _EnvironmentReflections: 1 - _GlossMapScale: 0 - _Glossiness: 0 - _GlossyReflections: 0 - _Metallic: 0 - _OcclusionStrength: 1 - _Parallax: 0.005 - _QueueOffset: 0 - _ReceiveShadows: 1 - _SampleGI: 0 - _Smoothness: 0.5 - _SmoothnessTextureChannel: 0 - _SpecularHighlights: 1 - _SrcBlend: 5 - _SrcBlendAlpha: 1 - _Surface: 1 - _WorkflowMode: 1 - _XRMotionVectorsPass: 1 - _ZWrite: 0 m_Colors: - _BaseColor: {r: 1, g: 1, b: 1, a: 1} - _Color: {r: 1, g: 1, b: 1, a: 1} - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} - _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1} m_BuildTextureStacks: [] m_AllowLocking: 1 --- !u!114 &4974996350567476565 MonoBehaviour: m_ObjectHideFlags: 11 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3} m_Name: m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion version: 10 ================================================ FILE: Assets/Demo/HapAlpha.mat.meta ================================================ fileFormatVersion: 2 guid: 6f1182baee9ac4863bd4c5ea5f7d536d NativeFormatImporter: externalObjects: {} mainObjectFileID: 2100000 userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Demo/HapQ.mat ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!114 &-1545691033646063671 MonoBehaviour: m_ObjectHideFlags: 11 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3} m_Name: m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion version: 10 --- !u!21 &2100000 Material: serializedVersion: 8 m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_Name: HapQ m_Shader: {fileID: 4800000, guid: c6a332340608cdd4f907eb4bc0c17f4a, type: 3} m_Parent: {fileID: 0} m_ModifiedSerializedProperties: 0 m_ValidKeywords: [] m_InvalidKeywords: [] m_LightmapFlags: 4 m_EnableInstancingVariants: 0 m_DoubleSidedGI: 0 m_CustomRenderQueue: -1 stringTagMap: {} disabledShaderPasses: - MOTIONVECTORS m_LockedProperties: m_SavedProperties: serializedVersion: 3 m_TexEnvs: - _BaseMap: m_Texture: {fileID: 0} 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} - _DetailAlbedoMap: 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_Offset: {x: 0, y: 0} - _DetailNormalMap: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - _EmissionMap: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - _MainTex: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - _MetallicGlossMap: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - _OcclusionMap: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - _ParallaxMap: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - _SpecGlossMap: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - unity_Lightmaps: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - unity_LightmapsInd: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} - unity_ShadowMasks: m_Texture: {fileID: 0} m_Scale: {x: 1, y: 1} m_Offset: {x: 0, y: 0} m_Ints: [] m_Floats: - _AddPrecomputedVelocity: 0 - _AlphaClip: 0 - _AlphaToMask: 0 - _Blend: 0 - _BlendModePreserveSpecular: 1 - _BumpScale: 1 - _ClearCoatMask: 0 - _ClearCoatSmoothness: 0 - _Cull: 2 - _Cutoff: 0.5 - _DetailAlbedoMapScale: 1 - _DetailNormalMapScale: 1 - _DstBlend: 0 - _DstBlendAlpha: 0 - _EnvironmentReflections: 1 - _GlossMapScale: 0 - _Glossiness: 0 - _GlossyReflections: 0 - _Metallic: 0 - _OcclusionStrength: 1 - _Parallax: 0.005 - _QueueOffset: 0 - _ReceiveShadows: 1 - _Smoothness: 0.5 - _SmoothnessTextureChannel: 0 - _SpecularHighlights: 1 - _SrcBlend: 1 - _SrcBlendAlpha: 1 - _Surface: 0 - _WorkflowMode: 1 - _XRMotionVectorsPass: 1 - _ZWrite: 1 m_Colors: - _BaseColor: {r: 1, g: 1, b: 1, a: 1} - _Color: {r: 1, g: 1, b: 1, a: 1} - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} - _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1} m_BuildTextureStacks: [] m_AllowLocking: 1 ================================================ FILE: Assets/Demo/HapQ.mat.meta ================================================ fileFormatVersion: 2 guid: f4060b9dfe1254ee9bb9d4fad7ae4ebd NativeFormatImporter: externalObjects: {} mainObjectFileID: 2100000 userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Demo/Manifest.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!114 &11400000 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 5acc6266be6eb4130afc5134ec35fcec, type: 3} m_Name: Manifest m_EditorClassIdentifier: KlutterTools.Editor::KlutterTools.Downloader.Manifest k__BackingField: 'Test video files are missing:' k__BackingField: - k__BackingField: https://huggingface.co/keijiro-tk/test-videos/resolve/main/Beeple-CleanRoom-Hap.mov k__BackingField: Assets/StreamingAssets - k__BackingField: https://huggingface.co/keijiro-tk/test-videos/resolve/main/Beeple-FiberOptical-Hap.mov k__BackingField: Assets/StreamingAssets - k__BackingField: https://huggingface.co/keijiro-tk/test-videos/resolve/main/Beeple-Hexxx-HapQ.mov k__BackingField: Assets/StreamingAssets - k__BackingField: https://huggingface.co/keijiro-tk/test-videos/resolve/main/TransparencyTest-HapAlpha.mov k__BackingField: Assets/StreamingAssets ================================================ FILE: Assets/Demo/Manifest.asset.meta ================================================ fileFormatVersion: 2 guid: 3dac4bcd1e58c42f1b91f538480ebc79 NativeFormatImporter: externalObjects: {} mainObjectFileID: 11400000 userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Demo/ScriptingTest.cs ================================================ using UnityEngine; using Klak.Hap; public sealed class ScriptingTest : MonoBehaviour { [SerializeField] string _filename = "Test.mov"; [SerializeField] RenderTexture _destination = null; [SerializeField] float _interval = 1; [SerializeField] int _iteration = 10; async void Start() { // Create a HAP player instance. var player = gameObject.AddComponent(); // Open the specified HAP video file. player.Open(_filename); // Route playback to the destination render texture. player.targetTexture = _destination; // Step through playback changes. for (var i = 0; i < _iteration; i++) { // Jump to a random timestamp after each interval. await Awaitable.WaitForSecondsAsync(_interval); player.time = Random.Range(0, (float)player.streamDuration - _interval); } // Clean up the player component. Destroy(player); } } ================================================ FILE: Assets/Demo/ScriptingTest.cs.meta ================================================ fileFormatVersion: 2 guid: 2c91f3289fa694fe3ba2e1e6149477b8 ================================================ FILE: Assets/Demo/ScriptingTest.renderTexture ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!84 &8400000 RenderTexture: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_Name: ScriptingTest m_ImageContentsHash: serializedVersion: 2 Hash: 00000000000000000000000000000000 m_IsAlphaChannelOptional: 0 serializedVersion: 6 m_Width: 1920 m_Height: 1080 m_AntiAliasing: 1 m_MipCount: -1 m_DepthStencilFormat: 0 m_ColorFormat: 4 m_MipMap: 0 m_GenerateMips: 1 m_SRGB: 1 m_UseDynamicScale: 0 m_UseDynamicScaleExplicit: 0 m_BindMS: 0 m_EnableCompatibleFormat: 1 m_EnableRandomWrite: 0 m_TextureSettings: serializedVersion: 2 m_FilterMode: 1 m_Aniso: 0 m_MipBias: 0 m_WrapU: 1 m_WrapV: 1 m_WrapW: 1 m_Dimension: 2 m_VolumeDepth: 1 m_ShadowSamplingMode: 2 ================================================ FILE: Assets/Demo/ScriptingTest.renderTexture.meta ================================================ fileFormatVersion: 2 guid: 1bba849debc024a4f8251bd62cceac60 NativeFormatImporter: externalObjects: {} mainObjectFileID: 8400000 userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Demo/SeekBarBind.cs ================================================ using Unity.Properties; using UnityEngine; using UnityEngine.UIElements; using Klak.Hap; public sealed class SeekBarBind : MonoBehaviour { [SerializeField] UIDocument _ui = null; HapPlayer _player; [CreateProperty] public float SeekPoint { get => _player.time; set => _player.time = value; } void Start() { _player = GetComponent(); var slider = _ui.rootVisualElement.Q("seek-bar"); slider.dataSource = this; slider.highValue = (float)_player.streamDuration; } } ================================================ FILE: Assets/Demo/SeekBarBind.cs.meta ================================================ fileFormatVersion: 2 guid: 538bb1d7707cb473f8493b44c63286cc ================================================ FILE: Assets/Demo/UITK/DefaultTheme.tss ================================================ @import url("unity-theme://default"); ================================================ FILE: Assets/Demo/UITK/DefaultTheme.tss.meta ================================================ fileFormatVersion: 2 guid: affeaa67622c24c3991a769c52b9c780 ScriptedImporter: internalIDToNameTable: [] externalObjects: {} serializedVersion: 2 userData: assetBundleName: assetBundleVariant: script: {fileID: 12388, guid: 0000000000000000e000000000000000, type: 0} disableValidation: 0 unsupportedSelectorAction: 0 ================================================ FILE: Assets/Demo/UITK/Demo.uxml ================================================ ================================================ FILE: Assets/Demo/UITK/Demo.uxml.meta ================================================ fileFormatVersion: 2 guid: f9d1722b31b6d45a8b38e8167775d54d ScriptedImporter: internalIDToNameTable: [] externalObjects: {} serializedVersion: 2 userData: assetBundleName: assetBundleVariant: script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0} ================================================ FILE: Assets/Demo/UITK/PanelSettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!114 &11400000 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 19101, guid: 0000000000000000e000000000000000, type: 0} m_Name: PanelSettings m_EditorClassIdentifier: UnityEngine.dll::UnityEngine.UIElements.PanelSettings themeUss: {fileID: -4733365628477956816, guid: affeaa67622c24c3991a769c52b9c780, type: 3} m_DisableNoThemeWarning: 0 m_TargetTexture: {fileID: 0} m_RenderMode: 0 m_ColliderUpdateMode: 0 m_ColliderIsTrigger: 1 m_ScaleMode: 1 m_ReferenceSpritePixelsPerUnit: 100 m_PixelsPerUnit: 100 m_Scale: 1 m_ReferenceDpi: 96 m_FallbackDpi: 96 m_ReferenceResolution: {x: 1200, y: 800} m_ScreenMatchMode: 0 m_Match: 0 m_SortingOrder: 0 m_TargetDisplay: 0 m_BindingLogLevel: 0 m_ClearDepthStencil: 1 m_ClearColor: 0 m_ColorClearValue: {r: 0, g: 0, b: 0, a: 0} m_VertexBudget: 0 m_TextureSlotCount: 8 m_DynamicAtlasSettings: m_MinAtlasSize: 64 m_MaxAtlasSize: 4096 m_MaxSubTextureSize: 64 m_ActiveFilters: -1 m_AtlasBlitShader: {fileID: 9101, guid: 0000000000000000f000000000000000, type: 0} m_DefaultShader: {fileID: 9100, guid: 0000000000000000f000000000000000, type: 0} m_RuntimeGaussianBlurShader: {fileID: 20300, guid: 0000000000000000f000000000000000, type: 0} m_RuntimeColorEffectShader: {fileID: 20301, guid: 0000000000000000f000000000000000, type: 0} m_SDFShader: {fileID: 19011, guid: 0000000000000000f000000000000000, type: 0} m_BitmapShader: {fileID: 9001, guid: 0000000000000000f000000000000000, type: 0} m_SpriteShader: {fileID: 19012, guid: 0000000000000000f000000000000000, type: 0} m_ICUDataAsset: {fileID: 0} forceGammaRendering: 0 textSettings: {fileID: 0} ================================================ FILE: Assets/Demo/UITK/PanelSettings.asset.meta ================================================ fileFormatVersion: 2 guid: 217443c668dc34029b583fa73e5c4dfc NativeFormatImporter: externalObjects: {} mainObjectFileID: 11400000 userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Demo/UITK.meta ================================================ fileFormatVersion: 2 guid: 231a2176bf9354a2aaa754fd16e8e95e folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Demo/URP/DefaultRenderer.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!114 &11400000 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: de640fe3d0db1804a85f9fc8f5cadab6, type: 3} m_Name: DefaultRenderer m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.UniversalRendererData debugShaders: debugReplacementPS: {fileID: 4800000, guid: cf852408f2e174538bcd9b7fda1c5ae7, type: 3} hdrDebugViewPS: {fileID: 4800000, guid: 573620ae32aec764abd4d728906d2587, type: 3} probeVolumeSamplingDebugComputeShader: {fileID: 7200000, guid: 53626a513ea68ce47b59dc1299fe3959, type: 3} probeVolumeResources: probeVolumeDebugShader: {fileID: 0} probeVolumeFragmentationDebugShader: {fileID: 0} probeVolumeOffsetDebugShader: {fileID: 0} probeVolumeSamplingDebugShader: {fileID: 0} probeSamplingDebugMesh: {fileID: 0} probeSamplingDebugTexture: {fileID: 0} probeVolumeBlendStatesCS: {fileID: 0} m_RendererFeatures: [] m_RendererFeatureMap: m_UseNativeRenderPass: 0 postProcessData: {fileID: 11400000, guid: 41439944d30ece34e96484bdb6645b55, type: 2} m_AssetVersion: 3 m_PrepassLayerMask: serializedVersion: 2 m_Bits: 4294967295 m_OpaqueLayerMask: serializedVersion: 2 m_Bits: 4294967295 m_TransparentLayerMask: serializedVersion: 2 m_Bits: 4294967295 m_DefaultStencilState: overrideStencilState: 0 stencilReference: 0 stencilCompareFunction: 8 passOperation: 2 failOperation: 0 zFailOperation: 0 m_ShadowTransparentReceive: 1 m_RenderingMode: 0 m_DepthPrimingMode: 0 m_CopyDepthMode: 1 m_DepthAttachmentFormat: 0 m_DepthTextureFormat: 0 m_AccurateGbufferNormals: 0 m_IntermediateTextureMode: 1 ================================================ FILE: Assets/Demo/URP/DefaultRenderer.asset.meta ================================================ fileFormatVersion: 2 guid: e707f25f3295f48408bbc74cefc67e0d NativeFormatImporter: externalObjects: {} mainObjectFileID: 11400000 userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Demo/URP/DefaultVolume.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!114 &-8324776220157134924 MonoBehaviour: m_ObjectHideFlags: 3 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 5485954d14dfb9a4c8ead8edb0ded5b1, type: 3} m_Name: LiftGammaGain m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.LiftGammaGain active: 1 lift: m_OverrideState: 1 m_Value: {x: 1, y: 1, z: 1, w: 0} gamma: m_OverrideState: 1 m_Value: {x: 1, y: 1, z: 1, w: 0} gain: m_OverrideState: 1 m_Value: {x: 1, y: 1, z: 1, w: 0} --- !u!114 &-4854300222326862063 MonoBehaviour: m_ObjectHideFlags: 3 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 70afe9e12c7a7ed47911bb608a23a8ff, type: 3} m_Name: SplitToning m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.SplitToning active: 1 shadows: m_OverrideState: 1 m_Value: {r: 0.5, g: 0.5, b: 0.5, a: 1} highlights: m_OverrideState: 1 m_Value: {r: 0.5, g: 0.5, b: 0.5, a: 1} balance: m_OverrideState: 1 m_Value: 0 --- !u!114 &-4422322214415224032 MonoBehaviour: m_ObjectHideFlags: 3 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 899c54efeace73346a0a16faa3afe726, type: 3} m_Name: Vignette m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.Vignette active: 1 color: m_OverrideState: 1 m_Value: {r: 0, g: 0, b: 0, a: 1} center: m_OverrideState: 1 m_Value: {x: 0.5, y: 0.5} intensity: m_OverrideState: 1 m_Value: 0 smoothness: m_OverrideState: 1 m_Value: 0.2 rounded: m_OverrideState: 1 m_Value: 0 --- !u!114 &-4084026468743712817 MonoBehaviour: m_ObjectHideFlags: 3 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 81180773991d8724ab7f2d216912b564, type: 3} m_Name: ChromaticAberration m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.ChromaticAberration active: 1 intensity: m_OverrideState: 1 m_Value: 0 --- !u!114 &-4056193433038019785 MonoBehaviour: m_ObjectHideFlags: 3 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: fb60a22f311433c4c962b888d1393f88, type: 3} m_Name: PaniniProjection m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.PaniniProjection active: 1 distance: m_OverrideState: 1 m_Value: 0 cropToFit: m_OverrideState: 1 m_Value: 1 --- !u!114 &-1795136986949859044 MonoBehaviour: m_ObjectHideFlags: 3 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 06437c1ff663d574d9447842ba0a72e4, type: 3} m_Name: ScreenSpaceLensFlare m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.ScreenSpaceLensFlare active: 1 intensity: m_OverrideState: 1 m_Value: 0 tintColor: m_OverrideState: 1 m_Value: {r: 1, g: 1, b: 1, a: 1} bloomMip: m_OverrideState: 1 m_Value: 1 firstFlareIntensity: m_OverrideState: 1 m_Value: 1 secondaryFlareIntensity: m_OverrideState: 1 m_Value: 1 warpedFlareIntensity: m_OverrideState: 1 m_Value: 1 warpedFlareScale: m_OverrideState: 1 m_Value: {x: 1, y: 1} samples: m_OverrideState: 1 m_Value: 1 sampleDimmer: m_OverrideState: 1 m_Value: 0.5 vignetteEffect: m_OverrideState: 1 m_Value: 1 startingPosition: m_OverrideState: 1 m_Value: 1.25 scale: m_OverrideState: 1 m_Value: 1.5 streaksIntensity: m_OverrideState: 1 m_Value: 0 streaksLength: m_OverrideState: 1 m_Value: 0.5 streaksOrientation: m_OverrideState: 1 m_Value: 0 streaksThreshold: m_OverrideState: 1 m_Value: 0.25 resolution: m_OverrideState: 1 m_Value: 4 chromaticAbberationIntensity: m_OverrideState: 1 m_Value: 0.5 --- !u!114 &-598097793118719588 MonoBehaviour: m_ObjectHideFlags: 3 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: e021b4c809a781e468c2988c016ebbea, type: 3} m_Name: ColorLookup m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.ColorLookup active: 1 texture: m_OverrideState: 1 m_Value: {fileID: 0} dimension: 1 contribution: m_OverrideState: 1 m_Value: 0 --- !u!114 &-491638309231600550 MonoBehaviour: m_ObjectHideFlags: 3 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 29fa0085f50d5e54f8144f766051a691, type: 3} m_Name: FilmGrain m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.FilmGrain active: 1 type: m_OverrideState: 1 m_Value: 0 intensity: m_OverrideState: 1 m_Value: 0 response: m_OverrideState: 1 m_Value: 0.8 texture: m_OverrideState: 1 m_Value: {fileID: 0} --- !u!114 &-262398843821731637 MonoBehaviour: m_ObjectHideFlags: 3 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 0b2db86121404754db890f4c8dfe81b2, type: 3} m_Name: Bloom m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.Bloom active: 1 skipIterations: m_OverrideState: 1 m_Value: 1 threshold: m_OverrideState: 1 m_Value: 0.9 intensity: m_OverrideState: 1 m_Value: 0 scatter: m_OverrideState: 1 m_Value: 0.7 clamp: m_OverrideState: 1 m_Value: 65472 tint: m_OverrideState: 1 m_Value: {r: 1, g: 1, b: 1, a: 1} highQualityFiltering: m_OverrideState: 1 m_Value: 0 filter: m_OverrideState: 1 m_Value: 0 downscale: m_OverrideState: 1 m_Value: 0 maxIterations: m_OverrideState: 1 m_Value: 6 dirtTexture: m_OverrideState: 1 m_Value: {fileID: 0} dimension: 1 dirtIntensity: m_OverrideState: 1 m_Value: 0 --- !u!114 &11400000 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: d7fd9488000d3734a9e00ee676215985, type: 3} m_Name: DefaultVolume m_EditorClassIdentifier: Unity.RenderPipelines.Core.Runtime::UnityEngine.Rendering.VolumeProfile components: - {fileID: 451328866785996911} - {fileID: 8855959688280746896} - {fileID: 4256406320919580142} - {fileID: 2026760357700040227} - {fileID: 8862907471677848668} - {fileID: -4422322214415224032} - {fileID: -8324776220157134924} - {fileID: -1795136986949859044} - {fileID: 3728775241584703705} - {fileID: -4854300222326862063} - {fileID: -262398843821731637} - {fileID: 1801325758739867974} - {fileID: -4056193433038019785} - {fileID: -4084026468743712817} - {fileID: 5078066985263128679} - {fileID: -491638309231600550} - {fileID: 1585749192924285193} - {fileID: -598097793118719588} - {fileID: 5892942527586337604} --- !u!114 &451328866785996911 MonoBehaviour: m_ObjectHideFlags: 3 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 558a8e2b6826cf840aae193990ba9f2e, type: 3} m_Name: ShadowsMidtonesHighlights m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.ShadowsMidtonesHighlights active: 1 shadows: m_OverrideState: 1 m_Value: {x: 1, y: 1, z: 1, w: 0} midtones: m_OverrideState: 1 m_Value: {x: 1, y: 1, z: 1, w: 0} highlights: m_OverrideState: 1 m_Value: {x: 1, y: 1, z: 1, w: 0} shadowsStart: m_OverrideState: 1 m_Value: 0 shadowsEnd: m_OverrideState: 1 m_Value: 0.3 highlightsStart: m_OverrideState: 1 m_Value: 0.55 highlightsEnd: m_OverrideState: 1 m_Value: 1 --- !u!114 &1585749192924285193 MonoBehaviour: m_ObjectHideFlags: 3 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 221518ef91623a7438a71fef23660601, type: 3} m_Name: WhiteBalance m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.WhiteBalance active: 1 temperature: m_OverrideState: 1 m_Value: 0 tint: m_OverrideState: 1 m_Value: 0 --- !u!114 &1801325758739867974 MonoBehaviour: m_ObjectHideFlags: 3 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 66f335fb1ffd8684294ad653bf1c7564, type: 3} m_Name: ColorAdjustments m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.ColorAdjustments active: 1 postExposure: m_OverrideState: 1 m_Value: 0 contrast: m_OverrideState: 1 m_Value: 0 colorFilter: m_OverrideState: 1 m_Value: {r: 1, g: 1, b: 1, a: 1} hueShift: m_OverrideState: 1 m_Value: 0 saturation: m_OverrideState: 1 m_Value: 0 --- !u!114 &2026760357700040227 MonoBehaviour: m_ObjectHideFlags: 3 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: c01700fd266d6914ababb731e09af2eb, type: 3} m_Name: DepthOfField m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.DepthOfField active: 1 mode: m_OverrideState: 1 m_Value: 0 gaussianStart: m_OverrideState: 1 m_Value: 10 gaussianEnd: m_OverrideState: 1 m_Value: 30 gaussianMaxRadius: m_OverrideState: 1 m_Value: 1 highQualitySampling: m_OverrideState: 1 m_Value: 0 focusDistance: m_OverrideState: 1 m_Value: 10 aperture: m_OverrideState: 1 m_Value: 5.6 focalLength: m_OverrideState: 1 m_Value: 50 bladeCount: m_OverrideState: 1 m_Value: 5 bladeCurvature: m_OverrideState: 1 m_Value: 1 bladeRotation: m_OverrideState: 1 m_Value: 0 --- !u!114 &3728775241584703705 MonoBehaviour: m_ObjectHideFlags: 3 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: c5e1dc532bcb41949b58bc4f2abfbb7e, type: 3} m_Name: LensDistortion m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.LensDistortion active: 1 intensity: m_OverrideState: 1 m_Value: 0 xMultiplier: m_OverrideState: 1 m_Value: 1 yMultiplier: m_OverrideState: 1 m_Value: 1 center: m_OverrideState: 1 m_Value: {x: 0.5, y: 0.5} scale: m_OverrideState: 1 m_Value: 1 --- !u!114 &4256406320919580142 MonoBehaviour: m_ObjectHideFlags: 3 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 3eb4b772797da9440885e8bd939e9560, type: 3} m_Name: ColorCurves m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.ColorCurves active: 1 master: m_OverrideState: 1 m_Value: k__BackingField: 2 m_Loop: 0 m_ZeroValue: 0 m_Range: 1 m_Curve: serializedVersion: 2 m_Curve: - serializedVersion: 3 time: 0 value: 0 inSlope: 1 outSlope: 1 tangentMode: 0 weightedMode: 0 inWeight: 0 outWeight: 0 - serializedVersion: 3 time: 1 value: 1 inSlope: 1 outSlope: 1 tangentMode: 0 weightedMode: 0 inWeight: 0 outWeight: 0 m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 red: m_OverrideState: 1 m_Value: k__BackingField: 2 m_Loop: 0 m_ZeroValue: 0 m_Range: 1 m_Curve: serializedVersion: 2 m_Curve: - serializedVersion: 3 time: 0 value: 0 inSlope: 1 outSlope: 1 tangentMode: 0 weightedMode: 0 inWeight: 0 outWeight: 0 - serializedVersion: 3 time: 1 value: 1 inSlope: 1 outSlope: 1 tangentMode: 0 weightedMode: 0 inWeight: 0 outWeight: 0 m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 green: m_OverrideState: 1 m_Value: k__BackingField: 2 m_Loop: 0 m_ZeroValue: 0 m_Range: 1 m_Curve: serializedVersion: 2 m_Curve: - serializedVersion: 3 time: 0 value: 0 inSlope: 1 outSlope: 1 tangentMode: 0 weightedMode: 0 inWeight: 0 outWeight: 0 - serializedVersion: 3 time: 1 value: 1 inSlope: 1 outSlope: 1 tangentMode: 0 weightedMode: 0 inWeight: 0 outWeight: 0 m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 blue: m_OverrideState: 1 m_Value: k__BackingField: 2 m_Loop: 0 m_ZeroValue: 0 m_Range: 1 m_Curve: serializedVersion: 2 m_Curve: - serializedVersion: 3 time: 0 value: 0 inSlope: 1 outSlope: 1 tangentMode: 0 weightedMode: 0 inWeight: 0 outWeight: 0 - serializedVersion: 3 time: 1 value: 1 inSlope: 1 outSlope: 1 tangentMode: 0 weightedMode: 0 inWeight: 0 outWeight: 0 m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 hueVsHue: m_OverrideState: 1 m_Value: k__BackingField: 0 m_Loop: 1 m_ZeroValue: 0.5 m_Range: 1 m_Curve: serializedVersion: 2 m_Curve: [] m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 hueVsSat: m_OverrideState: 1 m_Value: k__BackingField: 0 m_Loop: 1 m_ZeroValue: 0.5 m_Range: 1 m_Curve: serializedVersion: 2 m_Curve: [] m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 satVsSat: m_OverrideState: 1 m_Value: k__BackingField: 0 m_Loop: 0 m_ZeroValue: 0.5 m_Range: 1 m_Curve: serializedVersion: 2 m_Curve: [] m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 lumVsSat: m_OverrideState: 1 m_Value: k__BackingField: 0 m_Loop: 0 m_ZeroValue: 0.5 m_Range: 1 m_Curve: serializedVersion: 2 m_Curve: [] m_PreInfinity: 2 m_PostInfinity: 2 m_RotationOrder: 4 --- !u!114 &5078066985263128679 MonoBehaviour: m_ObjectHideFlags: 3 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 97c23e3b12dc18c42a140437e53d3951, type: 3} m_Name: Tonemapping m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.Tonemapping active: 1 mode: m_OverrideState: 1 m_Value: 0 neutralHDRRangeReductionMode: m_OverrideState: 1 m_Value: 2 acesPreset: m_OverrideState: 1 m_Value: 3 hueShiftAmount: m_OverrideState: 1 m_Value: 0 detectPaperWhite: m_OverrideState: 1 m_Value: 0 paperWhite: m_OverrideState: 1 m_Value: 300 detectBrightnessLimits: m_OverrideState: 1 m_Value: 1 minNits: m_OverrideState: 1 m_Value: 0.005 maxNits: m_OverrideState: 1 m_Value: 1000 --- !u!114 &5892942527586337604 MonoBehaviour: m_ObjectHideFlags: 3 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 6bd486065ce11414fa40e631affc4900, type: 3} m_Name: ProbeVolumesOptions m_EditorClassIdentifier: Unity.RenderPipelines.Core.Runtime::UnityEngine.Rendering.ProbeVolumesOptions active: 1 normalBias: m_OverrideState: 1 m_Value: 0.05 viewBias: m_OverrideState: 1 m_Value: 0.1 scaleBiasWithMinProbeDistance: m_OverrideState: 1 m_Value: 0 samplingNoise: m_OverrideState: 1 m_Value: 0.1 animateSamplingNoise: m_OverrideState: 1 m_Value: 1 leakReductionMode: m_OverrideState: 1 m_Value: 2 minValidDotProductValue: m_OverrideState: 1 m_Value: 0.1 occlusionOnlyReflectionNormalization: m_OverrideState: 1 m_Value: 1 intensityMultiplier: m_OverrideState: 1 m_Value: 1 skyOcclusionIntensityMultiplier: m_OverrideState: 1 m_Value: 1 worldOffset: m_OverrideState: 1 m_Value: {x: 0, y: 0, z: 0} --- !u!114 &8855959688280746896 MonoBehaviour: m_ObjectHideFlags: 3 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: cdfbdbb87d3286943a057f7791b43141, type: 3} m_Name: ChannelMixer m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.ChannelMixer active: 1 redOutRedIn: m_OverrideState: 1 m_Value: 100 redOutGreenIn: m_OverrideState: 1 m_Value: 0 redOutBlueIn: m_OverrideState: 1 m_Value: 0 greenOutRedIn: m_OverrideState: 1 m_Value: 0 greenOutGreenIn: m_OverrideState: 1 m_Value: 100 greenOutBlueIn: m_OverrideState: 1 m_Value: 0 blueOutRedIn: m_OverrideState: 1 m_Value: 0 blueOutGreenIn: m_OverrideState: 1 m_Value: 0 blueOutBlueIn: m_OverrideState: 1 m_Value: 100 --- !u!114 &8862907471677848668 MonoBehaviour: m_ObjectHideFlags: 3 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: ccf1aba9553839d41ae37dd52e9ebcce, type: 3} m_Name: MotionBlur m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.MotionBlur active: 1 mode: m_OverrideState: 1 m_Value: 0 quality: m_OverrideState: 1 m_Value: 0 intensity: m_OverrideState: 1 m_Value: 0 clamp: m_OverrideState: 1 m_Value: 0.05 ================================================ FILE: Assets/Demo/URP/DefaultVolume.asset.meta ================================================ fileFormatVersion: 2 guid: dc9bac492fb764e719f43f6fff2d42aa NativeFormatImporter: externalObjects: {} mainObjectFileID: 11400000 userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Demo/URP/GlobalSettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!114 &11400000 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 2ec995e51a6e251468d2a3fd8a686257, type: 3} m_Name: GlobalSettings m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.UniversalRenderPipelineGlobalSettings m_ShaderStrippingSetting: m_Version: 0 m_ExportShaderVariants: 1 m_ShaderVariantLogLevel: 0 m_StripRuntimeDebugShaders: 1 m_URPShaderStrippingSetting: m_Version: 0 m_StripUnusedPostProcessingVariants: 0 m_StripUnusedVariants: 1 m_StripScreenCoordOverrideVariants: 1 m_ShaderVariantLogLevel: 0 m_ExportShaderVariants: 1 m_StripDebugVariants: 1 m_StripUnusedPostProcessingVariants: 0 m_StripUnusedVariants: 1 m_StripScreenCoordOverrideVariants: 1 supportRuntimeDebugDisplay: 0 m_EnableRenderGraph: 0 m_Settings: m_SettingsList: m_List: - rid: 8017221388498632982 - rid: 8017221388498632983 - rid: 8017221388498632984 - rid: 8017221388498632985 - rid: 8017221388498632986 - rid: 8017221388498632987 - rid: 8017221388498632988 - rid: 8017221388498632989 - rid: 8017221388498632990 - rid: 8017221388498632991 - rid: 8017221388498632992 - rid: 8017221388498632993 - rid: 8017221388498632994 - rid: 8017221388498632995 - rid: 8017221388498632996 - rid: 8017221388498632997 - rid: 8017221388498632998 - rid: 8017221388498632999 - rid: 8017221388498633000 - rid: 8017221388498633001 - rid: 8017221388498633002 - rid: 8017221388498633003 - rid: 8017221388498633004 - rid: 8017221388498633005 - rid: 8017221388498633006 - rid: 8017221388498633007 - rid: 8017221388498633008 - rid: 8017221388498633009 - rid: 8017221388498633010 - rid: 8017221388498633011 - rid: 8017221388498633012 - rid: 8017221388498633013 m_RuntimeSettings: m_List: [] m_AssetVersion: 9 m_ObsoleteDefaultVolumeProfile: {fileID: 0} m_RenderingLayerNames: - Default m_ValidRenderingLayers: 0 lightLayerName0: lightLayerName1: lightLayerName2: lightLayerName3: lightLayerName4: lightLayerName5: lightLayerName6: lightLayerName7: apvScenesData: obsoleteSceneBounds: m_Keys: [] m_Values: [] obsoleteHasProbeVolumes: m_Keys: [] m_Values: references: version: 2 RefIds: - rid: 8017221388498632982 type: {class: RayTracingRenderPipelineResources, ns: UnityEngine.Rendering.UnifiedRayTracing, asm: Unity.UnifiedRayTracing.Runtime} data: m_Version: 1 m_GeometryPoolKernels: {fileID: 7200000, guid: 98e3d58cae7210c4786f67f504c9e899, type: 3} m_CopyBuffer: {fileID: 7200000, guid: 1b95b5dcf48d1914c9e1e7405c7660e3, type: 3} m_CopyPositions: {fileID: 7200000, guid: 1ad53a96b58d3c3488dde4f14db1aaeb, type: 3} m_BitHistogram: {fileID: 7200000, guid: 8670f7ce4b60cef43bed36148aa1b0a2, type: 3} m_BlockReducePart: {fileID: 7200000, guid: 4e034cc8ea2635c4e9f063e5ddc7ea7a, type: 3} m_BlockScan: {fileID: 7200000, guid: 4d6d5de35fa45ef4a92119397a045cc9, type: 3} m_BuildHlbvh: {fileID: 7200000, guid: 2d70cd6be91bd7843a39a54b51c15b13, type: 3} m_RestructureBvh: {fileID: 7200000, guid: 56641cb88dcb31a4398a4997ef7a7a8c, type: 3} m_Scatter: {fileID: 7200000, guid: a2eaeefdac4637a44b734e85b7be9186, type: 3} - rid: 8017221388498632983 type: {class: UniversalRenderPipelineEditorMaterials, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} data: m_DefaultMaterial: {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2} m_DefaultParticleMaterial: {fileID: 2100000, guid: e823cd5b5d27c0f4b8256e7c12ee3e6d, type: 2} m_DefaultLineMaterial: {fileID: 2100000, guid: e823cd5b5d27c0f4b8256e7c12ee3e6d, type: 2} m_DefaultTerrainMaterial: {fileID: 2100000, guid: 594ea882c5a793440b60ff72d896021e, type: 2} m_DefaultDecalMaterial: {fileID: 2100000, guid: 31d0dcc6f2dd4e4408d18036a2c93862, type: 2} m_DefaultSpriteMaterial: {fileID: 2100000, guid: 9dfc825aed78fcd4ba02077103263b40, type: 2} - rid: 8017221388498632984 type: {class: PostProcessData/TextureResources, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} data: blueNoise16LTex: [] filmGrainTex: - {fileID: 2800000, guid: 654c582f7f8a5a14dbd7d119cbde215d, type: 3} - {fileID: 2800000, guid: dd77ffd079630404e879388999033049, type: 3} - {fileID: 2800000, guid: 1097e90e1306e26439701489f391a6c0, type: 3} - {fileID: 2800000, guid: f0b67500f7fad3b4c9f2b13e8f41ba6e, type: 3} - {fileID: 2800000, guid: 9930fb4528622b34687b00bbe6883de7, type: 3} - {fileID: 2800000, guid: bd9e8c758250ef449a4b4bfaad7a2133, type: 3} - {fileID: 2800000, guid: 510a2f57334933e4a8dbabe4c30204e4, type: 3} - {fileID: 2800000, guid: b4db8180660810945bf8d55ab44352ad, type: 3} - {fileID: 2800000, guid: fd2fd78b392986e42a12df2177d3b89c, type: 3} - {fileID: 2800000, guid: 5cdee82a77d13994f83b8fdabed7c301, type: 3} smaaAreaTex: {fileID: 2800000, guid: d1f1048909d55cd4fa1126ab998f617e, type: 3} smaaSearchTex: {fileID: 2800000, guid: 51eee22c2a633ef4aada830eed57c3fd, type: 3} m_TexturesResourcesVersion: 0 - rid: 8017221388498632985 type: {class: URPDefaultVolumeProfileSettings, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} data: m_Version: 0 m_VolumeProfile: {fileID: 11400000, guid: dc9bac492fb764e719f43f6fff2d42aa, type: 2} - rid: 8017221388498632986 type: {class: ScreenSpaceAmbientOcclusionDynamicResources, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} data: m_BlueNoise256Textures: - {fileID: 2800000, guid: 36f118343fc974119bee3d09e2111500, type: 3} - {fileID: 2800000, guid: 4b7b083e6b6734e8bb2838b0b50a0bc8, type: 3} - {fileID: 2800000, guid: c06cc21c692f94f5fb5206247191eeee, type: 3} - {fileID: 2800000, guid: cb76dd40fa7654f9587f6a344f125c9a, type: 3} - {fileID: 2800000, guid: e32226222ff144b24bf3a5a451de54bc, type: 3} - {fileID: 2800000, guid: 3302065f671a8450b82c9ddf07426f3a, type: 3} - {fileID: 2800000, guid: 56a77a3e8d64f47b6afe9e3c95cb57d5, type: 3} m_Version: 0 - rid: 8017221388498632987 type: {class: RenderGraphSettings, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} data: m_Version: 0 m_EnableRenderCompatibilityMode: 0 - rid: 8017221388498632988 type: {class: Renderer2DResources, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} data: m_Version: 0 m_LightShader: {fileID: 4800000, guid: 3f6c848ca3d7bca4bbe846546ac701a1, type: 3} m_ProjectedShadowShader: {fileID: 4800000, guid: ce09d4a80b88c5a4eb9768fab4f1ee00, type: 3} m_SpriteShadowShader: {fileID: 4800000, guid: 44fc62292b65ab04eabcf310e799ccf6, type: 3} m_SpriteUnshadowShader: {fileID: 4800000, guid: de02b375720b5c445afe83cd483bedf3, type: 3} m_GeometryShadowShader: {fileID: 4800000, guid: 19349a0f9a7ed4c48a27445bcf92e5e1, type: 3} m_GeometryUnshadowShader: {fileID: 4800000, guid: 77774d9009bb81447b048c907d4c6273, type: 3} m_CopyDepthPS: {fileID: 4800000, guid: d6dae50ee9e1bfa4db75f19f99355220, type: 3} m_DefaultLitMaterial: {fileID: 2100000, guid: a97c105638bdf8b4a8650670310a4cd3, type: 2} m_DefaultUnlitMaterial: {fileID: 2100000, guid: 9dfc825aed78fcd4ba02077103263b40, type: 2} m_DefaultMaskMaterial: {fileID: 2100000, guid: 15d0c3709176029428a0da2f8cecf0b5, type: 2} m_DefaultMesh2DLitMaterial: {fileID: 2100000, guid: 9452ae1262a74094f8a68013fbcd1834, type: 2} - rid: 8017221388498632989 type: {class: URPShaderStrippingSetting, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} data: m_Version: 0 m_StripUnusedPostProcessingVariants: 0 m_StripUnusedVariants: 1 m_StripScreenCoordOverrideVariants: 1 - rid: 8017221388498632990 type: {class: UniversalRendererResources, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} data: m_Version: 0 m_CopyDepthPS: {fileID: 4800000, guid: d6dae50ee9e1bfa4db75f19f99355220, type: 3} m_CameraMotionVector: {fileID: 4800000, guid: c56b7e0d4c7cb484e959caeeedae9bbf, type: 3} m_StencilDeferredPS: {fileID: 4800000, guid: e9155b26e1bc55942a41e518703fe304, type: 3} m_ClusterDeferred: {fileID: 4800000, guid: 222cce62363a44a380c36bf03b392608, type: 3} m_StencilDitherMaskSeedPS: {fileID: 4800000, guid: 8c3ee818f2efa514c889881ccb2e95a2, type: 3} m_DBufferClear: {fileID: 4800000, guid: f056d8bd2a1c7e44e9729144b4c70395, type: 3} - rid: 8017221388498632991 type: {class: UniversalRenderPipelineRuntimeShaders, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} data: m_Version: 0 m_FallbackErrorShader: {fileID: 4800000, guid: e6e9a19c3678ded42a3bc431ebef7dbd, type: 3} m_BlitHDROverlay: {fileID: 4800000, guid: a89bee29cffa951418fc1e2da94d1959, type: 3} m_CoreBlitPS: {fileID: 4800000, guid: 93446b5c5339d4f00b85c159e1159b7c, type: 3} m_CoreBlitColorAndDepthPS: {fileID: 4800000, guid: d104b2fc1ca6445babb8e90b0758136b, type: 3} m_SamplingPS: {fileID: 4800000, guid: 04c410c9937594faa893a11dceb85f7e, type: 3} m_TerrainDetailLit: {fileID: 4800000, guid: f6783ab646d374f94b199774402a5144, type: 3} m_TerrainDetailGrassBillboard: {fileID: 4800000, guid: 29868e73b638e48ca99a19ea58c48d90, type: 3} m_TerrainDetailGrass: {fileID: 4800000, guid: e507fdfead5ca47e8b9a768b51c291a1, type: 3} - rid: 8017221388498632992 type: {class: OnTilePostProcessResource, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} data: m_Version: 0 m_UberPostShader: {fileID: 4800000, guid: fe4f13c1004a07d4ea1e30bfd0326d9e, type: 3} - rid: 8017221388498632993 type: {class: URPReflectionProbeSettings, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Universal.Runtime} data: version: 1 useReflectionProbeRotation: 1 - rid: 8017221388498632994 type: {class: UniversalRenderPipelineEditorShaders, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} data: m_AutodeskInteractive: {fileID: 4800000, guid: 0e9d5a909a1f7e84882a534d0d11e49f, type: 3} m_AutodeskInteractiveTransparent: {fileID: 4800000, guid: 5c81372d981403744adbdda4433c9c11, type: 3} m_AutodeskInteractiveMasked: {fileID: 4800000, guid: 80aa867ac363ac043847b06ad71604cd, type: 3} m_DefaultSpeedTree7Shader: {fileID: 4800000, guid: 0f4122b9a743b744abe2fb6a0a88868b, type: 3} m_DefaultSpeedTree8Shader: {fileID: -6465566751694194690, guid: 9920c1f1781549a46ba081a2a15a16ec, type: 3} m_DefaultSpeedTree9Shader: {fileID: -6465566751694194690, guid: cbd3e1cc4ae141c42a30e33b4d666a61, type: 3} - rid: 8017221388498632995 type: {class: ScreenSpaceAmbientOcclusionPersistentResources, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} data: m_Shader: {fileID: 4800000, guid: 0849e84e3d62649e8882e9d6f056a017, type: 3} m_Version: 0 - rid: 8017221388498632996 type: {class: UniversalRenderPipelineDebugShaders, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} data: m_DebugReplacementPS: {fileID: 4800000, guid: cf852408f2e174538bcd9b7fda1c5ae7, type: 3} m_HdrDebugViewPS: {fileID: 4800000, guid: 573620ae32aec764abd4d728906d2587, type: 3} m_ProbeVolumeSamplingDebugComputeShader: {fileID: 7200000, guid: 53626a513ea68ce47b59dc1299fe3959, type: 3} - rid: 8017221388498632997 type: {class: UniversalRenderPipelineRuntimeXRResources, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} data: m_xrOcclusionMeshPS: {fileID: 4800000, guid: 4431b1f1f743fbf4eb310a967890cbea, type: 3} m_xrMirrorViewPS: {fileID: 4800000, guid: d5a307c014552314b9f560906d708772, type: 3} m_xrMotionVector: {fileID: 4800000, guid: f89aac1e4f84468418fe30e611dff395, type: 3} - rid: 8017221388498632998 type: {class: UniversalRenderPipelineEditorAssets, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} data: m_DefaultSettingsVolumeProfile: {fileID: 11400000, guid: eda47df5b85f4f249abf7abd73db2cb2, type: 2} - rid: 8017221388498632999 type: {class: PostProcessData/ShaderResources, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} data: stopNanPS: {fileID: 4800000, guid: 1121bb4e615ca3c48b214e79e841e823, type: 3} subpixelMorphologicalAntialiasingPS: {fileID: 4800000, guid: 63eaba0ebfb82cc43bde059b4a8c65f6, type: 3} gaussianDepthOfFieldPS: {fileID: 4800000, guid: 5e7134d6e63e0bc47a1dd2669cedb379, type: 3} bokehDepthOfFieldPS: {fileID: 4800000, guid: 2aed67ad60045d54ba3a00c91e2d2631, type: 3} cameraMotionBlurPS: {fileID: 4800000, guid: 1edcd131364091c46a17cbff0b1de97a, type: 3} paniniProjectionPS: {fileID: 4800000, guid: a15b78cf8ca26ca4fb2090293153c62c, type: 3} lutBuilderLdrPS: {fileID: 4800000, guid: 65df88701913c224d95fc554db28381a, type: 3} lutBuilderHdrPS: {fileID: 4800000, guid: ec9fec698a3456d4fb18cf8bacb7a2bc, type: 3} bloomPS: {fileID: 4800000, guid: 5f1864addb451f54bae8c86d230f736e, type: 3} temporalAntialiasingPS: {fileID: 4800000, guid: 9c70c1a35ff15f340b38ea84842358bf, type: 3} LensFlareDataDrivenPS: {fileID: 4800000, guid: 6cda457ac28612740adb23da5d39ea92, type: 3} LensFlareScreenSpacePS: {fileID: 4800000, guid: 701880fecb344ea4c9cd0db3407ab287, type: 3} scalingSetupPS: {fileID: 4800000, guid: e8ee25143a34b8c4388709ea947055d1, type: 3} easuPS: {fileID: 4800000, guid: 562b7ae4f629f144aa97780546fce7c6, type: 3} uberPostPS: {fileID: 4800000, guid: e7857e9d0c934dc4f83f270f8447b006, type: 3} finalPostPassPS: {fileID: 4800000, guid: c49e63ed1bbcb334780a3bd19dfed403, type: 3} m_ShaderResourcesVersion: 0 - rid: 8017221388498633000 type: {class: UniversalRenderPipelineRuntimeTextures, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} data: m_Version: 1 m_BlueNoise64LTex: {fileID: 2800000, guid: e3d24661c1e055f45a7560c033dbb837, type: 3} m_BayerMatrixTex: {fileID: 2800000, guid: f9ee4ed84c1d10c49aabb9b210b0fc44, type: 3} m_DebugFontTex: {fileID: 2800000, guid: 26a413214480ef144b2915d6ff4d0beb, type: 3} - rid: 8017221388498633001 type: {class: GPUResidentDrawerResources, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.GPUDriven.Runtime} data: m_Version: 0 m_InstanceDataBufferCopyKernels: {fileID: 7200000, guid: f984aeb540ded8b4fbb8a2047ab5b2e2, type: 3} m_InstanceDataBufferUploadKernels: {fileID: 7200000, guid: 53864816eb00f2343b60e1a2c5a262ef, type: 3} m_TransformUpdaterKernels: {fileID: 7200000, guid: 2a567b9b2733f8d47a700c3c85bed75b, type: 3} m_WindDataUpdaterKernels: {fileID: 7200000, guid: fde76746e4fd0ed418c224f6b4084114, type: 3} m_OccluderDepthPyramidKernels: {fileID: 7200000, guid: 08b2b5fb307b0d249860612774a987da, type: 3} m_InstanceOcclusionCullingKernels: {fileID: 7200000, guid: f6d223acabc2f974795a5a7864b50e6c, type: 3} m_OcclusionCullingDebugKernels: {fileID: 7200000, guid: b23e766bcf50ca4438ef186b174557df, type: 3} m_DebugOcclusionTestPS: {fileID: 4800000, guid: d3f0849180c2d0944bc71060693df100, type: 3} m_DebugOccluderPS: {fileID: 4800000, guid: b3c92426a88625841ab15ca6a7917248, type: 3} - rid: 8017221388498633002 type: {class: RenderGraphGlobalSettings, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime} data: m_version: 0 m_EnableCompilationCaching: 1 m_EnableValidityChecks: 1 - rid: 8017221388498633003 type: {class: VrsRenderPipelineRuntimeResources, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime} data: m_TextureComputeShader: {fileID: 7200000, guid: cacb30de6c40c7444bbc78cb0a81fd2a, type: 3} m_VisualizationShader: {fileID: 4800000, guid: 620b55b8040a88d468e94abe55bed5ba, type: 3} m_VisualizationLookupTable: m_Data: - {r: 0.785, g: 0.23, b: 0.2, a: 1} - {r: 1, g: 0.8, b: 0.8, a: 1} - {r: 0.4, g: 0.2, b: 0.2, a: 1} - {r: 0.51, g: 0.8, b: 0.6, a: 1} - {r: 0.6, g: 0.8, b: 1, a: 1} - {r: 0.2, g: 0.4, b: 0.6, a: 1} - {r: 0.8, g: 1, b: 0.8, a: 1} - {r: 0.2, g: 0.4, b: 0.2, a: 1} - {r: 0.125, g: 0.22, b: 0.36, a: 1} m_ConversionLookupTable: m_Data: - {r: 0.785, g: 0.23, b: 0.2, a: 1} - {r: 1, g: 0.8, b: 0.8, a: 1} - {r: 0.4, g: 0.2, b: 0.2, a: 1} - {r: 0.51, g: 0.8, b: 0.6, a: 1} - {r: 0.6, g: 0.8, b: 1, a: 1} - {r: 0.2, g: 0.4, b: 0.6, a: 1} - {r: 0.8, g: 1, b: 0.8, a: 1} - {r: 0.2, g: 0.4, b: 0.2, a: 1} - {r: 0.125, g: 0.22, b: 0.36, a: 1} - rid: 8017221388498633004 type: {class: RenderGraphUtilsResources, ns: UnityEngine.Rendering.RenderGraphModule.Util, asm: Unity.RenderPipelines.Core.Runtime} data: m_Version: 0 m_CoreCopyPS: {fileID: 4800000, guid: 12dc59547ea167a4ab435097dd0f9add, type: 3} - rid: 8017221388498633005 type: {class: ProbeVolumeGlobalSettings, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime} data: m_Version: 1 m_ProbeVolumeDisableStreamingAssets: 0 - rid: 8017221388498633006 type: {class: RenderingDebuggerRuntimeResources, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime} data: m_version: 0 - rid: 8017221388498633007 type: {class: ShaderStrippingSetting, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime} data: m_Version: 0 m_ExportShaderVariants: 1 m_ShaderVariantLogLevel: 0 m_StripRuntimeDebugShaders: 1 - rid: 8017221388498633008 type: {class: LightmapSamplingSettings, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime} data: m_Version: 1 m_UseBicubicLightmapSampling: 0 - rid: 8017221388498633009 type: {class: ProbeVolumeBakingResources, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime} data: m_Version: 1 dilationShader: {fileID: 7200000, guid: 6bb382f7de370af41b775f54182e491d, type: 3} subdivideSceneCS: {fileID: 7200000, guid: bb86f1f0af829fd45b2ebddda1245c22, type: 3} voxelizeSceneShader: {fileID: 4800000, guid: c8b6a681c7b4e2e4785ffab093907f9e, type: 3} traceVirtualOffsetCS: {fileID: -6772857160820960102, guid: ff2cbab5da58bf04d82c5f34037ed123, type: 3} traceVirtualOffsetRT: {fileID: -5126288278712620388, guid: ff2cbab5da58bf04d82c5f34037ed123, type: 3} skyOcclusionCS: {fileID: -6772857160820960102, guid: 5a2a534753fbdb44e96c3c78b5a6999d, type: 3} skyOcclusionRT: {fileID: -5126288278712620388, guid: 5a2a534753fbdb44e96c3c78b5a6999d, type: 3} renderingLayerCS: {fileID: -6772857160820960102, guid: 94a070d33e408384bafc1dea4a565df9, type: 3} renderingLayerRT: {fileID: -5126288278712620388, guid: 94a070d33e408384bafc1dea4a565df9, type: 3} - rid: 8017221388498633010 type: {class: ProbeVolumeRuntimeResources, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime} data: m_Version: 1 probeVolumeBlendStatesCS: {fileID: 7200000, guid: a3f7b8c99de28a94684cb1daebeccf5d, type: 3} probeVolumeUploadDataCS: {fileID: 7200000, guid: 0951de5992461754fa73650732c4954c, type: 3} probeVolumeUploadDataL2CS: {fileID: 7200000, guid: 6196f34ed825db14b81fb3eb0ea8d931, type: 3} - rid: 8017221388498633011 type: {class: STP/RuntimeResources, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime} data: m_setupCS: {fileID: 7200000, guid: 33be2e9a5506b2843bdb2bdff9cad5e1, type: 3} m_preTaaCS: {fileID: 7200000, guid: a679dba8ec4d9ce45884a270b0e22dda, type: 3} m_taaCS: {fileID: 7200000, guid: 3923900e2b41b5e47bc25bfdcbcdc9e6, type: 3} - rid: 8017221388498633012 type: {class: IncludeAdditionalRPAssets, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime} data: m_version: 0 m_IncludeReferencedInScenes: 0 m_IncludeAssetsByLabel: 0 m_LabelToInclude: - rid: 8017221388498633013 type: {class: ProbeVolumeDebugResources, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime} data: m_Version: 1 probeVolumeDebugShader: {fileID: 4800000, guid: 3b21275fd12d65f49babb5286f040f2d, type: 3} probeVolumeFragmentationDebugShader: {fileID: 4800000, guid: 3a80877c579b9144ebdcc6d923bca303, type: 3} probeVolumeSamplingDebugShader: {fileID: 4800000, guid: bf54e6528c79a224e96346799064c393, type: 3} probeVolumeOffsetDebugShader: {fileID: 4800000, guid: db8bd7436dc2c5f4c92655307d198381, type: 3} probeSamplingDebugMesh: {fileID: -3555484719484374845, guid: 20be25aac4e22ee49a7db76fb3df6de2, type: 3} numbersDisplayTex: {fileID: 2800000, guid: 73fe53b428c5b3440b7e87ee830b608a, type: 3} ================================================ FILE: Assets/Demo/URP/GlobalSettings.asset.meta ================================================ fileFormatVersion: 2 guid: a0e1abbc8745e464ab699ff4e71959f6 NativeFormatImporter: externalObjects: {} mainObjectFileID: 11400000 userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Demo/URP/URP.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!114 &11400000 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: bf2edee5c58d82540a51f03df9d42094, type: 3} m_Name: URP m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.UniversalRenderPipelineAsset k_AssetVersion: 13 k_AssetPreviousVersion: 13 m_RendererType: 1 m_RendererData: {fileID: 0} m_RendererDataList: - {fileID: 11400000, guid: e707f25f3295f48408bbc74cefc67e0d, type: 2} m_DefaultRendererIndex: 0 m_RequireDepthTexture: 0 m_RequireOpaqueTexture: 0 m_OpaqueDownsampling: 1 m_SupportsTerrainHoles: 1 m_SupportsHDR: 1 m_HDRColorBufferPrecision: 0 m_MSAA: 1 m_RenderScale: 1 m_UpscalingFilter: 0 m_FsrOverrideSharpness: 0 m_FsrSharpness: 0.92 m_EnableLODCrossFade: 1 m_LODCrossFadeDitheringType: 1 m_ShEvalMode: 0 m_LightProbeSystem: 0 m_ProbeVolumeMemoryBudget: 1024 m_ProbeVolumeBlendingMemoryBudget: 256 m_SupportProbeVolumeGPUStreaming: 0 m_SupportProbeVolumeDiskStreaming: 0 m_SupportProbeVolumeScenarios: 0 m_SupportProbeVolumeScenarioBlending: 0 m_ProbeVolumeSHBands: 1 m_MainLightRenderingMode: 1 m_MainLightShadowsSupported: 1 m_MainLightShadowmapResolution: 2048 m_AdditionalLightsRenderingMode: 1 m_AdditionalLightsPerObjectLimit: 4 m_AdditionalLightShadowsSupported: 0 m_AdditionalLightsShadowmapResolution: 2048 m_AdditionalLightsShadowResolutionTierLow: 256 m_AdditionalLightsShadowResolutionTierMedium: 512 m_AdditionalLightsShadowResolutionTierHigh: 1024 m_ReflectionProbeBlending: 0 m_ReflectionProbeBoxProjection: 0 m_ReflectionProbeAtlas: 0 m_ShadowDistance: 50 m_ShadowCascadeCount: 1 m_Cascade2Split: 0.25 m_Cascade3Split: {x: 0.1, y: 0.3} m_Cascade4Split: {x: 0.067, y: 0.2, z: 0.467} m_CascadeBorder: 0.2 m_ShadowDepthBias: 1 m_ShadowNormalBias: 1 m_AnyShadowsSupported: 1 m_SoftShadowsSupported: 0 m_ConservativeEnclosingSphere: 1 m_NumIterationsEnclosingSphere: 64 m_SoftShadowQuality: 2 m_AdditionalLightsCookieResolution: 2048 m_AdditionalLightsCookieFormat: 3 m_UseSRPBatcher: 1 m_SupportsDynamicBatching: 0 m_MixedLightingSupported: 1 m_SupportsLightCookies: 1 m_SupportsLightLayers: 0 m_DebugLevel: 0 m_StoreActionsOptimization: 0 m_UseAdaptivePerformance: 1 m_ColorGradingMode: 0 m_ColorGradingLutSize: 32 m_AllowPostProcessAlphaOutput: 0 m_UseFastSRGBLinearConversion: 0 m_SupportDataDrivenLensFlare: 1 m_SupportScreenSpaceLensFlare: 1 m_GPUResidentDrawerMode: 0 m_SmallMeshScreenPercentage: 0 m_GPUResidentDrawerEnableOcclusionCullingInCameras: 0 m_ShadowType: 1 m_LocalShadowsSupported: 0 m_LocalShadowsAtlasResolution: 256 m_MaxPixelLights: 0 m_ShadowAtlasResolution: 256 m_VolumeFrameworkUpdateMode: 0 m_VolumeProfile: {fileID: 0} apvScenesData: obsoleteSceneBounds: m_Keys: [] m_Values: [] obsoleteHasProbeVolumes: m_Keys: [] m_Values: m_PrefilteringModeMainLightShadows: 1 m_PrefilteringModeAdditionalLight: 4 m_PrefilteringModeAdditionalLightShadows: 1 m_PrefilterXRKeywords: 0 m_PrefilteringModeForwardPlus: 1 m_PrefilteringModeDeferredRendering: 1 m_PrefilteringModeScreenSpaceOcclusion: 1 m_PrefilterDebugKeywords: 0 m_PrefilterWriteRenderingLayers: 0 m_PrefilterHDROutput: 0 m_PrefilterAlphaOutput: 0 m_PrefilterSSAODepthNormals: 0 m_PrefilterSSAOSourceDepthLow: 0 m_PrefilterSSAOSourceDepthMedium: 0 m_PrefilterSSAOSourceDepthHigh: 0 m_PrefilterSSAOInterleaved: 0 m_PrefilterSSAOBlueNoise: 0 m_PrefilterSSAOSampleCountLow: 0 m_PrefilterSSAOSampleCountMedium: 0 m_PrefilterSSAOSampleCountHigh: 0 m_PrefilterDBufferMRT1: 0 m_PrefilterDBufferMRT2: 0 m_PrefilterDBufferMRT3: 0 m_PrefilterSoftShadowsQualityLow: 0 m_PrefilterSoftShadowsQualityMedium: 0 m_PrefilterSoftShadowsQualityHigh: 0 m_PrefilterSoftShadows: 0 m_PrefilterScreenCoord: 0 m_PrefilterScreenSpaceIrradiance: 0 m_PrefilterNativeRenderPass: 0 m_PrefilterUseLegacyLightmaps: 0 m_PrefilterBicubicLightmapSampling: 0 m_PrefilterReflectionProbeRotation: 0 m_PrefilterReflectionProbeBlending: 0 m_PrefilterReflectionProbeBoxProjection: 0 m_PrefilterReflectionProbeAtlas: 0 m_ShaderVariantLogLevel: 0 m_ShadowCascades: 0 m_Textures: blueNoise64LTex: {fileID: 2800000, guid: e3d24661c1e055f45a7560c033dbb837, type: 3} bayerMatrixTex: {fileID: 2800000, guid: f9ee4ed84c1d10c49aabb9b210b0fc44, type: 3} ================================================ FILE: Assets/Demo/URP/URP.asset.meta ================================================ fileFormatVersion: 2 guid: 0f2789a7f25c049d88040a322e46da7c NativeFormatImporter: externalObjects: {} mainObjectFileID: 11400000 userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Demo/URP.meta ================================================ fileFormatVersion: 2 guid: dea8b7a5d656145a3aba10170830c74e folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Demo.meta ================================================ fileFormatVersion: 2 guid: 2c79d8564f0c64a0684cb4e548733c30 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Demo.unity ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!29 &1 OcclusionCullingSettings: m_ObjectHideFlags: 0 serializedVersion: 2 m_OcclusionBakeSettings: smallestOccluder: 5 smallestHole: 0.25 backfaceThreshold: 100 m_SceneGUID: 00000000000000000000000000000000 m_OcclusionCullingData: {fileID: 0} --- !u!104 &2 RenderSettings: m_ObjectHideFlags: 0 serializedVersion: 10 m_Fog: 0 m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} m_FogMode: 3 m_FogDensity: 0.01 m_LinearFogStart: 0 m_LinearFogEnd: 300 m_AmbientSkyColor: {r: 4.541206, g: 4.541206, b: 4.541206, a: 1} m_AmbientEquatorColor: {r: 0.00073146477, g: 0.00073146477, b: 0.00073146477, a: 1} m_AmbientGroundColor: {r: 0.26915348, g: 0.13803844, b: 0.6226415, a: 1} m_AmbientIntensity: 1 m_AmbientMode: 1 m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} m_SkyboxMaterial: {fileID: 0} m_HaloStrength: 0.5 m_FlareStrength: 1 m_FlareFadeSpeed: 3 m_HaloTexture: {fileID: 0} m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} m_DefaultReflectionMode: 1 m_DefaultReflectionResolution: 128 m_ReflectionBounces: 1 m_ReflectionIntensity: 1 m_CustomReflection: {fileID: 0} m_Sun: {fileID: 0} m_UseRadianceAmbientProbe: 0 --- !u!157 &3 LightmapSettings: m_ObjectHideFlags: 0 serializedVersion: 13 m_BakeOnSceneLoad: 0 m_GISettings: serializedVersion: 2 m_BounceScale: 1 m_IndirectOutputScale: 1 m_AlbedoBoost: 1 m_EnvironmentLightingMode: 0 m_EnableBakedLightmaps: 1 m_EnableRealtimeLightmaps: 0 m_LightmapEditorSettings: serializedVersion: 12 m_Resolution: 2 m_BakeResolution: 40 m_AtlasSize: 1024 m_AO: 0 m_AOMaxDistance: 1 m_CompAOExponent: 1 m_CompAOExponentDirect: 0 m_ExtractAmbientOcclusion: 0 m_Padding: 2 m_LightmapParameters: {fileID: 0} m_LightmapsBakeMode: 1 m_TextureCompression: 1 m_ReflectionCompression: 2 m_MixedBakeMode: 2 m_BakeBackend: 2 m_PVRSampling: 1 m_PVRDirectSampleCount: 32 m_PVRSampleCount: 512 m_PVRBounces: 2 m_PVREnvironmentSampleCount: 256 m_PVREnvironmentReferencePointCount: 2048 m_PVRFilteringMode: 1 m_PVRDenoiserTypeDirect: 1 m_PVRDenoiserTypeIndirect: 1 m_PVRDenoiserTypeAO: 1 m_PVRFilterTypeDirect: 0 m_PVRFilterTypeIndirect: 0 m_PVRFilterTypeAO: 0 m_PVREnvironmentMIS: 1 m_PVRCulling: 1 m_PVRFilteringGaussRadiusDirect: 1 m_PVRFilteringGaussRadiusIndirect: 1 m_PVRFilteringGaussRadiusAO: 1 m_PVRFilteringAtrousPositionSigmaDirect: 0.5 m_PVRFilteringAtrousPositionSigmaIndirect: 2 m_PVRFilteringAtrousPositionSigmaAO: 1 m_ExportTrainingData: 0 m_TrainingDataDestination: TrainingData m_LightProbeSampleCountMultiplier: 4 m_LightingDataAsset: {fileID: 20201, guid: 0000000000000000f000000000000000, type: 0} m_LightingSettings: {fileID: 0} --- !u!196 &4 NavMeshSettings: serializedVersion: 2 m_ObjectHideFlags: 0 m_BuildSettings: serializedVersion: 3 agentTypeID: 0 agentRadius: 0.5 agentHeight: 2 agentSlope: 45 agentClimb: 0.4 ledgeDropHeight: 0 maxJumpAcrossDistance: 0 minRegionArea: 2 manualCellSize: 0 cellSize: 0.16666667 manualTileSize: 0 tileSize: 256 buildHeightMesh: 0 maxJobWorkers: 0 preserveTilesOutsideBounds: 0 debug: m_Flags: 0 m_NavMeshData: {fileID: 0} --- !u!1 &11714847 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 11714850} - component: {fileID: 11714849} - component: {fileID: 11714848} m_Layer: 0 m_Name: Quad m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!23 &11714848 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 11714847} m_Enabled: 1 m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 m_RayTracingMode: 2 m_RayTraceProcedural: 0 m_RayTracingAccelStructBuildFlagsOverride: 0 m_RayTracingAccelStructBuildFlags: 1 m_SmallMeshCulling: 1 m_ForceMeshLod: -1 m_MeshLodSelectionBias: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - {fileID: 2100000, guid: 6f1182baee9ac4863bd4c5ea5f7d536d, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 m_StaticBatchRoot: {fileID: 0} m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 m_StitchLightmapSeams: 1 m_SelectedEditorRenderState: 3 m_MinimumChartSize: 4 m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} m_GlobalIlluminationMeshLod: 0 m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 m_MaskInteraction: 0 m_AdditionalVertexStreams: {fileID: 0} --- !u!33 &11714849 MeshFilter: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 11714847} m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} --- !u!4 &11714850 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 11714847} serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 7.5510006, y: 4.247438, z: 4.247438} m_ConstrainProportionsScale: 1 m_Children: [] m_Father: {fileID: 28633265} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &28633263 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 28633265} - component: {fileID: 28633264} m_Layer: 0 m_Name: Player (HAP Alpha) m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!114 &28633264 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 28633263} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: f3775962864755b43a790abf4d029c32, type: 3} m_Name: m_EditorClassIdentifier: Klak.Hap::Klak.Hap.HapPlayer _pathMode: 0 _filePath: TransparencyTest-HapAlpha.mov _time: 0 _speed: 1 _loop: 1 _targetTexture: {fileID: 0} _targetRenderer: {fileID: 11714848} _targetMaterialProperty: _BaseMap --- !u!4 &28633265 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 28633263} serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: - {fileID: 11714850} m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &201439697 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 201439700} - component: {fileID: 201439699} - component: {fileID: 201439698} - component: {fileID: 201439702} m_Layer: 0 m_Name: Cube m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!23 &201439698 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 201439697} m_Enabled: 1 m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 m_RayTracingMode: 2 m_RayTraceProcedural: 0 m_RayTracingAccelStructBuildFlagsOverride: 0 m_RayTracingAccelStructBuildFlags: 1 m_SmallMeshCulling: 1 m_ForceMeshLod: -1 m_MeshLodSelectionBias: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 m_StaticBatchRoot: {fileID: 0} m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 m_StitchLightmapSeams: 1 m_SelectedEditorRenderState: 3 m_MinimumChartSize: 4 m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} m_GlobalIlluminationMeshLod: 0 m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 m_MaskInteraction: 0 m_AdditionalVertexStreams: {fileID: 0} --- !u!33 &201439699 MeshFilter: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 201439697} m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} --- !u!4 &201439700 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 201439697} serializedVersion: 2 m_LocalRotation: {x: 0.25000006, y: -0.06698733, z: 0.25000006, w: 0.9330127} m_LocalPosition: {x: 1.6, y: 0, z: -4} m_LocalScale: {x: 0.5, y: 0.5, z: 0.5} m_ConstrainProportionsScale: 1 m_Children: [] m_Father: {fileID: 246925888} m_LocalEulerAnglesHint: {x: 30, y: 0, z: 30} --- !u!114 &201439702 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 201439697} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 99b3dd156c03a80f48913a83361c6d4c, type: 3} m_Name: m_EditorClassIdentifier: Klak.Motion::Klak.Motion.LinearMotion velocity: x: 0 y: 0 z: 0 angularVelocity: x: -90 y: 0 z: 0 --- !u!1 &246925887 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 246925888} - component: {fileID: 246925889} - component: {fileID: 246925890} m_Layer: 0 m_Name: Player (HAP) m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!4 &246925888 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 246925887} serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: - {fileID: 327780397} - {fileID: 201439700} m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &246925889 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 246925887} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: f3775962864755b43a790abf4d029c32, type: 3} m_Name: m_EditorClassIdentifier: Klak.Hap::Klak.Hap.HapPlayer _pathMode: 0 _filePath: Beeple-FiberOptical-Hap.mov _time: 0 _speed: 1 _loop: 1 _targetTexture: {fileID: 8400000, guid: a4f22813f74e84455bd3d9e41d54ced6, type: 2} _targetRenderer: {fileID: 201439698} _targetMaterialProperty: _BaseMap --- !u!114 &246925890 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 246925887} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 538bb1d7707cb473f8493b44c63286cc, type: 3} m_Name: m_EditorClassIdentifier: Assembly-CSharp::SeekBar _ui: {fileID: 1940121732} --- !u!1 &327780394 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 327780397} - component: {fileID: 327780396} - component: {fileID: 327780395} m_Layer: 0 m_Name: Backdrop m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!23 &327780395 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 327780394} m_Enabled: 1 m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 m_RayTracingMode: 2 m_RayTraceProcedural: 0 m_RayTracingAccelStructBuildFlagsOverride: 0 m_RayTracingAccelStructBuildFlags: 1 m_SmallMeshCulling: 1 m_ForceMeshLod: -1 m_MeshLodSelectionBias: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - {fileID: 2100000, guid: e127fa53264f341caab6ab276ff39b77, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 m_StaticBatchRoot: {fileID: 0} m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 m_StitchLightmapSeams: 1 m_SelectedEditorRenderState: 3 m_MinimumChartSize: 4 m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} m_GlobalIlluminationMeshLod: 0 m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 m_MaskInteraction: 0 m_AdditionalVertexStreams: {fileID: 0} --- !u!33 &327780396 MeshFilter: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 327780394} m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} --- !u!4 &327780397 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 327780394} serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 7.5510006, y: 4.247438, z: 4.247438} m_ConstrainProportionsScale: 1 m_Children: [] m_Father: {fileID: 246925888} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &395240107 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 395240109} - component: {fileID: 395240108} m_Layer: 0 m_Name: Player (HAP Q) m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!114 &395240108 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 395240107} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: f3775962864755b43a790abf4d029c32, type: 3} m_Name: m_EditorClassIdentifier: Klak.Hap::Klak.Hap.HapPlayer _pathMode: 0 _filePath: Beeple-Hexxx-HapQ.mov _time: 0 _speed: 1 _loop: 1 _targetTexture: {fileID: 0} _targetRenderer: {fileID: 2137533910} _targetMaterialProperty: _MainTex --- !u!4 &395240109 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 395240107} serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: - {fileID: 2137533909} m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1585188254 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 1585188257} - component: {fileID: 1585188256} - component: {fileID: 1585188255} m_Layer: 0 m_Name: Main Camera m_TagString: MainCamera m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!114 &1585188255 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1585188254} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3} m_Name: m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Runtime::UnityEngine.Rendering.Universal.UniversalAdditionalCameraData m_RenderShadows: 0 m_RequiresDepthTextureOption: 2 m_RequiresOpaqueTextureOption: 2 m_CameraType: 0 m_Cameras: [] m_RendererIndex: -1 m_VolumeLayerMask: serializedVersion: 2 m_Bits: 1 m_VolumeTrigger: {fileID: 0} m_VolumeFrameworkUpdateModeOption: 2 m_RenderPostProcessing: 0 m_Antialiasing: 0 m_AntialiasingQuality: 2 m_StopNaN: 0 m_Dithering: 0 m_ClearDepth: 1 m_AllowXRRendering: 1 m_AllowHDROutput: 1 m_UseScreenCoordOverride: 0 m_ScreenSizeOverride: {x: 0, y: 0, z: 0, w: 0} m_ScreenCoordScaleBias: {x: 0, y: 0, z: 0, w: 0} m_RequiresDepthTexture: 0 m_RequiresColorTexture: 0 m_TaaSettings: m_Quality: 3 m_FrameInfluence: 0.1 m_JitterScale: 1 m_MipBias: 0 m_VarianceClampScale: 0.9 m_ContrastAdaptiveSharpening: 0 m_Version: 2 --- !u!20 &1585188256 Camera: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1585188254} m_Enabled: 1 serializedVersion: 2 m_ClearFlags: 2 m_BackGroundColor: {r: 0.07489321, g: 0.103773594, b: 0.09363752, a: 0} m_projectionMatrixMode: 1 m_GateFitMode: 2 m_FOVAxisMode: 0 m_Iso: 200 m_ShutterSpeed: 0.005 m_Aperture: 16 m_FocusDistance: 10 m_FocalLength: 50 m_BladeCount: 5 m_Curvature: {x: 2, y: 11} m_BarrelClipping: 0.25 m_Anamorphism: 0 m_SensorSize: {x: 36, y: 24} m_LensShift: {x: 0, y: 0} m_NormalizedViewPortRect: serializedVersion: 2 x: 0 y: 0 width: 1 height: 1 near clip plane: 1 far clip plane: 20 field of view: 24 orthographic: 0 orthographic size: 5 m_Depth: -1 m_CullingMask: serializedVersion: 2 m_Bits: 4294967295 m_RenderingPath: -1 m_TargetTexture: {fileID: 0} m_TargetDisplay: 0 m_TargetEye: 3 m_HDR: 1 m_AllowMSAA: 1 m_AllowDynamicResolution: 0 m_ForceIntoRT: 0 m_OcclusionCulling: 0 m_StereoConvergence: 10 m_StereoSeparation: 0.022 --- !u!4 &1585188257 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1585188254} serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: -10} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1940121731 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 1940121733} - component: {fileID: 1940121732} m_Layer: 0 m_Name: UI m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!114 &1940121732 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1940121731} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 19102, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: UnityEngine.dll::UnityEngine.UIElements.UIDocument m_PanelSettings: {fileID: 11400000, guid: 217443c668dc34029b583fa73e5c4dfc, type: 2} m_ParentUI: {fileID: 0} sourceAsset: {fileID: 9197481963319205126, guid: f9d1722b31b6d45a8b38e8167775d54d, type: 3} m_SortingOrder: 0 m_Position: 0 m_WorldSpaceSizeMode: 1 m_WorldSpaceWidth: 1920 m_WorldSpaceHeight: 1080 m_PivotReferenceSize: 0 m_Pivot: 0 m_WorldSpaceCollider: {fileID: 0} --- !u!4 &1940121733 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1940121731} serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &2111630447 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 2111630449} - component: {fileID: 2111630448} m_Layer: 0 m_Name: Scripting Test m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!114 &2111630448 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2111630447} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 2c91f3289fa694fe3ba2e1e6149477b8, type: 3} m_Name: m_EditorClassIdentifier: Assembly-CSharp::ScriptingTest _filename: Beeple-CleanRoom-Hap.mov _destination: {fileID: 8400000, guid: 1bba849debc024a4f8251bd62cceac60, type: 2} _interval: 1 _iteration: 10 --- !u!4 &2111630449 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2111630447} serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &2137533908 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - component: {fileID: 2137533909} - component: {fileID: 2137533911} - component: {fileID: 2137533910} m_Layer: 0 m_Name: Quad m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 --- !u!4 &2137533909 Transform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2137533908} serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: -2.75, y: 0, z: 0} m_LocalScale: {x: 1.7777778, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 395240109} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!23 &2137533910 MeshRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2137533908} m_Enabled: 1 m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 m_RayTracingMode: 2 m_RayTraceProcedural: 0 m_RayTracingAccelStructBuildFlagsOverride: 0 m_RayTracingAccelStructBuildFlags: 1 m_SmallMeshCulling: 1 m_ForceMeshLod: -1 m_MeshLodSelectionBias: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - {fileID: 2100000, guid: f4060b9dfe1254ee9bb9d4fad7ae4ebd, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 m_StaticBatchRoot: {fileID: 0} m_ProbeAnchor: {fileID: 0} m_LightProbeVolumeOverride: {fileID: 0} m_ScaleInLightmap: 1 m_ReceiveGI: 1 m_PreserveUVs: 0 m_IgnoreNormalsForChartDetection: 0 m_ImportantGI: 0 m_StitchLightmapSeams: 1 m_SelectedEditorRenderState: 3 m_MinimumChartSize: 4 m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} m_GlobalIlluminationMeshLod: 0 m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 m_MaskInteraction: 0 m_AdditionalVertexStreams: {fileID: 0} --- !u!33 &2137533911 MeshFilter: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2137533908} m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0} --- !u!1660057539 &9223372036854775807 SceneRoots: m_ObjectHideFlags: 0 m_Roots: - {fileID: 1940121733} - {fileID: 1585188257} - {fileID: 246925888} - {fileID: 28633265} - {fileID: 395240109} - {fileID: 2111630449} ================================================ FILE: Assets/Demo.unity.meta ================================================ fileFormatVersion: 2 guid: 9d1e54e0ecdcb476597522d5c402dcdb DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/StreamingAssets/Tests/Hap/000001.png.meta ================================================ fileFormatVersion: 2 guid: f71b7a4c945804f1ca842cff0c10b46d DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/StreamingAssets/Tests/Hap/000002.png.meta ================================================ fileFormatVersion: 2 guid: ff68e82d75fdf47d8a5ad6b9c5fc99d3 DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/StreamingAssets/Tests/Hap/000003.png.meta ================================================ fileFormatVersion: 2 guid: 0a2d50a92701046c4b2f72fb11f038b8 DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/StreamingAssets/Tests/Hap/000004.png.meta ================================================ fileFormatVersion: 2 guid: ded47b6e71da14e1abb2ea48f2e60375 DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/StreamingAssets/Tests/Hap/000005.png.meta ================================================ fileFormatVersion: 2 guid: 6861db0b44d0d4e78b701b38376b62c3 DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/StreamingAssets/Tests/Hap/TestCards.mov.meta ================================================ fileFormatVersion: 2 guid: e3addd278331242d6b95bb6990e5216f DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/StreamingAssets/Tests/Hap.meta ================================================ fileFormatVersion: 2 guid: c4ca400c14640478aaf9bc0cec9b12c7 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/StreamingAssets/Tests/HapAlpha/000001.png.meta ================================================ fileFormatVersion: 2 guid: 016f08f72c65549f69c9baa63b0e3ed0 DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/StreamingAssets/Tests/HapAlpha/HapAlpha.mov.meta ================================================ fileFormatVersion: 2 guid: 9daa334d7e07e4a7092db093efe0efb8 DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/StreamingAssets/Tests/HapAlpha.meta ================================================ fileFormatVersion: 2 guid: 362186a70fcc54855bcddda1af9f0e90 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/StreamingAssets/Tests/HapQ/000001.png.meta ================================================ fileFormatVersion: 2 guid: 1c1d2bb1e597e432391ee23db4b75d75 DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/StreamingAssets/Tests/HapQ/000002.png.meta ================================================ fileFormatVersion: 2 guid: 469568f79f88b49f593d58b043aab26c DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/StreamingAssets/Tests/HapQ/000003.png.meta ================================================ fileFormatVersion: 2 guid: 6f8073f45b28847209cec3fa77e89e29 DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/StreamingAssets/Tests/HapQ/000004.png.meta ================================================ fileFormatVersion: 2 guid: 554c0617cfb824ddfa28f0ea0ae8869c DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/StreamingAssets/Tests/HapQ/000005.png.meta ================================================ fileFormatVersion: 2 guid: 44d4725e9db92447ca83e803922e54f3 DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/StreamingAssets/Tests/HapQ/TestCards.mov.meta ================================================ fileFormatVersion: 2 guid: 4ab52f88a57a94f50946db395a7e32c6 DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/StreamingAssets/Tests/HapQ.meta ================================================ fileFormatVersion: 2 guid: 0a1a105c4ce2a40b28466bfc9f59f08f folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/StreamingAssets/Tests/RGBCycle/RGBCycle24.mov.meta ================================================ fileFormatVersion: 2 guid: d17c63c5f7293493ca0b65bfc71c7808 DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/StreamingAssets/Tests/RGBCycle/RGBCycle24000-1001.mov.meta ================================================ fileFormatVersion: 2 guid: 9b88a94eb79614c7b8be1396430b7fb9 DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/StreamingAssets/Tests/RGBCycle/RGBCycle25.mov.meta ================================================ fileFormatVersion: 2 guid: 20f5482d0639347b4b0d62b7f553d61a DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/StreamingAssets/Tests/RGBCycle/RGBCycle30.mov.meta ================================================ fileFormatVersion: 2 guid: 04a711eb985854488a36cecaa629492c DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/StreamingAssets/Tests/RGBCycle/RGBCycle30000-1001.mov.meta ================================================ fileFormatVersion: 2 guid: 5b06b773e4b22470b9572d25f575eb91 DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/StreamingAssets/Tests/RGBCycle/RGBCycle50.mov.meta ================================================ fileFormatVersion: 2 guid: 35a0a8d8451fe4dff943ae124f8fc02c DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/StreamingAssets/Tests/RGBCycle/RGBCycle60.mov.meta ================================================ fileFormatVersion: 2 guid: 1103893dcc48f48cbbf5f4681f02ccb4 DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/StreamingAssets/Tests/RGBCycle/RGBCycle60000-1001.mov.meta ================================================ fileFormatVersion: 2 guid: 005f182f1b759493eadafadc1102eea9 DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/StreamingAssets/Tests/RGBCycle.meta ================================================ fileFormatVersion: 2 guid: eaa1315fa4f1141c987260e3f3023bcf folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/StreamingAssets/Tests/日本語/Test.mov.meta ================================================ fileFormatVersion: 2 guid: a32999a4f135840ec9876594a54a0b6a DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/StreamingAssets/Tests/日本語.meta ================================================ fileFormatVersion: 2 guid: 7a8b3546c2bae4b729e75ccdedeab1d8 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/StreamingAssets/Tests.meta ================================================ fileFormatVersion: 2 guid: 026a9869dae424ee8b8609aa087fec1c folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/StreamingAssets.meta ================================================ fileFormatVersion: 2 guid: de35ded2fd399421a88ca973172cc856 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Tests/Runtime/HapAlphaPlaybackTest.cs ================================================ using System.Collections; using System.IO; using Klak.Hap; using NUnit.Framework; using UnityEngine; using UnityEngine.TestTools; public sealed class HapAlphaPlaybackTest { const string MoviePath = "Tests/HapAlpha/HapAlpha.mov"; const string FramesDirName = "Tests/HapAlpha"; const string ExpectedFrame = "000001.png"; const float MatchThreshold = 0.99f; const float ChannelTolerance = 0.08f; const float AlphaTolerance = 0.08f; const int SamplesPerAxis = 64; const bool FlipExpectedY = true; [UnityTest] public IEnumerator HapAlphaMatchesExtractedFrame() { if (!Application.isPlaying) { Assert.Ignore("PlayMode only."); yield break; } var go = new GameObject("HapAlphaTestPlayer"); try { var player = go.AddComponent(); player.Open(MoviePath, HapPlayer.PathMode.StreamingAssets); player.loop = false; player.speed = 0; yield return null; player.UpdateNow(); var pathInfo = $"Resolved path: {player.resolvedFilePath}"; Assert.That(player.isValid, Is.True, pathInfo); Assert.That(player.frameCount, Is.GreaterThan(0), pathInfo); Assert.That(player.frameWidth, Is.GreaterThan(0), pathInfo); Assert.That(player.frameHeight, Is.GreaterThan(0), pathInfo); Assert.That(player.texture, Is.Not.Null, pathInfo); var duration = (float)player.streamDuration; var dt = duration / player.frameCount; player.time = dt * 0.1f; player.UpdateNow(); yield return null; yield return null; var expected = LoadExpectedTexture(ExpectedFrame); try { var ratio = ComputeMatchRatio(player.texture, expected); var message = $"{pathInfo} Expected:{ExpectedFrame} Match:{ratio:0.000}"; Assert.That(ratio, Is.GreaterThanOrEqualTo(MatchThreshold), message); } finally { Object.Destroy(expected); } } finally { Object.Destroy(go); } } static Texture2D LoadExpectedTexture(string fileName) { var path = Path.Combine(Application.streamingAssetsPath, FramesDirName, fileName); var data = File.ReadAllBytes(path); var tex = new Texture2D(2, 2, TextureFormat.RGBA32, false); tex.LoadImage(data); return tex; } static float ComputeMatchRatio(Texture2D actual, Texture2D expected) { var width = Mathf.Min(actual.width, expected.width); var height = Mathf.Min(actual.height, expected.height); var stepX = Mathf.Max(1, width / SamplesPerAxis); var stepY = Mathf.Max(1, height / SamplesPerAxis); var match = 0; var total = 0; for (var y = stepY / 2; y < height; y += stepY) for (var x = stepX / 2; x < width; x += stepX) { var a = actual.GetPixel(x, y); var ey = FlipExpectedY ? height - 1 - y : y; var e = expected.GetPixel(x, ey); if (IsMatch(a, e)) match++; total++; } return total > 0 ? (float)match / total : 0; } static bool IsMatch(Color actual, Color expected) => Mathf.Abs(actual.r - expected.r) <= ChannelTolerance && Mathf.Abs(actual.g - expected.g) <= ChannelTolerance && Mathf.Abs(actual.b - expected.b) <= ChannelTolerance && Mathf.Abs(actual.a - expected.a) <= AlphaTolerance; } ================================================ FILE: Assets/Tests/Runtime/HapAlphaPlaybackTest.cs.meta ================================================ fileFormatVersion: 2 guid: 836849b85f39e4f2eb69dc82dd0bf0fe ================================================ FILE: Assets/Tests/Runtime/JapanesePathPlaybackTest.cs ================================================ using System.Collections; using Klak.Hap; using NUnit.Framework; using UnityEngine; using UnityEngine.TestTools; public sealed class JapanesePathPlaybackTest { const string TestPath = "Tests/日本語/Test.mov"; [UnityTest] public IEnumerator CanPlayHapInJapanesePath() { if (!Application.isPlaying) { Assert.Ignore("PlayMode only."); yield break; } var go = new GameObject("HapTestPlayer"); try { var player = go.AddComponent(); player.Open(TestPath, HapPlayer.PathMode.StreamingAssets); for (var i = 0; i < 5; i++) { yield return null; player.UpdateNow(); } var pathInfo = $"Resolved path: {player.resolvedFilePath}"; Assert.That(player.isValid, Is.True, pathInfo); Assert.That(player.frameCount, Is.GreaterThan(0), pathInfo); Assert.That(player.frameWidth, Is.GreaterThan(0), pathInfo); Assert.That(player.frameHeight, Is.GreaterThan(0), pathInfo); Assert.That(player.texture, Is.Not.Null, pathInfo); Assert.That(player.codecType, Is.Not.EqualTo(CodecType.Unsupported), pathInfo); } finally { Object.Destroy(go); } } } ================================================ FILE: Assets/Tests/Runtime/JapanesePathPlaybackTest.cs.meta ================================================ fileFormatVersion: 2 guid: 0397f44a895774978874377ddf34f82c ================================================ FILE: Assets/Tests/Runtime/Klak.Hap.Runtime.Tests.asmdef ================================================ { "name": "Klak.Hap.Runtime.Tests", "rootNamespace": "", "references": [ "Klak.Hap", "UnityEngine.TestRunner" ], "includePlatforms": [], "excludePlatforms": [], "allowUnsafeCode": false, "overrideReferences": false, "precompiledReferences": [], "autoReferenced": true, "defineConstraints": [], "versionDefines": [], "noEngineReferences": false } ================================================ FILE: Assets/Tests/Runtime/Klak.Hap.Runtime.Tests.asmdef.meta ================================================ fileFormatVersion: 2 guid: 1c2d58c8d70c843c7a4747274235802b AssemblyDefinitionImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Tests/Runtime/RgbCycleFrameMappingTest.cs ================================================ using System.Collections; using Klak.Hap; using NUnit.Framework; using UnityEngine; using UnityEngine.TestTools; public sealed class RgbCycleFrameMappingTest { static readonly string[] TestPaths = { "Tests/RGBCycle/RGBCycle24.mov", "Tests/RGBCycle/RGBCycle24000-1001.mov", "Tests/RGBCycle/RGBCycle25.mov", "Tests/RGBCycle/RGBCycle30.mov", "Tests/RGBCycle/RGBCycle30000-1001.mov", "Tests/RGBCycle/RGBCycle50.mov", "Tests/RGBCycle/RGBCycle60.mov", "Tests/RGBCycle/RGBCycle60000-1001.mov" }; enum RgbExpected { Red, Green, Blue } [UnityTest] public IEnumerator RgbCycleFrameMappingIsCorrect() { if (!Application.isPlaying) { Assert.Ignore("PlayMode only."); yield break; } foreach (var path in TestPaths) foreach (var step in VerifyFile(path)) yield return step; } IEnumerable VerifyFile(string path) { var go = new GameObject("HapTestPlayer"); try { var player = go.AddComponent(); player.Open(path, HapPlayer.PathMode.StreamingAssets); player.loop = false; player.speed = 0; yield return null; player.UpdateNow(); var pathInfo = $"Resolved path: {player.resolvedFilePath}"; Assert.That(player.isValid, Is.True, pathInfo); Assert.That(player.frameCount, Is.GreaterThan(0), pathInfo); Assert.That(player.frameWidth, Is.GreaterThan(0), pathInfo); Assert.That(player.frameHeight, Is.GreaterThan(0), pathInfo); Assert.That(player.texture, Is.Not.Null, pathInfo); var duration = (float)player.streamDuration; var frameCount = player.frameCount; var dt = duration / frameCount; var maxFrames = Mathf.Min(frameCount, Mathf.FloorToInt(0.5f / dt) + 1); var cx = player.frameWidth / 2; var cy = player.frameHeight / 2; for (var i = 0; i < maxFrames; i++) { player.time = i * dt + dt * 0.1f; player.UpdateNow(); yield return null; yield return null; var actual = player.texture.GetPixel(cx, cy); var expected = ExpectedForFrame(i); AssertRgbColor(actual, expected, pathInfo, i); yield return null; yield return null; } } finally { Object.Destroy(go); } } static RgbExpected ExpectedForFrame(int frameIndex) => (RgbExpected)(frameIndex % 3); static void AssertRgbColor(Color actual, RgbExpected expected, string pathInfo, int frameIndex) { var message = $"{pathInfo} Frame: {frameIndex} Color: {actual}"; if (expected == RgbExpected.Red) { Assert.That(actual.r, Is.GreaterThan(0.8f), message); Assert.That(actual.g, Is.LessThan(0.2f), message); Assert.That(actual.b, Is.LessThan(0.2f), message); } else if (expected == RgbExpected.Green) { Assert.That(actual.g, Is.GreaterThan(0.8f), message); Assert.That(actual.r, Is.LessThan(0.2f), message); Assert.That(actual.b, Is.LessThan(0.2f), message); } else { Assert.That(actual.b, Is.GreaterThan(0.8f), message); Assert.That(actual.r, Is.LessThan(0.2f), message); Assert.That(actual.g, Is.LessThan(0.2f), message); } } } ================================================ FILE: Assets/Tests/Runtime/RgbCycleFrameMappingTest.cs.meta ================================================ fileFormatVersion: 2 guid: e48e4d437ac8346139c9ba9f277e7beb ================================================ FILE: Assets/Tests/Runtime/TestCardsPlaybackTest.cs ================================================ using System.Collections; using System.IO; using Klak.Hap; using NUnit.Framework; using UnityEngine; using UnityEngine.TestTools; public sealed class TestCardsPlaybackTest { const string HapMoviePath = "Tests/Hap/TestCards.mov"; const string HapFramesDirName = "Tests/Hap"; const string HapQMoviePath = "Tests/HapQ/TestCards.mov"; const string HapQFramesDirName = "Tests/HapQ"; const float MatchThreshold = 0.99f; const float ChannelTolerance = 0.08f; static readonly string[] FramePngs = { "000001.png", "000002.png", "000003.png", "000004.png", "000005.png" }; [UnityTest] public IEnumerator TestCardsMatchPngFrames() { if (!Application.isPlaying) { Assert.Ignore("PlayMode only."); yield break; } foreach (var step in VerifySet("HapTestPlayer", HapMoviePath, HapFramesDirName)) yield return step; foreach (var step in VerifySet("HapQTestPlayer", HapQMoviePath, HapQFramesDirName)) yield return step; } IEnumerable VerifySet(string objectName, string moviePath, string framesDirName) { var go = new GameObject(objectName); RenderTexture targetTexture = null; Texture2D readbackTexture = null; try { var player = go.AddComponent(); player.Open(moviePath, HapPlayer.PathMode.StreamingAssets); player.loop = false; player.speed = 0; yield return null; player.UpdateNow(); var pathInfo = $"Resolved path: {player.resolvedFilePath}"; Assert.That(player.isValid, Is.True, pathInfo); Assert.That(player.frameCount, Is.GreaterThanOrEqualTo(FramePngs.Length), pathInfo); Assert.That(player.frameWidth, Is.GreaterThan(0), pathInfo); Assert.That(player.frameHeight, Is.GreaterThan(0), pathInfo); Assert.That(player.texture, Is.Not.Null, pathInfo); targetTexture = new RenderTexture( player.frameWidth, player.frameHeight, 0, RenderTextureFormat.ARGB32 ); targetTexture.wrapMode = TextureWrapMode.Clamp; player.targetTexture = targetTexture; readbackTexture = new Texture2D( player.frameWidth, player.frameHeight, TextureFormat.RGBA32, false ); var duration = (float)player.streamDuration; var frameCount = player.frameCount; var dt = duration / frameCount; for (var i = 0; i < FramePngs.Length; i++) { player.time = i * dt + dt * 0.1f; player.UpdateNow(); yield return null; yield return null; ReadRenderTexture(targetTexture, readbackTexture); var actualTexture = readbackTexture; var expected = LoadExpectedTexture(framesDirName, FramePngs[i]); try { var ratio = ComputeMatchRatio(actualTexture, expected); var message = $"{pathInfo} Frame:{i} Expected:{FramePngs[i]} Match:{ratio:0.000}"; Assert.That(ratio, Is.GreaterThanOrEqualTo(MatchThreshold), message); } finally { Object.Destroy(expected); } yield return null; yield return null; } } finally { if (targetTexture != null) targetTexture.Release(); Object.Destroy(targetTexture); Object.Destroy(readbackTexture); Object.Destroy(go); } } static Texture2D LoadExpectedTexture(string framesDirName, string fileName) { var path = Path.Combine(Application.streamingAssetsPath, framesDirName, fileName); var data = File.ReadAllBytes(path); var tex = new Texture2D(2, 2, TextureFormat.RGBA32, false); tex.LoadImage(data); return tex; } static float ComputeMatchRatio(Texture2D actual, Texture2D expected) { var width = Mathf.Min(actual.width, expected.width); var height = Mathf.Min(actual.height, expected.height); var match = 0; var total = width * height; for (var y = 0; y < height; y++) for (var x = 0; x < width; x++) { var a = actual.GetPixel(x, y); var e = expected.GetPixel(x, y); if (IsMatch(a, e)) match++; } return total > 0 ? (float)match / total : 0; } static bool IsMatch(Color actual, Color expected) => Mathf.Abs(actual.r - expected.r) <= ChannelTolerance && Mathf.Abs(actual.g - expected.g) <= ChannelTolerance && Mathf.Abs(actual.b - expected.b) <= ChannelTolerance; static void ReadRenderTexture(RenderTexture source, Texture2D destination) { var previous = RenderTexture.active; RenderTexture.active = source; destination.ReadPixels(new Rect(0, 0, source.width, source.height), 0, 0); destination.Apply(); RenderTexture.active = previous; } } ================================================ FILE: Assets/Tests/Runtime/TestCardsPlaybackTest.cs.meta ================================================ fileFormatVersion: 2 guid: 501079e0a007c4c9686d841e0bcb0b60 ================================================ FILE: Assets/Tests/Runtime.meta ================================================ fileFormatVersion: 2 guid: 6f2caeec0887b4dd5a03360e87b42af7 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Assets/Tests.meta ================================================ fileFormatVersion: 2 guid: c9392bf0e434b4e378744f7ed2695485 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: CHANGELOG.md ================================================ # Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [1.0.0] - 2026-02-05 ### Added - Added a Unity 6.3 demo scene updated for URP and UI Toolkit. - Added runtime tests and a test data downloader for Hap, HapQ, and HapAlpha assets. ### Changed - Updated HAP and Snappy source code and refreshed native plugin binaries. - Migrated Windows builds to MinGW and refactored the native build pipelines and Makefiles. ### Fixed - Fixed a Windows crash when paths contain Japanese characters. (PR #49) - Fixed obsolete ShaderUtil API usage in the editor code. ## [0.1.20] - 2021-12-03 ### Added - Added `UpdateNow` for custom Timeline integration. ## [0.1.19] - 2021-06-05 ### Added - Added arm64 binary to the macOS plugin. - Added no-delay mode. - Added decoder thread control improvement. ### Changed - Updated for Unity 2020.3. - Moved package contexts into the Packages directory. ### Fixed - Fixed rounding error issue. - Fixed compilation error on Unity 2019.4. ================================================ FILE: LICENSE ================================================ License summary: KlakHAP - MIT license HAP codec - FreeBSD license Snappy - BSD 3-clause license MP4 demuxer - CC0 (public domain) ------------------------------------------------------------------------------- KlakHAP https://github.com/keijiro/KlakHap ------------------------------------------------------------------------------- Copyright (c) 2019 Unity Technologies Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------- HAP https://github.com/Vidvox/hap ------------------------------------------------------------------------------- Copyright (c) 2012-2013, Tom Butterworth and Vidvox LLC. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ------------------------------------------------------------------------------- Snappy https://github.com/google/snappy ------------------------------------------------------------------------------- Copyright 2011, Google Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ------------------------------------------------------------------------------- Minimalistic MP4 muxer & demuxer https://github.com/aspt/mp4 ------------------------------------------------------------------------------- CC0 1.0 Universal Statement of Purpose The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; ii. moral rights retained by the original author(s) and/or performer(s); iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; v. rights protecting the extraction, dissemination, use and reuse of data in a Work; vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 4. Limitations and Disclaimers. a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. For more information, please see ================================================ FILE: Packages/jp.keijiro.klak.hap/CHANGELOG.md ================================================ # Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [1.0.0] - 2026-02-05 ### Added - Added a Unity 6.3 demo scene updated for URP and UI Toolkit. - Added runtime tests and a test data downloader for Hap, HapQ, and HapAlpha assets. ### Changed - Updated HAP and Snappy source code and refreshed native plugin binaries. - Migrated Windows builds to MinGW and refactored the native build pipelines and Makefiles. ### Fixed - Fixed a Windows crash when paths contain Japanese characters. (PR #49) - Fixed obsolete ShaderUtil API usage in the editor code. ## [0.1.20] - 2021-12-03 ### Added - Added `UpdateNow` for custom Timeline integration. ## [0.1.19] - 2021-06-05 ### Added - Added arm64 binary to the macOS plugin. - Added no-delay mode. - Added decoder thread control improvement. ### Changed - Updated for Unity 2020.3. - Moved package contexts into the Packages directory. ### Fixed - Fixed rounding error issue. - Fixed compilation error on Unity 2019.4. ================================================ FILE: Packages/jp.keijiro.klak.hap/CHANGELOG.md.meta ================================================ fileFormatVersion: 2 guid: 1f2cacd9a85145aca9f81904d75ae53b TextScriptImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Packages/jp.keijiro.klak.hap/Editor/HapPlayerEditor.cs ================================================ using UnityEngine; using UnityEditor; namespace Klak.Hap { [CanEditMultipleObjects] [CustomEditor(typeof(HapPlayer))] sealed class HapPlayerEditor : Editor { SerializedProperty _filePath; SerializedProperty _pathMode; SerializedProperty _time; SerializedProperty _speed; SerializedProperty _loop; SerializedProperty _targetTexture; SerializedProperty _targetRenderer; SerializedProperty _targetMaterialProperty; static class Labels { public static readonly GUIContent Property = new GUIContent("Property"); public static readonly GUIContent Select = new GUIContent("Select"); } string _sourceInfo; void ShowSourceInfo(HapPlayer player) { if (!player.enabled || !player.gameObject.activeInHierarchy) return; if (!player.isValid) { EditorGUILayout.HelpBox( "Failed to open file. " + "Please specify a valid HAP-encoded .mov file.", MessageType.Warning ); return; } if (_sourceInfo == null) _sourceInfo = string.Format( "Codec: {0}\n" + "Frame dimensions: {1} x {2}\n" + "Stream duration: {3:0.00}\n" + "Frame rate: {4:0.00}", player.codecType, player.frameWidth, player.frameHeight, player.streamDuration, player.frameCount / player.streamDuration ); EditorGUILayout.HelpBox(_sourceInfo, MessageType.None); } void OnEnable() { _filePath = serializedObject.FindProperty("_filePath"); _pathMode = serializedObject.FindProperty("_pathMode"); _time = serializedObject.FindProperty("_time"); _speed = serializedObject.FindProperty("_speed"); _loop = serializedObject.FindProperty("_loop"); _targetTexture = serializedObject.FindProperty("_targetTexture"); _targetRenderer = serializedObject.FindProperty("_targetRenderer"); _targetMaterialProperty = serializedObject.FindProperty("_targetMaterialProperty"); } public override void OnInspectorGUI() { var reload = false; serializedObject.Update(); // Source infomation if (!_filePath.hasMultipleDifferentValues && !_pathMode.hasMultipleDifferentValues && !string.IsNullOrEmpty(_filePath.stringValue)) { ShowSourceInfo((HapPlayer)target); } // Source file EditorGUI.BeginChangeCheck(); EditorGUILayout.DelayedTextField(_filePath); EditorGUILayout.PropertyField(_pathMode); reload = EditorGUI.EndChangeCheck(); // Playback control EditorGUILayout.PropertyField(_time); EditorGUILayout.PropertyField(_speed); EditorGUILayout.PropertyField(_loop); // Target texture/renderer EditorGUILayout.PropertyField(_targetTexture); EditorGUILayout.PropertyField(_targetRenderer); EditorGUI.indentLevel++; if (_targetRenderer.hasMultipleDifferentValues) { // Multiple renderers selected: Show a simple text field. EditorGUILayout.PropertyField(_targetMaterialProperty, Labels.Property); } else if (_targetRenderer.objectReferenceValue != null) { // Single renderer: Show the material property selection dropdown. MaterialPropertySelector.DropdownList(_targetRenderer, _targetMaterialProperty); } EditorGUI.indentLevel--; serializedObject.ApplyModifiedProperties(); if (reload) { // This is a little bit scary hack but we can force a HapPlayer // to reload a given video file by invoking OnDestroy. foreach (HapPlayer hp in targets) { hp.SendMessage("OnDestroy"); hp.SendMessage("LateUpdate"); } // Also the source information string should be refreshed. _sourceInfo = null; } } } } ================================================ FILE: Packages/jp.keijiro.klak.hap/Editor/HapPlayerEditor.cs.meta ================================================ fileFormatVersion: 2 guid: 5afaabe3aee5cdb46a3846f74cc3a8a7 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Packages/jp.keijiro.klak.hap/Editor/Klak.Hap.Editor.asmdef ================================================ { "name": "Klak.Hap.Editor", "references": [ "Klak.Hap" ], "optionalUnityReferences": [], "includePlatforms": [ "Editor" ], "excludePlatforms": [], "allowUnsafeCode": false, "overrideReferences": false, "precompiledReferences": [], "autoReferenced": true, "defineConstraints": [] } ================================================ FILE: Packages/jp.keijiro.klak.hap/Editor/Klak.Hap.Editor.asmdef.meta ================================================ fileFormatVersion: 2 guid: 59c1346744fe3194291b617e412c56c4 AssemblyDefinitionImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Packages/jp.keijiro.klak.hap/Editor/MaterialPropertySelector.cs ================================================ using UnityEngine; using UnityEngine.Rendering; using UnityEditor; using System; using System.Collections.Generic; namespace Klak.Hap { static class MaterialPropertySelector { #region Public method // Material property drop-down list public static void DropdownList( SerializedProperty rendererProperty, SerializedProperty materialProperty ) { // Try retrieving the target shader. var shader = RetrieveTargetShader(rendererProperty); // Abandon the current value if it failed to get a shader. if (shader == null) { materialProperty.stringValue = ""; return; } // Cache property names found in the target shader. CachePropertyNames(shader); // Abandon the current value if there is no property candidate. if (_propertyNames.Length == 0) { materialProperty.stringValue = ""; return; } // Show the dropdown list. var index = Array.IndexOf(_propertyNames, materialProperty.stringValue); var newIndex = EditorGUILayout.Popup("Property", index, _propertyNames); // Update the serialized property if the selection was changed. if (index != newIndex) materialProperty.stringValue = _propertyNames[newIndex]; } #endregion #region Private members static string[] _propertyNames; // Property name list static Shader _cachedShader; // Shader used to cache the name list // Retrieve a shader from a given renderer. static Shader RetrieveTargetShader(SerializedProperty rendererProperty) { var renderer = rendererProperty.objectReferenceValue as Renderer; if (renderer == null) return null; var material = renderer.sharedMaterial; if (material == null) return null; return material.shader; } // Cache property names provided within a specified shader. static void CachePropertyNames(Shader shader) { // Exit early when the shader is same to the cached one. if (shader == _cachedShader) return; var temp = new List(); var count = shader.GetPropertyCount(); for (var i = 0; i < count; i++) { var propType = shader.GetPropertyType(i); if (propType == ShaderPropertyType.Texture) temp.Add(shader.GetPropertyName(i)); } _propertyNames = temp.ToArray(); _cachedShader = shader; } #endregion } } ================================================ FILE: Packages/jp.keijiro.klak.hap/Editor/MaterialPropertySelector.cs.meta ================================================ fileFormatVersion: 2 guid: 8ab866082e30404428d5298d084cc13a MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Packages/jp.keijiro.klak.hap/Editor.meta ================================================ fileFormatVersion: 2 guid: 2a6f023ffc242a743a38d13e6cae3b66 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Packages/jp.keijiro.klak.hap/LICENSE ================================================ License summary: KlakHAP - MIT license HAP codec - FreeBSD license Snappy - BSD 3-clause license MP4 demuxer - CC0 (public domain) ------------------------------------------------------------------------------- KlakHAP https://github.com/keijiro/KlakHap ------------------------------------------------------------------------------- Copyright (c) 2019 Unity Technologies Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------- HAP https://github.com/Vidvox/hap ------------------------------------------------------------------------------- Copyright (c) 2012-2013, Tom Butterworth and Vidvox LLC. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ------------------------------------------------------------------------------- Snappy https://github.com/google/snappy ------------------------------------------------------------------------------- Copyright 2011, Google Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ------------------------------------------------------------------------------- Minimalistic MP4 muxer & demuxer https://github.com/aspt/mp4 ------------------------------------------------------------------------------- CC0 1.0 Universal Statement of Purpose The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; ii. moral rights retained by the original author(s) and/or performer(s); iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; v. rights protecting the extraction, dissemination, use and reuse of data in a Work; vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 4. Limitations and Disclaimers. a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. For more information, please see ================================================ FILE: Packages/jp.keijiro.klak.hap/LICENSE.meta ================================================ fileFormatVersion: 2 guid: ee05d860d6934052af1f4d5fb541a384 TextScriptImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Packages/jp.keijiro.klak.hap/Plugin/Linux/libKlakHap.so.meta ================================================ fileFormatVersion: 2 guid: aefd4aee030ec9a56930540080f31258 PluginImporter: externalObjects: {} serializedVersion: 2 iconMap: {} executionOrder: {} defineConstraints: [] isPreloaded: 0 isOverridable: 0 isExplicitlyReferenced: 0 platformData: - first: '': Any second: enabled: 0 settings: Exclude Editor: 0 Exclude Linux: 1 Exclude Linux64: 0 Exclude LinuxUniversal: 0 Exclude OSXUniversal: 1 Exclude Win: 0 Exclude Win64: 0 - first: Any: second: enabled: 0 settings: {} - first: Editor: Editor second: enabled: 1 settings: CPU: x86_64 DefaultValueInitialized: true OS: Linux - first: Facebook: Win second: enabled: 0 settings: CPU: AnyCPU - first: Facebook: Win64 second: enabled: 0 settings: CPU: AnyCPU - first: Standalone: Linux second: enabled: 0 settings: CPU: None - first: Standalone: Linux64 second: enabled: 1 settings: CPU: x86_64 - first: Standalone: LinuxUniversal second: enabled: 1 settings: CPU: x86_64 - first: Standalone: OSXUniversal second: enabled: 0 settings: CPU: None - first: Standalone: Win second: enabled: 1 settings: CPU: AnyCPU - first: Standalone: Win64 second: enabled: 1 settings: CPU: AnyCPU userData: assetBundleName: assetBundleVariant: ================================================ FILE: Packages/jp.keijiro.klak.hap/Plugin/Linux.meta ================================================ fileFormatVersion: 2 guid: 18114d790aea9f97e9b9f56ab458bc9d folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Packages/jp.keijiro.klak.hap/Plugin/MacOS/KlakHap.bundle.meta ================================================ fileFormatVersion: 2 guid: 68eb9c44d2cd647b8a246c85c2516a6c PluginImporter: externalObjects: {} serializedVersion: 2 iconMap: {} executionOrder: {} defineConstraints: [] isPreloaded: 0 isOverridable: 1 isExplicitlyReferenced: 0 validateReferences: 1 platformData: - first: : Any second: enabled: 0 settings: Exclude Editor: 0 Exclude Linux64: 1 Exclude OSXUniversal: 0 Exclude Win: 1 Exclude Win64: 1 Exclude iOS: 1 - first: Any: second: enabled: 0 settings: {} - first: Editor: Editor second: enabled: 1 settings: CPU: AnyCPU DefaultValueInitialized: true OS: OSX - first: Standalone: Linux64 second: enabled: 0 settings: CPU: None - first: Standalone: OSXUniversal second: enabled: 1 settings: CPU: AnyCPU - first: Standalone: Win second: enabled: 0 settings: CPU: x86 - first: Standalone: Win64 second: enabled: 0 settings: CPU: x86_64 - first: iPhone: iOS second: enabled: 0 settings: AddToEmbeddedBinaries: false CPU: AnyCPU CompileFlags: FrameworkDependencies: userData: assetBundleName: assetBundleVariant: ================================================ FILE: Packages/jp.keijiro.klak.hap/Plugin/MacOS.meta ================================================ fileFormatVersion: 2 guid: b3fc36a147f3a4fe19b3fe7371cfe554 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Packages/jp.keijiro.klak.hap/Plugin/Windows/KlakHap.dll.meta ================================================ fileFormatVersion: 2 guid: 3ab59e419166f214abf75fa29aac993e PluginImporter: externalObjects: {} serializedVersion: 2 iconMap: {} executionOrder: {} defineConstraints: [] isPreloaded: 0 isOverridable: 0 isExplicitlyReferenced: 0 platformData: - first: '': Any second: enabled: 0 settings: Exclude Editor: 0 Exclude Linux: 0 Exclude Linux64: 0 Exclude LinuxUniversal: 0 Exclude OSXUniversal: 0 Exclude Win: 1 Exclude Win64: 0 - first: Any: second: enabled: 1 settings: {} - first: Editor: Editor second: enabled: 1 settings: CPU: x86_64 DefaultValueInitialized: true OS: Windows - first: Facebook: Win second: enabled: 0 settings: CPU: None - first: Facebook: Win64 second: enabled: 0 settings: CPU: AnyCPU - first: Standalone: Linux second: enabled: 1 settings: CPU: x86 - first: Standalone: Linux64 second: enabled: 1 settings: CPU: x86_64 - first: Standalone: LinuxUniversal second: enabled: 1 settings: CPU: AnyCPU - first: Standalone: OSXUniversal second: enabled: 1 settings: CPU: AnyCPU - first: Standalone: Win second: enabled: 0 settings: CPU: None - first: Standalone: Win64 second: enabled: 1 settings: CPU: AnyCPU userData: assetBundleName: assetBundleVariant: ================================================ FILE: Packages/jp.keijiro.klak.hap/Plugin/Windows.meta ================================================ fileFormatVersion: 2 guid: dc34545ab496569488b8cd56973907d2 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Packages/jp.keijiro.klak.hap/Plugin.meta ================================================ fileFormatVersion: 2 guid: 39f09b654953c8e45bc1d12d19cceaef folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Packages/jp.keijiro.klak.hap/README.md ================================================ # KlakHap ![GIF](https://i.imgur.com/exuJAIA.gif) **KlakHap** is a Unity plugin for playing back video streams encoded with the [HAP video codecs]. HAP is a fast and high-quality video codec often used in real-time interactive applications. From the HAP Codecs website: > The HAP codecs are designed to fit the needs of a variety of real-time video > workflows where ultra high resolution video is needed such as live event > production, set design, 360 video for gaming, projection mapping and creative > coding. KlakHap provides decoded frames as textures that you can use anywhere in Unity's rendering pipeline: apply them to a material, present full-screen video, animate a UI element, and more. Thanks to the efficient design and implementation of the HAP codecs, it can adjust playback time and speed dynamically without hiccups. [HAP video codecs]: https://hap.video/ # System requirements - Unity 2022.3 or later Currently, KlakHap supports only 64-bit desktop platforms (Windows, macOS, and Linux). # Supported formats KlakHap supports **HAP**, **HAP Alpha**, and **HAP Q**. **HAP Q Alpha** is not supported. KlakHap only supports the QuickTime File Format as a container, i.e., `.mov` files. # How to install Install the KlakHap package (`jp.keijiro.klak.hap`) from the "Keijiro" scoped registry in Package Manager. Follow [these instructions] to add the registry to your project. [these instructions]: https://gist.github.com/keijiro/f8c7e8ff29bfe63d86b888901b82644c # How to specify a video file There are two ways to specify a video file in the plugin: - **Streaming Assets Mode**: Put a video file in the [Streaming Assets] directory and specify its file name. - **Local File System Mode**: Put a video file somewhere in local drive and specify its full path. The former method is recommended when the video file ships with the application. The latter method is useful when you need to play external content. [Streaming Assets]: https://docs.unity3d.com/Manual/StreamingAssets.html # Hap Player component ![Inspector](https://i.imgur.com/pIACL4W.png) **File Path** and **Path Mode** specify the source video file. See the previous section for details. **Time**, **Speed** and **Loop** are used to set the initial playback state. You can also change these values during playback. **Target Texture** stores decoded frames in a render texture. Note that this allocates a small amount of GPU time for data transfer. **Target Renderer** applies the decoded texture to a specific material property. Although this is the most performant way to render video frames, it requires a few extra steps to render correctly. Keep the following points in mind: - UV coordinate incompatibility: Decoded textures are upside down due to differences in UV coordinate conventions between Unity and HAP. You can fix this using a vertically inverted texture scale/offset. You can also use the `Klak/Hap` shader for this purpose. - Color space conversion for HAP Q: [YCoCg conversion] must be added to a shader when using HAP Q. You can also use the `Klak/HAP Q` shader for this purpose. [YCoCg conversion]: https://gist.github.com/dlublin/90f879cfe027ebf5792bdadf2c911bb5 # How to control playback `HapPlayer` provides only a few properties and methods for controlling playback. This is an intentional design choice; I avoid ambiguous methods like `Play`, `Stop`, and `Pause`. Use the basic properties and methods instead. - To jump to a specific point: Assign a time in seconds to `time`. - To jump to a specific frame: Calculate the time in seconds using `frameCount` and `streamDuration`, then assign it to `time`. - To reverse the playback direction: Assign a negative value to `speed`. - To pause: Assign `0` to `speed`. - To resume: Assign `1` to `speed`. - To stop: Assign `false` to `enabled`. - To close the video file: Destroy the `HapPlayer` component. - To open another video file: Call `AddComponent`, then call `Open`. # Timeline support ![GIF](https://i.imgur.com/efrvvye.gif) The HAP Player component implements the [ITimeControl] interface, which allows it to control playback time from a Control Track in a [Timeline]. You can create a control track by dragging and dropping a HAP Player game object into the Timeline Editor, or manually create a Control Track/Clip and set the source game object. [ITimeControl]: https://docs.unity3d.com/ScriptReference/Timeline.ITimeControl.html [Timeline]: https://docs.unity3d.com/Manual/TimelineSection.html # Platform differences (internal latency) On Windows, KlakHap uses the [Custom Texture Update] feature to hide the synchronization point in the background thread. It guarantees exact-frame playback with minimal load on the main thread. On macOS and Linux, the Custom Texture Update feature is unavailable for this purpose[^1]. Instead, KlakHap delays synchronization to the next frame to avoid main thread stalls. In other words, it guarantees exact-frame playback but adds a single-frame latency. You can turn off this behavior by adding `HAP_NO_DELAY` to the [Scripting Define Symbols] in the project settings. This stalls the main thread for every frame decoding. It significantly slows down the application but is useful when exact frame matching is essential (e.g., [volumetric video playback] with Alembic animation). [Custom Texture Update]: https://github.com/keijiro/TextureUpdateExample [Scripting Define Symbols]: https://docs.unity3d.com/Manual/CustomScriptingSymbols.html [volumetric video playback]: https://github.com/keijiro/Abcvfx [^1]: The Custom Texture Update feature is available even on macOS/Linux but doesn't support compressed texture formats, which are essential for HAP decoding. ================================================ FILE: Packages/jp.keijiro.klak.hap/README.md.meta ================================================ fileFormatVersion: 2 guid: 2a0b411fba8e4ef48cbb46596fdc2de8 TextScriptImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Packages/jp.keijiro.klak.hap/Resources/Hap1.shader ================================================ Shader "Klak/HAP" { Properties { _MainTex("Texture", 2D) = "white" {} } CGINCLUDE #include "UnityCG.cginc" struct Attributes { float4 position : POSITION; float2 texcoord : TEXCOORD; UNITY_VERTEX_INPUT_INSTANCE_ID }; struct Varyings { float4 position : SV_Position; float2 texcoord : TEXCOORD; }; sampler2D _MainTex; float4 _MainTex_ST; Varyings Vertex(Attributes input) { UNITY_SETUP_INSTANCE_ID(input); Varyings output; output.position = UnityObjectToClipPos(input.position); output.texcoord = TRANSFORM_TEX(input.texcoord, _MainTex); output.texcoord.y = 1 - output.texcoord.y; return output; } fixed4 Fragment(Varyings input) : SV_Target { return tex2D(_MainTex, input.texcoord); } ENDCG SubShader { Tags { "RenderType"="Opaque" } Pass { CGPROGRAM #pragma vertex Vertex #pragma fragment Fragment #pragma multi_compile_instancing ENDCG } } } ================================================ FILE: Packages/jp.keijiro.klak.hap/Resources/Hap1.shader.meta ================================================ fileFormatVersion: 2 guid: e99f77a91d9883a4b890a1fc833b7158 ShaderImporter: externalObjects: {} defaultTextures: [] nonModifiableTextures: [] userData: assetBundleName: assetBundleVariant: ================================================ FILE: Packages/jp.keijiro.klak.hap/Resources/Hap5.shader ================================================ Shader "Klak/HAP Alpha" { Properties { _MainTex("Texture", 2D) = "white" {} } CGINCLUDE #include "UnityCG.cginc" struct Attributes { float4 position : POSITION; float2 texcoord : TEXCOORD; UNITY_VERTEX_INPUT_INSTANCE_ID }; struct Varyings { float4 position : SV_Position; float2 texcoord : TEXCOORD; }; sampler2D _MainTex; float4 _MainTex_ST; Varyings Vertex(Attributes input) { UNITY_SETUP_INSTANCE_ID(input); Varyings output; output.position = UnityObjectToClipPos(input.position); output.texcoord = TRANSFORM_TEX(input.texcoord, _MainTex); output.texcoord.y = 1 - output.texcoord.y; return output; } fixed4 Fragment(Varyings input) : SV_Target { return tex2D(_MainTex, input.texcoord); } ENDCG SubShader { Tags { "RenderType"="Transparent" "Queue"="Transparent" } Pass { ZWrite Off Blend SrcAlpha OneMinusSrcAlpha CGPROGRAM #pragma vertex Vertex #pragma fragment Fragment #pragma multi_compile_instancing ENDCG } } } ================================================ FILE: Packages/jp.keijiro.klak.hap/Resources/Hap5.shader.meta ================================================ fileFormatVersion: 2 guid: 2fc3460268e46c64695de0f674e4e320 ShaderImporter: externalObjects: {} defaultTextures: [] nonModifiableTextures: [] userData: assetBundleName: assetBundleVariant: ================================================ FILE: Packages/jp.keijiro.klak.hap/Resources/HapY.shader ================================================ Shader "Klak/HAP Q" { Properties { _MainTex("Texture", 2D) = "white" {} } CGINCLUDE #include "UnityCG.cginc" struct Attributes { float4 position : POSITION; float2 texcoord : TEXCOORD; UNITY_VERTEX_INPUT_INSTANCE_ID }; struct Varyings { float4 position : SV_Position; float2 texcoord : TEXCOORD; }; sampler2D _MainTex; float4 _MainTex_ST; half3 CoCgSY2RGB(half4 i) { #if !defined(UNITY_COLORSPACE_GAMMA) i.xyz = LinearToGammaSpace(i.xyz); #endif i.xy -= half2(0.50196078431373, 0.50196078431373); half s = 1 / ((i.z * (255.0 / 8)) + 1); half3 rgb = half3(i.x - i.y, i.y, -i.x - i.y) * s + i.w; #if !defined(UNITY_COLORSPACE_GAMMA) rgb = GammaToLinearSpace(rgb); #endif return rgb; } Varyings Vertex(Attributes input) { UNITY_SETUP_INSTANCE_ID(input); Varyings output; output.position = UnityObjectToClipPos(input.position); output.texcoord = TRANSFORM_TEX(input.texcoord, _MainTex); output.texcoord.y = 1 - output.texcoord.y; return output; } fixed4 Fragment(Varyings input) : SV_Target { return fixed4(CoCgSY2RGB(tex2D(_MainTex, input.texcoord)), 1); } ENDCG SubShader { Tags { "RenderType"="Opaque" } Pass { CGPROGRAM #pragma multi_compile _ UNITY_COLORSPACE_GAMMA #pragma vertex Vertex #pragma fragment Fragment #pragma multi_compile_instancing ENDCG } } } ================================================ FILE: Packages/jp.keijiro.klak.hap/Resources/HapY.shader.meta ================================================ fileFormatVersion: 2 guid: c6a332340608cdd4f907eb4bc0c17f4a ShaderImporter: externalObjects: {} defaultTextures: [] nonModifiableTextures: [] userData: assetBundleName: assetBundleVariant: ================================================ FILE: Packages/jp.keijiro.klak.hap/Resources.meta ================================================ fileFormatVersion: 2 guid: 1b69560a2379aa94b9fb309127d58020 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Packages/jp.keijiro.klak.hap/Runtime/Common.cs ================================================ namespace Klak.Hap { public enum CodecType { Unsupported, Hap, HapQ, HapAlpha } } ================================================ FILE: Packages/jp.keijiro.klak.hap/Runtime/Common.cs.meta ================================================ fileFormatVersion: 2 guid: ab49f28befbab1341821f02280a3199f MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Packages/jp.keijiro.klak.hap/Runtime/HapPlayer.cs ================================================ using UnityEngine; using UnityEngine.Playables; #if KLAKHAP_HAS_TIMELINE using UnityEngine.Timeline; #endif namespace Klak.Hap { [ExecuteInEditMode, AddComponentMenu("Klak/HAP/HAP Player")] #if KLAKHAP_HAS_TIMELINE public sealed class HapPlayer : MonoBehaviour , ITimeControl, IPropertyPreview #else public sealed class HapPlayer : MonoBehaviour #endif { #region Editable attributes public enum PathMode { StreamingAssets, LocalFileSystem } [SerializeField] PathMode _pathMode = PathMode.StreamingAssets; [SerializeField] string _filePath = ""; [SerializeField] float _time = 0; [SerializeField, Range(-10, 10)] float _speed = 1; [SerializeField] bool _loop = true; [SerializeField] RenderTexture _targetTexture = null; [SerializeField] Renderer _targetRenderer = null; [SerializeField] string _targetMaterialProperty = "_MainTex"; #endregion #region Public properties public float time { get { return _time; } set { _time = value; } } public float speed { get { return _speed; } set { _speed = value; } } public bool loop { get { return _loop; } set { _loop = value; } } public RenderTexture targetTexture { get { return _targetTexture; } set { _targetTexture = value; } } public Renderer targetRenderer { get { return _targetRenderer; } set { _targetRenderer = value; } } public string targetMaterialProperty { get { return _targetMaterialProperty; } set { _targetMaterialProperty = value; } } #endregion #region Read-only properties public bool isValid { get { return _demuxer != null; } } public int frameWidth { get { return _demuxer?.Width ?? 0; } } public int frameHeight { get { return _demuxer?.Height ?? 0; } } public int frameCount { get { return _demuxer?.FrameCount ?? 0; } } public double streamDuration { get { return _demuxer?.Duration ?? 0; } } public CodecType codecType { get { return Utility.DetermineCodecType(_demuxer?.VideoType ?? 0); } } public string resolvedFilePath { get { if (_pathMode == PathMode.StreamingAssets) return System.IO.Path.Combine(Application.streamingAssetsPath, _filePath); else return _filePath; } } public Texture2D texture { get { return _texture; } } #endregion #region Public methods public void Open(string filePath, PathMode pathMode = PathMode.StreamingAssets) { if (_demuxer != null) { Debug.LogError("Stream has already been opened."); return; } _filePath = filePath; _pathMode = pathMode; OpenInternal(); } public void UpdateNow() => LateUpdate(); #endregion #region Private members Demuxer _demuxer; StreamReader _stream; Decoder _decoder; Texture2D _texture; TextureUpdater _updater; float _storedTime; float _storedSpeed; void OpenInternal() { // Demuxer instantiation _demuxer = new Demuxer(resolvedFilePath); if (!_demuxer.IsValid) { if (Application.isPlaying) { // In play mode, show an error message, then disable itself // to prevent spamming the console. Debug.LogError("Failed to open stream (" + resolvedFilePath + ")."); enabled = false; } _demuxer.Dispose(); _demuxer = null; return; } // Stream reader instantiation _stream = new StreamReader(_demuxer, _time, _speed / 60); (_storedTime, _storedSpeed) = (_time, _speed); // Decoder instantiation _decoder = new Decoder( _stream, _demuxer.Width, _demuxer.Height, _demuxer.VideoType ); // Texture initialization _texture = new Texture2D( _demuxer.Width, _demuxer.Height, Utility.DetermineTextureFormat(_demuxer.VideoType), false ); _texture.wrapMode = TextureWrapMode.Clamp; _texture.hideFlags = HideFlags.DontSave; _updater = new TextureUpdater(_texture, _decoder); } #endregion #region External object updaters Material _blitMaterial; MaterialPropertyBlock _propertyBlock; void UpdateTargetTexture() { if (_targetTexture == null) return; // Material lazy initialization if (_blitMaterial == null) { _blitMaterial = new Material(Utility.DetermineBlitShader(_demuxer.VideoType)); _blitMaterial.hideFlags = HideFlags.DontSave; } // Blit Graphics.Blit(_texture, _targetTexture, _blitMaterial, 0); } void UpdateTargetRenderer() { if (_targetRenderer == null) return; // Material property block lazy initialization if (_propertyBlock == null) _propertyBlock = new MaterialPropertyBlock(); // Read-modify-write _targetRenderer.GetPropertyBlock(_propertyBlock); _propertyBlock.SetTexture(_targetMaterialProperty, _texture); _targetRenderer.SetPropertyBlock(_propertyBlock); } #endregion #region ITimeControl implementation bool _externalTime; public void OnControlTimeStart() { _externalTime = true; // In the external time mode, we can't know the actual playback // speed but sure that it's positive (Control Track doesn't support // reverse playback), so we assume that the speed is 1.0. // Cons: Resync could happen every frame for high speed play back. _speed = 1; } public void OnControlTimeStop() { _externalTime = false; } public void SetTime(double time) { _time = (float)time; _speed = 1; } #endregion #region IPropertyPreview implementation #if KLAKHAP_HAS_TIMELINE public void GatherProperties(PlayableDirector director, IPropertyCollector driver) { driver.AddFromName(gameObject, "_time"); } #endif #endregion #region MonoBehaviour implementation void OnDestroy() { if (_updater != null) { _updater.Dispose(); _updater = null; } if (_decoder != null) { _decoder.Dispose(); _decoder = null; } if (_stream != null) { _stream.Dispose(); _stream = null; } if (_demuxer != null) { _demuxer.Dispose(); _demuxer = null; } Utility.Destroy(_texture); Utility.Destroy(_blitMaterial); } int _lastUpdateFrameCount = -1; void LateUpdate() { // Double update check if (Time.frameCount == _lastUpdateFrameCount) return; _lastUpdateFrameCount = Time.frameCount; // Lazy initialization of demuxer if (_demuxer == null && !string.IsNullOrEmpty(_filePath)) OpenInternal(); // Do nothing if the demuxer hasn't been instantiated. if (_demuxer == null) return; var duration = (float)_demuxer.Duration; // Check if _time is still in the same frame of _storedTime. // Resync is needed when it went out of the frame. var dt = duration / _demuxer.FrameCount; var resync = _time < _storedTime || _time > _storedTime + dt; // Check if the speed was externally modified. if (_speed != _storedSpeed) { resync = true; // Resync to adapt to the new speed. _storedSpeed = _speed; } // Time clamping var t = _loop ? _time : Mathf.Clamp(_time, 0, duration - 1e-4f); // Determine if background decoding is available. // Resync shouldn't happen. Not preferable in edit mode. var bgdec = !resync && Application.isPlaying; // Restart the stream reader on resync. if (resync) _stream.Restart(t, _speed / 60); if (TextureUpdater.AsyncSupport) { // Asynchronous texture update supported: // Decode a frame and request a texture update. if (bgdec) _decoder.UpdateAsync(t); else _decoder.UpdateSync(t); _updater.RequestAsyncUpdate(); } #if !HAP_NO_DELAY else if (bgdec) { // Synchronous texture update with background decoding: // Update first, then start background decoding. This // introduces a single frame delay but makes it possible to // offload decoding load to a background thread. _updater.UpdateNow(); _decoder.UpdateAsync(t); } #endif else { // Synchronous decoding and texture update. _decoder.UpdateSync(t); _updater.UpdateNow(); } // Update the stored time. if (Application.isPlaying && !_externalTime) _time += Time.deltaTime * _speed; _storedTime = _time; // External object updates UpdateTargetRenderer(); UpdateTargetTexture(); } #endregion } } ================================================ FILE: Packages/jp.keijiro.klak.hap/Runtime/HapPlayer.cs.meta ================================================ fileFormatVersion: 2 guid: f3775962864755b43a790abf4d029c32 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Packages/jp.keijiro.klak.hap/Runtime/Internal/Decoder.cs ================================================ using System; using System.Runtime.InteropServices; using System.Threading; namespace Klak.Hap { internal sealed class Decoder : IDisposable { #region Initialization/finalization public Decoder(StreamReader stream, int width, int height, int videoType) { _stream = stream; // Plugin initialization _plugin = KlakHap_CreateDecoder(width, height, videoType); _id = ++_instantiationCount; KlakHap_AssignDecoder(_id, _plugin); // By default, start from the first frame. _time = 0; // Decoder thread startup _resume.req = new AutoResetEvent(true); _resume.ack = new AutoResetEvent(false); _thread = new Thread(DecoderThread); _thread.Start(); } public void Dispose() { if (_thread != null) { _terminate = true; _resume.req.Set(); _thread.Join(); _thread = null; } if (_plugin != IntPtr.Zero) { KlakHap_AssignDecoder(_id, IntPtr.Zero); KlakHap_DestroyDecoder(_plugin); _plugin = IntPtr.Zero; } } #endregion #region Public members public uint CallbackID { get { return _id; } } public int BufferSize { get { return KlakHap_GetDecoderBufferSize(_plugin); } } public void UpdateSync(float time) { _time = time; var buffer = _stream.Advance(_time); if (buffer != null) KlakHap_DecodeFrame(_plugin, buffer.PluginPointer); } public void UpdateAsync(float time) { _time = time; _resume.req.Set(); _resume.ack.WaitOne(); } public IntPtr LockBuffer() { return KlakHap_LockDecoderBuffer(_plugin); } public void UnlockBuffer() { KlakHap_UnlockDecoderBuffer(_plugin); } #endregion #region Private members static uint _instantiationCount; IntPtr _plugin; uint _id; Thread _thread; (AutoResetEvent req, AutoResetEvent ack) _resume; bool _terminate; StreamReader _stream; float _time; #endregion #region Thread function void DecoderThread() { while (true) { _resume.req.WaitOne(); _resume.ack.Set(); if (_terminate) break; var buffer = _stream.Advance(_time); if (buffer == null) continue; KlakHap_DecodeFrame(_plugin, buffer.PluginPointer); } } #endregion #region Native plugin entry points [DllImport("KlakHap")] internal static extern IntPtr KlakHap_CreateDecoder(int width, int height, int typeID); [DllImport("KlakHap")] internal static extern void KlakHap_DestroyDecoder(IntPtr decoder); [DllImport("KlakHap")] internal static extern void KlakHap_AssignDecoder(uint id, IntPtr decoder); [DllImport("KlakHap")] internal static extern void KlakHap_DecodeFrame(IntPtr decoder, IntPtr input); [DllImport("KlakHap")] internal static extern IntPtr KlakHap_LockDecoderBuffer(IntPtr decoder); [DllImport("KlakHap")] internal static extern void KlakHap_UnlockDecoderBuffer(IntPtr decoder); [DllImport("KlakHap")] internal static extern int KlakHap_GetDecoderBufferSize(IntPtr decoder); #endregion } } ================================================ FILE: Packages/jp.keijiro.klak.hap/Runtime/Internal/Decoder.cs.meta ================================================ fileFormatVersion: 2 guid: 6a95a95ecf0e26740a6507fe3474b8f8 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Packages/jp.keijiro.klak.hap/Runtime/Internal/Demuxer.cs ================================================ using System; using System.Runtime.InteropServices; namespace Klak.Hap { internal sealed class Demuxer : IDisposable { #region Public properties public bool IsValid { get { return _plugin != IntPtr.Zero; } } public int Width { get { return _width; } } public int Height { get { return _height; } } public int VideoType { get { return _videoType; } } public double Duration { get { return _duration; } } public int FrameCount { get { return _frameCount; } } #endregion #region Initialization/finalization public Demuxer(string filePath) { _plugin = KlakHap_OpenDemuxer(filePath); if (KlakHap_DemuxerIsValid(_plugin) == 0) { // Instantiation failed; Close and stop. KlakHap_CloseDemuxer(_plugin); _plugin = IntPtr.Zero; return; } // Video properties _width = KlakHap_GetVideoWidth(_plugin); _height = KlakHap_GetVideoHeight(_plugin); _videoType = KlakHap_AnalyzeVideoType(_plugin); _duration = KlakHap_GetDuration(_plugin); _frameCount = KlakHap_CountFrames(_plugin); } public void Dispose() { if (_plugin != IntPtr.Zero) { KlakHap_CloseDemuxer(_plugin); _plugin = IntPtr.Zero; } } #endregion #region Public methods public void ReadFrame(ReadBuffer buffer, int index, float time) { KlakHap_ReadFrame(_plugin, index, buffer.PluginPointer); buffer.Index = index; buffer.Time = time; } #endregion #region Private members IntPtr _plugin; int _width, _height, _videoType; double _duration; int _frameCount; #endregion #region Native plugin entry points [DllImport("KlakHap", CharSet = CharSet.Ansi)] internal static extern IntPtr KlakHap_OpenDemuxer([MarshalAs(UnmanagedType.LPUTF8Str)] string filepath); [DllImport("KlakHap")] internal static extern void KlakHap_CloseDemuxer(IntPtr demuxer); [DllImport("KlakHap")] internal static extern int KlakHap_DemuxerIsValid(IntPtr demuxer); [DllImport("KlakHap")] internal static extern int KlakHap_CountFrames(IntPtr demuxer); [DllImport("KlakHap")] internal static extern double KlakHap_GetDuration(IntPtr demuxer); [DllImport("KlakHap")] internal static extern int KlakHap_GetVideoWidth(IntPtr demuxer); [DllImport("KlakHap")] internal static extern int KlakHap_GetVideoHeight(IntPtr demuxer); [DllImport("KlakHap")] internal static extern int KlakHap_AnalyzeVideoType(IntPtr demuxer); [DllImport("KlakHap")] internal static extern void KlakHap_ReadFrame(IntPtr demuxer, int frameNumber, IntPtr buffer); #endregion } } ================================================ FILE: Packages/jp.keijiro.klak.hap/Runtime/Internal/Demuxer.cs.meta ================================================ fileFormatVersion: 2 guid: 080b998a80cb20044adf8fa4a011a182 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Packages/jp.keijiro.klak.hap/Runtime/Internal/ReadBuffer.cs ================================================ using System; using System.Runtime.InteropServices; namespace Klak.Hap { internal sealed class ReadBuffer : IDisposable { #region Initialization/finalization public IntPtr PluginPointer { get { return _plugin; } } public int Index { get; set; } public float Time { get; set; } #endregion #region Initialization/finalization public ReadBuffer() { _plugin = KlakHap_CreateReadBuffer(); Index = Int32.MaxValue; Time = Single.MaxValue; } public void Dispose() { KlakHap_DestroyReadBuffer(_plugin); } #endregion #region Private members IntPtr _plugin; #endregion #region Native plugin entry points [DllImport("KlakHap")] internal static extern IntPtr KlakHap_CreateReadBuffer(); [DllImport("KlakHap")] internal static extern void KlakHap_DestroyReadBuffer(IntPtr buffer); #endregion } } ================================================ FILE: Packages/jp.keijiro.klak.hap/Runtime/Internal/ReadBuffer.cs.meta ================================================ fileFormatVersion: 2 guid: 4e6e9144df4395d4fbff558262502c67 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Packages/jp.keijiro.klak.hap/Runtime/Internal/StreamReader.cs ================================================ using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Threading; namespace Klak.Hap { internal sealed class StreamReader : IDisposable { #region Public methods public StreamReader(Demuxer demuxer, float time, float delta) { _demuxer = demuxer; _leadQueue = new Queue(); _freeBuffers = new List(); // Initial buffer entry allocation _freeBuffers.Add(new ReadBuffer()); _freeBuffers.Add(new ReadBuffer()); _freeBuffers.Add(new ReadBuffer()); _freeBuffers.Add(new ReadBuffer()); // Initial playback settings _restart = (time, SafeDelta(delta)); // Reader thread startup _updateEvent = new AutoResetEvent(true); _readEvent = new AutoResetEvent(false); _thread = new Thread(ReaderThread); _thread.Start(); } public void Dispose() { if (_thread != null) { _terminate = true; _updateEvent.Set(); _thread.Join(); _thread = null; } if (_updateEvent != null) { _updateEvent.Dispose(); _updateEvent = null; } if (_readEvent != null) { _readEvent.Dispose(); _readEvent = null; } if (_current != null) { _current.Dispose(); _current = null; } if (_leadQueue != null) { foreach (var rb in _leadQueue) rb.Dispose(); _leadQueue.Clear(); _leadQueue = null; } if (_freeBuffers != null) { foreach (var rb in _freeBuffers) rb.Dispose(); _freeBuffers.Clear(); _freeBuffers = null; } } public void Restart(float time, float delta) { // Restart request lock (_restartLock) _restart = (time, SafeDelta(delta)); // Wait for reset/read on the reader thread. _readEvent.Reset(); while (_restart != null) { _updateEvent.Set(); _readEvent.WaitOne(); } } public ReadBuffer Advance(float time) { // Add an epsilon-ish value to avoid rounding error. time += 1e-6f; var changed = false; // There is no slow path in this function, so we prefer holding // the queue lock for the entire function block rather than // acquiring/releasing it for each operation. lock (_queueLock) { // Scan the lead queue. while (_leadQueue.Count > 0) { var peek = _leadQueue.Peek(); if (_current != null) { if (_current.Time <= peek.Time) { // Forward playback case: // Break if it hasn't reached the next frame. if (time < peek.Time) break; } else { // Reverse playback case: // Break if it's still on the current frame. if (_current.Time < time) break; } // Free the current frame before replacing it. _freeBuffers.Add(_current); } _current = _leadQueue.Dequeue(); changed = true; } } // Poke the reader thread. _updateEvent.Set(); // Only returns a buffer object when the frame was changed. return changed ? _current : null; } #endregion #region Private members // Assigned demuxer Demuxer _demuxer; // Thread and synchronization objects Thread _thread; AutoResetEvent _updateEvent; AutoResetEvent _readEvent; bool _terminate; // Read buffer objects ReadBuffer _current; Queue _leadQueue; List _freeBuffers; readonly object _queueLock = new object(); // Restart request (float, float)? _restart; readonly object _restartLock = new object(); // Used to avoid too small delta time values. float SafeDelta(float delta) { var min = (float)(_demuxer.Duration / _demuxer.FrameCount); return Math.Max(Math.Abs(delta), min) * (delta < 0 ? -1 : 1); } #endregion #region Thread function void ReaderThread() { // Initial time settings from the restart request tuple var (time, delta) = _restart.Value; _restart = null; // Stream attributes var totalTime = _demuxer.Duration; var totalFrames = _demuxer.FrameCount; while (true) { // Synchronization with the parent thread _updateEvent.WaitOne(); if (_terminate) break; // Check if there is a restart request. lock (_restartLock) if (_restart != null) { // Flush out the current contents of the lead queue. lock (_queueLock) while (_leadQueue.Count > 0) _freeBuffers.Add(_leadQueue.Dequeue()); // Apply the restart request. (time, delta) = _restart.Value; _restart = null; } // Time -> Frame count // Rounding strategy: We don't prefer Math.Round because it can // show a frame before the playhead reaches it (especially when // using slow-mo). On the other hand, Math.Floor causes frame // skipping due to rounding errors. To avoid these problems, // we use the "adding a very-very small fractional frame" // approach. 1/1000 might be safe and enough for all the cases. var frameCount = (int)(time * totalFrames / totalTime + 1e-3f); // Frame count -> Frame snapped time var snappedTime = (float)(frameCount * totalTime / totalFrames); // Frame count -> Wrapped frame number var frameNumber = frameCount % totalFrames; if (frameNumber < 0) frameNumber += totalFrames; lock (_queueLock) { // Do nothing if there is no free buffer; It indicates that // the lead queue is fully filled. if (_freeBuffers.Count == 0) continue; ReadBuffer buffer = null; // Look for a free buffer that has the same frame number. foreach (var temp in _freeBuffers) { if (temp.Index == frameNumber) { buffer = temp; break; } } if (buffer != null) { // Reuse the found buffer; Although we can use it // without reading frame data, the time field should // be updated to handle wrapping-around hits. _freeBuffers.Remove(buffer); buffer.Time = snappedTime; } else { // Allocate a buffer from the free buffer list. buffer = _freeBuffers[_freeBuffers.Count - 1]; _freeBuffers.RemoveAt(_freeBuffers.Count - 1); // Frame data read _demuxer.ReadFrame(buffer, frameNumber, snappedTime); } // Push the buffer to the lead queue. _leadQueue.Enqueue(buffer); } _readEvent.Set(); time += delta; } } #endregion } } ================================================ FILE: Packages/jp.keijiro.klak.hap/Runtime/Internal/StreamReader.cs.meta ================================================ fileFormatVersion: 2 guid: 435d6c4db4ba1ea40b2289d7ef613a3c MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Packages/jp.keijiro.klak.hap/Runtime/Internal/TextureUpdater.cs ================================================ using System; using System.Runtime.InteropServices; using UnityEngine; using UnityEngine.Rendering; namespace Klak.Hap { internal sealed class TextureUpdater : IDisposable { #region Public properties public static bool AsyncSupport { get { return SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D11; } } #endregion #region Public methods public TextureUpdater(Texture2D texture, Decoder decoder) { _texture = texture; _decoder = decoder; if (AsyncSupport) { _command = new CommandBuffer(); _command.name = "Klak HAP"; _command.IssuePluginCustomTextureUpdateV2( KlakHap_GetTextureUpdateCallback(), texture, decoder.CallbackID ); } } public void Dispose() { if (_command != null) { _command.Dispose(); _command = null; } } public void UpdateNow() { _texture.LoadRawTextureData( _decoder.LockBuffer(), _decoder.BufferSize ); _texture.Apply(); _decoder.UnlockBuffer(); } public void RequestAsyncUpdate() { if (_command != null) Graphics.ExecuteCommandBuffer(_command); } #endregion #region Private fields Texture2D _texture; Decoder _decoder; CommandBuffer _command; #endregion #region Native plugin entry points [DllImport("KlakHap")] internal static extern IntPtr KlakHap_GetTextureUpdateCallback(); #endregion } } ================================================ FILE: Packages/jp.keijiro.klak.hap/Runtime/Internal/TextureUpdater.cs.meta ================================================ fileFormatVersion: 2 guid: f9e27f5a05bda2e4eab2f548840a9fb1 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Packages/jp.keijiro.klak.hap/Runtime/Internal/Utility.cs ================================================ using UnityEngine; namespace Klak.Hap { internal static class Utility { public static void Destroy(Object o) { if (o == null) return; if (Application.isPlaying) Object.Destroy(o); else Object.DestroyImmediate(o); } public static CodecType DetermineCodecType(int videoType) { switch (videoType & 0xf) { case 0xb: return CodecType.Hap; case 0xe: return CodecType.HapAlpha; case 0xf: return CodecType.HapQ; } return CodecType.Unsupported; } public static TextureFormat DetermineTextureFormat(int videoType) { switch (videoType & 0xf) { case 0xb: return TextureFormat.DXT1; case 0xe: return TextureFormat.DXT5; case 0xf: return TextureFormat.DXT5; case 0xc: return TextureFormat.BC7; case 0x1: return TextureFormat.BC4; } return TextureFormat.DXT1; } public static Shader DetermineBlitShader(int videoType) { if ((videoType & 0xf) == 0xf) return Shader.Find("Klak/HAP Q"); else return Shader.Find("Klak/HAP"); } } } ================================================ FILE: Packages/jp.keijiro.klak.hap/Runtime/Internal/Utility.cs.meta ================================================ fileFormatVersion: 2 guid: 970c4e0f94959ac4b9b108dc1a34306e MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Packages/jp.keijiro.klak.hap/Runtime/Internal.meta ================================================ fileFormatVersion: 2 guid: d37984629e38a1c439a98815991ec09f folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Packages/jp.keijiro.klak.hap/Runtime/Klak.Hap.asmdef ================================================ { "name": "Klak.Hap", "references": [ "GUID:f06555f75b070af458a003d92f9efb00" ], "includePlatforms": [], "excludePlatforms": [], "allowUnsafeCode": false, "overrideReferences": false, "precompiledReferences": [], "autoReferenced": true, "defineConstraints": [], "versionDefines": [ { "name": "com.unity.timeline", "expression": "1.0.0", "define": "KLAKHAP_HAS_TIMELINE" } ], "noEngineReferences": false } ================================================ FILE: Packages/jp.keijiro.klak.hap/Runtime/Klak.Hap.asmdef.meta ================================================ fileFormatVersion: 2 guid: 63ee2b3a69c095740bf1e4ff4d11f294 AssemblyDefinitionImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Packages/jp.keijiro.klak.hap/Runtime.meta ================================================ fileFormatVersion: 2 guid: 2d10b798fb99ee94981f7dbf19d8d6a9 folderAsset: yes DefaultImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Packages/jp.keijiro.klak.hap/package.json ================================================ { "name": "jp.keijiro.klak.hap", "version": "1.0.0", "displayName": "KlakHAP", "description": "HAP video player", "unity": "2022.3", "author": "Keijiro Takahashi", "dependencies": {}, "changelogUrl": "https://github.com/keijiro/KlakHap/blob/master/CHANGELOG.md", "documentationUrl": "https://github.com/keijiro/KlakHap", "licensesUrl": "https://github.com/keijiro/KlakHap/blob/master/LICENSE", "license": "SEE LICENSE IN LICENSE", "_upm": { "changelog": "Added
- Added a Unity 6.3 demo scene updated for URP and UI Toolkit.
- Added runtime tests and a test data downloader for Hap, HapQ, and HapAlpha assets.

Changed
- Updated HAP and Snappy source code and refreshed native plugin binaries.
- Migrated Windows builds to MinGW and refactored the native build pipelines and Makefiles.

Fixed
- Fixed a Windows crash when paths contain Japanese characters. (PR #49)
- Fixed obsolete ShaderUtil API usage in the editor code." } } ================================================ FILE: Packages/jp.keijiro.klak.hap/package.json.meta ================================================ fileFormatVersion: 2 guid: 251c3b05c5e2c694da8fe8fe9b07eb5c TextScriptImporter: externalObjects: {} userData: assetBundleName: assetBundleVariant: ================================================ FILE: Packages/manifest.json ================================================ { "dependencies": { "com.unity.inputsystem": "1.18.0", "com.unity.render-pipelines.universal": "17.3.0", "com.unity.timeline": "1.8.10", "jp.keijiro.klak.motion": "1.1.1", "jp.keijiro.klutter-tools": "2.6.0", "com.unity.modules.imageconversion": "1.0.0", "com.unity.modules.uielements": "1.0.0" }, "scopedRegistries": [ { "name": "Keijiro", "url": "https://registry.npmjs.com", "scopes": [ "jp.keijiro" ] } ] } ================================================ FILE: Packages/packages-lock.json ================================================ { "dependencies": { "com.unity.burst": { "version": "1.8.27", "depth": 2, "source": "registry", "dependencies": { "com.unity.mathematics": "1.2.1", "com.unity.modules.jsonserialize": "1.0.0" }, "url": "https://packages.unity.com" }, "com.unity.collections": { "version": "2.6.2", "depth": 2, "source": "registry", "dependencies": { "com.unity.burst": "1.8.23", "com.unity.mathematics": "1.3.2", "com.unity.test-framework": "1.4.6", "com.unity.nuget.mono-cecil": "1.11.5", "com.unity.test-framework.performance": "3.0.3" }, "url": "https://packages.unity.com" }, "com.unity.ext.nunit": { "version": "2.0.5", "depth": 4, "source": "builtin", "dependencies": {} }, "com.unity.inputsystem": { "version": "1.18.0", "depth": 0, "source": "registry", "dependencies": { "com.unity.modules.uielements": "1.0.0" }, "url": "https://packages.unity.com" }, "com.unity.mathematics": { "version": "1.3.3", "depth": 1, "source": "registry", "dependencies": {}, "url": "https://packages.unity.com" }, "com.unity.nuget.mono-cecil": { "version": "1.11.6", "depth": 3, "source": "registry", "dependencies": {}, "url": "https://packages.unity.com" }, "com.unity.render-pipelines.core": { "version": "17.3.0", "depth": 1, "source": "builtin", "dependencies": { "com.unity.burst": "1.8.14", "com.unity.mathematics": "1.3.2", "com.unity.ugui": "2.0.0", "com.unity.collections": "2.4.3", "com.unity.modules.physics": "1.0.0", "com.unity.modules.terrain": "1.0.0", "com.unity.modules.jsonserialize": "1.0.0" } }, "com.unity.render-pipelines.universal": { "version": "17.3.0", "depth": 0, "source": "builtin", "dependencies": { "com.unity.render-pipelines.core": "17.3.0", "com.unity.shadergraph": "17.3.0", "com.unity.render-pipelines.universal-config": "17.0.3" } }, "com.unity.render-pipelines.universal-config": { "version": "17.0.3", "depth": 1, "source": "builtin", "dependencies": { "com.unity.render-pipelines.core": "17.0.3" } }, "com.unity.searcher": { "version": "4.9.4", "depth": 2, "source": "registry", "dependencies": {}, "url": "https://packages.unity.com" }, "com.unity.shadergraph": { "version": "17.3.0", "depth": 1, "source": "builtin", "dependencies": { "com.unity.render-pipelines.core": "17.3.0", "com.unity.searcher": "4.9.3" } }, "com.unity.test-framework": { "version": "1.6.0", "depth": 3, "source": "builtin", "dependencies": { "com.unity.ext.nunit": "2.0.3", "com.unity.modules.imgui": "1.0.0", "com.unity.modules.jsonserialize": "1.0.0" } }, "com.unity.test-framework.performance": { "version": "3.2.0", "depth": 3, "source": "registry", "dependencies": { "com.unity.test-framework": "1.1.33", "com.unity.modules.jsonserialize": "1.0.0" }, "url": "https://packages.unity.com" }, "com.unity.timeline": { "version": "1.8.10", "depth": 0, "source": "registry", "dependencies": { "com.unity.modules.audio": "1.0.0", "com.unity.modules.director": "1.0.0", "com.unity.modules.animation": "1.0.0", "com.unity.modules.particlesystem": "1.0.0" }, "url": "https://packages.unity.com" }, "com.unity.ugui": { "version": "2.0.0", "depth": 2, "source": "builtin", "dependencies": { "com.unity.modules.ui": "1.0.0", "com.unity.modules.imgui": "1.0.0" } }, "jp.keijiro.klak.hap": { "version": "file:jp.keijiro.klak.hap", "depth": 0, "source": "embedded", "dependencies": {} }, "jp.keijiro.klak.motion": { "version": "1.1.1", "depth": 0, "source": "registry", "dependencies": { "com.unity.mathematics": "1.2.1" }, "url": "https://registry.npmjs.com" }, "jp.keijiro.klutter-tools": { "version": "2.6.0", "depth": 0, "source": "registry", "dependencies": {}, "url": "https://registry.npmjs.com" }, "com.unity.modules.animation": { "version": "1.0.0", "depth": 1, "source": "builtin", "dependencies": {} }, "com.unity.modules.audio": { "version": "1.0.0", "depth": 1, "source": "builtin", "dependencies": {} }, "com.unity.modules.director": { "version": "1.0.0", "depth": 1, "source": "builtin", "dependencies": { "com.unity.modules.audio": "1.0.0", "com.unity.modules.animation": "1.0.0" } }, "com.unity.modules.hierarchycore": { "version": "1.0.0", "depth": 1, "source": "builtin", "dependencies": {} }, "com.unity.modules.imageconversion": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": {} }, "com.unity.modules.imgui": { "version": "1.0.0", "depth": 1, "source": "builtin", "dependencies": {} }, "com.unity.modules.jsonserialize": { "version": "1.0.0", "depth": 1, "source": "builtin", "dependencies": {} }, "com.unity.modules.particlesystem": { "version": "1.0.0", "depth": 1, "source": "builtin", "dependencies": {} }, "com.unity.modules.physics": { "version": "1.0.0", "depth": 1, "source": "builtin", "dependencies": {} }, "com.unity.modules.terrain": { "version": "1.0.0", "depth": 2, "source": "builtin", "dependencies": {} }, "com.unity.modules.ui": { "version": "1.0.0", "depth": 1, "source": "builtin", "dependencies": {} }, "com.unity.modules.uielements": { "version": "1.0.0", "depth": 0, "source": "builtin", "dependencies": { "com.unity.modules.ui": "1.0.0", "com.unity.modules.imgui": "1.0.0", "com.unity.modules.jsonserialize": "1.0.0", "com.unity.modules.hierarchycore": "1.0.0", "com.unity.modules.physics": "1.0.0" } } } } ================================================ FILE: Plugin/Common.mk ================================================ # # File listings # PRODUCT = KlakHap SRCS_C = Hap/hap.c MP4/mp4demux.c vpath %.c Hap MP4 SRCS_CC = Snappy/snappy-c.cc \ Snappy/snappy-sinksource.cc \ Snappy/snappy-stubs-internal.cc \ Snappy/snappy.cc vpath %.cc Snappy SRCS_CPP = Source/KlakHap.cpp vpath %.cpp Source SRCS = $(SRCS_C) $(SRCS_CC) $(SRCS_CPP) OBJ_DIR = build-$(PLATFORM)-$(ARCH) # # Intermediate/output files # OBJS_C = $(addprefix $(OBJ_DIR)/, $(notdir $(patsubst %.c, %.o, $(SRCS_C) ))) OBJS_CC = $(addprefix $(OBJ_DIR)/, $(notdir $(patsubst %.cc, %.o, $(SRCS_CC) ))) OBJS_CPP = $(addprefix $(OBJ_DIR)/, $(notdir $(patsubst %.cpp,%.o, $(SRCS_CPP)))) OBJS = $(OBJS_C) $(OBJS_CC) $(OBJS_CPP) ifeq ($(TARGET_TYPE), dll) TARGET = $(OBJ_DIR)/$(PRODUCT).$(TARGET_TYPE) else TARGET = $(OBJ_DIR)/lib$(PRODUCT).$(TARGET_TYPE) endif # # Toolchain # ifndef AR AR = ar endif ifeq ($(origin CC),default) CC = clang endif ifeq ($(origin CXX),default) CXX = clang++ endif ifndef STRIP STRIP = strip endif # # Compiler/linker options # CPPFLAGS += -ISnappy -IHap -IMP4 -IUnity CFLAGS += -O2 -Wall -Wextra -Wno-sign-compare -Wno-implicit-fallthrough CXXFLAGS += -O2 -Wall -Wextra -Wno-unused-parameter -Wno-switch -Wno-unknown-pragmas -std=c++17 # # Building rules # all: $(TARGET) clean: rm -f $(TARGET) $(OBJS) copy: $(TARGET) cp $(TARGET) ../Packages/jp.keijiro.klak.hap/Plugin/$(PLATFORM) $(OBJ_DIR)/$(PRODUCT).dll: $(OBJS) $(CXX) $(LDFLAGS) -o $@ $^ $(LIBS) $(STRIP) $@ $(OBJ_DIR)/lib$(PRODUCT).dylib: $(OBJS) $(CXX) $(LDFLAGS) -o $@ $^ $(LIBS) $(OBJ_DIR)/lib$(PRODUCT).so: $(OBJS) $(CXX) $(LDFLAGS) -o $@ $^ $(LIBS) $(OBJ_DIR)/%.o: %.c | $(OBJ_DIR) $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< $(OBJ_DIR)/%.o: %.cc | $(OBJ_DIR) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $< $(OBJ_DIR)/%.o: %.cpp | $(OBJ_DIR) $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $< $(OBJ_DIR): mkdir -p $(OBJ_DIR) ================================================ FILE: Plugin/Hap/hap.c ================================================ /* hap.c Copyright (c) 2011-2013, Tom Butterworth and Vidvox LLC. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "hap.h" #include #include #include // For memcpy for uncompressed frames #include "snappy-c.h" #define kHapUInt24Max 0x00FFFFFF /* Hap Constants First four bits represent the compressor Second four bits represent the format */ #define kHapCompressorNone 0xA #define kHapCompressorSnappy 0xB #define kHapCompressorComplex 0xC #define kHapFormatRGBDXT1 0xB #define kHapFormatRGBADXT5 0xE #define kHapFormatYCoCgDXT5 0xF #define kHapFormatARGTC1 0x1 #define kHapFormatRGBABPTC 0xC #define kHapFormatRGBBPTCUF 0x2 #define kHapFormatRGBBPTCSF 0x3 /* Packed byte values for Hap Format Compressor Byte Code ---------------------------------------------------- RGB_DXT1 None 0xAB RGB_DXT1 Snappy 0xBB RGB_DXT1 Complex 0xCB RGBA_DXT5 None 0xAE RGBA_DXT5 Snappy 0xBE RGBA_DXT5 Complex 0xCE YCoCg_DXT5 None 0xAF YCoCg_DXT5 Snappy 0xBF YCoCg_DXT5 Complex 0xCF A_RGTC1 None 0xA1 A_RGTC1 Snappy 0xB1 A_RGTC1 Complex 0xC1 RGBA_BPTC_UNORM None 0xAC RGBA_BPTC_UNORM Snappy 0xBC RGBA_BPTC_UNORM Complex 0xCC RGB_BPTC_UNSIGNED_FLOAT None 0xA2 RGB_BPTC_UNSIGNED_FLOAT Snappy 0xB2 RGB_BPTC_UNSIGNED_FLOAT Complex 0xC2 RGB_BPTC_SIGNED_FLOAT None 0xA3 RGB_BPTC_SIGNED_FLOAT Snappy 0xB3 RGB_BPTC_SIGNED_FLOAT Complex 0xC3 */ /* Hap Frame Section Types */ #define kHapSectionMultipleImages 0x0D #define kHapSectionDecodeInstructionsContainer 0x01 #define kHapSectionChunkSecondStageCompressorTable 0x02 #define kHapSectionChunkSizeTable 0x03 #define kHapSectionChunkOffsetTable 0x04 /* To decode we use a struct to store details of each chunk */ typedef struct HapChunkDecodeInfo { unsigned int result; unsigned int compressor; const char *compressed_chunk_data; size_t compressed_chunk_size; char *uncompressed_chunk_data; size_t uncompressed_chunk_size; } HapChunkDecodeInfo; // TODO: rename the defines we use for codes used in stored frames // to better differentiate them from the enums used for the API // These read and write little-endian values on big or little-endian architectures static unsigned int hap_read_3_byte_uint(const void *buffer) { return (*(uint8_t *)buffer) + ((*(((uint8_t *)buffer) + 1)) << 8) + ((*(((uint8_t *)buffer) + 2)) << 16); } static void hap_write_3_byte_uint(void *buffer, unsigned int value) { *(uint8_t *)buffer = value & 0xFF; *(((uint8_t *)buffer) + 1) = (value >> 8) & 0xFF; *(((uint8_t *)buffer) + 2) = (value >> 16) & 0xFF; } static unsigned int hap_read_4_byte_uint(const void *buffer) { return (*(uint8_t *)buffer) + ((*(((uint8_t *)buffer) + 1)) << 8) + ((*(((uint8_t *)buffer) + 2)) << 16) + ((*(((uint8_t *)buffer) + 3)) << 24); } static void hap_write_4_byte_uint(const void *buffer, unsigned int value) { *(uint8_t *)buffer = value & 0xFF; *(((uint8_t *)buffer) + 1) = (value >> 8) & 0xFF; *(((uint8_t *)buffer) + 2) = (value >> 16) & 0xFF; *(((uint8_t *)buffer) + 3) = (value >> 24) & 0xFF; } #define hap_top_4_bits(x) (((x) & 0xF0) >> 4) #define hap_bottom_4_bits(x) ((x) & 0x0F) #define hap_4_bit_packed_byte(top_bits, bottom_bits) (((top_bits) << 4) | ((bottom_bits) & 0x0F)) static int hap_read_section_header(const void *buffer, uint32_t buffer_length, uint32_t *out_header_length, uint32_t *out_section_length, unsigned int *out_section_type) { /* Verify buffer is big enough to contain a four-byte header */ if (buffer_length < 4U) { return HapResult_Bad_Frame; } /* The first three bytes are the length of the section (not including the header) or zero if the length is stored in the last four bytes of an eight-byte header */ *out_section_length = hap_read_3_byte_uint(buffer); /* If the first three bytes are zero, the size is in the following four bytes */ if (*out_section_length == 0U) { /* Verify buffer is big enough to contain an eight-byte header */ if (buffer_length < 8U) { return HapResult_Bad_Frame; } *out_section_length = hap_read_4_byte_uint(((uint8_t *)buffer) + 4U); *out_header_length = 8U; } else { *out_header_length = 4U; } /* The fourth byte stores the section type */ *out_section_type = *(((uint8_t *)buffer) + 3U); /* Verify the section does not extend beyond the buffer */ if (*out_header_length + *out_section_length > buffer_length) { return HapResult_Bad_Frame; } return HapResult_No_Error; } static void hap_write_section_header(void *buffer, size_t header_length, uint32_t section_length, unsigned int section_type) { /* The first three bytes are the length of the section (not including the header) or zero if using an eight-byte header */ if (header_length == 4U) { hap_write_3_byte_uint(buffer, (unsigned int)section_length); } else { /* For an eight-byte header, the length is in the last four bytes */ hap_write_3_byte_uint(buffer, 0U); hap_write_4_byte_uint(((uint8_t *)buffer) + 4U, section_length); } /* The fourth byte stores the section type */ *(((uint8_t *)buffer) + 3) = section_type; } // Returns an API texture format constant or 0 if not recognised static unsigned int hap_texture_format_constant_for_format_identifier(unsigned int identifier) { switch (identifier) { case kHapFormatRGBDXT1: return HapTextureFormat_RGB_DXT1; case kHapFormatRGBADXT5: return HapTextureFormat_RGBA_DXT5; case kHapFormatYCoCgDXT5: return HapTextureFormat_YCoCg_DXT5; case kHapFormatARGTC1: return HapTextureFormat_A_RGTC1; case kHapFormatRGBABPTC: return HapTextureFormat_RGBA_BPTC_UNORM; case kHapFormatRGBBPTCUF: return HapTextureFormat_RGB_BPTC_UNSIGNED_FLOAT; case kHapFormatRGBBPTCSF: return HapTextureFormat_RGB_BPTC_SIGNED_FLOAT; default: return 0; } } // Returns a frame identifier or 0 if not recognised static unsigned int hap_texture_format_identifier_for_format_constant(unsigned int constant) { switch (constant) { case HapTextureFormat_RGB_DXT1: return kHapFormatRGBDXT1; case HapTextureFormat_RGBA_DXT5: return kHapFormatRGBADXT5; case HapTextureFormat_YCoCg_DXT5: return kHapFormatYCoCgDXT5; case HapTextureFormat_A_RGTC1: return kHapFormatARGTC1; case HapTextureFormat_RGBA_BPTC_UNORM: return kHapFormatRGBABPTC; case HapTextureFormat_RGB_BPTC_UNSIGNED_FLOAT: return kHapFormatRGBBPTCUF; case HapTextureFormat_RGB_BPTC_SIGNED_FLOAT: return kHapFormatRGBBPTCSF; default: return 0; } } // Returns the length of a decode instructions container of chunk_count chunks // not including the section header static size_t hap_decode_instructions_length(unsigned int chunk_count) { /* Calculate the size of our Decode Instructions Section = Second-Stage Compressor Table + Chunk Size Table + headers for both sections = chunk_count + (4 * chunk_count) + 4 + 4 */ size_t length = (5 * chunk_count) + 8; return length; } static unsigned int hap_limited_chunk_count_for_frame(size_t input_bytes, unsigned int texture_format, unsigned int chunk_count) { // This is a hard limit due to the 4-byte headers we use for the decode instruction container // (0xFFFFFF == count + (4 x count) + 20) if (chunk_count > 3355431) { chunk_count = 3355431; } // Divide frame equally on DXT block boundries (8 or 16 bytes) unsigned long dxt_block_count; switch (texture_format) { case HapTextureFormat_RGB_DXT1: case HapTextureFormat_A_RGTC1: dxt_block_count = input_bytes / 8; break; default: dxt_block_count = input_bytes / 16; } while (dxt_block_count % chunk_count != 0) { chunk_count--; } return chunk_count; } static size_t hap_max_encoded_length(size_t input_bytes, unsigned int texture_format, unsigned int compressor, unsigned int chunk_count) { size_t decode_instructions_length, max_compressed_length; chunk_count = hap_limited_chunk_count_for_frame(input_bytes, texture_format, chunk_count); decode_instructions_length = hap_decode_instructions_length(chunk_count); if (compressor == HapCompressorSnappy) { size_t chunk_size = input_bytes / chunk_count; max_compressed_length = snappy_max_compressed_length(chunk_size) * chunk_count; } else { max_compressed_length = input_bytes; } // top section header + decode instructions section header + decode instructions + compressed data return max_compressed_length + 8U + decode_instructions_length + 4U; } unsigned long HapMaxEncodedLength(unsigned int count, unsigned long *inputBytes, unsigned int *textureFormats, unsigned int *chunkCounts) { // Start with the length of a multiple-image section header unsigned long total_length = 8; // Return 0 for bad arguments if (count == 0 || count > 2 || inputBytes == NULL || textureFormats == NULL || chunkCounts == NULL) { return 0; } for (int i = 0; i < count; i++) { if (chunkCounts[i] == 0) { return 0; } // Assume snappy, the worst case total_length += hap_max_encoded_length(inputBytes[i], textureFormats[i], HapCompressorSnappy, chunkCounts[i]); } return total_length; } static unsigned int hap_encode_texture(const void *inputBuffer, unsigned long inputBufferBytes, unsigned int textureFormat, unsigned int compressor, unsigned int chunkCount, void *outputBuffer, unsigned long outputBufferBytes, unsigned long *outputBufferBytesUsed) { size_t top_section_header_length; size_t top_section_length; unsigned int storedCompressor; unsigned int storedFormat; /* Check arguments */ if (inputBuffer == NULL || inputBufferBytes == 0 || (textureFormat != HapTextureFormat_RGB_DXT1 && textureFormat != HapTextureFormat_RGBA_DXT5 && textureFormat != HapTextureFormat_YCoCg_DXT5 && textureFormat != HapTextureFormat_A_RGTC1 && textureFormat != HapTextureFormat_RGBA_BPTC_UNORM && textureFormat != HapTextureFormat_RGB_BPTC_UNSIGNED_FLOAT && textureFormat != HapTextureFormat_RGB_BPTC_SIGNED_FLOAT ) || (compressor != HapCompressorNone && compressor != HapCompressorSnappy ) || outputBuffer == NULL || outputBufferBytesUsed == NULL ) { return HapResult_Bad_Arguments; } else if (outputBufferBytes < hap_max_encoded_length(inputBufferBytes, textureFormat, compressor, chunkCount)) { return HapResult_Buffer_Too_Small; } /* To store frames of length greater than can be expressed in three bytes, we use an eight byte header (the last four bytes are the frame size). We don't know the compressed size until we have performed compression, but we know the worst-case size (the uncompressed size), so choose header-length based on that. A simpler encoder could always use the eight-byte header variation. */ if (inputBufferBytes > kHapUInt24Max) { top_section_header_length = 8U; } else { top_section_header_length = 4U; } if (compressor == HapCompressorSnappy) { /* We attempt to chunk as requested, and if resulting frame is larger than it is uncompressed then store frame uncompressed */ size_t decode_instructions_length; size_t chunk_size, compress_buffer_remaining; uint8_t *second_stage_compressor_table; void *chunk_size_table; char *compressed_data; unsigned int i; chunkCount = hap_limited_chunk_count_for_frame(inputBufferBytes, textureFormat, chunkCount); decode_instructions_length = hap_decode_instructions_length(chunkCount); // Check we have space for the Decode Instructions Container if ((inputBufferBytes + decode_instructions_length + 4) > kHapUInt24Max) { top_section_header_length = 8U; } second_stage_compressor_table = ((uint8_t *)outputBuffer) + top_section_header_length + 4 + 4; chunk_size_table = ((uint8_t *)outputBuffer) + top_section_header_length + 4 + 4 + chunkCount + 4; chunk_size = inputBufferBytes / chunkCount; // write the Decode Instructions section header hap_write_section_header(((uint8_t *)outputBuffer) + top_section_header_length, 4U, decode_instructions_length, kHapSectionDecodeInstructionsContainer); // write the Second Stage Compressor Table section header hap_write_section_header(((uint8_t *)outputBuffer) + top_section_header_length + 4U, 4U, chunkCount, kHapSectionChunkSecondStageCompressorTable); // write the Chunk Size Table section header hap_write_section_header(((uint8_t *)outputBuffer) + top_section_header_length + 4U + 4U + chunkCount, 4U, chunkCount * 4U, kHapSectionChunkSizeTable); compressed_data = (char *)(((uint8_t *)outputBuffer) + top_section_header_length + 4 + decode_instructions_length); compress_buffer_remaining = outputBufferBytes - top_section_header_length - 4 - decode_instructions_length; top_section_length = 4 + decode_instructions_length; for (i = 0; i < chunkCount; i++) { size_t chunk_packed_length = compress_buffer_remaining; const char *chunk_input_start = (const char *)(((uint8_t *)inputBuffer) + (chunk_size * i)); if (compressor == HapCompressorSnappy) { snappy_status result = snappy_compress(chunk_input_start, chunk_size, (char *)compressed_data, &chunk_packed_length); if (result != SNAPPY_OK) { return HapResult_Internal_Error; } } if (compressor == HapCompressorNone || chunk_packed_length >= chunk_size) { // store the chunk uncompressed memcpy(compressed_data, chunk_input_start, chunk_size); chunk_packed_length = chunk_size; second_stage_compressor_table[i] = kHapCompressorNone; } else { // ie we used snappy and saved some space second_stage_compressor_table[i] = kHapCompressorSnappy; } hap_write_4_byte_uint(((uint8_t *)chunk_size_table) + (i * 4), chunk_packed_length); compressed_data += chunk_packed_length; top_section_length += chunk_packed_length; compress_buffer_remaining -= chunk_packed_length; } if (top_section_length < inputBufferBytes + top_section_header_length) { // use the complex storage because snappy compression saved space storedCompressor = kHapCompressorComplex; } else { // Signal to store the frame uncompressed compressor = HapCompressorNone; } } if (compressor == HapCompressorNone) { memcpy(((uint8_t *)outputBuffer) + top_section_header_length, inputBuffer, inputBufferBytes); top_section_length = inputBufferBytes; storedCompressor = kHapCompressorNone; } storedFormat = hap_texture_format_identifier_for_format_constant(textureFormat); hap_write_section_header(outputBuffer, top_section_header_length, top_section_length, hap_4_bit_packed_byte(storedCompressor, storedFormat)); *outputBufferBytesUsed = top_section_length + top_section_header_length; return HapResult_No_Error; } unsigned int HapEncode(unsigned int count, const void **inputBuffers, unsigned long *inputBuffersBytes, unsigned int *textureFormats, unsigned int *compressors, unsigned int *chunkCounts, void *outputBuffer, unsigned long outputBufferBytes, unsigned long *outputBufferBytesUsed) { size_t top_section_header_length; size_t top_section_length; unsigned long section_length; if (count == 0 || count > 2 // A frame must contain one or two textures || inputBuffers == NULL || inputBuffersBytes == NULL || textureFormats == NULL || compressors == NULL || chunkCounts == NULL || outputBuffer == NULL || outputBufferBytes == 0 || outputBufferBytesUsed == NULL) { return HapResult_Bad_Arguments; } for (int i = 0; i < count; i++) { if (chunkCounts[i] == 0) { return HapResult_Bad_Arguments; } } if (count == 1) { // Encode without the multi-image layout return hap_encode_texture(inputBuffers[0], inputBuffersBytes[0], textureFormats[0], compressors[0], chunkCounts[0], outputBuffer, outputBufferBytes, outputBufferBytesUsed); } else if ((textureFormats[0] != HapTextureFormat_YCoCg_DXT5 && textureFormats[1] != HapTextureFormat_YCoCg_DXT5) && (textureFormats[0] != HapTextureFormat_A_RGTC1 && textureFormats[1] != HapTextureFormat_A_RGTC1)) { /* Permitted combinations: HapTextureFormat_YCoCg_DXT5 + HapTextureFormat_A_RGTC1 */ return HapResult_Bad_Arguments; } else { // Calculate the worst-case size for the top section and choose a header-length based on that top_section_length = 0; for (int i = 0; i < count; i++) { top_section_length += inputBuffersBytes[i] + hap_decode_instructions_length(chunkCounts[i]) + 4; } if (top_section_length > kHapUInt24Max) { top_section_header_length = 8U; } else { top_section_header_length = 4U; } // Encode each texture top_section_length = 0; for (int i = 0; i < count; i++) { void *section = ((uint8_t *)outputBuffer) + top_section_header_length + top_section_length; unsigned int result = hap_encode_texture(inputBuffers[i], inputBuffersBytes[i], textureFormats[i], compressors[i], chunkCounts[i], section, outputBufferBytes - (top_section_header_length + top_section_length), §ion_length); if (result != HapResult_No_Error) { return result; } top_section_length += section_length; } hap_write_section_header(outputBuffer, top_section_header_length, top_section_length, kHapSectionMultipleImages); *outputBufferBytesUsed = top_section_length + top_section_header_length; return HapResult_No_Error; } } static void hap_decode_chunk(HapChunkDecodeInfo chunks[], unsigned int index) { if (chunks) { if (chunks[index].compressor == kHapCompressorSnappy) { snappy_status snappy_result = snappy_uncompress(chunks[index].compressed_chunk_data, chunks[index].compressed_chunk_size, chunks[index].uncompressed_chunk_data, &chunks[index].uncompressed_chunk_size); switch (snappy_result) { case SNAPPY_INVALID_INPUT: chunks[index].result = HapResult_Bad_Frame; break; case SNAPPY_OK: chunks[index].result = HapResult_No_Error; break; default: chunks[index].result = HapResult_Internal_Error; break; } } else if (chunks[index].compressor == kHapCompressorNone) { memcpy(chunks[index].uncompressed_chunk_data, chunks[index].compressed_chunk_data, chunks[index].compressed_chunk_size); chunks[index].result = HapResult_No_Error; } else { chunks[index].result = HapResult_Bad_Frame; } } } static unsigned int hap_decode_header_complex_instructions(const void *texture_section, uint32_t texture_section_length, int * chunk_count, const void **compressors, const void **chunk_sizes, const void **chunk_offsets, const char **frame_data){ int result = HapResult_No_Error; const void *section_start; uint32_t section_header_length; uint32_t section_length; unsigned int section_type; size_t bytes_remaining = 0; *compressors = NULL; *chunk_sizes = NULL; *chunk_offsets = NULL; result = hap_read_section_header(texture_section, texture_section_length, §ion_header_length, §ion_length, §ion_type); if (result == HapResult_No_Error && section_type != kHapSectionDecodeInstructionsContainer) { result = HapResult_Bad_Frame; } if (result != HapResult_No_Error) { return result; } /* Frame data follows immediately after the Decode Instructions Container */ *frame_data = ((const char *)texture_section) + section_header_length + section_length; /* Step through the sections inside the Decode Instructions Container */ section_start = ((uint8_t *)texture_section) + section_header_length; bytes_remaining = section_length; while (bytes_remaining > 0) { unsigned int section_chunk_count = 0; result = hap_read_section_header(section_start, bytes_remaining, §ion_header_length, §ion_length, §ion_type); if (result != HapResult_No_Error) { return result; } section_start = ((uint8_t *)section_start) + section_header_length; switch (section_type) { case kHapSectionChunkSecondStageCompressorTable: *compressors = section_start; section_chunk_count = section_length; break; case kHapSectionChunkSizeTable: *chunk_sizes = section_start; section_chunk_count = section_length / 4; break; case kHapSectionChunkOffsetTable: *chunk_offsets = section_start; section_chunk_count = section_length / 4; break; default: // Ignore unrecognized sections break; } /* If we calculated a chunk count and already have one, make sure they match */ if (section_chunk_count != 0) { if ((*chunk_count) != 0 && section_chunk_count != (*chunk_count)) { return HapResult_Bad_Frame; } *chunk_count = section_chunk_count; } section_start = ((uint8_t *)section_start) + section_length; bytes_remaining -= section_header_length + section_length; } /* The Chunk Second-Stage Compressor Table and Chunk Size Table are required */ if (*compressors == NULL || *chunk_sizes == NULL) { return HapResult_Bad_Frame; } return result; } unsigned int hap_decode_single_texture(const void *texture_section, uint32_t texture_section_length, unsigned int texture_section_type, HapDecodeCallback callback, void *info, void *outputBuffer, unsigned long outputBufferBytes, unsigned long *outputBufferBytesUsed, unsigned int *outputBufferTextureFormat) { int result = HapResult_No_Error; unsigned int textureFormat; unsigned int compressor; size_t bytesUsed = 0; /* One top-level section type describes texture-format and second-stage compression Hap compressor/format constants can be unpacked by reading the top and bottom four bits. */ compressor = hap_top_4_bits(texture_section_type); textureFormat = hap_bottom_4_bits(texture_section_type); /* Pass the texture format out */ *outputBufferTextureFormat = hap_texture_format_constant_for_format_identifier(textureFormat); if (*outputBufferTextureFormat == 0) { return HapResult_Bad_Frame; } if (compressor == kHapCompressorComplex) { /* The top-level section should contain a Decode Instructions Container followed by frame data */ int chunk_count = 0; const void *compressors = NULL; const void *chunk_sizes = NULL; const void *chunk_offsets = NULL; const char *frame_data = NULL; result = hap_decode_header_complex_instructions(texture_section, texture_section_length, &chunk_count, &compressors, &chunk_sizes, &chunk_offsets, &frame_data); if (result != HapResult_No_Error) { return result; } if (chunk_count > 0) { /* Step through the chunks, storing information for their decompression */ HapChunkDecodeInfo *chunk_info = (HapChunkDecodeInfo *)malloc(sizeof(HapChunkDecodeInfo) * chunk_count); size_t running_compressed_chunk_size = 0; size_t running_uncompressed_chunk_size = 0; int i; if (chunk_info == NULL) { return HapResult_Internal_Error; } for (i = 0; i < chunk_count; i++) { chunk_info[i].compressor = *(((uint8_t *)compressors) + i); chunk_info[i].compressed_chunk_size = hap_read_4_byte_uint(((uint8_t *)chunk_sizes) + (i * 4)); if (chunk_offsets) { chunk_info[i].compressed_chunk_data = frame_data + hap_read_4_byte_uint(((uint8_t *)chunk_offsets) + (i * 4)); } else { chunk_info[i].compressed_chunk_data = frame_data + running_compressed_chunk_size; } running_compressed_chunk_size += chunk_info[i].compressed_chunk_size; if (chunk_info[i].compressor == kHapCompressorSnappy) { snappy_status snappy_result = snappy_uncompressed_length(chunk_info[i].compressed_chunk_data, chunk_info[i].compressed_chunk_size, &(chunk_info[i].uncompressed_chunk_size)); if (snappy_result != SNAPPY_OK) { switch (snappy_result) { case SNAPPY_INVALID_INPUT: result = HapResult_Bad_Frame; break; default: result = HapResult_Internal_Error; break; } break; } } else { chunk_info[i].uncompressed_chunk_size = chunk_info[i].compressed_chunk_size; } chunk_info[i].uncompressed_chunk_data = (char *)(((uint8_t *)outputBuffer) + running_uncompressed_chunk_size); running_uncompressed_chunk_size += chunk_info[i].uncompressed_chunk_size; } if (result == HapResult_No_Error && running_uncompressed_chunk_size > outputBufferBytes) { result = HapResult_Buffer_Too_Small; } if (result == HapResult_No_Error) { /* Perform decompression */ bytesUsed = running_uncompressed_chunk_size; if (chunk_count == 1) { /* We don't invoke the callback for one chunk, just decode it directly */ hap_decode_chunk(chunk_info, 0); } else { callback((HapDecodeWorkFunction)hap_decode_chunk, chunk_info, chunk_count, info); } /* Check to see if we encountered any errors and report one of them */ for (i = 0; i < chunk_count; i++) { if (chunk_info[i].result != HapResult_No_Error) { result = chunk_info[i].result; break; } } } free(chunk_info); if (result != HapResult_No_Error) { return result; } } } else if (compressor == kHapCompressorSnappy) { /* Only one section is present containing a single block of snappy-compressed texture data */ snappy_status snappy_result = snappy_uncompressed_length((const char *)texture_section, texture_section_length, &bytesUsed); if (snappy_result != SNAPPY_OK) { return HapResult_Internal_Error; } if (bytesUsed > outputBufferBytes) { return HapResult_Buffer_Too_Small; } snappy_result = snappy_uncompress((const char *)texture_section, texture_section_length, (char *)outputBuffer, &bytesUsed); if (snappy_result != SNAPPY_OK) { return HapResult_Internal_Error; } } else if (compressor == kHapCompressorNone) { /* Only one section is present containing a single block of uncompressed texture data */ bytesUsed = texture_section_length; if (texture_section_length > outputBufferBytes) { return HapResult_Buffer_Too_Small; } memcpy(outputBuffer, texture_section, texture_section_length); } else { return HapResult_Bad_Frame; } /* Fill out the remaining return value */ if (outputBufferBytesUsed != NULL) { *outputBufferBytesUsed = bytesUsed; } return HapResult_No_Error; } int hap_get_section_at_index(const void *input_buffer, uint32_t input_buffer_bytes, unsigned int index, const void **section, uint32_t *section_length, unsigned int *section_type) { int result; uint32_t section_header_length; result = hap_read_section_header(input_buffer, input_buffer_bytes, §ion_header_length, section_length, section_type); if (result != HapResult_No_Error) { return result; } if (*section_type == kHapSectionMultipleImages) { /* Step through until we find the section at index */ size_t offset = 0; size_t top_section_length = *section_length; input_buffer = ((uint8_t *)input_buffer) + section_header_length; section_header_length = 0; *section_length = 0; for (int i = 0; i <= index; i++) { offset += section_header_length + *section_length; if (offset >= top_section_length) { return HapResult_Bad_Arguments; } result = hap_read_section_header(((uint8_t *)input_buffer) + offset, top_section_length - offset, §ion_header_length, section_length, section_type); if (result != HapResult_No_Error) { return result; } } offset += section_header_length; *section = ((uint8_t *)input_buffer) + offset; return HapResult_No_Error; } else if (index == 0) { /* A single-texture frame with the texture as the top section. */ *section = ((uint8_t *)input_buffer) + section_header_length; return HapResult_No_Error; } else { *section = NULL; *section_length = 0; *section_type = 0; return HapResult_Bad_Arguments; } } unsigned int HapDecode(const void *inputBuffer, unsigned long inputBufferBytes, unsigned int index, HapDecodeCallback callback, void *info, void *outputBuffer, unsigned long outputBufferBytes, unsigned long *outputBufferBytesUsed, unsigned int *outputBufferTextureFormat) { int result = HapResult_No_Error; const void *section; uint32_t section_length; unsigned int section_type; /* Check arguments */ if (inputBuffer == NULL || index > 1 || callback == NULL || outputBuffer == NULL || outputBufferTextureFormat == NULL ) { return HapResult_Bad_Arguments; } /* Locate the section at the given index, which will either be the top-level section in a single texture image, or one of the sections inside a multi-image top-level section. */ result = hap_get_section_at_index(inputBuffer, inputBufferBytes, index, §ion, §ion_length, §ion_type); if (result == HapResult_No_Error) { /* Decode the located texture */ result = hap_decode_single_texture(section, section_length, section_type, callback, info, outputBuffer, outputBufferBytes, outputBufferBytesUsed, outputBufferTextureFormat); } return result; } unsigned int HapGetFrameTextureCount(const void *inputBuffer, unsigned long inputBufferBytes, unsigned int *outputTextureCount) { int result; uint32_t section_header_length; uint32_t section_length; unsigned int section_type; result = hap_read_section_header(inputBuffer, inputBufferBytes, §ion_header_length, §ion_length, §ion_type); if (result != HapResult_No_Error) { return result; } if (section_type == kHapSectionMultipleImages) { /* Step through, counting sections */ uint32_t offset = section_header_length; uint32_t top_section_length = section_length; *outputTextureCount = 0; while (offset < top_section_length) { result = hap_read_section_header(((uint8_t *)inputBuffer) + offset, inputBufferBytes - offset, §ion_header_length, §ion_length, §ion_type); if (result != HapResult_No_Error) { return result; } offset += section_header_length + section_length; *outputTextureCount += 1; } return HapResult_No_Error; } else { /* A single-texture frame with the texture as the top section. */ *outputTextureCount = 1; return HapResult_No_Error; } } unsigned int HapGetFrameTextureFormat(const void *inputBuffer, unsigned long inputBufferBytes, unsigned int index, unsigned int *outputBufferTextureFormat) { unsigned int result = HapResult_No_Error; const void *section; uint32_t section_length; unsigned int section_type; /* Check arguments */ if (inputBuffer == NULL || index > 1 || outputBufferTextureFormat == NULL ) { return HapResult_Bad_Arguments; } /* Locate the section at the given index, which will either be the top-level section in a single texture image, or one of the sections inside a multi-image top-level section. */ result = hap_get_section_at_index(inputBuffer, inputBufferBytes, index, §ion, §ion_length, §ion_type); if (result == HapResult_No_Error) { /* Pass the API enum value to match the constant out */ *outputBufferTextureFormat = hap_texture_format_constant_for_format_identifier(hap_bottom_4_bits(section_type)); /* Check a valid format was present */ if (*outputBufferTextureFormat == 0) { result = HapResult_Bad_Frame; } } return result; } unsigned int HapGetFrameTextureChunkCount(const void *inputBuffer, unsigned long inputBufferBytes, unsigned int index, int *chunk_count) { unsigned int result = HapResult_No_Error; const void *section; uint32_t section_length; unsigned int section_type; *chunk_count = 0; /* Check arguments */ if (inputBuffer == NULL || index > 1 ) { return HapResult_Bad_Arguments; } /* Locate the section at the given index, which will either be the top-level section in a single texture image, or one of the sections inside a multi-image top-level section. */ result = hap_get_section_at_index(inputBuffer, inputBufferBytes, index, §ion, §ion_length, §ion_type); if (result == HapResult_No_Error) { unsigned int compressor; /* One top-level section type describes texture-format and second-stage compression Hap compressor/format constants can be unpacked by reading the top and bottom four bits. */ compressor = hap_top_4_bits(section_type); if (compressor == kHapCompressorComplex) { /* The top-level section should contain a Decode Instructions Container followed by frame data */ const void *compressors = NULL; const void *chunk_sizes = NULL; const void *chunk_offsets = NULL; const char *frame_data = NULL; result = hap_decode_header_complex_instructions(section, section_length, chunk_count, &compressors, &chunk_sizes, &chunk_offsets, &frame_data); if (result != HapResult_No_Error) { return result; } } else if ((compressor == kHapCompressorSnappy)||(compressor == kHapCompressorNone)) { *chunk_count = 1; } else { return HapResult_Bad_Frame; } } return result; } ================================================ FILE: Plugin/Hap/hap.h ================================================ /* hap.h Copyright (c) 2011-2013, Tom Butterworth and Vidvox LLC. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef hap_h #define hap_h #ifdef __cplusplus extern "C" { #endif /* These match the constants defined by GL_EXT_texture_compression_s3tc, GL_ARB_texture_compression_rgtc and GL_ARB_texture_compression_bptc */ enum HapTextureFormat { HapTextureFormat_RGB_DXT1 = 0x83F0, HapTextureFormat_RGBA_DXT5 = 0x83F3, HapTextureFormat_YCoCg_DXT5 = 0x01, HapTextureFormat_A_RGTC1 = 0x8DBB, HapTextureFormat_RGBA_BPTC_UNORM = 0x8E8C, HapTextureFormat_RGB_BPTC_UNSIGNED_FLOAT = 0x8E8F, HapTextureFormat_RGB_BPTC_SIGNED_FLOAT = 0x8E8E, }; enum HapCompressor { HapCompressorNone, HapCompressorSnappy }; enum HapResult { HapResult_No_Error = 0, HapResult_Bad_Arguments, HapResult_Buffer_Too_Small, HapResult_Bad_Frame, HapResult_Internal_Error }; /* See HapDecode for descriptions of these function types. */ typedef void (*HapDecodeWorkFunction)(void *p, unsigned int index); typedef void (*HapDecodeCallback)(HapDecodeWorkFunction function, void *p, unsigned int count, void *info); /* Returns the maximum size of an output buffer for a frame composed of one or more textures, or returns 0 on error. count is the number of textures (1 or 2) and matches the number of values in the array arguments lengths is an array of input texture lengths in bytes textureFormats is an array of HapTextureFormats chunkCounts is an array of chunk counts (1 or more) */ unsigned long HapMaxEncodedLength(unsigned int count, unsigned long *lengths, unsigned int *textureFormats, unsigned int *chunkCounts); /* Encodes one or multiple textures into one Hap frame, or returns an error. Permitted multiple-texture combinations are: HapTextureFormat_YCoCg_DXT5 + HapTextureFormat_A_RGTC1 Use HapMaxEncodedLength() to discover the minimal value for outputBufferBytes. count is the number of textures (1 or 2) and matches the number of values in the array arguments inputBuffers is an array of count pointers to texture data inputBufferBytes is an array of texture data lengths in bytes textureFormats is an array of HapTextureFormats compressors is an array of HapCompressors chunkCounts is an array of chunk counts to permit multithreaded decoding (1 or more) outputBuffer is the destination buffer to receive the encoded frame outputBufferBytes is the destination buffer's length in bytes outputBufferBytesUsed will be set to the actual encoded length of the frame on return */ unsigned int HapEncode(unsigned int count, const void **inputBuffers, unsigned long *inputBuffersBytes, unsigned int *textureFormats, unsigned int *compressors, unsigned int *chunkCounts, void *outputBuffer, unsigned long outputBufferBytes, unsigned long *outputBufferBytesUsed); /* Decodes a texture from inputBuffer which is a Hap frame. A frame may contain multiple textures which are to be combined to create the final image. Use HapGetFrameTextureCount() to discover the number of textures in a frame, and then access each texture by incrementing the index argument to this function. If the frame permits multithreaded decoding, callback will be called once for you to invoke a platform-appropriate mechanism to assign work to threads, and trigger that work by calling the function passed to your callback the number of times indicated by the count argument, usually from a number of different threads. This callback must not return until all the work has been completed. void MyHapDecodeCallback(HapDecodeWorkFunction function, void *p, unsigned int count, void *info) { int i; for (i = 0; i < count; i++) { // Invoke your multithreading mechanism to cause this function to be called // on a suitable number of threads. function(p, i); } } info is an argument for your own use to pass context to the callback. If the frame does not permit multithreaded decoding, callback will not be called. If outputBufferBytesUsed is not NULL then it will be set to the decoded length of the output buffer. outputBufferTextureFormat must be non-NULL, and will be set to one of the HapTextureFormat constants. */ unsigned int HapDecode(const void *inputBuffer, unsigned long inputBufferBytes, unsigned int index, HapDecodeCallback callback, void *info, void *outputBuffer, unsigned long outputBufferBytes, unsigned long *outputBufferBytesUsed, unsigned int *outputBufferTextureFormat); /* If this returns HapResult_No_Error then outputTextureCount is set to the count of textures in the frame. */ unsigned int HapGetFrameTextureCount(const void *inputBuffer, unsigned long inputBufferBytes, unsigned int *outputTextureCount); /* On return sets outputBufferTextureFormat to a HapTextureFormat constant describing the format of the texture at index in the frame. */ unsigned int HapGetFrameTextureFormat(const void *inputBuffer, unsigned long inputBufferBytes, unsigned int index, unsigned int *outputBufferTextureFormat); /* On return sets chunk_count to the chunk count value of the texture at index in the frame. */ unsigned int HapGetFrameTextureChunkCount(const void *inputBuffer, unsigned long inputBufferBytes, unsigned int index, int *chunk_count); #ifdef __cplusplus } #endif #endif ================================================ FILE: Plugin/MP4/mp4defs.h ================================================ /** 25.09.2018 @file * * Common MP4 definitions * * Acronyms: * AVC = Advanced Video Coding (AKA H.264) * AAC = Advanced Audio Coding * OD = Object descriptor * DSI = Decoder Specific Info (AAC element) * LC = Low Complexity (AAC profile) * SPS = Sequence Parameter Set (H.264 element) * PPS = Picture Parameter Set (H.264 element) * * The MP4 file has several tracks. Each track contains a number of 'samples' * (audio or video frames). Position and size of each sample in the track * encoded in the track index. * */ #ifndef mp4defs_H_INCLUDED #define mp4defs_H_INCLUDED /************************************************************************/ /* Some values of MP4X_track_t::object_type_indication */ /************************************************************************/ enum { // MPEG-4 AAC (all profiles) MP4_OBJECT_TYPE_AUDIO_ISO_IEC_14496_3 = 0x40, // MPEG-2 AAC, Main profile MP4_OBJECT_TYPE_AUDIO_ISO_IEC_13818_7_MAIN_PROFILE = 0x66, // MPEG-2 AAC, LC profile MP4_OBJECT_TYPE_AUDIO_ISO_IEC_13818_7_LC_PROFILE = 0x67, // MPEG-2 AAC, SSR profile MP4_OBJECT_TYPE_AUDIO_ISO_IEC_13818_7_SSR_PROFILE = 0x68, // H.264 (AVC) video MP4_OBJECT_TYPE_AVC = 0x21, // http://www.mp4ra.org/object.html 0xC0-E0 && 0xE2 - 0xFE are specified as "user private" MP4_OBJECT_TYPE_USER_PRIVATE = 0xC0 }; /************************************************************************/ /* Some values of MP4X_track_t::handler_type */ /************************************************************************/ enum { // Video track : 'vide' MP4_HANDLER_TYPE_VIDE = 0x76696465, // Audio track : 'soun' MP4_HANDLER_TYPE_SOUN = 0x736F756E, // General MPEG-4 systems streams (without specific handler). // Used for private stream, as suggested in http://www.mp4ra.org/handler.html MP4_HANDLER_TYPE_GESM = 0x6765736D, // Text comment track MP4_HANDLER_TYPE_MDIR = 0x6d646972 }; /************************************************************************/ /* Complete box list (most of them not used here) */ /************************************************************************/ #define FOUR_CHAR_INT( a, b, c, d ) (((((((unsigned)(a)<<8)|(b))<<8)|(c))<<8)|(d)) enum { BOX_co64 = FOUR_CHAR_INT( 'c', 'o', '6', '4' ),//ChunkLargeOffsetAtomType BOX_stco = FOUR_CHAR_INT( 's', 't', 'c', 'o' ),//ChunkOffsetAtomType BOX_crhd = FOUR_CHAR_INT( 'c', 'r', 'h', 'd' ),//ClockReferenceMediaHeaderAtomType BOX_ctts = FOUR_CHAR_INT( 'c', 't', 't', 's' ),//CompositionOffsetAtomType BOX_cprt = FOUR_CHAR_INT( 'c', 'p', 'r', 't' ),//CopyrightAtomType BOX_url_ = FOUR_CHAR_INT( 'u', 'r', 'l', ' ' ),//DataEntryURLAtomType BOX_urn_ = FOUR_CHAR_INT( 'u', 'r', 'n', ' ' ),//DataEntryURNAtomType BOX_dinf = FOUR_CHAR_INT( 'd', 'i', 'n', 'f' ),//DataInformationAtomType BOX_dref = FOUR_CHAR_INT( 'd', 'r', 'e', 'f' ),//DataReferenceAtomType BOX_stdp = FOUR_CHAR_INT( 's', 't', 'd', 'p' ),//DegradationPriorityAtomType BOX_edts = FOUR_CHAR_INT( 'e', 'd', 't', 's' ),//EditAtomType BOX_elst = FOUR_CHAR_INT( 'e', 'l', 's', 't' ),//EditListAtomType BOX_uuid = FOUR_CHAR_INT( 'u', 'u', 'i', 'd' ),//ExtendedAtomType BOX_free = FOUR_CHAR_INT( 'f', 'r', 'e', 'e' ),//FreeSpaceAtomType BOX_hdlr = FOUR_CHAR_INT( 'h', 'd', 'l', 'r' ),//HandlerAtomType BOX_hmhd = FOUR_CHAR_INT( 'h', 'm', 'h', 'd' ),//HintMediaHeaderAtomType BOX_hint = FOUR_CHAR_INT( 'h', 'i', 'n', 't' ),//HintTrackReferenceAtomType BOX_mdia = FOUR_CHAR_INT( 'm', 'd', 'i', 'a' ),//MediaAtomType BOX_mdat = FOUR_CHAR_INT( 'm', 'd', 'a', 't' ),//MediaDataAtomType BOX_mdhd = FOUR_CHAR_INT( 'm', 'd', 'h', 'd' ),//MediaHeaderAtomType BOX_minf = FOUR_CHAR_INT( 'm', 'i', 'n', 'f' ),//MediaInformationAtomType BOX_moov = FOUR_CHAR_INT( 'm', 'o', 'o', 'v' ),//MovieAtomType BOX_mvhd = FOUR_CHAR_INT( 'm', 'v', 'h', 'd' ),//MovieHeaderAtomType BOX_stsd = FOUR_CHAR_INT( 's', 't', 's', 'd' ),//SampleDescriptionAtomType BOX_stsz = FOUR_CHAR_INT( 's', 't', 's', 'z' ),//SampleSizeAtomType BOX_stz2 = FOUR_CHAR_INT( 's', 't', 'z', '2' ),//CompactSampleSizeAtomType BOX_stbl = FOUR_CHAR_INT( 's', 't', 'b', 'l' ),//SampleTableAtomType BOX_stsc = FOUR_CHAR_INT( 's', 't', 's', 'c' ),//SampleToChunkAtomType BOX_stsh = FOUR_CHAR_INT( 's', 't', 's', 'h' ),//ShadowSyncAtomType BOX_skip = FOUR_CHAR_INT( 's', 'k', 'i', 'p' ),//SkipAtomType BOX_smhd = FOUR_CHAR_INT( 's', 'm', 'h', 'd' ),//SoundMediaHeaderAtomType BOX_stss = FOUR_CHAR_INT( 's', 't', 's', 's' ),//SyncSampleAtomType BOX_stts = FOUR_CHAR_INT( 's', 't', 't', 's' ),//TimeToSampleAtomType BOX_trak = FOUR_CHAR_INT( 't', 'r', 'a', 'k' ),//TrackAtomType BOX_tkhd = FOUR_CHAR_INT( 't', 'k', 'h', 'd' ),//TrackHeaderAtomType BOX_tref = FOUR_CHAR_INT( 't', 'r', 'e', 'f' ),//TrackReferenceAtomType BOX_udta = FOUR_CHAR_INT( 'u', 'd', 't', 'a' ),//UserDataAtomType BOX_vmhd = FOUR_CHAR_INT( 'v', 'm', 'h', 'd' ),//VideoMediaHeaderAtomType BOX_url = FOUR_CHAR_INT( 'u', 'r', 'l', ' ' ), BOX_urn = FOUR_CHAR_INT( 'u', 'r', 'n', ' ' ), BOX_gnrv = FOUR_CHAR_INT( 'g', 'n', 'r', 'v' ),//GenericVisualSampleEntryAtomType BOX_gnra = FOUR_CHAR_INT( 'g', 'n', 'r', 'a' ),//GenericAudioSampleEntryAtomType //V2 atoms BOX_ftyp = FOUR_CHAR_INT( 'f', 't', 'y', 'p' ),//FileTypeAtomType BOX_padb = FOUR_CHAR_INT( 'p', 'a', 'd', 'b' ),//PaddingBitsAtomType //MP4 Atoms BOX_sdhd = FOUR_CHAR_INT( 's', 'd', 'h', 'd' ),//SceneDescriptionMediaHeaderAtomType BOX_dpnd = FOUR_CHAR_INT( 'd', 'p', 'n', 'd' ),//StreamDependenceAtomType BOX_iods = FOUR_CHAR_INT( 'i', 'o', 'd', 's' ),//ObjectDescriptorAtomType BOX_odhd = FOUR_CHAR_INT( 'o', 'd', 'h', 'd' ),//ObjectDescriptorMediaHeaderAtomType BOX_mpod = FOUR_CHAR_INT( 'm', 'p', 'o', 'd' ),//ODTrackReferenceAtomType BOX_nmhd = FOUR_CHAR_INT( 'n', 'm', 'h', 'd' ),//MPEGMediaHeaderAtomType BOX_esds = FOUR_CHAR_INT( 'e', 's', 'd', 's' ),//ESDAtomType BOX_sync = FOUR_CHAR_INT( 's', 'y', 'n', 'c' ),//OCRReferenceAtomType BOX_ipir = FOUR_CHAR_INT( 'i', 'p', 'i', 'r' ),//IPIReferenceAtomType BOX_mp4s = FOUR_CHAR_INT( 'm', 'p', '4', 's' ),//MPEGSampleEntryAtomType BOX_mp4a = FOUR_CHAR_INT( 'm', 'p', '4', 'a' ),//MPEGAudioSampleEntryAtomType BOX_mp4v = FOUR_CHAR_INT( 'm', 'p', '4', 'v' ),//MPEGVisualSampleEntryAtomType // http://www.itscj.ipsj.or.jp/sc29/open/29view/29n7644t.doc BOX_avc1 = FOUR_CHAR_INT( 'a', 'v', 'c', '1' ), BOX_avc2 = FOUR_CHAR_INT( 'a', 'v', 'c', '2' ), BOX_svc1 = FOUR_CHAR_INT( 's', 'v', 'c', '1' ), BOX_avcC = FOUR_CHAR_INT( 'a', 'v', 'c', 'C' ), BOX_svcC = FOUR_CHAR_INT( 's', 'v', 'c', 'C' ), BOX_btrt = FOUR_CHAR_INT( 'b', 't', 'r', 't' ), BOX_m4ds = FOUR_CHAR_INT( 'm', '4', 'd', 's' ), BOX_seib = FOUR_CHAR_INT( 's', 'e', 'i', 'b' ), //3GPP atoms BOX_samr = FOUR_CHAR_INT( 's', 'a', 'm', 'r' ),//AMRSampleEntryAtomType BOX_sawb = FOUR_CHAR_INT( 's', 'a', 'w', 'b' ),//WB_AMRSampleEntryAtomType BOX_damr = FOUR_CHAR_INT( 'd', 'a', 'm', 'r' ),//AMRConfigAtomType BOX_s263 = FOUR_CHAR_INT( 's', '2', '6', '3' ),//H263SampleEntryAtomType BOX_d263 = FOUR_CHAR_INT( 'd', '2', '6', '3' ),//H263ConfigAtomType //V2 atoms - Movie Fragments BOX_mvex = FOUR_CHAR_INT( 'm', 'v', 'e', 'x' ),//MovieExtendsAtomType BOX_trex = FOUR_CHAR_INT( 't', 'r', 'e', 'x' ),//TrackExtendsAtomType BOX_moof = FOUR_CHAR_INT( 'm', 'o', 'o', 'f' ),//MovieFragmentAtomType BOX_mfhd = FOUR_CHAR_INT( 'm', 'f', 'h', 'd' ),//MovieFragmentHeaderAtomType BOX_traf = FOUR_CHAR_INT( 't', 'r', 'a', 'f' ),//TrackFragmentAtomType BOX_tfhd = FOUR_CHAR_INT( 't', 'f', 'h', 'd' ),//TrackFragmentHeaderAtomType BOX_trun = FOUR_CHAR_INT( 't', 'r', 'u', 'n' ),//TrackFragmentRunAtomType // Object Descriptors (OD) data coding // These takes only 1 byte; this implementation translate to // + OD_BASE to keep API uniform and safe for string functions OD_BASE = FOUR_CHAR_INT( '$', '$', '$', '0' ),// OD_ESD = FOUR_CHAR_INT( '$', '$', '$', '3' ),//SDescriptor_Tag OD_DCD = FOUR_CHAR_INT( '$', '$', '$', '4' ),//DecoderConfigDescriptor_Tag OD_DSI = FOUR_CHAR_INT( '$', '$', '$', '5' ),//DecoderSpecificInfo_Tag OD_SLC = FOUR_CHAR_INT( '$', '$', '$', '6' ),//SLConfigDescriptor_Tag BOX_meta = FOUR_CHAR_INT( 'm', 'e', 't', 'a' ), BOX_ilst = FOUR_CHAR_INT( 'i', 'l', 's', 't' ), // Metagata tags, see http://atomicparsley.sourceforge.net/mpeg-4files.html BOX_calb = FOUR_CHAR_INT( '\xa9', 'a', 'l', 'b'), // album BOX_cart = FOUR_CHAR_INT( '\xa9', 'a', 'r', 't'), // artist BOX_aART = FOUR_CHAR_INT( 'a', 'A', 'R', 'T' ), // album artist BOX_ccmt = FOUR_CHAR_INT( '\xa9', 'c', 'm', 't'), // comment BOX_cday = FOUR_CHAR_INT( '\xa9', 'd', 'a', 'y'), // year (as string) BOX_cnam = FOUR_CHAR_INT( '\xa9', 'n', 'a', 'm'), // title BOX_cgen = FOUR_CHAR_INT( '\xa9', 'g', 'e', 'n'), // custom genre (as string or as byte!) BOX_trkn = FOUR_CHAR_INT( 't', 'r', 'k', 'n'), // track number (byte) BOX_disk = FOUR_CHAR_INT( 'd', 'i', 's', 'k'), // disk number (byte) BOX_cwrt = FOUR_CHAR_INT( '\xa9', 'w', 'r', 't'), // composer BOX_ctoo = FOUR_CHAR_INT( '\xa9', 't', 'o', 'o'), // encoder BOX_tmpo = FOUR_CHAR_INT( 't', 'm', 'p', 'o'), // bpm (byte) BOX_cpil = FOUR_CHAR_INT( 'c', 'p', 'i', 'l'), // compilation (byte) BOX_covr = FOUR_CHAR_INT( 'c', 'o', 'v', 'r'), // cover art (JPEG/PNG) BOX_rtng = FOUR_CHAR_INT( 'r', 't', 'n', 'g'), // rating/advisory (byte) BOX_cgrp = FOUR_CHAR_INT( '\xa9', 'g', 'r', 'p'), // grouping BOX_stik = FOUR_CHAR_INT( 's', 't', 'i', 'k'), // stik (byte) 0 = Movie 1 = Normal 2 = Audiobook 5 = Whacked Bookmark 6 = Music Video 9 = Short Film 10 = TV Show 11 = Booklet 14 = Ringtone BOX_pcst = FOUR_CHAR_INT( 'p', 'c', 's', 't'), // podcast (byte) BOX_catg = FOUR_CHAR_INT( 'c', 'a', 't', 'g'), // category BOX_keyw = FOUR_CHAR_INT( 'k', 'e', 'y', 'w'), // keyword BOX_purl = FOUR_CHAR_INT( 'p', 'u', 'r', 'l'), // podcast URL (byte) BOX_egid = FOUR_CHAR_INT( 'e', 'g', 'i', 'd'), // episode global unique ID (byte) BOX_desc = FOUR_CHAR_INT( 'd', 'e', 's', 'c'), // description BOX_clyr = FOUR_CHAR_INT( '\xa9', 'l', 'y', 'r'), // lyrics (may be > 255 bytes) BOX_tven = FOUR_CHAR_INT( 't', 'v', 'e', 'n'), // tv episode number BOX_tves = FOUR_CHAR_INT( 't', 'v', 'e', 's'), // tv episode (byte) BOX_tvnn = FOUR_CHAR_INT( 't', 'v', 'n', 'n'), // tv network name BOX_tvsh = FOUR_CHAR_INT( 't', 'v', 's', 'h'), // tv show name BOX_tvsn = FOUR_CHAR_INT( 't', 'v', 's', 'n'), // tv season (byte) BOX_purd = FOUR_CHAR_INT( 'p', 'u', 'r', 'd'), // purchase date BOX_pgap = FOUR_CHAR_INT( 'p', 'g', 'a', 'p'), // Gapless Playback (byte) //BOX_aart = FOUR_CHAR_INT( 'a', 'a', 'r', 't' ), // Album artist BOX_cART = FOUR_CHAR_INT( '\xa9', 'A', 'R', 'T'), // artist BOX_gnre = FOUR_CHAR_INT( 'g', 'n', 'r', 'e'), // 3GPP metatags (http://cpansearch.perl.org/src/JHAR/MP4-Info-1.12/Info.pm) BOX_auth = FOUR_CHAR_INT( 'a', 'u', 't', 'h'), // author BOX_titl = FOUR_CHAR_INT( 't', 'i', 't', 'l'), // title BOX_dscp = FOUR_CHAR_INT( 'd', 's', 'c', 'p'), // description BOX_perf = FOUR_CHAR_INT( 'p', 'e', 'r', 'f'), // performer BOX_mean = FOUR_CHAR_INT( 'm', 'e', 'a', 'n'), // BOX_name = FOUR_CHAR_INT( 'n', 'a', 'm', 'e'), // BOX_data = FOUR_CHAR_INT( 'd', 'a', 't', 'a'), // // these from http://lists.mplayerhq.hu/pipermail/ffmpeg-devel/2008-September/053151.html BOX_albm = FOUR_CHAR_INT( 'a', 'l', 'b', 'm'), // album BOX_yrrc = FOUR_CHAR_INT( 'y', 'r', 'r', 'c'), // album // Hap subtypes BOX_Hap1 = FOUR_CHAR_INT( 'H', 'a', 'p', '1' ), BOX_Hap5 = FOUR_CHAR_INT( 'H', 'a', 'p', '5' ), BOX_HapY = FOUR_CHAR_INT( 'H', 'a', 'p', 'Y' ), BOX_HapM = FOUR_CHAR_INT( 'H', 'a', 'p', 'M' ), BOX_HapA = FOUR_CHAR_INT( 'H', 'a', 'p', 'A' ) }; #endif //mp4defs_H_INCLUDED ================================================ FILE: Plugin/MP4/mp4demux.c ================================================ /** 20.09.2009 ASP @file * */ #include "mp4demux.h" #include #include #include #include // LONG_MAX #include // struct stat #include // fstat - for file size /************************************************************************/ /* Build config */ /************************************************************************/ // Max chunks nesting level #define MP4D_MAX_CHUNKS_DEPTH 64 // Debug trace #ifndef MP4D_DEBUG_TRACE # define MP4D_DEBUG_TRACE 0 #endif #if MP4D_DEBUG_TRACE # define MP4D_TRACE(x) printf x #else # define MP4D_TRACE(x) #endif // Box type: ATOM box, or 'Object Descriptor' box inside the atom. typedef enum {BOX_ATOM, BOX_OD} mp4d_boxtype_t; /************************************************************************/ /* File input (non-portable) stuff */ /************************************************************************/ /** * Return 64-bit file size in most portable way */ static off_t mp4d_fsize(FILE * f) { struct stat st; #ifdef _MSC_VER if (fstat(_fileno(f), &st) == 0) #else if (fstat(fileno(f), &st) == 0) #endif { return st.st_size; } return -1; } /** * Read given number of bytes from the file * Used to read box headers */ static unsigned mp4d_read(FILE * f, int nb, int * eof_flag) { uint32_t v = 0; int last_byte; switch (nb) { case 4: v = (v << 8) | fgetc(f); case 3: v = (v << 8) | fgetc(f); case 2: v = (v << 8) | fgetc(f); default: case 1: v = (v << 8) | (last_byte = fgetc(f)); } if (last_byte == EOF) { *eof_flag = 1; } return v; } /** * Read given number of bytes, but no more than *payload_bytes specifies... * Used to read box payload */ static uint32_t mp4d_read_payload(FILE * f, unsigned nb, mp4d_size_t * payload_bytes, int * eof_flag) { if (*payload_bytes < nb) { *eof_flag = 1; nb = (int)*payload_bytes; } *payload_bytes -= nb; return mp4d_read(f, nb, eof_flag); } /** * Skips given number of bytes. */ static void mp4d_skip_bytes(FILE * f, mp4d_size_t skip, int * eof_flag) { while (skip > 0) { long lpos = (long)(skip < (mp4d_size_t)LONG_MAX ? skip : LONG_MAX); if (fseek(f, lpos, SEEK_CUR)) { *eof_flag = 1; return; } skip -= lpos; } } #define READ(n) mp4d_read_payload(f, n, &payload_bytes, &eof_flag) #define SKIP(n) {mp4d_size_t t = payload_bytes < (n) ? payload_bytes : (n); mp4d_skip_bytes(f, t, &eof_flag); payload_bytes -= t;} #define MP4D_MALLOC(p, size) p = malloc(size); if (!(p)) {MP4D_ERROR("out of memory");} #define MP4D_REALLOC(p, size) {void * r = realloc(p, size); if (!(r)) {MP4D_ERROR("out of memory");} else p = r;}; /* * On error: release resources, rewind the file. */ #define MP4D_RETURN_ERROR(mess) { \ MP4D_TRACE(("\nMP4 ERROR: " mess)); \ fseek(f, 0, SEEK_SET); \ MP4D__close(mp4); \ return 0; \ } /* * Any errors, occurred on top-level hierarchy is passed to exit check: 'if (!mp4->track_count) ... ' */ #define MP4D_ERROR(mess) \ if (!depth) \ break; \ else \ MP4D_RETURN_ERROR(mess); /************************************************************************/ /* Exported API functions */ /************************************************************************/ /** * Parse given file as MP4 file. Allocate and store data indexes. */ int MP4D__open(MP4D_demux_t * mp4, FILE * f) { int depth = 0; // box stack size // box stack struct { // remaining bytes for box in the stack mp4d_size_t bytes; // kind of box children's: OD chunks handled in the same manner as name chunks mp4d_boxtype_t format; } stack[MP4D_MAX_CHUNKS_DEPTH]; off_t file_size = mp4d_fsize(f); int eof_flag = 0; unsigned i; MP4D_track_t * tr = NULL; int read_hdlr = 0; #if MP4D_DEBUG_TRACE // path of current element: List0/List1/... etc uint32_t box_path[MP4D_MAX_CHUNKS_DEPTH]; #endif if (!f || !mp4) { MP4D_TRACE(("\nERROR: invlaid arguments!")); return 0; } if (fseek(f, 0, SEEK_SET)) // some platforms missing rewind() { return 0; } memset(mp4, 0, sizeof(MP4D_demux_t)); stack[0].format = BOX_ATOM; // start with atom box stack[0].bytes = 0; // never accessed do { // List of boxes, derived from 'FullBox' // ~~~~~~~~~~~~~~~~~~~~~ // need read version field and check version for these boxes static const struct { uint32_t name; unsigned max_version; unsigned use_track_flag; } g_fullbox[] = { {BOX_mdhd, 1, 1}, {BOX_mvhd, 1, 0}, {BOX_hdlr, 0, 0}, {BOX_meta, 0, 0}, {BOX_stts, 0, 0}, {BOX_ctts, 0, 0}, {BOX_stz2, 0, 1}, {BOX_stsz, 0, 1}, {BOX_stsc, 0, 1}, {BOX_stco, 0, 1}, {BOX_co64, 0, 1}, {BOX_stsd, 0, 0}, {BOX_esds, 0, 1} // esds does not use track, but switches to OD mode. Check here, to avoid OD check }; // List of boxes, which contains other boxes ('envelopes') // Parser will descend down for boxes in this list, otherwise parsing will proceed to // the next sibling box // OD boxes handled in the same way as atom boxes... static const struct { uint32_t name; mp4d_boxtype_t type; } g_envelope_box[] = { {BOX_esds, BOX_OD}, // TODO: BOX_esds can be used for both audio and video, but this code supports audio only! {OD_ESD, BOX_OD}, {OD_DCD, BOX_OD}, {OD_DSI, BOX_OD}, {BOX_trak, BOX_ATOM}, {BOX_moov, BOX_ATOM}, {BOX_mdia, BOX_ATOM}, {BOX_tref, BOX_ATOM}, {BOX_minf, BOX_ATOM}, {BOX_dinf, BOX_ATOM}, {BOX_stbl, BOX_ATOM}, {BOX_stsd, BOX_ATOM}, {BOX_mp4a, BOX_ATOM}, {BOX_mp4s, BOX_ATOM}, {BOX_mp4v, BOX_ATOM}, {BOX_avc1, BOX_ATOM}, {BOX_udta, BOX_ATOM}, {BOX_meta, BOX_ATOM}, {BOX_ilst, BOX_ATOM} }; uint32_t FullAtomVersionAndFlags = 0; mp4d_size_t payload_bytes; mp4d_size_t box_bytes; uint32_t box_name; unsigned char ** ptag = NULL; int read_bytes = 0; // Read header box type and it's length if (stack[depth].format == BOX_ATOM) { box_bytes = mp4d_read(f, 4, &eof_flag); if (eof_flag) { break; // normal exit } if (box_bytes >= 2 && box_bytes < 8) { MP4D_ERROR("invalid box size (broken file?)"); } box_name = mp4d_read(f, 4, &eof_flag); read_bytes = 8; // Decode box size if (box_bytes == 0 || // standard indication of 'till eof' size box_bytes == (mp4d_size_t)0xFFFFFFFFU // some files uses non-standard 'till eof' signaling ) { box_bytes = ~(mp4d_size_t)0; } payload_bytes = box_bytes - 8; if (box_bytes == 1) // 64-bit sizes { box_bytes = mp4d_read(f, 4, &eof_flag); box_bytes <<= 32; box_bytes |= mp4d_read(f, 4, &eof_flag); if (box_bytes < 16) { MP4D_ERROR("invalid box size (broken file?)"); } payload_bytes = box_bytes - 16; } // Read and check box version for some boxes for (i = 0; i < sizeof(g_fullbox)/sizeof(g_fullbox[0]); i++) { if (box_name == g_fullbox[i].name) { FullAtomVersionAndFlags = READ(4); read_bytes += 4; if ((FullAtomVersionAndFlags >> 24) > g_fullbox[i].max_version) { MP4D_ERROR("unsupported box version!"); } if (g_fullbox[i].use_track_flag && !tr) { MP4D_ERROR("broken file structure!"); } } } } else // stack[depth].format == BOX_OD { int val; box_name = OD_BASE + mp4d_read(f, 1, &eof_flag); // 1-byte box type read_bytes += 1; if (eof_flag) { break; } payload_bytes = 0; box_bytes = 1; do { val = mp4d_read(f, 1, &eof_flag); read_bytes += 1; if (eof_flag) { MP4D_ERROR("premature EOF!"); } payload_bytes = (payload_bytes << 7) | (val & 0x7F); box_bytes++; } while (val & 0x80); box_bytes += payload_bytes; } #if MP4D_DEBUG_TRACE box_path[depth] = (box_name >> 24) | (box_name << 24) | ((box_name >> 8) & 0x0000FF00) | ((box_name << 8) & 0x00FF0000); MP4D_TRACE(("%2d %8d %.*s (%d bytes remains for sibilings) \n", depth, (int)box_bytes, depth*4, (char*)box_path, (int)stack[depth].bytes)); #endif // Check that box size <= parent size if (depth) { // Skip box with bad size assert(box_bytes > 0); if (box_bytes > stack[depth].bytes) { MP4D_TRACE(("Wrong %c%c%c%c box size: broken file?\n", (box_name >> 24)&255, (box_name >> 16)&255, (box_name >> 8)&255, box_name&255)); box_bytes = stack[depth].bytes; box_name = 0; payload_bytes = box_bytes - read_bytes; } stack[depth].bytes -= box_bytes; } // Read box header switch(box_name) { case BOX_stz2: //ISO/IEC 14496-1 Page 38. Section 8.17.2 - Sample Size Box. case BOX_stsz: { int carry_size = 0; uint32_t sample_size = READ(4); tr->sample_count = READ(4); MP4D_MALLOC(tr->entry_size, tr->sample_count*4); for (i = 0; i < tr->sample_count; i++) { if (box_name == BOX_stsz) { tr->entry_size[i] = (sample_size?sample_size:READ(4)); } else { switch (sample_size & 0xFF) { case 16: tr->entry_size[i] = READ(2); break; case 8: tr->entry_size[i] = READ(1); break; case 4: if (i&1) { tr->entry_size[i] = carry_size & 15; } else { carry_size = READ(1); tr->entry_size[i] = (carry_size >> 4); } break; } } } } break; case BOX_stsc: //ISO/IEC 14496-12 Page 38. Section 8.18 - Sample To Chunk Box. tr->sample_to_chunk_count = READ(4); MP4D_MALLOC(tr->sample_to_chunk, tr->sample_to_chunk_count*sizeof(tr->sample_to_chunk[0])); for (i = 0; i < tr->sample_to_chunk_count; i++) { tr->sample_to_chunk[i].first_chunk = READ(4); tr->sample_to_chunk[i].samples_per_chunk = READ(4); SKIP(4); // sample_description_index } break; case BOX_stts: { unsigned count = READ(4); unsigned j, k = 0, ts = 0, ts_count = count; MP4D_MALLOC(tr->timestamp, ts_count*4); MP4D_MALLOC(tr->duration, ts_count*4); for (i = 0; i < count; i++) { unsigned sc = READ(4); int d = READ(4); MP4D_TRACE(("sample %8d count %8d duration %8d\n",i,sc,d)); if (k + sc > ts_count) { ts_count = k + sc; MP4D_REALLOC(tr->timestamp, ts_count * sizeof(unsigned)); MP4D_REALLOC(tr->duration, ts_count * sizeof(unsigned)); } for (j = 0; j < sc; j++) { tr->duration[k] = d; tr->timestamp[k++] = ts; ts += d; } } } break; case BOX_ctts: { unsigned count = READ(4); for (i = 0; i < count; i++) { int sc = READ(4); int d = READ(4); (void)sc; (void)d; MP4D_TRACE(("sample %8d count %8d decoding to composition offset %8d\n",i,sc,d)); } } break; case BOX_stco: //ISO/IEC 14496-12 Page 39. Section 8.19 - Chunk Offset Box. case BOX_co64: tr->chunk_count = READ(4); MP4D_MALLOC(tr->chunk_offset, tr->chunk_count*sizeof(mp4d_size_t)); for (i = 0; i < tr->chunk_count; i++) { tr->chunk_offset[i] = READ(4); if (box_name == BOX_co64) { // 64-bit chunk_offset tr->chunk_offset[i] <<= 32; tr->chunk_offset[i] |= READ(4); } } break; case BOX_mvhd: SKIP(((FullAtomVersionAndFlags >> 24) == 1) ? 8+8 : 4+4); mp4->timescale = READ(4); mp4->duration_hi = ((FullAtomVersionAndFlags >> 24) == 1) ? READ(4) : 0; mp4->duration_lo = READ(4); SKIP(4+2+2+4*2+4*9+4*6+4); break; case BOX_mdhd: SKIP(((FullAtomVersionAndFlags >> 24) == 1) ? 8+8 : 4+4); tr->timescale = READ(4); tr->duration_hi = ((FullAtomVersionAndFlags >> 24) == 1) ? READ(4) : 0; tr->duration_lo = READ(4); { int ISO_639_2_T = READ(2); tr->language[2] = (ISO_639_2_T & 31) + 0x60;ISO_639_2_T >>= 5; tr->language[1] = (ISO_639_2_T & 31) + 0x60;ISO_639_2_T >>= 5; tr->language[0] = (ISO_639_2_T & 31) + 0x60; } // the rest of this box is skipped by default ... break; case BOX_mdia: read_hdlr = 1; break; case BOX_minf: read_hdlr = 0; break; case BOX_hdlr: if (tr && read_hdlr) // When this box is within 'meta' box, the track may not be avaialable { SKIP(4); // pre_defined tr->handler_type = READ(4); } // typically hdlr box does not contain any useful info. // the rest of this box is skipped by default ... break; case BOX_btrt: if (!tr) { MP4D_ERROR("broken file structure!"); } SKIP(4+4); tr->avg_bitrate_bps = READ(4); break; // Set pointer to tag to be read... case BOX_calb: ptag = &mp4->tag.album; break; case BOX_cART: ptag = &mp4->tag.artist; break; case BOX_cnam: ptag = &mp4->tag.title; break; case BOX_cday: ptag = &mp4->tag.year; break; case BOX_ccmt: ptag = &mp4->tag.comment; break; case BOX_cgen: ptag = &mp4->tag.genre; break; case BOX_stsd: SKIP(4); // entry_count, BOX_mp4a & BOX_mp4v boxes follows immediately break; case BOX_mp4s: // private stream if (!tr) { MP4D_ERROR("broken file structure!"); } SKIP(6*1+2/*Base SampleEntry*/); break; case BOX_mp4a: if (!tr) { MP4D_ERROR("broken file structure!"); } SKIP(6*1+2/*Base SampleEntry*/ + 4*2); tr->SampleDescription.audio.channelcount = READ(2); SKIP(2/*samplesize*/ + 2 + 2); tr->SampleDescription.audio.samplerate_hz = READ(4) >> 16; break; // Hap subtypes case BOX_Hap1: case BOX_Hap5: case BOX_HapY: case BOX_HapM: case BOX_HapA: // vvvvvvvvvvvvv AVC support vvvvvvvvvvvvv case BOX_avc1: // AVCSampleEntry extends VisualSampleEntry // case BOX_avc2: - no test // case BOX_svc1: - no test case BOX_mp4v: if (!tr) { MP4D_ERROR("broken file structure!"); } SKIP(6*1 + 2/*Base SampleEntry*/ + 2 + 2 + 4*3); tr->SampleDescription.video.width = READ(2); tr->SampleDescription.video.height = READ(2); // frame_count is always 1 // compressorname is rarely set.. SKIP(4 + 4 + 4 + 2/*frame_count*/ + 32/*compressorname*/ + 2 + 2); // ^^^ end of VisualSampleEntry // now follows for BOX_avc1: // BOX_avcC // BOX_btrt (optional) // BOX_m4ds (optional) // for BOX_mp4v: // BOX_esds break; case BOX_avcC: // AVCDecoderConfigurationRecord() // hack: AAC-specific DSI field reused (for it have same purpose as sps/pps) // TODO: check this hack if BOX_esds co-exist with BOX_avcC tr->object_type_indication = MP4_OBJECT_TYPE_AVC; tr->dsi = malloc((size_t)box_bytes); tr->dsi_bytes = (unsigned)box_bytes; { int spspps; unsigned char * p = tr->dsi; unsigned int configurationVersion = READ(1); unsigned int AVCProfileIndication = READ(1); unsigned int profile_compatibility = READ(1); unsigned int AVCLevelIndication = READ(1); //bit(6) reserved = 111111b; unsigned int lengthSizeMinusOne = READ(1) & 3; (void)configurationVersion; (void)AVCProfileIndication; (void)profile_compatibility; (void)AVCLevelIndication; (void)lengthSizeMinusOne; for (spspps = 0; spspps < 2; spspps++) { unsigned int numOfSequenceParameterSets= READ(1); if (!spspps) { numOfSequenceParameterSets &= 31; // clears 3 msb for SPS } *p++ = numOfSequenceParameterSets; for (i=0; i< numOfSequenceParameterSets; i++) { unsigned k, sequenceParameterSetLength = READ(2); *p++ = sequenceParameterSetLength >> 8; *p++ = sequenceParameterSetLength ; for (k = 0; k < sequenceParameterSetLength ; k++) { *p++ = READ(1); } } } } break; // ^^^^^^^^^^^^^ AVC support ^^^^^^^^^^^^^ case OD_ESD: { unsigned flags = READ(3); // ES_ID(2) + flags(1) if (flags & 0x80) // steamdependflag { SKIP(2); // dependsOnESID } if (flags & 0x40) // urlflag { unsigned bytecount = READ(1); SKIP(bytecount); // skip URL } if (flags & 0x20) // ocrflag (was reserved in MPEG-4 v.1) { SKIP(2); // OCRESID } break; } case OD_DCD: //ISO/IEC 14496-1 Page 28. Section 8.6.5 - DecoderConfigDescriptor. assert(tr); // ensured by g_fullbox[] check tr->object_type_indication = READ(1); tr->stream_type = READ(1) >> 2; SKIP(3/*bufferSizeDB*/ + 4/*maxBitrate*/); tr->avg_bitrate_bps = READ(4); break; case OD_DSI: //ISO/IEC 14496-1 Page 28. Section 8.6.5 - DecoderConfigDescriptor. assert(tr); // ensured by g_fullbox[] check if (!tr->dsi && payload_bytes) { MP4D_MALLOC(tr->dsi, (int)payload_bytes); for (i = 0; i < payload_bytes; i++) { tr->dsi[i] = mp4d_read(f, 1, &eof_flag); // These bytes available due to check above } tr->dsi_bytes = i; payload_bytes -= i; break; } default: MP4D_TRACE(("[%c%c%c%c] %d\n", box_name>>24, box_name>>16, box_name>>8, box_name, (int)payload_bytes)); } // Read tag is tag pointer is set if (ptag && !*ptag && payload_bytes > 16) { SKIP(4+4+4+4); MP4D_MALLOC(*ptag, (unsigned)payload_bytes + 1); for (i = 0; payload_bytes != 0; i++) { (*ptag)[i] = READ(1); } (*ptag)[i] = 0; // zero-terminated string } if (box_name == BOX_trak) { // New track found: allocate memory using realloc() // Typically there are 1 audio track for AAC audio file, // 4 tracks for movie file, // 3-5 tracks for scalable audio (CELP+AAC) // and up to 50 tracks for BSAC scalable audio MP4D_REALLOC(mp4->track, (mp4->track_count + 1)*sizeof(MP4D_track_t)); tr = mp4->track + mp4->track_count++; memset(tr, 0, sizeof(MP4D_track_t)); } else if (box_name == BOX_meta) { tr = NULL; // Avoid update of 'hdlr' box, which may contains in the 'meta' box } // If this box is envelope, save it's size in box stack for (i = 0; i < sizeof(g_envelope_box)/sizeof(g_envelope_box[0]); i++) { if (box_name == g_envelope_box[i].name) { if (++depth >= MP4D_MAX_CHUNKS_DEPTH) { MP4D_ERROR("too deep atoms nesting!"); } stack[depth].bytes = payload_bytes; stack[depth].format = g_envelope_box[i].type; break; } } // if box is not envelope, just skip it if (i == sizeof(g_envelope_box)/sizeof(g_envelope_box[0])) { if (payload_bytes > file_size) { eof_flag = 1; } else { SKIP(payload_bytes); } } // remove empty boxes from stack // don't touch box with index 0 (which indicates whole file) while (depth > 0 && !stack[depth].bytes) { depth--; } } while(!eof_flag); if (!mp4->track_count) { MP4D_RETURN_ERROR("no tracks found"); } fseek(f, 0, SEEK_SET); return 1; } /** * Find chunk, containing given sample. * Returns chunk number, and first sample in this chunk. */ static int mp4d_sample_to_chunk(MP4D_track_t * tr, unsigned nsample, unsigned * nfirst_sample_in_chunk) { unsigned chunk_group = 0, nc; unsigned sum = 0; *nfirst_sample_in_chunk = 0; if (tr->chunk_count <= 1) { return 0; } for (nc = 0; nc < tr->chunk_count; nc++) { if (chunk_group+1 < tr->sample_to_chunk_count // stuck at last entry till EOF && nc + 1 == // Chunks counted starting with '1' tr->sample_to_chunk[chunk_group+1].first_chunk) // next group? { chunk_group++; } sum += tr->sample_to_chunk[chunk_group].samples_per_chunk; if (nsample < sum) { return nc; } // TODO: this can be calculated once per file *nfirst_sample_in_chunk = sum; } return -1; } /** * Return position and size for given sample from given track. */ mp4d_size_t MP4D__frame_offset(const MP4D_demux_t * mp4, unsigned ntrack, unsigned nsample, unsigned * frame_bytes, unsigned * timestamp, unsigned * duration) { MP4D_track_t * tr = mp4->track + ntrack; unsigned ns; int nchunk = mp4d_sample_to_chunk(tr, nsample, &ns); mp4d_size_t offset; if (nchunk < 0) { *frame_bytes = 0; return 0; } offset = tr->chunk_offset[nchunk]; for (;ns < nsample; ns++) { offset += tr->entry_size[ns]; } *frame_bytes = tr->entry_size[ns]; if (timestamp) { *timestamp = tr->timestamp[ns]; } if (duration) { *duration = tr->duration[ns]; } return offset; } /** * De-allocated memory */ void MP4D__close(MP4D_demux_t * mp4) { #define FREE(x) if (x) {free(x); x = NULL;} while (mp4->track_count) { MP4D_track_t *tr = mp4->track + --mp4->track_count; FREE(tr->entry_size); FREE(tr->timestamp); FREE(tr->duration); FREE(tr->sample_to_chunk); FREE(tr->chunk_offset); FREE(tr->dsi); } FREE(mp4->track); FREE(mp4->tag.title); FREE(mp4->tag.artist); FREE(mp4->tag.album); FREE(mp4->tag.year); FREE(mp4->tag.comment); FREE(mp4->tag.genre); } /** * skip given number of SPS/PPS in the list. * return number of bytes skipped */ static int mp4d_skip_spspps(const unsigned char * p, int nbytes, int nskip) { int i, k = 0; for (i = 0; i < nskip; i++) { unsigned segmbytes; if (k > nbytes - 2) { return -1; } segmbytes = p[k]*256 + p[k+1]; k += 2 + segmbytes; } return k; } /** * Read SPS/PPS with given index */ static const unsigned char * MP4D__read_spspps(const MP4D_demux_t * mp4, unsigned int ntrack, int pps_flag, int nsps, int * sps_bytes) { int sps_count, skip_bytes; int bytepos = 0; unsigned char * p = mp4->track[ntrack].dsi; if (ntrack >= mp4->track_count) { return NULL; } if (mp4->track[ntrack].object_type_indication != MP4_OBJECT_TYPE_AVC) { return NULL; // SPS/PPS are specific for AVC format only } if (pps_flag) { // Skip all SPS sps_count = p[bytepos++]; skip_bytes = mp4d_skip_spspps(p+bytepos, mp4->track[ntrack].dsi_bytes - bytepos, sps_count); if (skip_bytes < 0) { return NULL; } bytepos += skip_bytes; } // Skip sps/pps before the given target sps_count = p[bytepos++]; if (nsps >= sps_count) { return NULL; } skip_bytes = mp4d_skip_spspps(p+bytepos, mp4->track[ntrack].dsi_bytes - bytepos, nsps); if (skip_bytes < 0) { return NULL; } bytepos += skip_bytes; *sps_bytes = p[bytepos]*256 + p[bytepos+1]; return p + bytepos + 2; } /** * Read SPS with given index * Return pointer to internal mp4 memory, it must not be free()-ed */ const unsigned char * MP4D__read_sps(const MP4D_demux_t * mp4, unsigned int ntrack, int nsps, int * sps_bytes) { return MP4D__read_spspps(mp4, ntrack, 0, nsps, sps_bytes); } /** * Read PPS with given index * Return pointer to internal mp4 memory, it must not be free()-ed */ const unsigned char * MP4D__read_pps(const MP4D_demux_t * mp4, unsigned int ntrack, int npps, int * pps_bytes) { return MP4D__read_spspps(mp4, ntrack, 1, npps, pps_bytes); } /************************************************************************/ /* Purely informational part, may be removed for embedded applications */ /************************************************************************/ /** * Decodes ISO/IEC 14496 MP4 stream type to ASCII string */ const char * MP4D__stream_type_to_ascii(int stream_type) { switch (stream_type) { case 0x00: return "Forbidden"; case 0x01: return "ObjectDescriptorStream"; case 0x02: return "ClockReferenceStream"; case 0x03: return "SceneDescriptionStream"; case 0x04: return "VisualStream"; case 0x05: return "AudioStream"; case 0x06: return "MPEG7Stream"; case 0x07: return "IPMPStream"; case 0x08: return "ObjectContentInfoStream"; case 0x09: return "MPEGJStream"; default: if (stream_type >= 0x20 && stream_type <= 0x3F) { return "User private"; } else { return "Reserved for ISO use"; } } } /** * Decodes ISO/IEC 14496 MP4 object type to ASCII string */ const char * MP4D__object_type_to_ascii(int object_type_indication) { switch (object_type_indication) { case 0x00: return "Forbidden"; case 0x01: return "Systems ISO/IEC 14496-1"; case 0x02: return "Systems ISO/IEC 14496-1"; case 0x20: return "Visual ISO/IEC 14496-2"; case 0x21: return "Visual ISO/IEC 14496-10"; case 0x22: return "Visual ISO/IEC 14496-10 Parameter Sets"; case 0x40: return "Audio ISO/IEC 14496-3"; case 0x60: return "Visual ISO/IEC 13818-2 Simple Profile"; case 0x61: return "Visual ISO/IEC 13818-2 Main Profile"; case 0x62: return "Visual ISO/IEC 13818-2 SNR Profile"; case 0x63: return "Visual ISO/IEC 13818-2 Spatial Profile"; case 0x64: return "Visual ISO/IEC 13818-2 High Profile"; case 0x65: return "Visual ISO/IEC 13818-2 422 Profile"; case 0x66: return "Audio ISO/IEC 13818-7 Main Profile"; case 0x67: return "Audio ISO/IEC 13818-7 LC Profile"; case 0x68: return "Audio ISO/IEC 13818-7 SSR Profile"; case 0x69: return "Audio ISO/IEC 13818-3"; case 0x6A: return "Visual ISO/IEC 11172-2"; case 0x6B: return "Audio ISO/IEC 11172-3"; case 0x6C: return "Visual ISO/IEC 10918-1"; case 0xFF: return "no object type specified"; default: if (object_type_indication >= 0xC0 && object_type_indication <= 0xFE) { return "User private"; } else { return "Reserved for ISO use"; } } } #ifdef mp4demux_test /****************************************************************************** !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!! !!!! !!!! !!!!!!!! !!!!!!!! !!!!!!! !!!!!!!! !!!! !!!! !! !! !! !! !!!! !!!! !! !! !! !! !!!! !!!! !! !!!!!! !!!!!!! !! !!!! !!!! !! !! !! !! !!!! !!!! !! !! !! !! !!!! !!!! !! !!!!!!!! !!!!!!! !! !!!! !!!! !!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ******************************************************************************/ #include #include #include #include #include #include #include "mp4demux.h" /** * Print MP4 information to stdout. */ static void print_mp4_info(const MP4D_demux_t * mp4_demux) { unsigned i; printf("\nMP4 FILE: %d tracks found. Movie time %.2f sec\n", mp4_demux->track_count, (4294967296.0*mp4_demux->duration_hi + mp4_demux->duration_lo) / mp4_demux->timescale); printf("\nNo|type|lng| duration | bitrate| %-23s| Object type","Stream type"); for (i = 0; i < mp4_demux->track_count; i++) { MP4D_track_t * tr = mp4_demux->track + i; printf("\n%2d|%c%c%c%c|%c%c%c|%7.2f s %6d frm| %7d|", i, (tr->handler_type>>24), (tr->handler_type>>16), (tr->handler_type>>8), (tr->handler_type>>0), tr->language[0],tr->language[1],tr->language[2], (65536.0* 65536.0*tr->duration_hi + tr->duration_lo) / tr->timescale, tr->sample_count, tr->avg_bitrate_bps ); printf(" %-23s|", MP4D__stream_type_to_ascii(tr->stream_type)); printf(" %-23s", MP4D__object_type_to_ascii(tr->object_type_indication)); if (tr->handler_type == MP4_HANDLER_TYPE_SOUN) { printf(" - %d ch %d hz", tr->SampleDescription.audio.channelcount, tr->SampleDescription.audio.samplerate_hz); } else if (tr->handler_type == MP4_HANDLER_TYPE_VIDE) { printf(" - %dx%d", tr->SampleDescription.video.width, tr->SampleDescription.video.height); } } printf("\n"); } /** * Print MP4 file comment to stdout. */ static void print_comment(const MP4D_demux_t * mp4_demux) { #define STR_TAG(name) if (mp4_demux->tag.name) printf("%10s = %s\n", #name, mp4_demux->tag.name) STR_TAG(title); STR_TAG(artist); STR_TAG(album); STR_TAG(year); STR_TAG(comment); STR_TAG(genre); } /** * Print SPS/PPS/DSI data in hex to stdout. */ static void print_dsi_data(const MP4D_demux_t * mp4_demux) { unsigned k, ntrack; for (ntrack = 0; ntrack < mp4_demux->track_count; ntrack++) { MP4D_track_t *tr = mp4_demux->track + ntrack; if (tr->dsi_bytes) { int i, k, sps_bytes, pps_bytes, sps_pps_found = 0; for (i = 0; i < 256; i++) { const unsigned char *sps = MP4D__read_sps(mp4_demux, ntrack, i, &sps_bytes); const unsigned char *pps = MP4D__read_pps(mp4_demux, ntrack, i, &pps_bytes); if (sps && sps_bytes) { printf("%d SPS bytes found for track #%d:\n", sps_bytes, ntrack); for (k = 0; k < sps_bytes; k++) { printf("%02x ", sps[k]); } printf("\n"); sps_pps_found = 1; } if (pps && pps_bytes) { printf("%d PPS bytes found for track #%d:\n", pps_bytes, ntrack); for (k = 0; k < pps_bytes; k++) { printf("%02x ", pps[k]); } printf("\n"); sps_pps_found = 1; } } if (!sps_pps_found) { printf("%d DSI bytes found for track #%d:\n", tr->dsi_bytes, ntrack); for (k = 0; k < tr->dsi_bytes; k++) { printf("%02x ", tr->dsi[k]); } printf("\n"); } } } } /** * Save AVC & audio tracks data to files */ static void save_track_data(const MP4D_demux_t * mp4_demux, FILE * mp4_file, unsigned ntrack) { unsigned i, frame_bytes, timestamp, duration; unsigned avc_bytes_to_next_nal = 0; MP4D_track_t *tr = mp4_demux->track + ntrack; FILE * track_file; char name[100]; const char * ext = (tr->object_type_indication == MP4_OBJECT_TYPE_AVC) ? "264" : (tr->handler_type == MP4_HANDLER_TYPE_SOUN) ? "audio" : (tr->handler_type == MP4_HANDLER_TYPE_VIDE) ? "video" : "data"; sprintf(name, "track%d.%s", ntrack, ext); track_file = fopen(name,"wb"); for (i = 0; i < tr->sample_count; i++) { unsigned char * frame_mem; mp4d_size_t frame_ofs = MP4D__frame_offset(mp4_demux, ntrack, i, &frame_bytes, ×tamp, &duration); // print frame offset //printf("%4d %06x %08d %d\n", i, (unsigned)ofs, duration, frame_bytes); //printf("%4d %06x %08d %d\n", i, (unsigned)ofs, timestamp, frame_bytes); // save payload frame_mem = malloc(frame_bytes); fseek(mp4_file, (long)frame_ofs, SEEK_SET); fread(frame_mem, 1, frame_bytes, mp4_file); if (mp4_demux->track[ntrack].object_type_indication == MP4_OBJECT_TYPE_AVC) { // replace 4-byte length field with start code unsigned startcode = 0x01000000; unsigned k = avc_bytes_to_next_nal; while (k < frame_bytes - 4) { avc_bytes_to_next_nal = 4 + ((frame_mem[k] * 256 + frame_mem[k+1])*256 + frame_mem[k+2])*256 + frame_mem[k+3]; *(unsigned *)(frame_mem + k) = startcode; k += avc_bytes_to_next_nal; } avc_bytes_to_next_nal = k - frame_bytes; // Write SPS/PPS for 1st frame if (!i) { const void * data; int nps, bytes; for (nps = 0; NULL != (data = MP4D__read_sps(mp4_demux, ntrack, nps, &bytes)); nps++) { fwrite(&startcode, 1, 4, track_file); fwrite(data, 1, bytes, track_file); } for (nps = 0; NULL != (data = MP4D__read_pps(mp4_demux, ntrack, nps, &bytes)); nps++) { fwrite(&startcode, 1, 4, track_file); fwrite(data, 1, bytes, track_file); } } } fwrite(frame_mem, 1, frame_bytes, track_file); free(frame_mem); } fclose(track_file); } int main(int argc, char* argv[]) { unsigned ntrack = 0; MP4D_demux_t mp4_demux = {0,}; char* file_name = (argc>1)?argv[1]:"default_input.mp4"; FILE * mp4_file = fopen(file_name, "rb"); if (!mp4_file) { printf("\nERROR: can't open file %s for reading\n", file_name); return 0; } if (!MP4D__open(&mp4_demux, mp4_file)) { printf("\nERROR: can't parse %s \n", file_name); return 0; } print_mp4_info(&mp4_demux); for (ntrack = 0; ntrack < mp4_demux.track_count; ntrack++) { save_track_data(&mp4_demux, mp4_file, ntrack); } print_comment(&mp4_demux); print_dsi_data(&mp4_demux); MP4D__close(&mp4_demux); fclose(mp4_file); return 0; } // dmc mp4demux.c -Dmp4demux_test && del *.obj *.map #endif // mp4demux_test ================================================ FILE: Plugin/MP4/mp4demux.h ================================================ /** 20.09.2009 @file * ISO MP4 file parsing * * Portability note: this module uses: * - Dynamic memory allocation (malloc(), realloc() and free() * - Direct file access (fgetc(), fread() & fseek()) * - File size (fstat()) * * This module provide functions to decode mp4 indexes, and retrieve * file position and size for each sample in given track. * * Integration scenario example: * * // 1. Parse MP4 structure * if (MP4D__open(mp4, file_handle)) * { * // 2. Find tracks, supported by the application * for (i = 0; i < mp4->track_count; i++) * { * if (supported(mp4->track[i].object_type_indication)) * { * // 3. Initialize decoder from transparent 'Decoder Specific Info' data * init_decoder(mp4->track[i].dsi); * * // 4. Read track data for each frame * for (k = 0; k < mp4->track[i].sample_count; k++) * { * fseek(file_handle, MP4D__frame_offset(mp4, i, k, &frame_size, NULL, NULL), SEEK_SET); * fread(buf, frame_size, 1, file_handle); * decode(buf); * } * } * } * } * */ #ifndef mp4demux_H_INCLUDED #define mp4demux_H_INCLUDED #include #include "mp4defs.h" #ifdef __cplusplus extern "C" { #endif //__cplusplus /************************************************************************/ /* Portable 64-bit type definition */ /************************************************************************/ #if (defined(__GNUC__) && __GNUC__ >= 4) || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901) # include // hope that all GCC compilers support this C99 extension typedef uint64_t mp4d_size_t; #else # if defined (_MSC_VER) typedef unsigned __int64 mp4d_size_t; # else typedef unsigned long long mp4d_size_t; # endif typedef unsigned int uint32_t; #endif typedef struct { unsigned first_chunk; unsigned samples_per_chunk; } MP4D_sample_to_chunk_t; typedef struct { /************************************************************************/ /* mandatory public data */ /************************************************************************/ // How many 'samples' in the track // The 'sample' is MP4 term, denoting audio or video frame unsigned sample_count; // Decoder-specific info (DSI) data unsigned char * dsi; // DSI data size unsigned dsi_bytes; // MP4 object type code // case 0x00: return "Forbidden"; // case 0x01: return "Systems ISO/IEC 14496-1"; // case 0x02: return "Systems ISO/IEC 14496-1"; // case 0x20: return "Visual ISO/IEC 14496-2"; // case 0x40: return "Audio ISO/IEC 14496-3"; // case 0x60: return "Visual ISO/IEC 13818-2 Simple Profile"; // case 0x61: return "Visual ISO/IEC 13818-2 Main Profile"; // case 0x62: return "Visual ISO/IEC 13818-2 SNR Profile"; // case 0x63: return "Visual ISO/IEC 13818-2 Spatial Profile"; // case 0x64: return "Visual ISO/IEC 13818-2 High Profile"; // case 0x65: return "Visual ISO/IEC 13818-2 422 Profile"; // case 0x66: return "Audio ISO/IEC 13818-7 Main Profile"; // case 0x67: return "Audio ISO/IEC 13818-7 LC Profile"; // case 0x68: return "Audio ISO/IEC 13818-7 SSR Profile"; // case 0x69: return "Audio ISO/IEC 13818-3"; // case 0x6A: return "Visual ISO/IEC 11172-2"; // case 0x6B: return "Audio ISO/IEC 11172-3"; // case 0x6C: return "Visual ISO/IEC 10918-1"; unsigned object_type_indication; /************************************************************************/ /* informational public data, not needed for decoding */ /************************************************************************/ // handler_type when present in a media box, is an integer containing one of // the following values, or a value from a derived specification: // 'vide' Video track // 'soun' Audio track // 'hint' Hint track unsigned handler_type; // Track duration: 64-bit value split into 2 variables unsigned duration_hi; unsigned duration_lo; // duration scale: duration = timescale*seconds unsigned timescale; // Average bitrate, bits per second unsigned avg_bitrate_bps; // Track language: 3-char ISO 639-2T code: "und", "eng", "rus", "jpn" etc... unsigned char language[4]; // MP4 stream type // case 0x00: return "Forbidden"; // case 0x01: return "ObjectDescriptorStream"; // case 0x02: return "ClockReferenceStream"; // case 0x03: return "SceneDescriptionStream"; // case 0x04: return "VisualStream"; // case 0x05: return "AudioStream"; // case 0x06: return "MPEG7Stream"; // case 0x07: return "IPMPStream"; // case 0x08: return "ObjectContentInfoStream"; // case 0x09: return "MPEGJStream"; unsigned stream_type; union { // for handler_type == 'soun' tracks struct { unsigned channelcount; unsigned samplerate_hz; } audio; // for handler_type == 'vide' tracks struct { unsigned width; unsigned height; } video; } SampleDescription; /************************************************************************/ /* private data: MP4 indexes */ /************************************************************************/ unsigned *entry_size; // [sample_count] unsigned *timestamp; // [sample_count] unsigned *duration; // [sample_count] unsigned sample_to_chunk_count; MP4D_sample_to_chunk_t * sample_to_chunk; // [sample_to_chunk_count] unsigned chunk_count; mp4d_size_t * chunk_offset; // [chunk_count] } MP4D_track_t; typedef struct MP4D_demux_tag { /************************************************************************/ /* mandatory public data */ /************************************************************************/ // number of tracks in the movie unsigned track_count; // track data (public/private) MP4D_track_t * track; /************************************************************************/ /* informational public data, not needed for decoding */ /************************************************************************/ // Movie duration: 64-bit value split into 2 variables unsigned duration_hi; unsigned duration_lo; // duration scale: duration = timescale*seconds unsigned timescale; // Metadata tag (optional) // Tags provided 'as-is', without any re-encoding struct { unsigned char *title; unsigned char *artist; unsigned char *album; unsigned char *year; unsigned char *comment; unsigned char *genre; } tag; } MP4D_demux_t; /** * Parse given file as MP4 file. Allocate and store data indexes. * return 1 on success, 0 on failure * Given file rewind()'ed on return. * The MP4 indexes may be stored at the end of file, so this * function may parse all file, using fseek(). * It is guaranteed that function will read/seek the file sequentially, * and will never jump back. */ int MP4D__open(MP4D_demux_t * mp4, FILE * f); /** * Return position and size for given sample from given track. The 'sample' is a * MP4 term for 'frame' * * frame_bytes [OUT] - return coded frame size in bytes * timestamp [OUT] - return frame timestamp (in mp4->timescale units) * duration [OUT] - return frame duration (in mp4->timescale units) * * function return file offset for the frame */ mp4d_size_t MP4D__frame_offset(const MP4D_demux_t * mp4, unsigned int ntrack, unsigned int nsample, unsigned int * frame_bytes, unsigned * timestamp, unsigned * duration); /** * De-allocated memory */ void MP4D__close(MP4D_demux_t * mp4); /** * Helper functions to parse mp4.track[ntrack].dsi for H.264 SPS/PPS * Return pointer to internal mp4 memory, it must not be free()-ed * * Example: process all SPS in MP4 file: * while (sps = MP4D__read_sps(mp4, num_of_avc_track, sps_count, &sps_bytes)) * { * process(sps, sps_bytes); * sps_count++; * } */ const unsigned char * MP4D__read_sps(const MP4D_demux_t * mp4, unsigned int ntrack, int nsps, int * sps_bytes); const unsigned char * MP4D__read_pps(const MP4D_demux_t * mp4, unsigned int ntrack, int npps, int * pps_bytes); /** * Decode MP4D_track_t::stream_type to ASCII string */ const char * MP4D__stream_type_to_ascii(int stream_type); /** * Decode MP4D_track_t::object_type_indication to ASCII string */ const char * MP4D__object_type_to_ascii(int object_type_indication); #ifdef __cplusplus } #endif //__cplusplus #endif //mp4demux_H_INCLUDED ================================================ FILE: Plugin/Makefile.linux ================================================ PLATFORM = Linux ARCH = x86_64 TARGET_TYPE = so CFLAGS = -fPIC -ffunction-sections -fdata-sections CXXFLAGS = -fPIC -ffunction-sections -fdata-sections LDFLAGS = -shared -Wl,--gc-sections include Common.mk ================================================ FILE: Plugin/Makefile.macos ================================================ PLATFORM = macOS TARGET_TYPE = dylib SDK_MIN_VER = 11.0 ARCH_OPT = -arch $(ARCH) -mmacosx-version-min=$(SDK_MIN_VER) CFLAGS += $(ARCH_OPT) CXXFLAGS += $(ARCH_OPT) LDFLAGS += $(ARCH_OPT) -dynamiclib include Common.mk ================================================ FILE: Plugin/Makefile.windows ================================================ PLATFORM = Windows ARCH = x86_64 TARGET_TYPE = dll TOOLCHAIN = $(ARCH)-w64-mingw32 CC = $(TOOLCHAIN)-gcc-posix CXX = $(TOOLCHAIN)-g++-posix STRIP = $(TOOLCHAIN)-strip LDFLAGS = -shared LIBS = -static -Wl,--subsystem,windows include Common.mk ================================================ FILE: Plugin/Snappy/snappy-c.cc ================================================ // Copyright 2011 Martin Gieseking . // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "snappy.h" #include "snappy-c.h" extern "C" { snappy_status snappy_compress(const char* input, size_t input_length, char* compressed, size_t *compressed_length) { if (*compressed_length < snappy_max_compressed_length(input_length)) { return SNAPPY_BUFFER_TOO_SMALL; } snappy::RawCompress(input, input_length, compressed, compressed_length); return SNAPPY_OK; } snappy_status snappy_uncompress(const char* compressed, size_t compressed_length, char* uncompressed, size_t* uncompressed_length) { size_t real_uncompressed_length; if (!snappy::GetUncompressedLength(compressed, compressed_length, &real_uncompressed_length)) { return SNAPPY_INVALID_INPUT; } if (*uncompressed_length < real_uncompressed_length) { return SNAPPY_BUFFER_TOO_SMALL; } if (!snappy::RawUncompress(compressed, compressed_length, uncompressed)) { return SNAPPY_INVALID_INPUT; } *uncompressed_length = real_uncompressed_length; return SNAPPY_OK; } size_t snappy_max_compressed_length(size_t source_length) { return snappy::MaxCompressedLength(source_length); } snappy_status snappy_uncompressed_length(const char *compressed, size_t compressed_length, size_t *result) { if (snappy::GetUncompressedLength(compressed, compressed_length, result)) { return SNAPPY_OK; } else { return SNAPPY_INVALID_INPUT; } } snappy_status snappy_validate_compressed_buffer(const char *compressed, size_t compressed_length) { if (snappy::IsValidCompressedBuffer(compressed, compressed_length)) { return SNAPPY_OK; } else { return SNAPPY_INVALID_INPUT; } } } // extern "C" ================================================ FILE: Plugin/Snappy/snappy-c.h ================================================ /* * Copyright 2011 Martin Gieseking . * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Plain C interface (a wrapper around the C++ implementation). */ #ifndef THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_C_H_ #define THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_C_H_ #ifdef __cplusplus extern "C" { #endif #include /* * Return values; see the documentation for each function to know * what each can return. */ typedef enum { SNAPPY_OK = 0, SNAPPY_INVALID_INPUT = 1, SNAPPY_BUFFER_TOO_SMALL = 2 } snappy_status; /* * Takes the data stored in "input[0..input_length-1]" and stores * it in the array pointed to by "compressed". * * signals the space available in "compressed". * If it is not at least equal to "snappy_max_compressed_length(input_length)", * SNAPPY_BUFFER_TOO_SMALL is returned. After successful compression, * contains the true length of the compressed output, * and SNAPPY_OK is returned. * * Example: * size_t output_length = snappy_max_compressed_length(input_length); * char* output = (char*)malloc(output_length); * if (snappy_compress(input, input_length, output, &output_length) * == SNAPPY_OK) { * ... Process(output, output_length) ... * } * free(output); */ snappy_status snappy_compress(const char* input, size_t input_length, char* compressed, size_t* compressed_length); /* * Given data in "compressed[0..compressed_length-1]" generated by * calling the snappy_compress routine, this routine stores * the uncompressed data to * uncompressed[0..uncompressed_length-1]. * Returns failure (a value not equal to SNAPPY_OK) if the message * is corrupted and could not be decrypted. * * signals the space available in "uncompressed". * If it is not at least equal to the value returned by * snappy_uncompressed_length for this stream, SNAPPY_BUFFER_TOO_SMALL * is returned. After successful decompression, * contains the true length of the decompressed output. * * Example: * size_t output_length; * if (snappy_uncompressed_length(input, input_length, &output_length) * != SNAPPY_OK) { * ... fail ... * } * char* output = (char*)malloc(output_length); * if (snappy_uncompress(input, input_length, output, &output_length) * == SNAPPY_OK) { * ... Process(output, output_length) ... * } * free(output); */ snappy_status snappy_uncompress(const char* compressed, size_t compressed_length, char* uncompressed, size_t* uncompressed_length); /* * Returns the maximal size of the compressed representation of * input data that is "source_length" bytes in length. */ size_t snappy_max_compressed_length(size_t source_length); /* * REQUIRES: "compressed[]" was produced by snappy_compress() * Returns SNAPPY_OK and stores the length of the uncompressed data in * *result normally. Returns SNAPPY_INVALID_INPUT on parsing error. * This operation takes O(1) time. */ snappy_status snappy_uncompressed_length(const char* compressed, size_t compressed_length, size_t* result); /* * Check if the contents of "compressed[]" can be uncompressed successfully. * Does not return the uncompressed data; if so, returns SNAPPY_OK, * or if not, returns SNAPPY_INVALID_INPUT. * Takes time proportional to compressed_length, but is usually at least a * factor of four faster than actual decompression. */ snappy_status snappy_validate_compressed_buffer(const char* compressed, size_t compressed_length); #ifdef __cplusplus } // extern "C" #endif #endif /* THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_C_H_ */ ================================================ FILE: Plugin/Snappy/snappy-internal.h ================================================ // Copyright 2008 Google Inc. All Rights Reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Internals shared between the Snappy implementation and its unittest. #ifndef THIRD_PARTY_SNAPPY_SNAPPY_INTERNAL_H_ #define THIRD_PARTY_SNAPPY_SNAPPY_INTERNAL_H_ #include #include "snappy-stubs-internal.h" #if SNAPPY_HAVE_SSSE3 // Please do not replace with or with headers that assume more // advanced SSE versions without checking with all the OWNERS. #include #include #endif #if SNAPPY_HAVE_NEON #include #endif #if SNAPPY_RVV_1 || SNAPPY_RVV_0_7 #define SNAPPY_HAVE_RVV 1 #include #else #define SNAPPY_HAVE_RVV 0 #endif #ifdef SNAPPY_RVV_1 #define VSETVL_E8M2 __riscv_vsetvl_e8m2 #define VLE8_V_U8M2 __riscv_vle8_v_u8m2 #define VSE8_V_U8M2 __riscv_vse8_v_u8m2 #elif SNAPPY_RVV_0_7 #define VSETVL_E8M2 vsetvl_e8m2 #define VLE8_V_U8M2 vle8_v_u8m2 #define VSE8_V_U8M2 vse8_v_u8m2 #endif #if SNAPPY_HAVE_SSSE3 || SNAPPY_HAVE_NEON #define SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE 1 #else #define SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE 0 #endif namespace snappy { namespace internal { #if SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE #if SNAPPY_HAVE_SSSE3 using V128 = __m128i; #elif SNAPPY_HAVE_NEON using V128 = uint8x16_t; #endif // Load 128 bits of integer data. `src` must be 16-byte aligned. inline V128 V128_Load(const V128* src); // Load 128 bits of integer data. `src` does not need to be aligned. inline V128 V128_LoadU(const V128* src); // Store 128 bits of integer data. `dst` does not need to be aligned. inline void V128_StoreU(V128* dst, V128 val); // Shuffle packed 8-bit integers using a shuffle mask. // Each packed integer in the shuffle mask must be in [0,16). inline V128 V128_Shuffle(V128 input, V128 shuffle_mask); // Constructs V128 with 16 chars |c|. inline V128 V128_DupChar(char c); #if SNAPPY_HAVE_SSSE3 inline V128 V128_Load(const V128* src) { return _mm_load_si128(src); } inline V128 V128_LoadU(const V128* src) { return _mm_loadu_si128(src); } inline void V128_StoreU(V128* dst, V128 val) { _mm_storeu_si128(dst, val); } inline V128 V128_Shuffle(V128 input, V128 shuffle_mask) { return _mm_shuffle_epi8(input, shuffle_mask); } inline V128 V128_DupChar(char c) { return _mm_set1_epi8(c); } #elif SNAPPY_HAVE_NEON inline V128 V128_Load(const V128* src) { return vld1q_u8(reinterpret_cast(src)); } inline V128 V128_LoadU(const V128* src) { return vld1q_u8(reinterpret_cast(src)); } inline void V128_StoreU(V128* dst, V128 val) { vst1q_u8(reinterpret_cast(dst), val); } inline V128 V128_Shuffle(V128 input, V128 shuffle_mask) { assert(vminvq_u8(shuffle_mask) >= 0 && vmaxvq_u8(shuffle_mask) <= 15); return vqtbl1q_u8(input, shuffle_mask); } inline V128 V128_DupChar(char c) { return vdupq_n_u8(c); } #endif #endif // SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE // Working memory performs a single allocation to hold all scratch space // required for compression. class WorkingMemory { public: explicit WorkingMemory(size_t input_size); ~WorkingMemory(); // Allocates and clears a hash table using memory in "*this", // stores the number of buckets in "*table_size" and returns a pointer to // the base of the hash table. uint16_t* GetHashTable(size_t fragment_size, int* table_size) const; char* GetScratchInput() const { return input_; } char* GetScratchOutput() const { return output_; } private: char* mem_; // the allocated memory, never nullptr size_t size_; // the size of the allocated memory, never 0 uint16_t* table_; // the pointer to the hashtable char* input_; // the pointer to the input scratch buffer char* output_; // the pointer to the output scratch buffer // No copying WorkingMemory(const WorkingMemory&); void operator=(const WorkingMemory&); }; // Flat array compression that does not emit the "uncompressed length" // prefix. Compresses "input" string to the "*op" buffer. // // REQUIRES: "input_length <= kBlockSize" // REQUIRES: "op" points to an array of memory that is at least // "MaxCompressedLength(input_length)" in size. // REQUIRES: All elements in "table[0..table_size-1]" are initialized to zero. // REQUIRES: "table_size" is a power of two // // Returns an "end" pointer into "op" buffer. // "end - op" is the compressed size of "input". char* CompressFragment(const char* input, size_t input_length, char* op, uint16_t* table, const int table_size); // Find the largest n such that // // s1[0,n-1] == s2[0,n-1] // and n <= (s2_limit - s2). // // Return make_pair(n, n < 8). // Does not read *s2_limit or beyond. // Does not read *(s1 + (s2_limit - s2)) or beyond. // Requires that s2_limit >= s2. // // In addition populate *data with the next 5 bytes from the end of the match. // This is only done if 8 bytes are available (s2_limit - s2 >= 8). The point is // that on some arch's this can be done faster in this routine than subsequent // loading from s2 + n. // // Separate implementation for 64-bit, little-endian cpus. // riscv and little-endian cpu choose this routinue can be done faster too. #if !SNAPPY_IS_BIG_ENDIAN && \ (defined(__x86_64__) || defined(_M_X64) || defined(ARCH_PPC) || \ defined(ARCH_ARM) || defined(__riscv)) static inline std::pair FindMatchLength(const char* s1, const char* s2, const char* s2_limit, uint64_t* data) { assert(s2_limit >= s2); size_t matched = 0; // This block isn't necessary for correctness; we could just start looping // immediately. As an optimization though, it is useful. It creates some not // uncommon code paths that determine, without extra effort, whether the match // length is less than 8. In short, we are hoping to avoid a conditional // branch, and perhaps get better code layout from the C++ compiler. if (SNAPPY_PREDICT_TRUE(s2 <= s2_limit - 16)) { uint64_t a1 = UNALIGNED_LOAD64(s1); uint64_t a2 = UNALIGNED_LOAD64(s2); if (SNAPPY_PREDICT_TRUE(a1 != a2)) { // This code is critical for performance. The reason is that it determines // how much to advance `ip` (s2). This obviously depends on both the loads // from the `candidate` (s1) and `ip`. Furthermore the next `candidate` // depends on the advanced `ip` calculated here through a load, hash and // new candidate hash lookup (a lot of cycles). This makes s1 (ie. // `candidate`) the variable that limits throughput. This is the reason we // go through hoops to have this function update `data` for the next iter. // The straightforward code would use *data, given by // // *data = UNALIGNED_LOAD64(s2 + matched_bytes) (Latency of 5 cycles), // // as input for the hash table lookup to find next candidate. However // this forces the load on the data dependency chain of s1, because // matched_bytes directly depends on s1. However matched_bytes is 0..7, so // we can also calculate *data by // // *data = AlignRight(UNALIGNED_LOAD64(s2), UNALIGNED_LOAD64(s2 + 8), // matched_bytes); // // The loads do not depend on s1 anymore and are thus off the bottleneck. // The straightforward implementation on x86_64 would be to use // // shrd rax, rdx, cl (cl being matched_bytes * 8) // // unfortunately shrd with a variable shift has a 4 cycle latency. So this // only wins 1 cycle. The BMI2 shrx instruction is a 1 cycle variable // shift instruction but can only shift 64 bits. If we focus on just // obtaining the least significant 4 bytes, we can obtain this by // // *data = ConditionalMove(matched_bytes < 4, UNALIGNED_LOAD64(s2), // UNALIGNED_LOAD64(s2 + 4) >> ((matched_bytes & 3) * 8); // // Writen like above this is not a big win, the conditional move would be // a cmp followed by a cmov (2 cycles) followed by a shift (1 cycle). // However matched_bytes < 4 is equal to // static_cast(xorval) != 0. Writen that way, the conditional // move (2 cycles) can execute in parallel with FindLSBSetNonZero64 // (tzcnt), which takes 3 cycles. uint64_t xorval = a1 ^ a2; int shift = Bits::FindLSBSetNonZero64(xorval); size_t matched_bytes = shift >> 3; uint64_t a3 = UNALIGNED_LOAD64(s2 + 4); #ifndef __x86_64__ a2 = static_cast(xorval) == 0 ? a3 : a2; #else // Ideally this would just be // // a2 = static_cast(xorval) == 0 ? a3 : a2; // // However clang correctly infers that the above statement participates on // a critical data dependency chain and thus, unfortunately, refuses to // use a conditional move (it's tuned to cut data dependencies). In this // case there is a longer parallel chain anyway AND this will be fairly // unpredictable. asm("testl %k2, %k2\n\t" "cmovzq %1, %0\n\t" : "+r"(a2) : "r"(a3), "r"(xorval) : "cc"); #endif *data = a2 >> (shift & (3 * 8)); return std::pair(matched_bytes, true); } else { matched = 8; s2 += 8; } } SNAPPY_PREFETCH(s1 + 64); SNAPPY_PREFETCH(s2 + 64); // Find out how long the match is. We loop over the data 64 bits at a // time until we find a 64-bit block that doesn't match; then we find // the first non-matching bit and use that to calculate the total // length of the match. while (SNAPPY_PREDICT_TRUE(s2 <= s2_limit - 16)) { uint64_t a1 = UNALIGNED_LOAD64(s1 + matched); uint64_t a2 = UNALIGNED_LOAD64(s2); if (a1 == a2) { s2 += 8; matched += 8; } else { uint64_t xorval = a1 ^ a2; int shift = Bits::FindLSBSetNonZero64(xorval); size_t matched_bytes = shift >> 3; uint64_t a3 = UNALIGNED_LOAD64(s2 + 4); #ifndef __x86_64__ a2 = static_cast(xorval) == 0 ? a3 : a2; #else asm("testl %k2, %k2\n\t" "cmovzq %1, %0\n\t" : "+r"(a2) : "r"(a3), "r"(xorval) : "cc"); #endif *data = a2 >> (shift & (3 * 8)); matched += matched_bytes; assert(matched >= 8); return std::pair(matched, false); } } while (SNAPPY_PREDICT_TRUE(s2 < s2_limit)) { if (s1[matched] == *s2) { ++s2; ++matched; } else { if (s2 <= s2_limit - 8) { *data = UNALIGNED_LOAD64(s2); } return std::pair(matched, matched < 8); } } return std::pair(matched, matched < 8); } #else static inline std::pair FindMatchLength(const char* s1, const char* s2, const char* s2_limit, uint64_t* data) { // Implementation based on the x86-64 version, above. assert(s2_limit >= s2); int matched = 0; while (s2 <= s2_limit - 4 && UNALIGNED_LOAD32(s2) == UNALIGNED_LOAD32(s1 + matched)) { s2 += 4; matched += 4; } if (LittleEndian::IsLittleEndian() && s2 <= s2_limit - 4) { uint32_t x = UNALIGNED_LOAD32(s2) ^ UNALIGNED_LOAD32(s1 + matched); int matching_bits = Bits::FindLSBSetNonZero(x); matched += matching_bits >> 3; s2 += matching_bits >> 3; } else { while ((s2 < s2_limit) && (s1[matched] == *s2)) { ++s2; ++matched; } } if (s2 <= s2_limit - 8) *data = LittleEndian::Load64(s2); return std::pair(matched, matched < 8); } #endif static inline size_t FindMatchLengthPlain(const char* s1, const char* s2, const char* s2_limit) { // Implementation based on the x86-64 version, above. assert(s2_limit >= s2); int matched = 0; while (s2 <= s2_limit - 8 && UNALIGNED_LOAD64(s2) == UNALIGNED_LOAD64(s1 + matched)) { s2 += 8; matched += 8; } if (LittleEndian::IsLittleEndian() && s2 <= s2_limit - 8) { uint64_t x = UNALIGNED_LOAD64(s2) ^ UNALIGNED_LOAD64(s1 + matched); int matching_bits = Bits::FindLSBSetNonZero64(x); matched += matching_bits >> 3; s2 += matching_bits >> 3; } else { while ((s2 < s2_limit) && (s1[matched] == *s2)) { ++s2; ++matched; } } return matched; } // Lookup tables for decompression code. Give --snappy_dump_decompression_table // to the unit test to recompute char_table. enum { LITERAL = 0, COPY_1_BYTE_OFFSET = 1, // 3 bit length + 3 bits of offset in opcode COPY_2_BYTE_OFFSET = 2, COPY_4_BYTE_OFFSET = 3 }; static const int kMaximumTagLength = 5; // COPY_4_BYTE_OFFSET plus the actual offset. // Data stored per entry in lookup table: // Range Bits-used Description // ------------------------------------ // 1..64 0..7 Literal/copy length encoded in opcode byte // 0..7 8..10 Copy offset encoded in opcode byte / 256 // 0..4 11..13 Extra bytes after opcode // // We use eight bits for the length even though 7 would have sufficed // because of efficiency reasons: // (1) Extracting a byte is faster than a bit-field // (2) It properly aligns copy offset so we do not need a <<8 static constexpr uint16_t char_table[256] = { // clang-format off 0x0001, 0x0804, 0x1001, 0x2001, 0x0002, 0x0805, 0x1002, 0x2002, 0x0003, 0x0806, 0x1003, 0x2003, 0x0004, 0x0807, 0x1004, 0x2004, 0x0005, 0x0808, 0x1005, 0x2005, 0x0006, 0x0809, 0x1006, 0x2006, 0x0007, 0x080a, 0x1007, 0x2007, 0x0008, 0x080b, 0x1008, 0x2008, 0x0009, 0x0904, 0x1009, 0x2009, 0x000a, 0x0905, 0x100a, 0x200a, 0x000b, 0x0906, 0x100b, 0x200b, 0x000c, 0x0907, 0x100c, 0x200c, 0x000d, 0x0908, 0x100d, 0x200d, 0x000e, 0x0909, 0x100e, 0x200e, 0x000f, 0x090a, 0x100f, 0x200f, 0x0010, 0x090b, 0x1010, 0x2010, 0x0011, 0x0a04, 0x1011, 0x2011, 0x0012, 0x0a05, 0x1012, 0x2012, 0x0013, 0x0a06, 0x1013, 0x2013, 0x0014, 0x0a07, 0x1014, 0x2014, 0x0015, 0x0a08, 0x1015, 0x2015, 0x0016, 0x0a09, 0x1016, 0x2016, 0x0017, 0x0a0a, 0x1017, 0x2017, 0x0018, 0x0a0b, 0x1018, 0x2018, 0x0019, 0x0b04, 0x1019, 0x2019, 0x001a, 0x0b05, 0x101a, 0x201a, 0x001b, 0x0b06, 0x101b, 0x201b, 0x001c, 0x0b07, 0x101c, 0x201c, 0x001d, 0x0b08, 0x101d, 0x201d, 0x001e, 0x0b09, 0x101e, 0x201e, 0x001f, 0x0b0a, 0x101f, 0x201f, 0x0020, 0x0b0b, 0x1020, 0x2020, 0x0021, 0x0c04, 0x1021, 0x2021, 0x0022, 0x0c05, 0x1022, 0x2022, 0x0023, 0x0c06, 0x1023, 0x2023, 0x0024, 0x0c07, 0x1024, 0x2024, 0x0025, 0x0c08, 0x1025, 0x2025, 0x0026, 0x0c09, 0x1026, 0x2026, 0x0027, 0x0c0a, 0x1027, 0x2027, 0x0028, 0x0c0b, 0x1028, 0x2028, 0x0029, 0x0d04, 0x1029, 0x2029, 0x002a, 0x0d05, 0x102a, 0x202a, 0x002b, 0x0d06, 0x102b, 0x202b, 0x002c, 0x0d07, 0x102c, 0x202c, 0x002d, 0x0d08, 0x102d, 0x202d, 0x002e, 0x0d09, 0x102e, 0x202e, 0x002f, 0x0d0a, 0x102f, 0x202f, 0x0030, 0x0d0b, 0x1030, 0x2030, 0x0031, 0x0e04, 0x1031, 0x2031, 0x0032, 0x0e05, 0x1032, 0x2032, 0x0033, 0x0e06, 0x1033, 0x2033, 0x0034, 0x0e07, 0x1034, 0x2034, 0x0035, 0x0e08, 0x1035, 0x2035, 0x0036, 0x0e09, 0x1036, 0x2036, 0x0037, 0x0e0a, 0x1037, 0x2037, 0x0038, 0x0e0b, 0x1038, 0x2038, 0x0039, 0x0f04, 0x1039, 0x2039, 0x003a, 0x0f05, 0x103a, 0x203a, 0x003b, 0x0f06, 0x103b, 0x203b, 0x003c, 0x0f07, 0x103c, 0x203c, 0x0801, 0x0f08, 0x103d, 0x203d, 0x1001, 0x0f09, 0x103e, 0x203e, 0x1801, 0x0f0a, 0x103f, 0x203f, 0x2001, 0x0f0b, 0x1040, 0x2040, // clang-format on }; } // end namespace internal } // end namespace snappy #endif // THIRD_PARTY_SNAPPY_SNAPPY_INTERNAL_H_ ================================================ FILE: Plugin/Snappy/snappy-sinksource.cc ================================================ // Copyright 2011 Google Inc. All Rights Reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include "snappy-sinksource.h" namespace snappy { Source::~Source() = default; Sink::~Sink() = default; char* Sink::GetAppendBuffer(size_t length, char* scratch) { // TODO: Switch to [[maybe_unused]] when we can assume C++17. (void)length; return scratch; } char* Sink::GetAppendBufferVariable( size_t min_size, size_t desired_size_hint, char* scratch, size_t scratch_size, size_t* allocated_size) { // TODO: Switch to [[maybe_unused]] when we can assume C++17. (void)min_size; (void)desired_size_hint; *allocated_size = scratch_size; return scratch; } void Sink::AppendAndTakeOwnership( char* bytes, size_t n, void (*deleter)(void*, const char*, size_t), void *deleter_arg) { Append(bytes, n); (*deleter)(deleter_arg, bytes, n); } ByteArraySource::~ByteArraySource() = default; size_t ByteArraySource::Available() const { return left_; } const char* ByteArraySource::Peek(size_t* len) { *len = left_; return ptr_; } void ByteArraySource::Skip(size_t n) { left_ -= n; ptr_ += n; } UncheckedByteArraySink::~UncheckedByteArraySink() { } void UncheckedByteArraySink::Append(const char* data, size_t n) { // Do no copying if the caller filled in the result of GetAppendBuffer() if (data != dest_) { std::memcpy(dest_, data, n); } dest_ += n; } char* UncheckedByteArraySink::GetAppendBuffer(size_t len, char* scratch) { // TODO: Switch to [[maybe_unused]] when we can assume C++17. (void)len; (void)scratch; return dest_; } void UncheckedByteArraySink::AppendAndTakeOwnership( char* bytes, size_t n, void (*deleter)(void*, const char*, size_t), void *deleter_arg) { if (bytes != dest_) { std::memcpy(dest_, bytes, n); (*deleter)(deleter_arg, bytes, n); } dest_ += n; } char* UncheckedByteArraySink::GetAppendBufferVariable( size_t min_size, size_t desired_size_hint, char* scratch, size_t scratch_size, size_t* allocated_size) { // TODO: Switch to [[maybe_unused]] when we can assume C++17. (void)min_size; (void)scratch; (void)scratch_size; *allocated_size = desired_size_hint; return dest_; } } // namespace snappy ================================================ FILE: Plugin/Snappy/snappy-sinksource.h ================================================ // Copyright 2011 Google Inc. All Rights Reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef THIRD_PARTY_SNAPPY_SNAPPY_SINKSOURCE_H_ #define THIRD_PARTY_SNAPPY_SNAPPY_SINKSOURCE_H_ #include namespace snappy { // A Sink is an interface that consumes a sequence of bytes. class Sink { public: Sink() { } virtual ~Sink(); // Append "bytes[0,n-1]" to this. virtual void Append(const char* bytes, size_t n) = 0; // Returns a writable buffer of the specified length for appending. // May return a pointer to the caller-owned scratch buffer which // must have at least the indicated length. The returned buffer is // only valid until the next operation on this Sink. // // After writing at most "length" bytes, call Append() with the // pointer returned from this function and the number of bytes // written. Many Append() implementations will avoid copying // bytes if this function returned an internal buffer. // // If a non-scratch buffer is returned, the caller may only pass a // prefix of it to Append(). That is, it is not correct to pass an // interior pointer of the returned array to Append(). // // The default implementation always returns the scratch buffer. virtual char* GetAppendBuffer(size_t length, char* scratch); // For higher performance, Sink implementations can provide custom // AppendAndTakeOwnership() and GetAppendBufferVariable() methods. // These methods can reduce the number of copies done during // compression/decompression. // Append "bytes[0,n-1] to the sink. Takes ownership of "bytes" // and calls the deleter function as (*deleter)(deleter_arg, bytes, n) // to free the buffer. deleter function must be non NULL. // // The default implementation just calls Append and frees "bytes". // Other implementations may avoid a copy while appending the buffer. virtual void AppendAndTakeOwnership( char* bytes, size_t n, void (*deleter)(void*, const char*, size_t), void *deleter_arg); // Returns a writable buffer for appending and writes the buffer's capacity to // *allocated_size. Guarantees *allocated_size >= min_size. // May return a pointer to the caller-owned scratch buffer which must have // scratch_size >= min_size. // // The returned buffer is only valid until the next operation // on this ByteSink. // // After writing at most *allocated_size bytes, call Append() with the // pointer returned from this function and the number of bytes written. // Many Append() implementations will avoid copying bytes if this function // returned an internal buffer. // // If the sink implementation allocates or reallocates an internal buffer, // it should use the desired_size_hint if appropriate. If a caller cannot // provide a reasonable guess at the desired capacity, it should set // desired_size_hint = 0. // // If a non-scratch buffer is returned, the caller may only pass // a prefix to it to Append(). That is, it is not correct to pass an // interior pointer to Append(). // // The default implementation always returns the scratch buffer. virtual char* GetAppendBufferVariable( size_t min_size, size_t desired_size_hint, char* scratch, size_t scratch_size, size_t* allocated_size); private: // No copying Sink(const Sink&); void operator=(const Sink&); }; // A Source is an interface that yields a sequence of bytes class Source { public: Source() { } virtual ~Source(); // Return the number of bytes left to read from the source virtual size_t Available() const = 0; // Peek at the next flat region of the source. Does not reposition // the source. The returned region is empty iff Available()==0. // // Returns a pointer to the beginning of the region and store its // length in *len. // // The returned region is valid until the next call to Skip() or // until this object is destroyed, whichever occurs first. // // The returned region may be larger than Available() (for example // if this ByteSource is a view on a substring of a larger source). // The caller is responsible for ensuring that it only reads the // Available() bytes. virtual const char* Peek(size_t* len) = 0; // Skip the next n bytes. Invalidates any buffer returned by // a previous call to Peek(). // REQUIRES: Available() >= n virtual void Skip(size_t n) = 0; private: // No copying Source(const Source&); void operator=(const Source&); }; // A Source implementation that yields the contents of a flat array class ByteArraySource : public Source { public: ByteArraySource(const char* p, size_t n) : ptr_(p), left_(n) { } ~ByteArraySource() override; size_t Available() const override; const char* Peek(size_t* len) override; void Skip(size_t n) override; private: const char* ptr_; size_t left_; }; // A Sink implementation that writes to a flat array without any bound checks. class UncheckedByteArraySink : public Sink { public: explicit UncheckedByteArraySink(char* dest) : dest_(dest) { } ~UncheckedByteArraySink() override; void Append(const char* data, size_t n) override; char* GetAppendBuffer(size_t len, char* scratch) override; char* GetAppendBufferVariable( size_t min_size, size_t desired_size_hint, char* scratch, size_t scratch_size, size_t* allocated_size) override; void AppendAndTakeOwnership( char* bytes, size_t n, void (*deleter)(void*, const char*, size_t), void *deleter_arg) override; // Return the current output pointer so that a caller can see how // many bytes were produced. // Note: this is not a Sink method. char* CurrentDestination() const { return dest_; } private: char* dest_; }; } // namespace snappy #endif // THIRD_PARTY_SNAPPY_SNAPPY_SINKSOURCE_H_ ================================================ FILE: Plugin/Snappy/snappy-stubs-internal.cc ================================================ // Copyright 2011 Google Inc. All Rights Reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include "snappy-stubs-internal.h" namespace snappy { void Varint::Append32(std::string* s, uint32_t value) { char buf[Varint::kMax32]; const char* p = Varint::Encode32(buf, value); s->append(buf, p - buf); } } // namespace snappy ================================================ FILE: Plugin/Snappy/snappy-stubs-internal.h ================================================ // Copyright 2011 Google Inc. All Rights Reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Various stubs for the open-source version of Snappy. #ifndef THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_INTERNAL_H_ #define THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_INTERNAL_H_ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #if HAVE_SYS_MMAN_H #include #endif #if HAVE_UNISTD_H #include #endif #if defined(_MSC_VER) #include #endif // defined(_MSC_VER) #ifndef __has_feature #define __has_feature(x) 0 #endif #if __has_feature(memory_sanitizer) #include #define SNAPPY_ANNOTATE_MEMORY_IS_INITIALIZED(address, size) \ __msan_unpoison((address), (size)) #else #define SNAPPY_ANNOTATE_MEMORY_IS_INITIALIZED(address, size) /* empty */ #endif // __has_feature(memory_sanitizer) #include "snappy-stubs-public.h" // Used to enable 64-bit optimized versions of some routines. #if defined(__PPC64__) || defined(__powerpc64__) #define ARCH_PPC 1 #elif defined(__aarch64__) || defined(_M_ARM64) #define ARCH_ARM 1 #endif // Needed by OS X, among others. #ifndef MAP_ANONYMOUS #define MAP_ANONYMOUS MAP_ANON #endif // The size of an array, if known at compile-time. // Will give unexpected results if used on a pointer. // We undefine it first, since some compilers already have a definition. #ifdef ARRAYSIZE #undef ARRAYSIZE #endif #define ARRAYSIZE(a) int{sizeof(a) / sizeof(*(a))} // Static prediction hints. #if HAVE_BUILTIN_EXPECT #define SNAPPY_PREDICT_FALSE(x) (__builtin_expect(x, 0)) #define SNAPPY_PREDICT_TRUE(x) (__builtin_expect(!!(x), 1)) #else #define SNAPPY_PREDICT_FALSE(x) x #define SNAPPY_PREDICT_TRUE(x) x #endif // HAVE_BUILTIN_EXPECT // Inlining hints. #if HAVE_ATTRIBUTE_ALWAYS_INLINE #define SNAPPY_ATTRIBUTE_ALWAYS_INLINE __attribute__((always_inline)) #else #define SNAPPY_ATTRIBUTE_ALWAYS_INLINE #endif // HAVE_ATTRIBUTE_ALWAYS_INLINE #if HAVE_BUILTIN_PREFETCH #define SNAPPY_PREFETCH(ptr) __builtin_prefetch(ptr, 0, 3) #else #define SNAPPY_PREFETCH(ptr) (void)(ptr) #endif // Stubbed version of ABSL_FLAG. // // In the open source version, flags can only be changed at compile time. #define SNAPPY_FLAG(flag_type, flag_name, default_value, help) \ flag_type FLAGS_ ## flag_name = default_value namespace snappy { // Stubbed version of absl::GetFlag(). template inline T GetFlag(T flag) { return flag; } static const uint32_t kuint32max = std::numeric_limits::max(); static const int64_t kint64max = std::numeric_limits::max(); // Potentially unaligned loads and stores. inline uint16_t UNALIGNED_LOAD16(const void *p) { // Compiles to a single movzx/ldrh on clang/gcc/msvc. uint16_t v; std::memcpy(&v, p, sizeof(v)); return v; } inline uint32_t UNALIGNED_LOAD32(const void *p) { // Compiles to a single mov/ldr on clang/gcc/msvc. uint32_t v; std::memcpy(&v, p, sizeof(v)); return v; } inline uint64_t UNALIGNED_LOAD64(const void *p) { // Compiles to a single mov/ldr on clang/gcc/msvc. uint64_t v; std::memcpy(&v, p, sizeof(v)); return v; } inline void UNALIGNED_STORE16(void *p, uint16_t v) { // Compiles to a single mov/strh on clang/gcc/msvc. std::memcpy(p, &v, sizeof(v)); } inline void UNALIGNED_STORE32(void *p, uint32_t v) { // Compiles to a single mov/str on clang/gcc/msvc. std::memcpy(p, &v, sizeof(v)); } inline void UNALIGNED_STORE64(void *p, uint64_t v) { // Compiles to a single mov/str on clang/gcc/msvc. std::memcpy(p, &v, sizeof(v)); } // Convert to little-endian storage, opposite of network format. // Convert x from host to little endian: x = LittleEndian.FromHost(x); // convert x from little endian to host: x = LittleEndian.ToHost(x); // // Store values into unaligned memory converting to little endian order: // LittleEndian.Store16(p, x); // // Load unaligned values stored in little endian converting to host order: // x = LittleEndian.Load16(p); class LittleEndian { public: // Functions to do unaligned loads and stores in little-endian order. static inline uint16_t Load16(const void *ptr) { // Compiles to a single mov/str on recent clang and gcc. #if SNAPPY_IS_BIG_ENDIAN const uint8_t* const buffer = reinterpret_cast(ptr); return (static_cast(buffer[0])) | (static_cast(buffer[1]) << 8); #else // memcpy() turns into a single instruction early in the optimization // pipeline (relatively to a series of byte accesses). So, using memcpy // instead of byte accesses may lead to better decisions in more stages of // the optimization pipeline. uint16_t value; std::memcpy(&value, ptr, 2); return value; #endif } static inline uint32_t Load32(const void *ptr) { // Compiles to a single mov/str on recent clang and gcc. #if SNAPPY_IS_BIG_ENDIAN const uint8_t* const buffer = reinterpret_cast(ptr); return (static_cast(buffer[0])) | (static_cast(buffer[1]) << 8) | (static_cast(buffer[2]) << 16) | (static_cast(buffer[3]) << 24); #else // See Load16() for the rationale of using memcpy(). uint32_t value; std::memcpy(&value, ptr, 4); return value; #endif } static inline uint64_t Load64(const void *ptr) { // Compiles to a single mov/str on recent clang and gcc. #if SNAPPY_IS_BIG_ENDIAN const uint8_t* const buffer = reinterpret_cast(ptr); return (static_cast(buffer[0])) | (static_cast(buffer[1]) << 8) | (static_cast(buffer[2]) << 16) | (static_cast(buffer[3]) << 24) | (static_cast(buffer[4]) << 32) | (static_cast(buffer[5]) << 40) | (static_cast(buffer[6]) << 48) | (static_cast(buffer[7]) << 56); #else // See Load16() for the rationale of using memcpy(). uint64_t value; std::memcpy(&value, ptr, 8); return value; #endif } static inline void Store16(void *dst, uint16_t value) { // Compiles to a single mov/str on recent clang and gcc. #if SNAPPY_IS_BIG_ENDIAN uint8_t* const buffer = reinterpret_cast(dst); buffer[0] = static_cast(value); buffer[1] = static_cast(value >> 8); #else // See Load16() for the rationale of using memcpy(). std::memcpy(dst, &value, 2); #endif } static void Store32(void *dst, uint32_t value) { // Compiles to a single mov/str on recent clang and gcc. #if SNAPPY_IS_BIG_ENDIAN uint8_t* const buffer = reinterpret_cast(dst); buffer[0] = static_cast(value); buffer[1] = static_cast(value >> 8); buffer[2] = static_cast(value >> 16); buffer[3] = static_cast(value >> 24); #else // See Load16() for the rationale of using memcpy(). std::memcpy(dst, &value, 4); #endif } static void Store64(void* dst, uint64_t value) { // Compiles to a single mov/str on recent clang and gcc. #if SNAPPY_IS_BIG_ENDIAN uint8_t* const buffer = reinterpret_cast(dst); buffer[0] = static_cast(value); buffer[1] = static_cast(value >> 8); buffer[2] = static_cast(value >> 16); buffer[3] = static_cast(value >> 24); buffer[4] = static_cast(value >> 32); buffer[5] = static_cast(value >> 40); buffer[6] = static_cast(value >> 48); buffer[7] = static_cast(value >> 56); #else // See Load16() for the rationale of using memcpy(). std::memcpy(dst, &value, 8); #endif } static inline constexpr bool IsLittleEndian() { #if SNAPPY_IS_BIG_ENDIAN return false; #else return true; #endif // SNAPPY_IS_BIG_ENDIAN } }; // Some bit-manipulation functions. class Bits { public: // Return floor(log2(n)) for positive integer n. static int Log2FloorNonZero(uint32_t n); // Return floor(log2(n)) for positive integer n. Returns -1 iff n == 0. static int Log2Floor(uint32_t n); // Return the first set least / most significant bit, 0-indexed. Returns an // undefined value if n == 0. FindLSBSetNonZero() is similar to ffs() except // that it's 0-indexed. static int FindLSBSetNonZero(uint32_t n); static int FindLSBSetNonZero64(uint64_t n); private: // No copying Bits(const Bits&); void operator=(const Bits&); }; // In RISC-V, CLZ is supported by instructions from the ZBB bit-manipulation extension. #if HAVE_BUILTIN_CTZ inline int Bits::Log2FloorNonZero(uint32_t n) { assert(n != 0); // (31 ^ x) is equivalent to (31 - x) for x in [0, 31]. An easy proof // represents subtraction in base 2 and observes that there's no carry. // // GCC and Clang represent __builtin_clz on x86 as 31 ^ _bit_scan_reverse(x). // Using "31 ^" here instead of "31 -" allows the optimizer to strip the // function body down to _bit_scan_reverse(x). return 31 ^ __builtin_clz(n); } inline int Bits::Log2Floor(uint32_t n) { return (n == 0) ? -1 : Bits::Log2FloorNonZero(n); } inline int Bits::FindLSBSetNonZero(uint32_t n) { assert(n != 0); return __builtin_ctz(n); } #elif defined(_MSC_VER) inline int Bits::Log2FloorNonZero(uint32_t n) { assert(n != 0); // NOLINTNEXTLINE(runtime/int): The MSVC intrinsic demands unsigned long. unsigned long where; _BitScanReverse(&where, n); return static_cast(where); } inline int Bits::Log2Floor(uint32_t n) { // NOLINTNEXTLINE(runtime/int): The MSVC intrinsic demands unsigned long. unsigned long where; if (_BitScanReverse(&where, n)) return static_cast(where); return -1; } inline int Bits::FindLSBSetNonZero(uint32_t n) { assert(n != 0); // NOLINTNEXTLINE(runtime/int): The MSVC intrinsic demands unsigned long. unsigned long where; if (_BitScanForward(&where, n)) return static_cast(where); return 32; } #else // Portable versions. inline int Bits::Log2FloorNonZero(uint32_t n) { assert(n != 0); int log = 0; uint32_t value = n; for (int i = 4; i >= 0; --i) { int shift = (1 << i); uint32_t x = value >> shift; if (x != 0) { value = x; log += shift; } } assert(value == 1); return log; } inline int Bits::Log2Floor(uint32_t n) { return (n == 0) ? -1 : Bits::Log2FloorNonZero(n); } inline int Bits::FindLSBSetNonZero(uint32_t n) { assert(n != 0); int rc = 31; for (int i = 4, shift = 1 << 4; i >= 0; --i) { const uint32_t x = n << shift; if (x != 0) { n = x; rc -= shift; } shift >>= 1; } return rc; } #endif // End portable versions. // In RISC-V, CLZ is supported by instructions from the ZBB bit-manipulation extension. #if HAVE_BUILTIN_CTZ inline int Bits::FindLSBSetNonZero64(uint64_t n) { assert(n != 0); return __builtin_ctzll(n); } #elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64)) // _BitScanForward64() is only available on x64 and ARM64. inline int Bits::FindLSBSetNonZero64(uint64_t n) { assert(n != 0); // NOLINTNEXTLINE(runtime/int): The MSVC intrinsic demands unsigned long. unsigned long where; if (_BitScanForward64(&where, n)) return static_cast(where); return 64; } #else // Portable version. // FindLSBSetNonZero64() is defined in terms of FindLSBSetNonZero(). inline int Bits::FindLSBSetNonZero64(uint64_t n) { assert(n != 0); const uint32_t bottombits = static_cast(n); if (bottombits == 0) { // Bottom bits are zero, so scan the top bits. return 32 + FindLSBSetNonZero(static_cast(n >> 32)); } else { return FindLSBSetNonZero(bottombits); } } #endif // HAVE_BUILTIN_CTZ // Variable-length integer encoding. class Varint { public: // Maximum lengths of varint encoding of uint32_t. static const int kMax32 = 5; // Attempts to parse a varint32 from a prefix of the bytes in [ptr,limit-1]. // Never reads a character at or beyond limit. If a valid/terminated varint32 // was found in the range, stores it in *OUTPUT and returns a pointer just // past the last byte of the varint32. Else returns NULL. On success, // "result <= limit". static const char* Parse32WithLimit(const char* ptr, const char* limit, uint32_t* OUTPUT); // REQUIRES "ptr" points to a buffer of length sufficient to hold "v". // EFFECTS Encodes "v" into "ptr" and returns a pointer to the // byte just past the last encoded byte. static char* Encode32(char* ptr, uint32_t v); // EFFECTS Appends the varint representation of "value" to "*s". static void Append32(std::string* s, uint32_t value); }; inline const char* Varint::Parse32WithLimit(const char* p, const char* l, uint32_t* OUTPUT) { const unsigned char* ptr = reinterpret_cast(p); const unsigned char* limit = reinterpret_cast(l); uint32_t b, result; if (ptr >= limit) return NULL; b = *(ptr++); result = b & 127; if (b < 128) goto done; if (ptr >= limit) return NULL; b = *(ptr++); result |= (b & 127) << 7; if (b < 128) goto done; if (ptr >= limit) return NULL; b = *(ptr++); result |= (b & 127) << 14; if (b < 128) goto done; if (ptr >= limit) return NULL; b = *(ptr++); result |= (b & 127) << 21; if (b < 128) goto done; if (ptr >= limit) return NULL; b = *(ptr++); result |= (b & 127) << 28; if (b < 16) goto done; return NULL; // Value is too long to be a varint32 done: *OUTPUT = result; return reinterpret_cast(ptr); } inline char* Varint::Encode32(char* sptr, uint32_t v) { // Operate on characters as unsigneds uint8_t* ptr = reinterpret_cast(sptr); static const uint8_t B = 128; if (v < (1 << 7)) { *(ptr++) = static_cast(v); } else if (v < (1 << 14)) { *(ptr++) = static_cast(v | B); *(ptr++) = static_cast(v >> 7); } else if (v < (1 << 21)) { *(ptr++) = static_cast(v | B); *(ptr++) = static_cast((v >> 7) | B); *(ptr++) = static_cast(v >> 14); } else if (v < (1 << 28)) { *(ptr++) = static_cast(v | B); *(ptr++) = static_cast((v >> 7) | B); *(ptr++) = static_cast((v >> 14) | B); *(ptr++) = static_cast(v >> 21); } else { *(ptr++) = static_cast(v | B); *(ptr++) = static_cast((v>>7) | B); *(ptr++) = static_cast((v>>14) | B); *(ptr++) = static_cast((v>>21) | B); *(ptr++) = static_cast(v >> 28); } return reinterpret_cast(ptr); } // If you know the internal layout of the std::string in use, you can // replace this function with one that resizes the string without // filling the new space with zeros (if applicable) -- // it will be non-portable but faster. inline void STLStringResizeUninitialized(std::string* s, size_t new_size) { s->resize(new_size); } // Return a mutable char* pointing to a string's internal buffer, // which may not be null-terminated. Writing through this pointer will // modify the string. // // string_as_array(&str)[i] is valid for 0 <= i < str.size() until the // next call to a string method that invalidates iterators. // // As of 2006-04, there is no standard-blessed way of getting a // mutable reference to a string's internal buffer. However, issue 530 // (http://www.open-std.org/JTC1/SC22/WG21/docs/lwg-defects.html#530) // proposes this as the method. It will officially be part of the standard // for C++0x. This should already work on all current implementations. inline char* string_as_array(std::string* str) { return str->empty() ? NULL : &*str->begin(); } } // namespace snappy #endif // THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_INTERNAL_H_ ================================================ FILE: Plugin/Snappy/snappy-stubs-public.h ================================================ // Copyright 2011 Google Inc. All Rights Reserved. // Author: sesse@google.com (Steinar H. Gunderson) // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Various type stubs for the open-source version of Snappy. // // This file cannot include config.h, as it is included from snappy.h, // which is a public header. Instead, snappy-stubs-public.h is generated by // from snappy-stubs-public.h.in at configure time. #ifndef THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ #define THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ #include #include #include #if 0 // HAVE_SYS_UIO_H #include #endif // HAVE_SYS_UIO_H #define SNAPPY_MAJOR 1 #define SNAPPY_MINOR 1 #define SNAPPY_PATCHLEVEL 7 #define SNAPPY_VERSION \ ((SNAPPY_MAJOR << 16) | (SNAPPY_MINOR << 8) | SNAPPY_PATCHLEVEL) namespace snappy { using int8 = std::int8_t; using uint8 = std::uint8_t; using int16 = std::int16_t; using uint16 = std::uint16_t; using int32 = std::int32_t; using uint32 = std::uint32_t; using int64 = std::int64_t; using uint64 = std::uint64_t; using string = std::string; #if 1 // !HAVE_SYS_UIO_H // Windows does not have an iovec type, yet the concept is universally useful. // It is simple to define it ourselves, so we put it inside our own namespace. struct iovec { void* iov_base; size_t iov_len; }; #endif // !HAVE_SYS_UIO_H } // namespace snappy #endif // THIRD_PARTY_SNAPPY_OPENSOURCE_SNAPPY_STUBS_PUBLIC_H_ ================================================ FILE: Plugin/Snappy/snappy.cc ================================================ // Copyright 2005 Google Inc. All Rights Reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "snappy-internal.h" #include "snappy-sinksource.h" #include "snappy.h" #if !defined(SNAPPY_HAVE_BMI2) // __BMI2__ is defined by GCC and Clang. Visual Studio doesn't target BMI2 // specifically, but it does define __AVX2__ when AVX2 support is available. // Fortunately, AVX2 was introduced in Haswell, just like BMI2. // // BMI2 is not defined as a subset of AVX2 (unlike SSSE3 and AVX above). So, // GCC and Clang can build code with AVX2 enabled but BMI2 disabled, in which // case issuing BMI2 instructions results in a compiler error. #if defined(__BMI2__) || (defined(_MSC_VER) && defined(__AVX2__)) #define SNAPPY_HAVE_BMI2 1 #else #define SNAPPY_HAVE_BMI2 0 #endif #endif // !defined(SNAPPY_HAVE_BMI2) #if !defined(SNAPPY_HAVE_X86_CRC32) #if defined(__SSE4_2__) #define SNAPPY_HAVE_X86_CRC32 1 #else #define SNAPPY_HAVE_X86_CRC32 0 #endif #endif // !defined(SNAPPY_HAVE_X86_CRC32) #if !defined(SNAPPY_HAVE_NEON_CRC32) #if SNAPPY_HAVE_NEON && defined(__ARM_FEATURE_CRC32) #define SNAPPY_HAVE_NEON_CRC32 1 #else #define SNAPPY_HAVE_NEON_CRC32 0 #endif #endif // !defined(SNAPPY_HAVE_NEON_CRC32) #if SNAPPY_HAVE_BMI2 || SNAPPY_HAVE_X86_CRC32 // Please do not replace with . or with headers that assume more // advanced SSE versions without checking with all the OWNERS. #include #elif SNAPPY_HAVE_NEON_CRC32 #include #endif #include #include #include #include #include #include #include #include #include #include #include namespace snappy { namespace { // The amount of slop bytes writers are using for unconditional copies. constexpr int kSlopBytes = 64; using internal::char_table; using internal::COPY_1_BYTE_OFFSET; using internal::COPY_2_BYTE_OFFSET; using internal::COPY_4_BYTE_OFFSET; using internal::kMaximumTagLength; using internal::LITERAL; #if SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE using internal::V128; using internal::V128_Load; using internal::V128_LoadU; using internal::V128_Shuffle; using internal::V128_StoreU; using internal::V128_DupChar; #endif // We translate the information encoded in a tag through a lookup table to a // format that requires fewer instructions to decode. Effectively we store // the length minus the tag part of the offset. The lowest significant byte // thus stores the length. While total length - offset is given by // entry - ExtractOffset(type). The nice thing is that the subtraction // immediately sets the flags for the necessary check that offset >= length. // This folds the cmp with sub. We engineer the long literals and copy-4 to // always fail this check, so their presence doesn't affect the fast path. // To prevent literals from triggering the guard against offset < length (offset // does not apply to literals) the table is giving them a spurious offset of // 256. inline constexpr int16_t MakeEntry(int16_t len, int16_t offset) { return len - (offset << 8); } inline constexpr int16_t LengthMinusOffset(int data, int type) { return type == 3 ? 0xFF // copy-4 (or type == 3) : type == 2 ? MakeEntry(data + 1, 0) // copy-2 : type == 1 ? MakeEntry((data & 7) + 4, data >> 3) // copy-1 : data < 60 ? MakeEntry(data + 1, 1) // note spurious offset. : 0xFF; // long literal } inline constexpr int16_t LengthMinusOffset(uint8_t tag) { return LengthMinusOffset(tag >> 2, tag & 3); } template struct index_sequence {}; template struct make_index_sequence : make_index_sequence {}; template struct make_index_sequence<0, Is...> : index_sequence {}; template constexpr std::array MakeTable(index_sequence) { return std::array{LengthMinusOffset(seq)...}; } alignas(64) const std::array kLengthMinusOffset = MakeTable(make_index_sequence<256>{}); // Given a table of uint16_t whose size is mask / 2 + 1, return a pointer to the // relevant entry, if any, for the given bytes. Any hash function will do, // but a good hash function reduces the number of collisions and thus yields // better compression for compressible input. // // REQUIRES: mask is 2 * (table_size - 1), and table_size is a power of two. inline uint16_t* TableEntry(uint16_t* table, uint32_t bytes, uint32_t mask) { // Our choice is quicker-and-dirtier than the typical hash function; // empirically, that seems beneficial. The upper bits of kMagic * bytes are a // higher-quality hash than the lower bits, so when using kMagic * bytes we // also shift right to get a higher-quality end result. There's no similar // issue with a CRC because all of the output bits of a CRC are equally good // "hashes." So, a CPU instruction for CRC, if available, tends to be a good // choice. #if SNAPPY_HAVE_NEON_CRC32 // We use mask as the second arg to the CRC function, as it's about to // be used anyway; it'd be equally correct to use 0 or some constant. // Mathematically, _mm_crc32_u32 (or similar) is a function of the // xor of its arguments. const uint32_t hash = __crc32cw(bytes, mask); #elif SNAPPY_HAVE_X86_CRC32 const uint32_t hash = _mm_crc32_u32(bytes, mask); #else constexpr uint32_t kMagic = 0x1e35a7bd; const uint32_t hash = (kMagic * bytes) >> (31 - kMaxHashTableBits); #endif return reinterpret_cast(reinterpret_cast(table) + (hash & mask)); } inline uint16_t* TableEntry4ByteMatch(uint16_t* table, uint32_t bytes, uint32_t mask) { constexpr uint32_t kMagic = 2654435761U; const uint32_t hash = (kMagic * bytes) >> (32 - kMaxHashTableBits); return reinterpret_cast(reinterpret_cast(table) + (hash & mask)); } inline uint16_t* TableEntry8ByteMatch(uint16_t* table, uint64_t bytes, uint32_t mask) { constexpr uint64_t kMagic = 58295818150454627ULL; const uint32_t hash = (kMagic * bytes) >> (64 - kMaxHashTableBits); return reinterpret_cast(reinterpret_cast(table) + (hash & mask)); } } // namespace size_t MaxCompressedLength(size_t source_bytes) { // Compressed data can be defined as: // compressed := item* literal* // item := literal* copy // // The trailing literal sequence has a space blowup of at most 62/60 // since a literal of length 60 needs one tag byte + one extra byte // for length information. // // Item blowup is trickier to measure. Suppose the "copy" op copies // 4 bytes of data. Because of a special check in the encoding code, // we produce a 4-byte copy only if the offset is < 65536. Therefore // the copy op takes 3 bytes to encode, and this type of item leads // to at most the 62/60 blowup for representing literals. // // Suppose the "copy" op copies 5 bytes of data. If the offset is big // enough, it will take 5 bytes to encode the copy op. Therefore the // worst case here is a one-byte literal followed by a five-byte copy. // I.e., 6 bytes of input turn into 7 bytes of "compressed" data. // // This last factor dominates the blowup, so the final estimate is: return 32 + source_bytes + source_bytes / 6; } namespace { void UnalignedCopy64(const void* src, void* dst) { char tmp[8]; std::memcpy(tmp, src, 8); std::memcpy(dst, tmp, 8); } void UnalignedCopy128(const void* src, void* dst) { // std::memcpy() gets vectorized when the appropriate compiler options are // used. For example, x86 compilers targeting SSE2+ will optimize to an SSE2 // load and store. char tmp[16]; std::memcpy(tmp, src, 16); std::memcpy(dst, tmp, 16); } template inline void ConditionalUnalignedCopy128(const char* src, char* dst) { if (use_16bytes_chunk) { UnalignedCopy128(src, dst); } else { UnalignedCopy64(src, dst); UnalignedCopy64(src + 8, dst + 8); } } // Copy [src, src+(op_limit-op)) to [op, (op_limit-op)) a byte at a time. Used // for handling COPY operations where the input and output regions may overlap. // For example, suppose: // src == "ab" // op == src + 2 // op_limit == op + 20 // After IncrementalCopySlow(src, op, op_limit), the result will have eleven // copies of "ab" // ababababababababababab // Note that this does not match the semantics of either std::memcpy() or // std::memmove(). inline char* IncrementalCopySlow(const char* src, char* op, char* const op_limit) { // TODO: Remove pragma when LLVM is aware this // function is only called in cold regions and when cold regions don't get // vectorized or unrolled. #ifdef __clang__ #pragma clang loop unroll(disable) #endif while (op < op_limit) { *op++ = *src++; } return op_limit; } #if SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE // Computes the bytes for shuffle control mask (please read comments on // 'pattern_generation_masks' as well) for the given index_offset and // pattern_size. For example, when the 'offset' is 6, it will generate a // repeating pattern of size 6. So, the first 16 byte indexes will correspond to // the pattern-bytes {0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3} and the // next 16 byte indexes will correspond to the pattern-bytes {4, 5, 0, 1, 2, 3, // 4, 5, 0, 1, 2, 3, 4, 5, 0, 1}. These byte index sequences are generated by // calling MakePatternMaskBytes(0, 6, index_sequence<16>()) and // MakePatternMaskBytes(16, 6, index_sequence<16>()) respectively. template inline constexpr std::array MakePatternMaskBytes( int index_offset, int pattern_size, index_sequence) { return {static_cast((index_offset + indexes) % pattern_size)...}; } // Computes the shuffle control mask bytes array for given pattern-sizes and // returns an array. template inline constexpr std::array, sizeof...(pattern_sizes_minus_one)> MakePatternMaskBytesTable(int index_offset, index_sequence) { return { MakePatternMaskBytes(index_offset, pattern_sizes_minus_one + 1, make_index_sequence())...}; } // This is an array of shuffle control masks that can be used as the source // operand for PSHUFB to permute the contents of the destination XMM register // into a repeating byte pattern. alignas(16) constexpr std::array, 16> pattern_generation_masks = MakePatternMaskBytesTable( /*index_offset=*/0, /*pattern_sizes_minus_one=*/make_index_sequence<16>()); // Similar to 'pattern_generation_masks', this table is used to "rotate" the // pattern so that we can copy the *next 16 bytes* consistent with the pattern. // Basically, pattern_reshuffle_masks is a continuation of // pattern_generation_masks. It follows that, pattern_reshuffle_masks is same as // pattern_generation_masks for offsets 1, 2, 4, 8 and 16. alignas(16) constexpr std::array, 16> pattern_reshuffle_masks = MakePatternMaskBytesTable( /*index_offset=*/16, /*pattern_sizes_minus_one=*/make_index_sequence<16>()); SNAPPY_ATTRIBUTE_ALWAYS_INLINE static inline V128 LoadPattern(const char* src, const size_t pattern_size) { V128 generation_mask = V128_Load(reinterpret_cast( pattern_generation_masks[pattern_size - 1].data())); // Uninitialized bytes are masked out by the shuffle mask. // TODO: remove annotation and macro defs once MSan is fixed. SNAPPY_ANNOTATE_MEMORY_IS_INITIALIZED(src + pattern_size, 16 - pattern_size); return V128_Shuffle(V128_LoadU(reinterpret_cast(src)), generation_mask); } SNAPPY_ATTRIBUTE_ALWAYS_INLINE static inline std::pair LoadPatternAndReshuffleMask(const char* src, const size_t pattern_size) { V128 pattern = LoadPattern(src, pattern_size); // This mask will generate the next 16 bytes in-place. Doing so enables us to // write data by at most 4 V128_StoreU. // // For example, suppose pattern is: abcdefabcdefabcd // Shuffling with this mask will generate: efabcdefabcdefab // Shuffling again will generate: cdefabcdefabcdef V128 reshuffle_mask = V128_Load(reinterpret_cast( pattern_reshuffle_masks[pattern_size - 1].data())); return {pattern, reshuffle_mask}; } #endif // SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE // Fallback for when we need to copy while extending the pattern, for example // copying 10 bytes from 3 positions back abc -> abcabcabcabca. // // REQUIRES: [dst - offset, dst + 64) is a valid address range. SNAPPY_ATTRIBUTE_ALWAYS_INLINE static inline bool Copy64BytesWithPatternExtension(char* dst, size_t offset) { #if SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE if (SNAPPY_PREDICT_TRUE(offset <= 16)) { switch (offset) { case 0: return false; case 1: { // TODO: Ideally we should memset, move back once the // codegen issues are fixed. V128 pattern = V128_DupChar(dst[-1]); for (int i = 0; i < 4; i++) { V128_StoreU(reinterpret_cast(dst + 16 * i), pattern); } return true; } case 2: case 4: case 8: case 16: { V128 pattern = LoadPattern(dst - offset, offset); for (int i = 0; i < 4; i++) { V128_StoreU(reinterpret_cast(dst + 16 * i), pattern); } return true; } default: { auto pattern_and_reshuffle_mask = LoadPatternAndReshuffleMask(dst - offset, offset); V128 pattern = pattern_and_reshuffle_mask.first; V128 reshuffle_mask = pattern_and_reshuffle_mask.second; for (int i = 0; i < 4; i++) { V128_StoreU(reinterpret_cast(dst + 16 * i), pattern); pattern = V128_Shuffle(pattern, reshuffle_mask); } return true; } } } #else if (SNAPPY_PREDICT_TRUE(offset < 16)) { if (SNAPPY_PREDICT_FALSE(offset == 0)) return false; // Extend the pattern to the first 16 bytes. // The simpler formulation of `dst[i - offset]` induces undefined behavior. for (int i = 0; i < 16; i++) dst[i] = (dst - offset)[i]; // Find a multiple of pattern >= 16. static std::array pattern_sizes = []() { std::array res; for (int i = 1; i < 16; i++) res[i] = (16 / i + 1) * i; return res; }(); offset = pattern_sizes[offset]; for (int i = 1; i < 4; i++) { std::memcpy(dst + i * 16, dst + i * 16 - offset, 16); } return true; } #endif // SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE // Very rare. for (int i = 0; i < 4; i++) { std::memcpy(dst + i * 16, dst + i * 16 - offset, 16); } return true; } // Copy [src, src+(op_limit-op)) to [op, op_limit) but faster than // IncrementalCopySlow. buf_limit is the address past the end of the writable // region of the buffer. inline char* IncrementalCopy(const char* src, char* op, char* const op_limit, char* const buf_limit) { #if SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE constexpr int big_pattern_size_lower_bound = 16; #else constexpr int big_pattern_size_lower_bound = 8; #endif // Terminology: // // slop = buf_limit - op // pat = op - src // len = op_limit - op assert(src < op); assert(op < op_limit); assert(op_limit <= buf_limit); // NOTE: The copy tags use 3 or 6 bits to store the copy length, so len <= 64. assert(op_limit - op <= 64); // NOTE: In practice the compressor always emits len >= 4, so it is ok to // assume that to optimize this function, but this is not guaranteed by the // compression format, so we have to also handle len < 4 in case the input // does not satisfy these conditions. size_t pattern_size = op - src; // The cases are split into different branches to allow the branch predictor, // FDO, and static prediction hints to work better. For each input we list the // ratio of invocations that match each condition. // // input slop < 16 pat < 8 len > 16 // ------------------------------------------ // html|html4|cp 0% 1.01% 27.73% // urls 0% 0.88% 14.79% // jpg 0% 64.29% 7.14% // pdf 0% 2.56% 58.06% // txt[1-4] 0% 0.23% 0.97% // pb 0% 0.96% 13.88% // bin 0.01% 22.27% 41.17% // // It is very rare that we don't have enough slop for doing block copies. It // is also rare that we need to expand a pattern. Small patterns are common // for incompressible formats and for those we are plenty fast already. // Lengths are normally not greater than 16 but they vary depending on the // input. In general if we always predict len <= 16 it would be an ok // prediction. // // In order to be fast we want a pattern >= 16 bytes (or 8 bytes in non-SSE) // and an unrolled loop copying 1x 16 bytes (or 2x 8 bytes in non-SSE) at a // time. // Handle the uncommon case where pattern is less than 16 (or 8 in non-SSE) // bytes. if (pattern_size < big_pattern_size_lower_bound) { #if SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE // Load the first eight bytes into an 128-bit XMM register, then use PSHUFB // to permute the register's contents in-place into a repeating sequence of // the first "pattern_size" bytes. // For example, suppose: // src == "abc" // op == op + 3 // After V128_Shuffle(), "pattern" will have five copies of "abc" // followed by one byte of slop: abcabcabcabcabca. // // The non-SSE fallback implementation suffers from store-forwarding stalls // because its loads and stores partly overlap. By expanding the pattern // in-place, we avoid the penalty. // Typically, the op_limit is the gating factor so try to simplify the loop // based on that. if (SNAPPY_PREDICT_TRUE(op_limit <= buf_limit - 15)) { auto pattern_and_reshuffle_mask = LoadPatternAndReshuffleMask(src, pattern_size); V128 pattern = pattern_and_reshuffle_mask.first; V128 reshuffle_mask = pattern_and_reshuffle_mask.second; // There is at least one, and at most four 16-byte blocks. Writing four // conditionals instead of a loop allows FDO to layout the code with // respect to the actual probabilities of each length. // TODO: Replace with loop with trip count hint. V128_StoreU(reinterpret_cast(op), pattern); if (op + 16 < op_limit) { pattern = V128_Shuffle(pattern, reshuffle_mask); V128_StoreU(reinterpret_cast(op + 16), pattern); } if (op + 32 < op_limit) { pattern = V128_Shuffle(pattern, reshuffle_mask); V128_StoreU(reinterpret_cast(op + 32), pattern); } if (op + 48 < op_limit) { pattern = V128_Shuffle(pattern, reshuffle_mask); V128_StoreU(reinterpret_cast(op + 48), pattern); } return op_limit; } char* const op_end = buf_limit - 15; if (SNAPPY_PREDICT_TRUE(op < op_end)) { auto pattern_and_reshuffle_mask = LoadPatternAndReshuffleMask(src, pattern_size); V128 pattern = pattern_and_reshuffle_mask.first; V128 reshuffle_mask = pattern_and_reshuffle_mask.second; // This code path is relatively cold however so we save code size // by avoiding unrolling and vectorizing. // // TODO: Remove pragma when when cold regions don't get // vectorized or unrolled. #ifdef __clang__ #pragma clang loop unroll(disable) #endif do { V128_StoreU(reinterpret_cast(op), pattern); pattern = V128_Shuffle(pattern, reshuffle_mask); op += 16; } while (SNAPPY_PREDICT_TRUE(op < op_end)); } return IncrementalCopySlow(op - pattern_size, op, op_limit); #else // !SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE // If plenty of buffer space remains, expand the pattern to at least 8 // bytes. The way the following loop is written, we need 8 bytes of buffer // space if pattern_size >= 4, 11 bytes if pattern_size is 1 or 3, and 10 // bytes if pattern_size is 2. Precisely encoding that is probably not // worthwhile; instead, invoke the slow path if we cannot write 11 bytes // (because 11 are required in the worst case). if (SNAPPY_PREDICT_TRUE(op <= buf_limit - 11)) { while (pattern_size < 8) { UnalignedCopy64(src, op); op += pattern_size; pattern_size *= 2; } if (SNAPPY_PREDICT_TRUE(op >= op_limit)) return op_limit; } else { return IncrementalCopySlow(src, op, op_limit); } #endif // SNAPPY_HAVE_VECTOR_BYTE_SHUFFLE } assert(pattern_size >= big_pattern_size_lower_bound); constexpr bool use_16bytes_chunk = big_pattern_size_lower_bound == 16; // Copy 1x 16 bytes (or 2x 8 bytes in non-SSE) at a time. Because op - src can // be < 16 in non-SSE, a single UnalignedCopy128 might overwrite data in op. // UnalignedCopy64 is safe because expanding the pattern to at least 8 bytes // guarantees that op - src >= 8. // // Typically, the op_limit is the gating factor so try to simplify the loop // based on that. if (SNAPPY_PREDICT_TRUE(op_limit <= buf_limit - 15)) { // There is at least one, and at most four 16-byte blocks. Writing four // conditionals instead of a loop allows FDO to layout the code with respect // to the actual probabilities of each length. // TODO: Replace with loop with trip count hint. ConditionalUnalignedCopy128(src, op); if (op + 16 < op_limit) { ConditionalUnalignedCopy128(src + 16, op + 16); } if (op + 32 < op_limit) { ConditionalUnalignedCopy128(src + 32, op + 32); } if (op + 48 < op_limit) { ConditionalUnalignedCopy128(src + 48, op + 48); } return op_limit; } // Fall back to doing as much as we can with the available slop in the // buffer. This code path is relatively cold however so we save code size by // avoiding unrolling and vectorizing. // // TODO: Remove pragma when when cold regions don't get vectorized // or unrolled. #ifdef __clang__ #pragma clang loop unroll(disable) #endif for (char* op_end = buf_limit - 16; op < op_end; op += 16, src += 16) { ConditionalUnalignedCopy128(src, op); } if (op >= op_limit) return op_limit; // We only take this branch if we didn't have enough slop and we can do a // single 8 byte copy. if (SNAPPY_PREDICT_FALSE(op <= buf_limit - 8)) { UnalignedCopy64(src, op); src += 8; op += 8; } return IncrementalCopySlow(src, op, op_limit); } } // namespace template static inline char* EmitLiteral(char* op, const char* literal, int len) { // The vast majority of copies are below 16 bytes, for which a // call to std::memcpy() is overkill. This fast path can sometimes // copy up to 15 bytes too much, but that is okay in the // main loop, since we have a bit to go on for both sides: // // - The input will always have kInputMarginBytes = 15 extra // available bytes, as long as we're in the main loop, and // if not, allow_fast_path = false. // - The output will always have 32 spare bytes (see // MaxCompressedLength). assert(len > 0); // Zero-length literals are disallowed int n = len - 1; if (allow_fast_path && len <= 16) { // Fits in tag byte *op++ = LITERAL | (n << 2); UnalignedCopy128(literal, op); return op + len; } if (n < 60) { // Fits in tag byte *op++ = LITERAL | (n << 2); } else { int count = (Bits::Log2Floor(n) >> 3) + 1; assert(count >= 1); assert(count <= 4); *op++ = LITERAL | ((59 + count) << 2); // Encode in upcoming bytes. // Write 4 bytes, though we may care about only 1 of them. The output buffer // is guaranteed to have at least 3 more spaces left as 'len >= 61' holds // here and there is a std::memcpy() of size 'len' below. LittleEndian::Store32(op, n); op += count; } // When allow_fast_path is true, we can overwrite up to 16 bytes. if (allow_fast_path) { char* destination = op; const char* source = literal; const char* end = destination + len; do { std::memcpy(destination, source, 16); destination += 16; source += 16; } while (destination < end); } else { std::memcpy(op, literal, len); } return op + len; } template static inline char* EmitCopyAtMost64(char* op, size_t offset, size_t len) { assert(len <= 64); assert(len >= 4); assert(offset < 65536); assert(len_less_than_12 == (len < 12)); if (len_less_than_12) { uint32_t u = (len << 2) + (offset << 8); uint32_t copy1 = COPY_1_BYTE_OFFSET - (4 << 2) + ((offset >> 3) & 0xe0); uint32_t copy2 = COPY_2_BYTE_OFFSET - (1 << 2); // It turns out that offset < 2048 is a difficult to predict branch. // `perf record` shows this is the highest percentage of branch misses in // benchmarks. This code produces branch free code, the data dependency // chain that bottlenecks the throughput is so long that a few extra // instructions are completely free (IPC << 6 because of data deps). u += offset < 2048 ? copy1 : copy2; LittleEndian::Store32(op, u); op += offset < 2048 ? 2 : 3; } else { // Write 4 bytes, though we only care about 3 of them. The output buffer // is required to have some slack, so the extra byte won't overrun it. uint32_t u = COPY_2_BYTE_OFFSET + ((len - 1) << 2) + (offset << 8); LittleEndian::Store32(op, u); op += 3; } return op; } template static inline char* EmitCopy(char* op, size_t offset, size_t len) { assert(len_less_than_12 == (len < 12)); if (len_less_than_12) { return EmitCopyAtMost64(op, offset, len); } else { // A special case for len <= 64 might help, but so far measurements suggest // it's in the noise. // Emit 64 byte copies but make sure to keep at least four bytes reserved. while (SNAPPY_PREDICT_FALSE(len >= 68)) { op = EmitCopyAtMost64(op, offset, 64); len -= 64; } // One or two copies will now finish the job. if (len > 64) { op = EmitCopyAtMost64(op, offset, 60); len -= 60; } // Emit remainder. if (len < 12) { op = EmitCopyAtMost64(op, offset, len); } else { op = EmitCopyAtMost64(op, offset, len); } return op; } } bool GetUncompressedLength(const char* start, size_t n, size_t* result) { uint32_t v = 0; const char* limit = start + n; if (Varint::Parse32WithLimit(start, limit, &v) != NULL) { *result = v; return true; } else { return false; } } namespace { uint32_t CalculateTableSize(uint32_t input_size) { static_assert( kMaxHashTableSize >= kMinHashTableSize, "kMaxHashTableSize should be greater or equal to kMinHashTableSize."); if (input_size > kMaxHashTableSize) { return kMaxHashTableSize; } if (input_size < kMinHashTableSize) { return kMinHashTableSize; } // This is equivalent to Log2Ceiling(input_size), assuming input_size > 1. // 2 << Log2Floor(x - 1) is equivalent to 1 << (1 + Log2Floor(x - 1)). return 2u << Bits::Log2Floor(input_size - 1); } } // namespace namespace internal { WorkingMemory::WorkingMemory(size_t input_size) { const size_t max_fragment_size = std::min(input_size, kBlockSize); const size_t table_size = CalculateTableSize(max_fragment_size); size_ = table_size * sizeof(*table_) + max_fragment_size + MaxCompressedLength(max_fragment_size); mem_ = std::allocator().allocate(size_); table_ = reinterpret_cast(mem_); input_ = mem_ + table_size * sizeof(*table_); output_ = input_ + max_fragment_size; } WorkingMemory::~WorkingMemory() { std::allocator().deallocate(mem_, size_); } uint16_t* WorkingMemory::GetHashTable(size_t fragment_size, int* table_size) const { const size_t htsize = CalculateTableSize(fragment_size); memset(table_, 0, htsize * sizeof(*table_)); *table_size = htsize; return table_; } } // end namespace internal // Flat array compression that does not emit the "uncompressed length" // prefix. Compresses "input" string to the "*op" buffer. // // REQUIRES: "input" is at most "kBlockSize" bytes long. // REQUIRES: "op" points to an array of memory that is at least // "MaxCompressedLength(input.size())" in size. // REQUIRES: All elements in "table[0..table_size-1]" are initialized to zero. // REQUIRES: "table_size" is a power of two // // Returns an "end" pointer into "op" buffer. // "end - op" is the compressed size of "input". namespace internal { char* CompressFragment(const char* input, size_t input_size, char* op, uint16_t* table, const int table_size) { // "ip" is the input pointer, and "op" is the output pointer. const char* ip = input; assert(input_size <= kBlockSize); assert((table_size & (table_size - 1)) == 0); // table must be power of two const uint32_t mask = 2 * (table_size - 1); const char* ip_end = input + input_size; const char* base_ip = ip; const size_t kInputMarginBytes = 15; if (SNAPPY_PREDICT_TRUE(input_size >= kInputMarginBytes)) { const char* ip_limit = input + input_size - kInputMarginBytes; for (uint32_t preload = LittleEndian::Load32(ip + 1);;) { // Bytes in [next_emit, ip) will be emitted as literal bytes. Or // [next_emit, ip_end) after the main loop. const char* next_emit = ip++; uint64_t data = LittleEndian::Load64(ip); // The body of this loop calls EmitLiteral once and then EmitCopy one or // more times. (The exception is that when we're close to exhausting // the input we goto emit_remainder.) // // In the first iteration of this loop we're just starting, so // there's nothing to copy, so calling EmitLiteral once is // necessary. And we only start a new iteration when the // current iteration has determined that a call to EmitLiteral will // precede the next call to EmitCopy (if any). // // Step 1: Scan forward in the input looking for a 4-byte-long match. // If we get close to exhausting the input then goto emit_remainder. // // Heuristic match skipping: If 32 bytes are scanned with no matches // found, start looking only at every other byte. If 32 more bytes are // scanned (or skipped), look at every third byte, etc.. When a match is // found, immediately go back to looking at every byte. This is a small // loss (~5% performance, ~0.1% density) for compressible data due to more // bookkeeping, but for non-compressible data (such as JPEG) it's a huge // win since the compressor quickly "realizes" the data is incompressible // and doesn't bother looking for matches everywhere. // // The "skip" variable keeps track of how many bytes there are since the // last match; dividing it by 32 (ie. right-shifting by five) gives the // number of bytes to move ahead for each iteration. uint32_t skip = 32; const char* candidate; if (ip_limit - ip >= 16) { auto delta = ip - base_ip; for (int j = 0; j < 4; ++j) { for (int k = 0; k < 4; ++k) { int i = 4 * j + k; // These for-loops are meant to be unrolled. So we can freely // special case the first iteration to use the value already // loaded in preload. uint32_t dword = i == 0 ? preload : static_cast(data); assert(dword == LittleEndian::Load32(ip + i)); uint16_t* table_entry = TableEntry(table, dword, mask); candidate = base_ip + *table_entry; assert(candidate >= base_ip); assert(candidate < ip + i); *table_entry = delta + i; if (SNAPPY_PREDICT_FALSE(LittleEndian::Load32(candidate) == dword)) { *op = LITERAL | (i << 2); UnalignedCopy128(next_emit, op + 1); ip += i; op = op + i + 2; goto emit_match; } data >>= 8; } data = LittleEndian::Load64(ip + 4 * j + 4); } ip += 16; skip += 16; } while (true) { assert(static_cast(data) == LittleEndian::Load32(ip)); uint16_t* table_entry = TableEntry(table, data, mask); uint32_t bytes_between_hash_lookups = skip >> 5; skip += bytes_between_hash_lookups; const char* next_ip = ip + bytes_between_hash_lookups; if (SNAPPY_PREDICT_FALSE(next_ip > ip_limit)) { ip = next_emit; goto emit_remainder; } candidate = base_ip + *table_entry; assert(candidate >= base_ip); assert(candidate < ip); *table_entry = ip - base_ip; if (SNAPPY_PREDICT_FALSE(static_cast(data) == LittleEndian::Load32(candidate))) { break; } data = LittleEndian::Load32(next_ip); ip = next_ip; } // Step 2: A 4-byte match has been found. We'll later see if more // than 4 bytes match. But, prior to the match, input // bytes [next_emit, ip) are unmatched. Emit them as "literal bytes." assert(next_emit + 16 <= ip_end); op = EmitLiteral(op, next_emit, ip - next_emit); // Step 3: Call EmitCopy, and then see if another EmitCopy could // be our next move. Repeat until we find no match for the // input immediately after what was consumed by the last EmitCopy call. // // If we exit this loop normally then we need to call EmitLiteral next, // though we don't yet know how big the literal will be. We handle that // by proceeding to the next iteration of the main loop. We also can exit // this loop via goto if we get close to exhausting the input. emit_match: do { // We have a 4-byte match at ip, and no need to emit any // "literal bytes" prior to ip. const char* base = ip; std::pair p = FindMatchLength(candidate + 4, ip + 4, ip_end, &data); size_t matched = 4 + p.first; ip += matched; size_t offset = base - candidate; assert(0 == memcmp(base, candidate, matched)); if (p.second) { op = EmitCopy(op, offset, matched); } else { op = EmitCopy(op, offset, matched); } if (SNAPPY_PREDICT_FALSE(ip >= ip_limit)) { goto emit_remainder; } // Expect 5 bytes to match assert((data & 0xFFFFFFFFFF) == (LittleEndian::Load64(ip) & 0xFFFFFFFFFF)); // We are now looking for a 4-byte match again. We read // table[Hash(ip, mask)] for that. To improve compression, // we also update table[Hash(ip - 1, mask)] and table[Hash(ip, mask)]. *TableEntry(table, LittleEndian::Load32(ip - 1), mask) = ip - base_ip - 1; uint16_t* table_entry = TableEntry(table, data, mask); candidate = base_ip + *table_entry; *table_entry = ip - base_ip; // Measurements on the benchmarks have shown the following probabilities // for the loop to exit (ie. avg. number of iterations is reciprocal). // BM_Flat/6 txt1 p = 0.3-0.4 // BM_Flat/7 txt2 p = 0.35 // BM_Flat/8 txt3 p = 0.3-0.4 // BM_Flat/9 txt3 p = 0.34-0.4 // BM_Flat/10 pb p = 0.4 // BM_Flat/11 gaviota p = 0.1 // BM_Flat/12 cp p = 0.5 // BM_Flat/13 c p = 0.3 } while (static_cast(data) == LittleEndian::Load32(candidate)); // Because the least significant 5 bytes matched, we can utilize data // for the next iteration. preload = data >> 8; } } emit_remainder: // Emit the remaining bytes as a literal if (ip < ip_end) { op = EmitLiteral(op, ip, ip_end - ip); } return op; } char* CompressFragmentDoubleHash(const char* input, size_t input_size, char* op, uint16_t* table, const int table_size, uint16_t* table2, const int table_size2) { (void)table_size2; assert(table_size == table_size2); // "ip" is the input pointer, and "op" is the output pointer. const char* ip = input; assert(input_size <= kBlockSize); assert((table_size & (table_size - 1)) == 0); // table must be power of two const uint32_t mask = 2 * (table_size - 1); const char* ip_end = input + input_size; const char* base_ip = ip; const size_t kInputMarginBytes = 15; if (SNAPPY_PREDICT_TRUE(input_size >= kInputMarginBytes)) { const char* ip_limit = input + input_size - kInputMarginBytes; for (;;) { const char* next_emit = ip++; uint64_t data = LittleEndian::Load64(ip); uint32_t skip = 512; const char* candidate; uint32_t candidate_length; while (true) { assert(static_cast(data) == LittleEndian::Load32(ip)); uint16_t* table_entry2 = TableEntry8ByteMatch(table2, data, mask); uint32_t bytes_between_hash_lookups = skip >> 9; skip++; const char* next_ip = ip + bytes_between_hash_lookups; if (SNAPPY_PREDICT_FALSE(next_ip > ip_limit)) { ip = next_emit; goto emit_remainder; } candidate = base_ip + *table_entry2; assert(candidate >= base_ip); assert(candidate < ip); *table_entry2 = ip - base_ip; if (SNAPPY_PREDICT_FALSE(static_cast(data) == LittleEndian::Load32(candidate))) { candidate_length = FindMatchLengthPlain(candidate + 4, ip + 4, ip_end) + 4; break; } uint16_t* table_entry = TableEntry4ByteMatch(table, data, mask); candidate = base_ip + *table_entry; assert(candidate >= base_ip); assert(candidate < ip); *table_entry = ip - base_ip; if (SNAPPY_PREDICT_FALSE(static_cast(data) == LittleEndian::Load32(candidate))) { candidate_length = FindMatchLengthPlain(candidate + 4, ip + 4, ip_end) + 4; table_entry2 = TableEntry8ByteMatch(table2, LittleEndian::Load64(ip + 1), mask); auto candidate2 = base_ip + *table_entry2; size_t candidate_length2 = FindMatchLengthPlain(candidate2, ip + 1, ip_end); if (candidate_length2 > candidate_length) { *table_entry2 = ip - base_ip; candidate = candidate2; candidate_length = candidate_length2; ++ip; } break; } data = LittleEndian::Load64(next_ip); ip = next_ip; } // Backtrack to the point it matches fully. while (ip > next_emit && candidate > base_ip && *(ip - 1) == *(candidate - 1)) { --ip; --candidate; ++candidate_length; } *TableEntry8ByteMatch(table2, LittleEndian::Load64(ip + 1), mask) = ip - base_ip + 1; *TableEntry8ByteMatch(table2, LittleEndian::Load64(ip + 2), mask) = ip - base_ip + 2; *TableEntry4ByteMatch(table, LittleEndian::Load32(ip + 1), mask) = ip - base_ip + 1; // Step 2: A 4-byte or 8-byte match has been found. // We'll later see if more than 4 bytes match. But, prior to the match, // input bytes [next_emit, ip) are unmatched. Emit them as // "literal bytes." assert(next_emit + 16 <= ip_end); if (ip - next_emit > 0) { op = EmitLiteral(op, next_emit, ip - next_emit); } // Step 3: Call EmitCopy, and then see if another EmitCopy could // be our next move. Repeat until we find no match for the // input immediately after what was consumed by the last EmitCopy call. // // If we exit this loop normally then we need to call EmitLiteral next, // though we don't yet know how big the literal will be. We handle that // by proceeding to the next iteration of the main loop. We also can exit // this loop via goto if we get close to exhausting the input. do { // We have a 4-byte match at ip, and no need to emit any // "literal bytes" prior to ip. const char* base = ip; ip += candidate_length; size_t offset = base - candidate; if (candidate_length < 12) { op = EmitCopy(op, offset, candidate_length); } else { op = EmitCopy(op, offset, candidate_length); } if (SNAPPY_PREDICT_FALSE(ip >= ip_limit)) { goto emit_remainder; } // We are now looking for a 4-byte match again. We read // table[Hash(ip, mask)] for that. To improve compression, // we also update several previous table entries. if (ip - base_ip > 7) { *TableEntry8ByteMatch(table2, LittleEndian::Load64(ip - 7), mask) = ip - base_ip - 7; *TableEntry8ByteMatch(table2, LittleEndian::Load64(ip - 4), mask) = ip - base_ip - 4; } *TableEntry8ByteMatch(table2, LittleEndian::Load64(ip - 3), mask) = ip - base_ip - 3; *TableEntry8ByteMatch(table2, LittleEndian::Load64(ip - 2), mask) = ip - base_ip - 2; *TableEntry4ByteMatch(table, LittleEndian::Load32(ip - 2), mask) = ip - base_ip - 2; *TableEntry4ByteMatch(table, LittleEndian::Load32(ip - 1), mask) = ip - base_ip - 1; uint16_t* table_entry = TableEntry8ByteMatch(table2, LittleEndian::Load64(ip), mask); candidate = base_ip + *table_entry; *table_entry = ip - base_ip; if (LittleEndian::Load32(ip) == LittleEndian::Load32(candidate)) { candidate_length = FindMatchLengthPlain(candidate + 4, ip + 4, ip_end) + 4; continue; } table_entry = TableEntry4ByteMatch(table, LittleEndian::Load32(ip), mask); candidate = base_ip + *table_entry; *table_entry = ip - base_ip; if (LittleEndian::Load32(ip) == LittleEndian::Load32(candidate)) { candidate_length = FindMatchLengthPlain(candidate + 4, ip + 4, ip_end) + 4; continue; } break; } while (true); } } emit_remainder: // Emit the remaining bytes as a literal if (ip < ip_end) { op = EmitLiteral(op, ip, ip_end - ip); } return op; } } // end namespace internal static inline void Report(int token, const char *algorithm, size_t compressed_size, size_t uncompressed_size) { // TODO: Switch to [[maybe_unused]] when we can assume C++17. (void)token; (void)algorithm; (void)compressed_size; (void)uncompressed_size; } // Signature of output types needed by decompression code. // The decompression code is templatized on a type that obeys this // signature so that we do not pay virtual function call overhead in // the middle of a tight decompression loop. // // class DecompressionWriter { // public: // // Called before decompression // void SetExpectedLength(size_t length); // // // For performance a writer may choose to donate the cursor variable to the // // decompression function. The decompression will inject it in all its // // function calls to the writer. Keeping the important output cursor as a // // function local stack variable allows the compiler to keep it in // // register, which greatly aids performance by avoiding loads and stores of // // this variable in the fast path loop iterations. // T GetOutputPtr() const; // // // At end of decompression the loop donates the ownership of the cursor // // variable back to the writer by calling this function. // void SetOutputPtr(T op); // // // Called after decompression // bool CheckLength() const; // // // Called repeatedly during decompression // // Each function get a pointer to the op (output pointer), that the writer // // can use and update. Note it's important that these functions get fully // // inlined so that no actual address of the local variable needs to be // // taken. // bool Append(const char* ip, size_t length, T* op); // bool AppendFromSelf(uint32_t offset, size_t length, T* op); // // // The rules for how TryFastAppend differs from Append are somewhat // // convoluted: // // // // - TryFastAppend is allowed to decline (return false) at any // // time, for any reason -- just "return false" would be // // a perfectly legal implementation of TryFastAppend. // // The intention is for TryFastAppend to allow a fast path // // in the common case of a small append. // // - TryFastAppend is allowed to read up to bytes // // from the input buffer, whereas Append is allowed to read // // . However, if it returns true, it must leave // // at least five (kMaximumTagLength) bytes in the input buffer // // afterwards, so that there is always enough space to read the // // next tag without checking for a refill. // // - TryFastAppend must always return decline (return false) // // if is 61 or more, as in this case the literal length is not // // decoded fully. In practice, this should not be a big problem, // // as it is unlikely that one would implement a fast path accepting // // this much data. // // // bool TryFastAppend(const char* ip, size_t available, size_t length, T* op); // }; static inline uint32_t ExtractLowBytes(const uint32_t& v, int n) { assert(n >= 0); assert(n <= 4); #if SNAPPY_HAVE_BMI2 return _bzhi_u32(v, 8 * n); #else // This needs to be wider than uint32_t otherwise `mask << 32` will be // undefined. uint64_t mask = 0xffffffff; return v & ~(mask << (8 * n)); #endif } static inline bool LeftShiftOverflows(uint8_t value, uint32_t shift) { assert(shift < 32); static const uint8_t masks[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe}; return (value & masks[shift]) != 0; } inline bool Copy64BytesWithPatternExtension(ptrdiff_t dst, size_t offset) { // TODO: Switch to [[maybe_unused]] when we can assume C++17. (void)dst; return offset != 0; } // Copies between size bytes and 64 bytes from src to dest. size cannot exceed // 64. More than size bytes, but never exceeding 64, might be copied if doing // so gives better performance. [src, src + size) must not overlap with // [dst, dst + size), but [src, src + 64) may overlap with [dst, dst + 64). void MemCopy64(char* dst, const void* src, size_t size) { // Always copy this many bytes. If that's below size then copy the full 64. constexpr int kShortMemCopy = 32; (void)kShortMemCopy; assert(size <= 64); assert(std::less_equal()(static_cast(src) + size, dst) || std::less_equal()(dst + size, src)); // We know that src and dst are at least size bytes apart. However, because we // might copy more than size bytes the copy still might overlap past size. // E.g. if src and dst appear consecutively in memory (src + size >= dst). // TODO: Investigate wider copies on other platforms. #if defined(__x86_64__) && defined(__AVX__) assert(kShortMemCopy <= 32); __m256i data = _mm256_lddqu_si256(static_cast(src)); _mm256_storeu_si256(reinterpret_cast<__m256i *>(dst), data); // Profiling shows that nearly all copies are short. if (SNAPPY_PREDICT_FALSE(size > kShortMemCopy)) { data = _mm256_lddqu_si256(static_cast(src) + 1); _mm256_storeu_si256(reinterpret_cast<__m256i *>(dst) + 1, data); } // RVV acceleration available on RISC-V when compiled with -march=rv64gcv #elif defined(__riscv) && SNAPPY_HAVE_RVV // Cast pointers to the type we will operate on. unsigned char* dst_ptr = reinterpret_cast(dst); const unsigned char* src_ptr = reinterpret_cast(src); size_t remaining_bytes = size; // Loop as long as there are bytes remaining to be copied. while (remaining_bytes > 0) { // Set vector configuration: e8 (8-bit elements), m2 (LMUL=2). // Use e8m2 configuration to maximize throughput. size_t vl = VSETVL_E8M2(remaining_bytes); // Load data from the current source pointer. vuint8m2_t vec = VLE8_V_U8M2(src_ptr, vl); // Store data to the current destination pointer. VSE8_V_U8M2(dst_ptr, vec, vl); // Update pointers and the remaining count. src_ptr += vl; dst_ptr += vl; remaining_bytes -= vl; } #else std::memmove(dst, src, kShortMemCopy); // Profiling shows that nearly all copies are short. if (SNAPPY_PREDICT_FALSE(size > kShortMemCopy)) { std::memmove(dst + kShortMemCopy, static_cast(src) + kShortMemCopy, 64 - kShortMemCopy); } #endif } void MemCopy64(ptrdiff_t dst, const void* src, size_t size) { // TODO: Switch to [[maybe_unused]] when we can assume C++17. (void)dst; (void)src; (void)size; } void ClearDeferred(const void** deferred_src, size_t* deferred_length, uint8_t* safe_source) { *deferred_src = safe_source; *deferred_length = 0; } void DeferMemCopy(const void** deferred_src, size_t* deferred_length, const void* src, size_t length) { *deferred_src = src; *deferred_length = length; } SNAPPY_ATTRIBUTE_ALWAYS_INLINE inline size_t AdvanceToNextTagARMOptimized(const uint8_t** ip_p, size_t* tag) { const uint8_t*& ip = *ip_p; // This section is crucial for the throughput of the decompression loop. // The latency of an iteration is fundamentally constrained by the // following data chain on ip. // ip -> c = Load(ip) -> delta1 = (c & 3) -> ip += delta1 or delta2 // delta2 = ((c >> 2) + 1) ip++ // This is different from X86 optimizations because ARM has conditional add // instruction (csinc) and it removes several register moves. const size_t tag_type = *tag & 3; const bool is_literal = (tag_type == 0); if (is_literal) { size_t next_literal_tag = (*tag >> 2) + 1; *tag = ip[next_literal_tag]; ip += next_literal_tag + 1; } else { *tag = ip[tag_type]; ip += tag_type + 1; } return tag_type; } SNAPPY_ATTRIBUTE_ALWAYS_INLINE inline size_t AdvanceToNextTagX86Optimized(const uint8_t** ip_p, size_t* tag) { const uint8_t*& ip = *ip_p; // This section is crucial for the throughput of the decompression loop. // The latency of an iteration is fundamentally constrained by the // following data chain on ip. // ip -> c = Load(ip) -> ip1 = ip + 1 + (c & 3) -> ip = ip1 or ip2 // ip2 = ip + 2 + (c >> 2) // This amounts to 8 cycles. // 5 (load) + 1 (c & 3) + 1 (lea ip1, [ip + (c & 3) + 1]) + 1 (cmov) size_t literal_len = *tag >> 2; size_t tag_type = *tag; bool is_literal; #if defined(__GCC_ASM_FLAG_OUTPUTS__) && defined(__x86_64__) // TODO clang misses the fact that the (c & 3) already correctly // sets the zero flag. asm("and $3, %k[tag_type]\n\t" : [tag_type] "+r"(tag_type), "=@ccz"(is_literal) :: "cc"); #else tag_type &= 3; is_literal = (tag_type == 0); #endif // TODO // This is code is subtle. Loading the values first and then cmov has less // latency then cmov ip and then load. However clang would move the loads // in an optimization phase, volatile prevents this transformation. // Note that we have enough slop bytes (64) that the loads are always valid. size_t tag_literal = static_cast(ip)[1 + literal_len]; size_t tag_copy = static_cast(ip)[tag_type]; *tag = is_literal ? tag_literal : tag_copy; const uint8_t* ip_copy = ip + 1 + tag_type; const uint8_t* ip_literal = ip + 2 + literal_len; ip = is_literal ? ip_literal : ip_copy; #if defined(__GNUC__) && defined(__x86_64__) // TODO Clang is "optimizing" zero-extension (a totally free // operation) this means that after the cmov of tag, it emits another movzb // tag, byte(tag). It really matters as it's on the core chain. This dummy // asm, persuades clang to do the zero-extension at the load (it's automatic) // removing the expensive movzb. asm("" ::"r"(tag_copy)); #endif return tag_type; } // Extract the offset for copy-1 and copy-2 returns 0 for literals or copy-4. inline uint32_t ExtractOffset(uint32_t val, size_t tag_type) { // For x86 non-static storage works better. For ARM static storage is better. // TODO: Once the array is recognized as a register, improve the // readability for x86. #if defined(__x86_64__) constexpr uint64_t kExtractMasksCombined = 0x0000FFFF00FF0000ull; uint16_t result; memcpy(&result, reinterpret_cast(&kExtractMasksCombined) + 2 * tag_type, sizeof(result)); return val & result; #elif defined(__aarch64__) constexpr uint64_t kExtractMasksCombined = 0x0000FFFF00FF0000ull; return val & static_cast( (kExtractMasksCombined >> (tag_type * 16)) & 0xFFFF); #else static constexpr uint32_t kExtractMasks[4] = {0, 0xFF, 0xFFFF, 0}; return val & kExtractMasks[tag_type]; #endif }; // Core decompression loop, when there is enough data available. // Decompresses the input buffer [ip, ip_limit) into the output buffer // [op, op_limit_min_slop). Returning when either we are too close to the end // of the input buffer, or we exceed op_limit_min_slop or when a exceptional // tag is encountered (literal of length > 60) or a copy-4. // Returns {ip, op} at the points it stopped decoding. // TODO This function probably does not need to be inlined, as it // should decode large chunks at a time. This allows runtime dispatch to // implementations based on CPU capability (BMI2 / perhaps 32 / 64 byte memcpy). template std::pair DecompressBranchless( const uint8_t* ip, const uint8_t* ip_limit, ptrdiff_t op, T op_base, ptrdiff_t op_limit_min_slop) { // If deferred_src is invalid point it here. uint8_t safe_source[64]; const void* deferred_src; size_t deferred_length; ClearDeferred(&deferred_src, &deferred_length, safe_source); // We unroll the inner loop twice so we need twice the spare room. op_limit_min_slop -= kSlopBytes; if (2 * (kSlopBytes + 1) < ip_limit - ip && op < op_limit_min_slop) { const uint8_t* const ip_limit_min_slop = ip_limit - 2 * kSlopBytes - 1; ip++; // ip points just past the tag and we are touching at maximum kSlopBytes // in an iteration. size_t tag = ip[-1]; #if defined(__clang__) && defined(__aarch64__) // Workaround for https://bugs.llvm.org/show_bug.cgi?id=51317 // when loading 1 byte, clang for aarch64 doesn't realize that it(ldrb) // comes with free zero-extension, so clang generates another // 'and xn, xm, 0xff' before it use that as the offset. This 'and' is // redundant and can be removed by adding this dummy asm, which gives // clang a hint that we're doing the zero-extension at the load. asm("" ::"r"(tag)); #endif do { // The throughput is limited by instructions, unrolling the inner loop // twice reduces the amount of instructions checking limits and also // leads to reduced mov's. SNAPPY_PREFETCH(ip + 128); for (int i = 0; i < 2; i++) { const uint8_t* old_ip = ip; assert(tag == ip[-1]); // For literals tag_type = 0, hence we will always obtain 0 from // ExtractLowBytes. For literals offset will thus be kLiteralOffset. ptrdiff_t len_minus_offset = kLengthMinusOffset[tag]; uint32_t next; #if defined(__aarch64__) size_t tag_type = AdvanceToNextTagARMOptimized(&ip, &tag); // We never need more than 16 bits. Doing a Load16 allows the compiler // to elide the masking operation in ExtractOffset. next = LittleEndian::Load16(old_ip); #else size_t tag_type = AdvanceToNextTagX86Optimized(&ip, &tag); next = LittleEndian::Load32(old_ip); #endif size_t len = len_minus_offset & 0xFF; ptrdiff_t extracted = ExtractOffset(next, tag_type); ptrdiff_t len_min_offset = len_minus_offset - extracted; if (SNAPPY_PREDICT_FALSE(len_minus_offset > extracted)) { if (SNAPPY_PREDICT_FALSE(len & 0x80)) { // Exceptional case (long literal or copy 4). // Actually doing the copy here is negatively impacting the main // loop due to compiler incorrectly allocating a register for // this fallback. Hence we just break. break_loop: ip = old_ip; goto exit; } // Only copy-1 or copy-2 tags can get here. assert(tag_type == 1 || tag_type == 2); std::ptrdiff_t delta = (op + deferred_length) + len_min_offset - len; // Guard against copies before the buffer start. // Execute any deferred MemCopy since we write to dst here. MemCopy64(op_base + op, deferred_src, deferred_length); op += deferred_length; ClearDeferred(&deferred_src, &deferred_length, safe_source); if (SNAPPY_PREDICT_FALSE(delta < 0 || !Copy64BytesWithPatternExtension( op_base + op, len - len_min_offset))) { goto break_loop; } // We aren't deferring this copy so add length right away. op += len; continue; } std::ptrdiff_t delta = (op + deferred_length) + len_min_offset - len; if (SNAPPY_PREDICT_FALSE(delta < 0)) { // Due to the spurious offset in literals have this will trigger // at the start of a block when op is still smaller than 256. if (tag_type != 0) goto break_loop; MemCopy64(op_base + op, deferred_src, deferred_length); op += deferred_length; DeferMemCopy(&deferred_src, &deferred_length, old_ip, len); continue; } // For copies we need to copy from op_base + delta, for literals // we need to copy from ip instead of from the stream. const void* from = tag_type ? reinterpret_cast(op_base + delta) : old_ip; MemCopy64(op_base + op, deferred_src, deferred_length); op += deferred_length; DeferMemCopy(&deferred_src, &deferred_length, from, len); } } while (ip < ip_limit_min_slop && static_cast(op + deferred_length) < op_limit_min_slop); exit: ip--; assert(ip <= ip_limit); } // If we deferred a copy then we can perform. If we are up to date then we // might not have enough slop bytes and could run past the end. if (deferred_length) { MemCopy64(op_base + op, deferred_src, deferred_length); op += deferred_length; ClearDeferred(&deferred_src, &deferred_length, safe_source); } return {ip, op}; } // Helper class for decompression class SnappyDecompressor { private: Source* reader_; // Underlying source of bytes to decompress const char* ip_; // Points to next buffered byte const char* ip_limit_; // Points just past buffered bytes // If ip < ip_limit_min_maxtaglen_ it's safe to read kMaxTagLength from // buffer. const char* ip_limit_min_maxtaglen_; uint64_t peeked_; // Bytes peeked from reader (need to skip) bool eof_; // Hit end of input without an error? char scratch_[kMaximumTagLength]; // See RefillTag(). // Ensure that all of the tag metadata for the next tag is available // in [ip_..ip_limit_-1]. Also ensures that [ip,ip+4] is readable even // if (ip_limit_ - ip_ < 5). // // Returns true on success, false on error or end of input. bool RefillTag(); void ResetLimit(const char* ip) { ip_limit_min_maxtaglen_ = ip_limit_ - std::min(ip_limit_ - ip, kMaximumTagLength - 1); } public: explicit SnappyDecompressor(Source* reader) : reader_(reader), ip_(NULL), ip_limit_(NULL), peeked_(0), eof_(false) {} ~SnappyDecompressor() { // Advance past any bytes we peeked at from the reader reader_->Skip(peeked_); } // Returns true iff we have hit the end of the input without an error. bool eof() const { return eof_; } // Read the uncompressed length stored at the start of the compressed data. // On success, stores the length in *result and returns true. // On failure, returns false. bool ReadUncompressedLength(uint32_t* result) { assert(ip_ == NULL); // Must not have read anything yet // Length is encoded in 1..5 bytes *result = 0; uint32_t shift = 0; while (true) { if (shift >= 32) return false; size_t n; const char* ip = reader_->Peek(&n); if (n == 0) return false; const unsigned char c = *(reinterpret_cast(ip)); reader_->Skip(1); uint32_t val = c & 0x7f; if (LeftShiftOverflows(static_cast(val), shift)) return false; *result |= val << shift; if (c < 128) { break; } shift += 7; } return true; } // Process the next item found in the input. // Returns true if successful, false on error or end of input. template #if defined(__GNUC__) && defined(__x86_64__) __attribute__((aligned(32))) #endif void DecompressAllTags(Writer* writer) { const char* ip = ip_; ResetLimit(ip); auto op = writer->GetOutputPtr(); // We could have put this refill fragment only at the beginning of the loop. // However, duplicating it at the end of each branch gives the compiler more // scope to optimize the expression based on the local // context, which overall increases speed. #define MAYBE_REFILL() \ if (SNAPPY_PREDICT_FALSE(ip >= ip_limit_min_maxtaglen_)) { \ ip_ = ip; \ if (SNAPPY_PREDICT_FALSE(!RefillTag())) goto exit; \ ip = ip_; \ ResetLimit(ip); \ } \ preload = static_cast(*ip) // At the start of the for loop below the least significant byte of preload // contains the tag. uint32_t preload; MAYBE_REFILL(); for (;;) { { ptrdiff_t op_limit_min_slop; auto op_base = writer->GetBase(&op_limit_min_slop); if (op_base) { auto res = DecompressBranchless(reinterpret_cast(ip), reinterpret_cast(ip_limit_), op - op_base, op_base, op_limit_min_slop); ip = reinterpret_cast(res.first); op = op_base + res.second; MAYBE_REFILL(); } } const uint8_t c = static_cast(preload); ip++; // Ratio of iterations that have LITERAL vs non-LITERAL for different // inputs. // // input LITERAL NON_LITERAL // ----------------------------------- // html|html4|cp 23% 77% // urls 36% 64% // jpg 47% 53% // pdf 19% 81% // txt[1-4] 25% 75% // pb 24% 76% // bin 24% 76% if (SNAPPY_PREDICT_FALSE((c & 0x3) == LITERAL)) { size_t literal_length = (c >> 2) + 1u; if (writer->TryFastAppend(ip, ip_limit_ - ip, literal_length, &op)) { assert(literal_length < 61); ip += literal_length; // NOTE: There is no MAYBE_REFILL() here, as TryFastAppend() // will not return true unless there's already at least five spare // bytes in addition to the literal. preload = static_cast(*ip); continue; } if (SNAPPY_PREDICT_FALSE(literal_length >= 61)) { // Long literal. const size_t literal_length_length = literal_length - 60; literal_length = ExtractLowBytes(LittleEndian::Load32(ip), literal_length_length) + 1; ip += literal_length_length; } size_t avail = ip_limit_ - ip; while (avail < literal_length) { if (!writer->Append(ip, avail, &op)) goto exit; literal_length -= avail; reader_->Skip(peeked_); size_t n; ip = reader_->Peek(&n); avail = n; peeked_ = avail; if (avail == 0) goto exit; ip_limit_ = ip + avail; ResetLimit(ip); } if (!writer->Append(ip, literal_length, &op)) goto exit; ip += literal_length; MAYBE_REFILL(); } else { if (SNAPPY_PREDICT_FALSE((c & 3) == COPY_4_BYTE_OFFSET)) { const size_t copy_offset = LittleEndian::Load32(ip); const size_t length = (c >> 2) + 1; ip += 4; if (!writer->AppendFromSelf(copy_offset, length, &op)) goto exit; } else { const ptrdiff_t entry = kLengthMinusOffset[c]; preload = LittleEndian::Load32(ip); const uint32_t trailer = ExtractLowBytes(preload, c & 3); const uint32_t length = entry & 0xff; assert(length > 0); // copy_offset/256 is encoded in bits 8..10. By just fetching // those bits, we get copy_offset (since the bit-field starts at // bit 8). const uint32_t copy_offset = trailer - entry + length; if (!writer->AppendFromSelf(copy_offset, length, &op)) goto exit; ip += (c & 3); // By using the result of the previous load we reduce the critical // dependency chain of ip to 4 cycles. preload >>= (c & 3) * 8; if (ip < ip_limit_min_maxtaglen_) continue; } MAYBE_REFILL(); } } #undef MAYBE_REFILL exit: writer->SetOutputPtr(op); } }; constexpr uint32_t CalculateNeeded(uint8_t tag) { return ((tag & 3) == 0 && tag >= (60 * 4)) ? (tag >> 2) - 58 : (0x05030201 >> ((tag * 8) & 31)) & 0xFF; } #if __cplusplus >= 201402L constexpr bool VerifyCalculateNeeded() { for (int i = 0; i < 1; i++) { if (CalculateNeeded(i) != static_cast((char_table[i] >> 11)) + 1) return false; } return true; } // Make sure CalculateNeeded is correct by verifying it against the established // table encoding the number of added bytes needed. static_assert(VerifyCalculateNeeded(), ""); #endif // c++14 bool SnappyDecompressor::RefillTag() { const char* ip = ip_; if (ip == ip_limit_) { // Fetch a new fragment from the reader reader_->Skip(peeked_); // All peeked bytes are used up size_t n; ip = reader_->Peek(&n); peeked_ = n; eof_ = (n == 0); if (eof_) return false; ip_limit_ = ip + n; } // Read the tag character assert(ip < ip_limit_); const unsigned char c = *(reinterpret_cast(ip)); // At this point make sure that the data for the next tag is consecutive. // For copy 1 this means the next 2 bytes (tag and 1 byte offset) // For copy 2 the next 3 bytes (tag and 2 byte offset) // For copy 4 the next 5 bytes (tag and 4 byte offset) // For all small literals we only need 1 byte buf for literals 60...63 the // length is encoded in 1...4 extra bytes. const uint32_t needed = CalculateNeeded(c); assert(needed <= sizeof(scratch_)); // Read more bytes from reader if needed uint64_t nbuf = ip_limit_ - ip; if (nbuf < needed) { // Stitch together bytes from ip and reader to form the word // contents. We store the needed bytes in "scratch_". They // will be consumed immediately by the caller since we do not // read more than we need. std::memmove(scratch_, ip, nbuf); reader_->Skip(peeked_); // All peeked bytes are used up peeked_ = 0; while (nbuf < needed) { size_t length; const char* src = reader_->Peek(&length); if (length == 0) return false; uint64_t to_add = std::min(needed - nbuf, length); std::memcpy(scratch_ + nbuf, src, to_add); nbuf += to_add; reader_->Skip(to_add); } assert(nbuf == needed); ip_ = scratch_; ip_limit_ = scratch_ + needed; } else if (nbuf < kMaximumTagLength) { // Have enough bytes, but move into scratch_ so that we do not // read past end of input std::memmove(scratch_, ip, nbuf); reader_->Skip(peeked_); // All peeked bytes are used up peeked_ = 0; ip_ = scratch_; ip_limit_ = scratch_ + nbuf; } else { // Pass pointer to buffer returned by reader_. ip_ = ip; } return true; } template static bool InternalUncompress(Source* r, Writer* writer) { // Read the uncompressed length from the front of the compressed input SnappyDecompressor decompressor(r); uint32_t uncompressed_len = 0; if (!decompressor.ReadUncompressedLength(&uncompressed_len)) return false; return InternalUncompressAllTags(&decompressor, writer, r->Available(), uncompressed_len); } template static bool InternalUncompressAllTags(SnappyDecompressor* decompressor, Writer* writer, uint32_t compressed_len, uint32_t uncompressed_len) { int token = 0; Report(token, "snappy_uncompress", compressed_len, uncompressed_len); writer->SetExpectedLength(uncompressed_len); // Process the entire input decompressor->DecompressAllTags(writer); writer->Flush(); return (decompressor->eof() && writer->CheckLength()); } bool GetUncompressedLength(Source* source, uint32_t* result) { SnappyDecompressor decompressor(source); return decompressor.ReadUncompressedLength(result); } size_t Compress(Source* reader, Sink* writer) { return Compress(reader, writer, CompressionOptions{}); } size_t Compress(Source* reader, Sink* writer, CompressionOptions options) { assert(options.level == 1 || options.level == 2); int token = 0; size_t written = 0; size_t N = reader->Available(); assert(N <= 0xFFFFFFFFu); const size_t uncompressed_size = N; char ulength[Varint::kMax32]; char* p = Varint::Encode32(ulength, N); writer->Append(ulength, p - ulength); written += (p - ulength); internal::WorkingMemory wmem(N); while (N > 0) { // Get next block to compress (without copying if possible) size_t fragment_size; const char* fragment = reader->Peek(&fragment_size); assert(fragment_size != 0); // premature end of input const size_t num_to_read = std::min(N, kBlockSize); size_t bytes_read = fragment_size; size_t pending_advance = 0; if (bytes_read >= num_to_read) { // Buffer returned by reader is large enough pending_advance = num_to_read; fragment_size = num_to_read; } else { char* scratch = wmem.GetScratchInput(); std::memcpy(scratch, fragment, bytes_read); reader->Skip(bytes_read); while (bytes_read < num_to_read) { fragment = reader->Peek(&fragment_size); size_t n = std::min(fragment_size, num_to_read - bytes_read); std::memcpy(scratch + bytes_read, fragment, n); bytes_read += n; reader->Skip(n); } assert(bytes_read == num_to_read); fragment = scratch; fragment_size = num_to_read; } assert(fragment_size == num_to_read); // Get encoding table for compression int table_size; uint16_t* table = wmem.GetHashTable(num_to_read, &table_size); // Compress input_fragment and append to dest int max_output = MaxCompressedLength(num_to_read); // Since we encode kBlockSize regions followed by a region // which is <= kBlockSize in length, a previously allocated // scratch_output[] region is big enough for this iteration. // Need a scratch buffer for the output, in case the byte sink doesn't // have room for us directly. char* dest = writer->GetAppendBuffer(max_output, wmem.GetScratchOutput()); char* end = nullptr; if (options.level == 1) { end = internal::CompressFragment(fragment, fragment_size, dest, table, table_size); } else if (options.level == 2) { end = internal::CompressFragmentDoubleHash( fragment, fragment_size, dest, table, table_size >> 1, table + (table_size >> 1), table_size >> 1); } writer->Append(dest, end - dest); written += (end - dest); N -= num_to_read; reader->Skip(pending_advance); } Report(token, "snappy_compress", written, uncompressed_size); return written; } // ----------------------------------------------------------------------- // IOVec interfaces // ----------------------------------------------------------------------- // A `Source` implementation that yields the contents of an `iovec` array. Note // that `total_size` is the total number of bytes to be read from the elements // of `iov` (_not_ the total number of elements in `iov`). class SnappyIOVecReader : public Source { public: SnappyIOVecReader(const struct iovec* iov, size_t total_size) : curr_iov_(iov), curr_pos_(total_size > 0 ? reinterpret_cast(iov->iov_base) : nullptr), curr_size_remaining_(total_size > 0 ? iov->iov_len : 0), total_size_remaining_(total_size) { // Skip empty leading `iovec`s. if (total_size > 0 && curr_size_remaining_ == 0) Advance(); } ~SnappyIOVecReader() override = default; size_t Available() const override { return total_size_remaining_; } const char* Peek(size_t* len) override { *len = curr_size_remaining_; return curr_pos_; } void Skip(size_t n) override { while (n >= curr_size_remaining_ && n > 0) { n -= curr_size_remaining_; Advance(); } curr_size_remaining_ -= n; total_size_remaining_ -= n; curr_pos_ += n; } private: // Advances to the next nonempty `iovec` and updates related variables. void Advance() { do { assert(total_size_remaining_ >= curr_size_remaining_); total_size_remaining_ -= curr_size_remaining_; if (total_size_remaining_ == 0) { curr_pos_ = nullptr; curr_size_remaining_ = 0; return; } ++curr_iov_; curr_pos_ = reinterpret_cast(curr_iov_->iov_base); curr_size_remaining_ = curr_iov_->iov_len; } while (curr_size_remaining_ == 0); } // The `iovec` currently being read. const struct iovec* curr_iov_; // The location in `curr_iov_` currently being read. const char* curr_pos_; // The amount of unread data in `curr_iov_`. size_t curr_size_remaining_; // The amount of unread data in the entire input array. size_t total_size_remaining_; }; // A type that writes to an iovec. // Note that this is not a "ByteSink", but a type that matches the // Writer template argument to SnappyDecompressor::DecompressAllTags(). class SnappyIOVecWriter { private: // output_iov_end_ is set to iov + count and used to determine when // the end of the iovs is reached. const struct iovec* output_iov_end_; #if !defined(NDEBUG) const struct iovec* output_iov_; #endif // !defined(NDEBUG) // Current iov that is being written into. const struct iovec* curr_iov_; // Pointer to current iov's write location. char* curr_iov_output_; // Remaining bytes to write into curr_iov_output. size_t curr_iov_remaining_; // Total bytes decompressed into output_iov_ so far. size_t total_written_; // Maximum number of bytes that will be decompressed into output_iov_. size_t output_limit_; static inline char* GetIOVecPointer(const struct iovec* iov, size_t offset) { return reinterpret_cast(iov->iov_base) + offset; } public: // Does not take ownership of iov. iov must be valid during the // entire lifetime of the SnappyIOVecWriter. inline SnappyIOVecWriter(const struct iovec* iov, size_t iov_count) : output_iov_end_(iov + iov_count), #if !defined(NDEBUG) output_iov_(iov), #endif // !defined(NDEBUG) curr_iov_(iov), curr_iov_output_(iov_count ? reinterpret_cast(iov->iov_base) : nullptr), curr_iov_remaining_(iov_count ? iov->iov_len : 0), total_written_(0), output_limit_(-1) { } inline void SetExpectedLength(size_t len) { output_limit_ = len; } inline bool CheckLength() const { return total_written_ == output_limit_; } inline bool Append(const char* ip, size_t len, char**) { if (total_written_ + len > output_limit_) { return false; } return AppendNoCheck(ip, len); } char* GetOutputPtr() { return nullptr; } char* GetBase(ptrdiff_t*) { return nullptr; } void SetOutputPtr(char* op) { // TODO: Switch to [[maybe_unused]] when we can assume C++17. (void)op; } inline bool AppendNoCheck(const char* ip, size_t len) { while (len > 0) { if (curr_iov_remaining_ == 0) { // This iovec is full. Go to the next one. if (curr_iov_ + 1 >= output_iov_end_) { return false; } ++curr_iov_; curr_iov_output_ = reinterpret_cast(curr_iov_->iov_base); curr_iov_remaining_ = curr_iov_->iov_len; } const size_t to_write = std::min(len, curr_iov_remaining_); std::memcpy(curr_iov_output_, ip, to_write); curr_iov_output_ += to_write; curr_iov_remaining_ -= to_write; total_written_ += to_write; ip += to_write; len -= to_write; } return true; } inline bool TryFastAppend(const char* ip, size_t available, size_t len, char**) { const size_t space_left = output_limit_ - total_written_; if (len <= 16 && available >= 16 + kMaximumTagLength && space_left >= 16 && curr_iov_remaining_ >= 16) { // Fast path, used for the majority (about 95%) of invocations. UnalignedCopy128(ip, curr_iov_output_); curr_iov_output_ += len; curr_iov_remaining_ -= len; total_written_ += len; return true; } return false; } inline bool AppendFromSelf(size_t offset, size_t len, char**) { // See SnappyArrayWriter::AppendFromSelf for an explanation of // the "offset - 1u" trick. if (offset - 1u >= total_written_) { return false; } const size_t space_left = output_limit_ - total_written_; if (len > space_left) { return false; } // Locate the iovec from which we need to start the copy. const iovec* from_iov = curr_iov_; size_t from_iov_offset = curr_iov_->iov_len - curr_iov_remaining_; while (offset > 0) { if (from_iov_offset >= offset) { from_iov_offset -= offset; break; } offset -= from_iov_offset; --from_iov; #if !defined(NDEBUG) assert(from_iov >= output_iov_); #endif // !defined(NDEBUG) from_iov_offset = from_iov->iov_len; } // Copy bytes starting from the iovec pointed to by from_iov_index to // the current iovec. while (len > 0) { assert(from_iov <= curr_iov_); if (from_iov != curr_iov_) { const size_t to_copy = std::min(from_iov->iov_len - from_iov_offset, len); AppendNoCheck(GetIOVecPointer(from_iov, from_iov_offset), to_copy); len -= to_copy; if (len > 0) { ++from_iov; from_iov_offset = 0; } } else { size_t to_copy = curr_iov_remaining_; if (to_copy == 0) { // This iovec is full. Go to the next one. if (curr_iov_ + 1 >= output_iov_end_) { return false; } ++curr_iov_; curr_iov_output_ = reinterpret_cast(curr_iov_->iov_base); curr_iov_remaining_ = curr_iov_->iov_len; continue; } if (to_copy > len) { to_copy = len; } assert(to_copy > 0); IncrementalCopy(GetIOVecPointer(from_iov, from_iov_offset), curr_iov_output_, curr_iov_output_ + to_copy, curr_iov_output_ + curr_iov_remaining_); curr_iov_output_ += to_copy; curr_iov_remaining_ -= to_copy; from_iov_offset += to_copy; total_written_ += to_copy; len -= to_copy; } } return true; } inline void Flush() {} }; bool RawUncompressToIOVec(const char* compressed, size_t compressed_length, const struct iovec* iov, size_t iov_cnt) { ByteArraySource reader(compressed, compressed_length); return RawUncompressToIOVec(&reader, iov, iov_cnt); } bool RawUncompressToIOVec(Source* compressed, const struct iovec* iov, size_t iov_cnt) { SnappyIOVecWriter output(iov, iov_cnt); return InternalUncompress(compressed, &output); } // ----------------------------------------------------------------------- // Flat array interfaces // ----------------------------------------------------------------------- // A type that writes to a flat array. // Note that this is not a "ByteSink", but a type that matches the // Writer template argument to SnappyDecompressor::DecompressAllTags(). class SnappyArrayWriter { private: char* base_; char* op_; char* op_limit_; // If op < op_limit_min_slop_ then it's safe to unconditionally write // kSlopBytes starting at op. char* op_limit_min_slop_; public: inline explicit SnappyArrayWriter(char* dst) : base_(dst), op_(dst), op_limit_(dst), op_limit_min_slop_(dst) {} // Safe default see invariant. inline void SetExpectedLength(size_t len) { op_limit_ = op_ + len; // Prevent pointer from being past the buffer. op_limit_min_slop_ = op_limit_ - std::min(kSlopBytes - 1, len); } inline bool CheckLength() const { return op_ == op_limit_; } char* GetOutputPtr() { return op_; } char* GetBase(ptrdiff_t* op_limit_min_slop) { *op_limit_min_slop = op_limit_min_slop_ - base_; return base_; } void SetOutputPtr(char* op) { op_ = op; } inline bool Append(const char* ip, size_t len, char** op_p) { char* op = *op_p; const size_t space_left = op_limit_ - op; if (space_left < len) return false; std::memcpy(op, ip, len); *op_p = op + len; return true; } inline bool TryFastAppend(const char* ip, size_t available, size_t len, char** op_p) { char* op = *op_p; const size_t space_left = op_limit_ - op; if (len <= 16 && available >= 16 + kMaximumTagLength && space_left >= 16) { // Fast path, used for the majority (about 95%) of invocations. UnalignedCopy128(ip, op); *op_p = op + len; return true; } else { return false; } } SNAPPY_ATTRIBUTE_ALWAYS_INLINE inline bool AppendFromSelf(size_t offset, size_t len, char** op_p) { assert(len > 0); char* const op = *op_p; assert(op >= base_); char* const op_end = op + len; // Check if we try to append from before the start of the buffer. if (SNAPPY_PREDICT_FALSE(static_cast(op - base_) < offset)) return false; if (SNAPPY_PREDICT_FALSE((kSlopBytes < 64 && len > kSlopBytes) || op >= op_limit_min_slop_ || offset < len)) { if (op_end > op_limit_ || offset == 0) return false; *op_p = IncrementalCopy(op - offset, op, op_end, op_limit_); return true; } std::memmove(op, op - offset, kSlopBytes); *op_p = op_end; return true; } inline size_t Produced() const { assert(op_ >= base_); return op_ - base_; } inline void Flush() {} }; bool RawUncompress(const char* compressed, size_t compressed_length, char* uncompressed) { ByteArraySource reader(compressed, compressed_length); return RawUncompress(&reader, uncompressed); } bool RawUncompress(Source* compressed, char* uncompressed) { SnappyArrayWriter output(uncompressed); return InternalUncompress(compressed, &output); } bool Uncompress(const char* compressed, size_t compressed_length, std::string* uncompressed) { size_t ulength; if (!GetUncompressedLength(compressed, compressed_length, &ulength)) { return false; } // On 32-bit builds: max_size() < kuint32max. Check for that instead // of crashing (e.g., consider externally specified compressed data). if (ulength > uncompressed->max_size()) { return false; } STLStringResizeUninitialized(uncompressed, ulength); return RawUncompress(compressed, compressed_length, string_as_array(uncompressed)); } // A Writer that drops everything on the floor and just does validation class SnappyDecompressionValidator { private: size_t expected_; size_t produced_; public: inline SnappyDecompressionValidator() : expected_(0), produced_(0) {} inline void SetExpectedLength(size_t len) { expected_ = len; } size_t GetOutputPtr() { return produced_; } size_t GetBase(ptrdiff_t* op_limit_min_slop) { *op_limit_min_slop = std::numeric_limits::max() - kSlopBytes + 1; return 1; } void SetOutputPtr(size_t op) { produced_ = op; } inline bool CheckLength() const { return expected_ == produced_; } inline bool Append(const char* ip, size_t len, size_t* produced) { // TODO: Switch to [[maybe_unused]] when we can assume C++17. (void)ip; *produced += len; return *produced <= expected_; } inline bool TryFastAppend(const char* ip, size_t available, size_t length, size_t* produced) { // TODO: Switch to [[maybe_unused]] when we can assume C++17. (void)ip; (void)available; (void)length; (void)produced; return false; } inline bool AppendFromSelf(size_t offset, size_t len, size_t* produced) { // See SnappyArrayWriter::AppendFromSelf for an explanation of // the "offset - 1u" trick. if (*produced <= offset - 1u) return false; *produced += len; return *produced <= expected_; } inline void Flush() {} }; bool IsValidCompressedBuffer(const char* compressed, size_t compressed_length) { ByteArraySource reader(compressed, compressed_length); SnappyDecompressionValidator writer; return InternalUncompress(&reader, &writer); } bool IsValidCompressed(Source* compressed) { SnappyDecompressionValidator writer; return InternalUncompress(compressed, &writer); } void RawCompress(const char* input, size_t input_length, char* compressed, size_t* compressed_length) { RawCompress(input, input_length, compressed, compressed_length, CompressionOptions{}); } void RawCompress(const char* input, size_t input_length, char* compressed, size_t* compressed_length, CompressionOptions options) { ByteArraySource reader(input, input_length); UncheckedByteArraySink writer(compressed); Compress(&reader, &writer, options); // Compute how many bytes were added *compressed_length = (writer.CurrentDestination() - compressed); } void RawCompressFromIOVec(const struct iovec* iov, size_t uncompressed_length, char* compressed, size_t* compressed_length) { RawCompressFromIOVec(iov, uncompressed_length, compressed, compressed_length, CompressionOptions{}); } void RawCompressFromIOVec(const struct iovec* iov, size_t uncompressed_length, char* compressed, size_t* compressed_length, CompressionOptions options) { SnappyIOVecReader reader(iov, uncompressed_length); UncheckedByteArraySink writer(compressed); Compress(&reader, &writer, options); // Compute how many bytes were added. *compressed_length = writer.CurrentDestination() - compressed; } size_t Compress(const char* input, size_t input_length, std::string* compressed) { return Compress(input, input_length, compressed, CompressionOptions{}); } size_t Compress(const char* input, size_t input_length, std::string* compressed, CompressionOptions options) { // Pre-grow the buffer to the max length of the compressed output STLStringResizeUninitialized(compressed, MaxCompressedLength(input_length)); size_t compressed_length; RawCompress(input, input_length, string_as_array(compressed), &compressed_length, options); compressed->erase(compressed_length); return compressed_length; } size_t CompressFromIOVec(const struct iovec* iov, size_t iov_cnt, std::string* compressed) { return CompressFromIOVec(iov, iov_cnt, compressed, CompressionOptions{}); } size_t CompressFromIOVec(const struct iovec* iov, size_t iov_cnt, std::string* compressed, CompressionOptions options) { // Compute the number of bytes to be compressed. size_t uncompressed_length = 0; for (size_t i = 0; i < iov_cnt; ++i) { uncompressed_length += iov[i].iov_len; } // Pre-grow the buffer to the max length of the compressed output. STLStringResizeUninitialized(compressed, MaxCompressedLength( uncompressed_length)); size_t compressed_length; RawCompressFromIOVec(iov, uncompressed_length, string_as_array(compressed), &compressed_length, options); compressed->erase(compressed_length); return compressed_length; } // ----------------------------------------------------------------------- // Sink interface // ----------------------------------------------------------------------- // A type that decompresses into a Sink. The template parameter // Allocator must export one method "char* Allocate(int size);", which // allocates a buffer of "size" and appends that to the destination. template class SnappyScatteredWriter { Allocator allocator_; // We need random access into the data generated so far. Therefore // we keep track of all of the generated data as an array of blocks. // All of the blocks except the last have length kBlockSize. std::vector blocks_; size_t expected_; // Total size of all fully generated blocks so far size_t full_size_; // Pointer into current output block char* op_base_; // Base of output block char* op_ptr_; // Pointer to next unfilled byte in block char* op_limit_; // Pointer just past block // If op < op_limit_min_slop_ then it's safe to unconditionally write // kSlopBytes starting at op. char* op_limit_min_slop_; inline size_t Size() const { return full_size_ + (op_ptr_ - op_base_); } bool SlowAppend(const char* ip, size_t len); bool SlowAppendFromSelf(size_t offset, size_t len); public: inline explicit SnappyScatteredWriter(const Allocator& allocator) : allocator_(allocator), full_size_(0), op_base_(NULL), op_ptr_(NULL), op_limit_(NULL), op_limit_min_slop_(NULL) {} char* GetOutputPtr() { return op_ptr_; } char* GetBase(ptrdiff_t* op_limit_min_slop) { *op_limit_min_slop = op_limit_min_slop_ - op_base_; return op_base_; } void SetOutputPtr(char* op) { op_ptr_ = op; } inline void SetExpectedLength(size_t len) { assert(blocks_.empty()); expected_ = len; } inline bool CheckLength() const { return Size() == expected_; } // Return the number of bytes actually uncompressed so far inline size_t Produced() const { return Size(); } inline bool Append(const char* ip, size_t len, char** op_p) { char* op = *op_p; size_t avail = op_limit_ - op; if (len <= avail) { // Fast path std::memcpy(op, ip, len); *op_p = op + len; return true; } else { op_ptr_ = op; bool res = SlowAppend(ip, len); *op_p = op_ptr_; return res; } } inline bool TryFastAppend(const char* ip, size_t available, size_t length, char** op_p) { char* op = *op_p; const int space_left = op_limit_ - op; if (length <= 16 && available >= 16 + kMaximumTagLength && space_left >= 16) { // Fast path, used for the majority (about 95%) of invocations. UnalignedCopy128(ip, op); *op_p = op + length; return true; } else { return false; } } inline bool AppendFromSelf(size_t offset, size_t len, char** op_p) { char* op = *op_p; assert(op >= op_base_); // Check if we try to append from before the start of the buffer. if (SNAPPY_PREDICT_FALSE((kSlopBytes < 64 && len > kSlopBytes) || static_cast(op - op_base_) < offset || op >= op_limit_min_slop_ || offset < len)) { if (offset == 0) return false; if (SNAPPY_PREDICT_FALSE(static_cast(op - op_base_) < offset || op + len > op_limit_)) { op_ptr_ = op; bool res = SlowAppendFromSelf(offset, len); *op_p = op_ptr_; return res; } *op_p = IncrementalCopy(op - offset, op, op + len, op_limit_); return true; } // Fast path char* const op_end = op + len; std::memmove(op, op - offset, kSlopBytes); *op_p = op_end; return true; } // Called at the end of the decompress. We ask the allocator // write all blocks to the sink. inline void Flush() { allocator_.Flush(Produced()); } }; template bool SnappyScatteredWriter::SlowAppend(const char* ip, size_t len) { size_t avail = op_limit_ - op_ptr_; while (len > avail) { // Completely fill this block std::memcpy(op_ptr_, ip, avail); op_ptr_ += avail; assert(op_limit_ - op_ptr_ == 0); full_size_ += (op_ptr_ - op_base_); len -= avail; ip += avail; // Bounds check if (full_size_ + len > expected_) return false; // Make new block size_t bsize = std::min(kBlockSize, expected_ - full_size_); op_base_ = allocator_.Allocate(bsize); op_ptr_ = op_base_; op_limit_ = op_base_ + bsize; op_limit_min_slop_ = op_limit_ - std::min(kSlopBytes - 1, bsize); blocks_.push_back(op_base_); avail = bsize; } std::memcpy(op_ptr_, ip, len); op_ptr_ += len; return true; } template bool SnappyScatteredWriter::SlowAppendFromSelf(size_t offset, size_t len) { // Overflow check // See SnappyArrayWriter::AppendFromSelf for an explanation of // the "offset - 1u" trick. const size_t cur = Size(); if (offset - 1u >= cur) return false; if (expected_ - cur < len) return false; // Currently we shouldn't ever hit this path because Compress() chops the // input into blocks and does not create cross-block copies. However, it is // nice if we do not rely on that, since we can get better compression if we // allow cross-block copies and thus might want to change the compressor in // the future. // TODO Replace this with a properly optimized path. This is not // triggered right now. But this is so super slow, that it would regress // performance unacceptably if triggered. size_t src = cur - offset; char* op = op_ptr_; while (len-- > 0) { char c = blocks_[src >> kBlockLog][src & (kBlockSize - 1)]; if (!Append(&c, 1, &op)) { op_ptr_ = op; return false; } src++; } op_ptr_ = op; return true; } class SnappySinkAllocator { public: explicit SnappySinkAllocator(Sink* dest) : dest_(dest) {} char* Allocate(int size) { Datablock block(new char[size], size); blocks_.push_back(block); return block.data; } // We flush only at the end, because the writer wants // random access to the blocks and once we hand the // block over to the sink, we can't access it anymore. // Also we don't write more than has been actually written // to the blocks. void Flush(size_t size) { size_t size_written = 0; for (Datablock& block : blocks_) { size_t block_size = std::min(block.size, size - size_written); dest_->AppendAndTakeOwnership(block.data, block_size, &SnappySinkAllocator::Deleter, NULL); size_written += block_size; } blocks_.clear(); } private: struct Datablock { char* data; size_t size; Datablock(char* p, size_t s) : data(p), size(s) {} }; static void Deleter(void* arg, const char* bytes, size_t size) { // TODO: Switch to [[maybe_unused]] when we can assume C++17. (void)arg; (void)size; delete[] bytes; } Sink* dest_; std::vector blocks_; // Note: copying this object is allowed }; size_t UncompressAsMuchAsPossible(Source* compressed, Sink* uncompressed) { SnappySinkAllocator allocator(uncompressed); SnappyScatteredWriter writer(allocator); InternalUncompress(compressed, &writer); return writer.Produced(); } bool Uncompress(Source* compressed, Sink* uncompressed) { // Read the uncompressed length from the front of the compressed input SnappyDecompressor decompressor(compressed); uint32_t uncompressed_len = 0; if (!decompressor.ReadUncompressedLength(&uncompressed_len)) { return false; } char c; size_t allocated_size; char* buf = uncompressed->GetAppendBufferVariable(1, uncompressed_len, &c, 1, &allocated_size); const size_t compressed_len = compressed->Available(); // If we can get a flat buffer, then use it, otherwise do block by block // uncompression if (allocated_size >= uncompressed_len) { SnappyArrayWriter writer(buf); bool result = InternalUncompressAllTags(&decompressor, &writer, compressed_len, uncompressed_len); uncompressed->Append(buf, writer.Produced()); return result; } else { SnappySinkAllocator allocator(uncompressed); SnappyScatteredWriter writer(allocator); return InternalUncompressAllTags(&decompressor, &writer, compressed_len, uncompressed_len); } } } // namespace snappy ================================================ FILE: Plugin/Snappy/snappy.h ================================================ // Copyright 2005 and onwards Google Inc. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // A light-weight compression algorithm. It is designed for speed of // compression and decompression, rather than for the utmost in space // savings. // // For getting better compression ratios when you are compressing data // with long repeated sequences or compressing data that is similar to // other data, while still compressing fast, you might look at first // using BMDiff and then compressing the output of BMDiff with // Snappy. #ifndef THIRD_PARTY_SNAPPY_SNAPPY_H__ #define THIRD_PARTY_SNAPPY_SNAPPY_H__ #include #include #include #include "snappy-stubs-public.h" namespace snappy { class Source; class Sink; struct CompressionOptions { // Compression level. // Level 1 is the fastest // Level 2 is a little slower but provides better compression. Level 2 is // **EXPERIMENTAL** for the time being. It might happen that we decide to // fall back to level 1 in the future. // Levels 3+ are currently not supported. We plan to support levels up to // 9 in the future. // If you played with other compression algorithms, level 1 is equivalent to // fast mode (level 1) of LZ4, level 2 is equivalent to LZ4's level 2 mode // and compresses somewhere around zstd:-3 and zstd:-2 but generally with // faster decompression speeds than snappy:1 and zstd:-3. int level = DefaultCompressionLevel(); constexpr CompressionOptions() = default; constexpr CompressionOptions(int compression_level) : level(compression_level) {} static constexpr int MinCompressionLevel() { return 1; } static constexpr int MaxCompressionLevel() { return 2; } static constexpr int DefaultCompressionLevel() { return 1; } }; // ------------------------------------------------------------------------ // Generic compression/decompression routines. // ------------------------------------------------------------------------ // Compress the bytes read from "*reader" and append to "*writer". Return the // number of bytes written. // First version is to preserve ABI. size_t Compress(Source* reader, Sink* writer); size_t Compress(Source* reader, Sink* writer, CompressionOptions options); // Find the uncompressed length of the given stream, as given by the header. // Note that the true length could deviate from this; the stream could e.g. // be truncated. // // Also note that this leaves "*source" in a state that is unsuitable for // further operations, such as RawUncompress(). You will need to rewind // or recreate the source yourself before attempting any further calls. bool GetUncompressedLength(Source* source, uint32_t* result); // ------------------------------------------------------------------------ // Higher-level string based routines (should be sufficient for most users) // ------------------------------------------------------------------------ // Sets "*compressed" to the compressed version of "input[0..input_length-1]". // Original contents of *compressed are lost. // // REQUIRES: "input[]" is not an alias of "*compressed". // First version is to preserve ABI. size_t Compress(const char* input, size_t input_length, std::string* compressed); size_t Compress(const char* input, size_t input_length, std::string* compressed, CompressionOptions options); // Same as `Compress` above but taking an `iovec` array as input. Note that // this function preprocesses the inputs to compute the sum of // `iov[0..iov_cnt-1].iov_len` before reading. To avoid this, use // `RawCompressFromIOVec` below. // First version is to preserve ABI. size_t CompressFromIOVec(const struct iovec* iov, size_t iov_cnt, std::string* compressed); size_t CompressFromIOVec(const struct iovec* iov, size_t iov_cnt, std::string* compressed, CompressionOptions options); // Decompresses "compressed[0..compressed_length-1]" to "*uncompressed". // Original contents of "*uncompressed" are lost. // // REQUIRES: "compressed[]" is not an alias of "*uncompressed". // // returns false if the message is corrupted and could not be decompressed bool Uncompress(const char* compressed, size_t compressed_length, std::string* uncompressed); // Decompresses "compressed" to "*uncompressed". // // returns false if the message is corrupted and could not be decompressed bool Uncompress(Source* compressed, Sink* uncompressed); // This routine uncompresses as much of the "compressed" as possible // into sink. It returns the number of valid bytes added to sink // (extra invalid bytes may have been added due to errors; the caller // should ignore those). The emitted data typically has length // GetUncompressedLength(), but may be shorter if an error is // encountered. size_t UncompressAsMuchAsPossible(Source* compressed, Sink* uncompressed); // ------------------------------------------------------------------------ // Lower-level character array based routines. May be useful for // efficiency reasons in certain circumstances. // ------------------------------------------------------------------------ // REQUIRES: "compressed" must point to an area of memory that is at // least "MaxCompressedLength(input_length)" bytes in length. // // Takes the data stored in "input[0..input_length]" and stores // it in the array pointed to by "compressed". // // "*compressed_length" is set to the length of the compressed output. // // Example: // char* output = new char[snappy::MaxCompressedLength(input_length)]; // size_t output_length; // RawCompress(input, input_length, output, &output_length); // ... Process(output, output_length) ... // delete [] output; void RawCompress(const char* input, size_t input_length, char* compressed, size_t* compressed_length); void RawCompress(const char* input, size_t input_length, char* compressed, size_t* compressed_length, CompressionOptions options); // Same as `RawCompress` above but taking an `iovec` array as input. Note that // `uncompressed_length` is the total number of bytes to be read from the // elements of `iov` (_not_ the number of elements in `iov`). void RawCompressFromIOVec(const struct iovec* iov, size_t uncompressed_length, char* compressed, size_t* compressed_length); void RawCompressFromIOVec(const struct iovec* iov, size_t uncompressed_length, char* compressed, size_t* compressed_length, CompressionOptions options); // Given data in "compressed[0..compressed_length-1]" generated by // calling the Snappy::Compress routine, this routine // stores the uncompressed data to // uncompressed[0..GetUncompressedLength(compressed)-1] // returns false if the message is corrupted and could not be decrypted bool RawUncompress(const char* compressed, size_t compressed_length, char* uncompressed); // Given data from the byte source 'compressed' generated by calling // the Snappy::Compress routine, this routine stores the uncompressed // data to // uncompressed[0..GetUncompressedLength(compressed,compressed_length)-1] // returns false if the message is corrupted and could not be decrypted bool RawUncompress(Source* compressed, char* uncompressed); // Given data in "compressed[0..compressed_length-1]" generated by // calling the Snappy::Compress routine, this routine // stores the uncompressed data to the iovec "iov". The number of physical // buffers in "iov" is given by iov_cnt and their cumulative size // must be at least GetUncompressedLength(compressed). The individual buffers // in "iov" must not overlap with each other. // // returns false if the message is corrupted and could not be decrypted bool RawUncompressToIOVec(const char* compressed, size_t compressed_length, const struct iovec* iov, size_t iov_cnt); // Given data from the byte source 'compressed' generated by calling // the Snappy::Compress routine, this routine stores the uncompressed // data to the iovec "iov". The number of physical // buffers in "iov" is given by iov_cnt and their cumulative size // must be at least GetUncompressedLength(compressed). The individual buffers // in "iov" must not overlap with each other. // // returns false if the message is corrupted and could not be decrypted bool RawUncompressToIOVec(Source* compressed, const struct iovec* iov, size_t iov_cnt); // Returns the maximal size of the compressed representation of // input data that is "source_bytes" bytes in length; size_t MaxCompressedLength(size_t source_bytes); // REQUIRES: "compressed[]" was produced by RawCompress() or Compress() // Returns true and stores the length of the uncompressed data in // *result normally. Returns false on parsing error. // This operation takes O(1) time. bool GetUncompressedLength(const char* compressed, size_t compressed_length, size_t* result); // Returns true iff the contents of "compressed[]" can be uncompressed // successfully. Does not return the uncompressed data. Takes // time proportional to compressed_length, but is usually at least // a factor of four faster than actual decompression. bool IsValidCompressedBuffer(const char* compressed, size_t compressed_length); // Returns true iff the contents of "compressed" can be uncompressed // successfully. Does not return the uncompressed data. Takes // time proportional to *compressed length, but is usually at least // a factor of four faster than actual decompression. // On success, consumes all of *compressed. On failure, consumes an // unspecified prefix of *compressed. bool IsValidCompressed(Source* compressed); // The size of a compression block. Note that many parts of the compression // code assumes that kBlockSize <= 65536; in particular, the hash table // can only store 16-bit offsets, and EmitCopy() also assumes the offset // is 65535 bytes or less. Note also that if you change this, it will // affect the framing format (see framing_format.txt). // // Note that there might be older data around that is compressed with larger // block sizes, so the decompression code should not rely on the // non-existence of long backreferences. static constexpr int kBlockLog = 16; static constexpr size_t kBlockSize = 1 << kBlockLog; static constexpr int kMinHashTableBits = 8; static constexpr size_t kMinHashTableSize = 1 << kMinHashTableBits; static constexpr int kMaxHashTableBits = 15; static constexpr size_t kMaxHashTableSize = 1 << kMaxHashTableBits; } // end namespace snappy #endif // THIRD_PARTY_SNAPPY_SNAPPY_H__ ================================================ FILE: Plugin/Source/Decoder.h ================================================ #pragma once #include #include #include #include "ReadBuffer.h" #include "hap.h" namespace KlakHap { class Decoder { public: #pragma region Constructor/destructor Decoder(int width, int height, int typeID) { buffer_.resize(width * height * GetBppFromTypeID(typeID) / 8); } #pragma endregion #pragma region Public accessors const void* LockBuffer() { bufferLock_.lock(); return buffer_.data(); } void UnlockBuffer() { bufferLock_.unlock(); } size_t GetBufferSize() const { return buffer_.size(); } #pragma endregion #pragma region Decoding operations void DecodeFrame(const ReadBuffer& input) { std::lock_guard lock(bufferLock_); unsigned int format; HapDecode( input.storage.data(), static_cast(input.storage.size()), 0, hap_callback, nullptr, buffer_.data(), static_cast(buffer_.size()), nullptr, &format ); } #pragma endregion private: #pragma region Internal-use members std::vector buffer_; std::mutex bufferLock_; static size_t GetBppFromTypeID(int typeID) { switch (typeID & 0xf) { case 0xb: return 4; // DXT1 case 0xe: return 8; // DXT5 case 0xf: return 8; // DXT5 case 0xc: return 8; // BC7 case 0x1: return 4; // BC4 } return 0; } #pragma endregion #pragma region HAP callback implementation static void hap_callback( HapDecodeWorkFunction work, void* p, unsigned int count, void* info ) { // FIXME: This should be threaded. for (auto i = 0u; i < count; i++) work(p, i); } #pragma endregion }; } ================================================ FILE: Plugin/Source/Demuxer.h ================================================ #pragma once #include #include #include "mp4demux.h" #include "ReadBuffer.h" #ifdef _WIN32 #include #endif namespace KlakHap { class Demuxer { public: #pragma region Constructor/destructor Demuxer(const char* path) { std::memset(&demux_, 0, sizeof(MP4D_demux_t)); #ifdef _WIN32 // Convert UTF-8 to wide character for Windows int wlen = MultiByteToWideChar(CP_UTF8, 0, path, -1, nullptr, 0); if (wlen <= 0) return; wchar_t* wpath = new wchar_t[wlen]; MultiByteToWideChar(CP_UTF8, 0, path, -1, wpath, wlen); if (_wfopen_s(&file_, wpath, L"rb") != 0) { delete[] wpath; return; } delete[] wpath; #else file_ = fopen(path, "rb"); if (file_ == nullptr) return; #endif if (MP4D__open(&demux_, file_) == 0) { fclose(file_); file_ = nullptr; } } ~Demuxer() { MP4D__close(&demux_); if (file_ != nullptr) fclose(file_); } #pragma endregion #pragma region Public accessors bool IsValid() const { return file_ != nullptr; } const MP4D_track_t& GetVideoTrack() const { return demux_.track[0]; } #pragma endregion #pragma region Read methods uint8_t ReadVideoTypeField() { // Data offset for the first frame unsigned int size; auto offs = MP4D__frame_offset(&demux_, 0, 0, &size, nullptr, nullptr); // Read to a temporary buffer. uint8_t temp; fseek(file_, (long)offs + 3, SEEK_SET); fread(&temp, 1, 1, file_); return temp; } void ReadFrame(int index, ReadBuffer& buffer) { // Frame data offset unsigned int inSize, timestamp, duration; auto inOffs = MP4D__frame_offset(&demux_, 0, index, &inSize, ×tamp, &duration); // Frame data read #if defined(_WIN32) _fseeki64(file_, inOffs, SEEK_SET); #else fseek(file_, inOffs, SEEK_SET); #endif buffer.storage.resize(inSize); fread(buffer.storage.data(), inSize, 1, file_); } #pragma endregion private: #pragma region Private members FILE* file_ = nullptr; MP4D_demux_t demux_; #pragma endregion }; } ================================================ FILE: Plugin/Source/KlakHap.cpp ================================================ #include #include "Decoder.h" #include "Demuxer.h" #include "ReadBuffer.h" #include "IUnityRenderingExtensions.h" #if defined(_WIN32) #define WIN32_LEAN_AND_MEAN #include #endif using namespace KlakHap; namespace { #pragma region ID to decorder instance map std::unordered_map decoderMap_; #pragma endregion #pragma region Texture update callback implementation // // Get a "fake" byte-per-pixel value for a given compression format // // This is a workaround for a problem that Unity incorrectly uses a BPP // value to calculate a texture pitch. We have to give a mutiplier for // it instead of an actual BPP value. // uint32_t GetFakeBpp(UnityRenderingExtTextureFormat format) { switch (format) { case kUnityRenderingExtFormatRGBA_DXT1_SRGB: case kUnityRenderingExtFormatRGBA_DXT1_UNorm: case kUnityRenderingExtFormatR_BC4_UNorm: case kUnityRenderingExtFormatR_BC4_SNorm: return 2; case kUnityRenderingExtFormatRGBA_DXT5_SRGB: case kUnityRenderingExtFormatRGBA_DXT5_UNorm: case kUnityRenderingExtFormatRGBA_BC7_SRGB: case kUnityRenderingExtFormatRGBA_BC7_UNorm: return 4; } return 0; } // Callback for texture update events void TextureUpdateCallback(int eventID, void* data) { auto event = static_cast(eventID); if (event == kUnityRenderingExtEventUpdateTextureBeginV2) { // UpdateTextureBegin: Return texture image data. auto params = reinterpret_cast(data); auto it = decoderMap_.find(params->userData); if (it != decoderMap_.end()) { params->bpp = GetFakeBpp(params->format); params->texData = const_cast(it->second->LockBuffer()); } } else if (event == kUnityRenderingExtEventUpdateTextureEndV2) { // UpdateTextureEnd: auto params = reinterpret_cast(data); auto it = decoderMap_.find(params->userData); if (it != decoderMap_.end()) it->second->UnlockBuffer(); } } #pragma endregion } #pragma region Plugin common function #if defined(_WIN32) && defined(_DEBUG) extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityPluginLoad(IUnityInterfaces* interfaces) { // Open a new console for debug logging. FILE * pConsole; AllocConsole(); freopen_s(&pConsole, "CONOUT$", "wb", stdout); } #endif extern "C" UnityRenderingEventAndData UNITY_INTERFACE_EXPORT KlakHap_GetTextureUpdateCallback() { return TextureUpdateCallback; } #pragma endregion #pragma region Read buffer functions extern "C" ReadBuffer UNITY_INTERFACE_EXPORT * KlakHap_CreateReadBuffer() { return new ReadBuffer; } extern "C" void UNITY_INTERFACE_EXPORT KlakHap_DestroyReadBuffer(ReadBuffer* buffer) { if (buffer != nullptr) delete buffer; } #pragma endregion #pragma region Demuxer functions extern "C" Demuxer UNITY_INTERFACE_EXPORT * KlakHap_OpenDemuxer(const char* filepath) { return new Demuxer(filepath); } extern "C" void UNITY_INTERFACE_EXPORT KlakHap_CloseDemuxer(Demuxer* demuxer) { if (demuxer != nullptr) delete demuxer; } extern "C" int32_t UNITY_INTERFACE_EXPORT KlakHap_DemuxerIsValid(Demuxer* demuxer) { if (demuxer == nullptr) return 0; return demuxer->IsValid() ? 1 : 0; } extern "C" int32_t UNITY_INTERFACE_EXPORT KlakHap_CountFrames(Demuxer* demuxer) { if (demuxer == nullptr) return 0; return demuxer->GetVideoTrack().sample_count; } extern "C" double UNITY_INTERFACE_EXPORT KlakHap_GetDuration(Demuxer* demuxer) { if (demuxer == nullptr) return 0; auto track = demuxer->GetVideoTrack(); auto dur = static_cast(track.duration_hi); dur = dur * 0x100000000L + track.duration_lo; return dur / track.timescale; } extern "C" int32_t UNITY_INTERFACE_EXPORT KlakHap_GetVideoWidth(Demuxer* demuxer) { if (demuxer == nullptr) return 0; return demuxer->GetVideoTrack().SampleDescription.video.width; } extern "C" int32_t UNITY_INTERFACE_EXPORT KlakHap_GetVideoHeight(Demuxer* demuxer) { if (demuxer == nullptr) return 0; return demuxer->GetVideoTrack().SampleDescription.video.height; } extern "C" int32_t UNITY_INTERFACE_EXPORT KlakHap_AnalyzeVideoType(Demuxer* demuxer) { if (demuxer == nullptr) return 0; return demuxer->ReadVideoTypeField(); } extern "C" void UNITY_INTERFACE_EXPORT KlakHap_ReadFrame(Demuxer* demuxer, int frameNumber, ReadBuffer* buffer) { if (demuxer == nullptr || buffer == nullptr) return; return demuxer->ReadFrame(frameNumber, *buffer); } #pragma endregion #pragma region Decoder functions extern "C" Decoder UNITY_INTERFACE_EXPORT *KlakHap_CreateDecoder(int width, int height, int typeID) { return new Decoder(width, height, typeID); } extern "C" void UNITY_INTERFACE_EXPORT KlakHap_DestroyDecoder(Decoder* decoder) { delete decoder; } extern "C" void UNITY_INTERFACE_EXPORT KlakHap_AssignDecoder(uint32_t id, Decoder* decoder) { if (decoder != nullptr) decoderMap_[id] = decoder; else decoderMap_.erase(decoderMap_.find(id)); } extern "C" void UNITY_INTERFACE_EXPORT KlakHap_DecodeFrame(Decoder* decoder, const ReadBuffer* input) { if (decoder == nullptr) return; decoder->DecodeFrame(*input); } extern "C" const void UNITY_INTERFACE_EXPORT *KlakHap_LockDecoderBuffer(Decoder* decoder) { if (decoder == nullptr) return nullptr; return decoder->LockBuffer(); } extern "C" void UNITY_INTERFACE_EXPORT KlakHap_UnlockDecoderBuffer(Decoder* decoder) { if (decoder == nullptr) return; decoder->UnlockBuffer(); } extern "C" int32_t UNITY_INTERFACE_EXPORT KlakHap_GetDecoderBufferSize(Decoder* decoder) { if (decoder == nullptr) return 0; return static_cast(decoder->GetBufferSize()); } #pragma endregion ================================================ FILE: Plugin/Source/ReadBuffer.h ================================================ #pragma once #include #include namespace KlakHap { struct ReadBuffer { std::vector storage; }; } ================================================ FILE: Plugin/Unity/IUnityGraphics.h ================================================ // Unity Native Plugin API copyright © 2015 Unity Technologies ApS // // Licensed under the Unity Companion License for Unity - dependent projects--see[Unity Companion License](http://www.unity3d.com/legal/licenses/Unity_Companion_License). // // Unless expressly provided otherwise, the Software under this license is made available strictly on an “AS IS” BASIS WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED.Please review the license for details on these and other terms and conditions. #pragma once #include "IUnityInterface.h" // Has to match the GfxDeviceRenderer enum typedef enum UnityGfxRenderer { //kUnityGfxRendererOpenGL = 0, // Legacy OpenGL, removed //kUnityGfxRendererD3D9 = 1, // Direct3D 9, removed kUnityGfxRendererD3D11 = 2, // Direct3D 11 kUnityGfxRendererNull = 4, // "null" device (used in batch mode) //kUnityGfxRendererOpenGLES20 = 8, // OpenGL ES 2.0, removed kUnityGfxRendererOpenGLES30 = 11, // OpenGL ES 3.0 //kUnityGfxRendererGXM = 12, // PlayStation Vita, removed kUnityGfxRendererPS4 = 13, // PlayStation 4 kUnityGfxRendererXboxOne = 14, // Xbox One kUnityGfxRendererMetal = 16, // iOS Metal kUnityGfxRendererOpenGLCore = 17, // OpenGL core kUnityGfxRendererD3D12 = 18, // Direct3D 12 kUnityGfxRendererVulkan = 21, // Vulkan kUnityGfxRendererNvn = 22, // Nintendo Switch NVN API kUnityGfxRendererXboxOneD3D12 = 23, // MS XboxOne Direct3D 12 kUnityGfxRendererGameCoreXboxOne = 24, // GameCore Xbox One kUnityGfxRendererGameCoreXboxSeries = 25, // GameCore XboxSeries kUnityGfxRendererPS5 = 26, // PS5 kUnityGfxRendererPS5NGGC = 27, // PS5 NGGC kUnityGfxRendererNvn2 = 29 // Switch2 } UnityGfxRenderer; typedef enum UnityGfxDeviceEventType { kUnityGfxDeviceEventInitialize = 0, kUnityGfxDeviceEventShutdown = 1, kUnityGfxDeviceEventBeforeReset = 2, kUnityGfxDeviceEventAfterReset = 3, } UnityGfxDeviceEventType; typedef void (UNITY_INTERFACE_API * IUnityGraphicsDeviceEventCallback)(UnityGfxDeviceEventType eventType); // Should only be used on the rendering thread unless noted otherwise. UNITY_DECLARE_INTERFACE(IUnityGraphics) { UnityGfxRenderer(UNITY_INTERFACE_API * GetRenderer)(); // Thread safe // This callback will be called when graphics device is created, destroyed, reset, etc. // It is possible to miss the kUnityGfxDeviceEventInitialize event in case plugin is loaded at a later time, // when the graphics device is already created. void(UNITY_INTERFACE_API * RegisterDeviceEventCallback)(IUnityGraphicsDeviceEventCallback callback); void(UNITY_INTERFACE_API * UnregisterDeviceEventCallback)(IUnityGraphicsDeviceEventCallback callback); int(UNITY_INTERFACE_API * ReserveEventIDRange)(int count); // reserves 'count' event IDs. Plugins should use the result as a base index when issuing events back and forth to avoid event id clashes. }; UNITY_REGISTER_INTERFACE_GUID(0x7CBA0A9CA4DDB544ULL, 0x8C5AD4926EB17B11ULL, IUnityGraphics) // Certain Unity APIs (GL.IssuePluginEvent, CommandBuffer.IssuePluginEvent) can callback into native plugins. // Provide them with an address to a function of this signature. typedef void (UNITY_INTERFACE_API * UnityRenderingEvent)(int eventId); typedef void (UNITY_INTERFACE_API * UnityRenderingEventAndData)(int eventId, void* data); ================================================ FILE: Plugin/Unity/IUnityInterface.h ================================================ // Unity Native Plugin API copyright © 2015 Unity Technologies ApS // // Licensed under the Unity Companion License for Unity - dependent projects--see[Unity Companion License](http://www.unity3d.com/legal/licenses/Unity_Companion_License). // // Unless expressly provided otherwise, the Software under this license is made available strictly on an “AS IS” BASIS WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED.Please review the license for details on these and other terms and conditions. #pragma once // Unity native plugin API // Compatible with C99 #if defined(__CYGWIN32__) #define UNITY_INTERFACE_API __stdcall #define UNITY_INTERFACE_EXPORT __declspec(dllexport) #elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(_WIN64) || defined(WINAPI_FAMILY) #define UNITY_INTERFACE_API __stdcall #define UNITY_INTERFACE_EXPORT __declspec(dllexport) #elif defined(__MACH__) || defined(__ANDROID__) || defined(__linux__) || defined(LUMIN) #define UNITY_INTERFACE_API #define UNITY_INTERFACE_EXPORT __attribute__ ((visibility ("default"))) #else #define UNITY_INTERFACE_API #define UNITY_INTERFACE_EXPORT #endif ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // IUnityInterface is a registry of interfaces we choose to expose to plugins. // // USAGE: // --------- // To retrieve an interface a user can do the following from a plugin, assuming they have the header file for the interface: // // IMyInterface * ptr = registry->Get(); ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Unity Interface GUID // Ensures global uniqueness. // // Template specialization is used to produce a means of looking up a GUID from its interface type at compile time. // The net result should compile down to passing around the GUID. // // UNITY_REGISTER_INTERFACE_GUID should be placed in the header file of any interface definition outside of all namespaces. // The interface structure and the registration GUID are all that is required to expose the interface to other systems. struct UnityInterfaceGUID { #ifdef __cplusplus UnityInterfaceGUID(unsigned long long high, unsigned long long low) : m_GUIDHigh(high) , m_GUIDLow(low) { } UnityInterfaceGUID(const UnityInterfaceGUID& other) { m_GUIDHigh = other.m_GUIDHigh; m_GUIDLow = other.m_GUIDLow; } UnityInterfaceGUID& operator=(const UnityInterfaceGUID& other) { m_GUIDHigh = other.m_GUIDHigh; m_GUIDLow = other.m_GUIDLow; return *this; } bool Equals(const UnityInterfaceGUID& other) const { return m_GUIDHigh == other.m_GUIDHigh && m_GUIDLow == other.m_GUIDLow; } bool LessThan(const UnityInterfaceGUID& other) const { return m_GUIDHigh < other.m_GUIDHigh || (m_GUIDHigh == other.m_GUIDHigh && m_GUIDLow < other.m_GUIDLow); } #endif unsigned long long m_GUIDHigh; unsigned long long m_GUIDLow; }; #ifdef __cplusplus inline bool operator==(const UnityInterfaceGUID& left, const UnityInterfaceGUID& right) { return left.Equals(right); } inline bool operator!=(const UnityInterfaceGUID& left, const UnityInterfaceGUID& right) { return !left.Equals(right); } inline bool operator<(const UnityInterfaceGUID& left, const UnityInterfaceGUID& right) { return left.LessThan(right); } inline bool operator>(const UnityInterfaceGUID& left, const UnityInterfaceGUID& right) { return right.LessThan(left); } inline bool operator>=(const UnityInterfaceGUID& left, const UnityInterfaceGUID& right) { return !operator<(left, right); } inline bool operator<=(const UnityInterfaceGUID& left, const UnityInterfaceGUID& right) { return !operator>(left, right); } #else typedef struct UnityInterfaceGUID UnityInterfaceGUID; #endif #ifdef __cplusplus #define UNITY_DECLARE_INTERFACE(NAME) \ struct NAME : IUnityInterface // Generic version of GetUnityInterfaceGUID to allow us to specialize it // per interface below. The generic version has no actual implementation // on purpose. // // If you get errors about return values related to this method then // you have forgotten to include UNITY_REGISTER_INTERFACE_GUID with // your interface, or it is not visible at some point when you are // trying to retrieve or add an interface. template inline const UnityInterfaceGUID GetUnityInterfaceGUID(); // This is the macro you provide in your public interface header // outside of a namespace to allow us to map between type and GUID // without the user having to worry about it when attempting to // add or retrieve and interface from the registry. #define UNITY_REGISTER_INTERFACE_GUID(HASHH, HASHL, TYPE) \ template<> \ inline const UnityInterfaceGUID GetUnityInterfaceGUID() \ { \ return UnityInterfaceGUID(HASHH,HASHL); \ } // Same as UNITY_REGISTER_INTERFACE_GUID but allows the interface to live in // a particular namespace. As long as the namespace is visible at the time you call // GetUnityInterfaceGUID< INTERFACETYPE >() or you explicitly qualify it in the template // calls this will work fine, only the macro here needs to have the additional parameter #define UNITY_REGISTER_INTERFACE_GUID_IN_NAMESPACE(HASHH, HASHL, TYPE, NAMESPACE) \ const UnityInterfaceGUID TYPE##_GUID(HASHH, HASHL); \ template<> \ inline const UnityInterfaceGUID GetUnityInterfaceGUID< NAMESPACE :: TYPE >() \ { \ return UnityInterfaceGUID(HASHH,HASHL); \ } // These macros allow for C compatibility in user code. #define UNITY_GET_INTERFACE_GUID(TYPE) GetUnityInterfaceGUID< TYPE >() #else #define UNITY_DECLARE_INTERFACE(NAME) \ typedef struct NAME NAME; \ struct NAME // NOTE: This has the downside that one some compilers it will not get stripped from all compilation units that // can see a header containing this constant. However, it's only for C compatibility and thus should have // minimal impact. #define UNITY_REGISTER_INTERFACE_GUID(HASHH, HASHL, TYPE) \ const UnityInterfaceGUID TYPE##_GUID = {HASHH, HASHL}; // In general namespaces are going to be a problem for C code any interfaces we expose in a namespace are // not going to be usable from C. #define UNITY_REGISTER_INTERFACE_GUID_IN_NAMESPACE(HASHH, HASHL, TYPE, NAMESPACE) // These macros allow for C compatibility in user code. #define UNITY_GET_INTERFACE_GUID(TYPE) TYPE##_GUID #endif // Using this in user code rather than INTERFACES->Get() will be C compatible for those places in plugins where // this may be needed. Unity code itself does not need this. #define UNITY_GET_INTERFACE(INTERFACES, TYPE) (TYPE*)INTERFACES->GetInterfaceSplit (UNITY_GET_INTERFACE_GUID(TYPE).m_GUIDHigh, UNITY_GET_INTERFACE_GUID(TYPE).m_GUIDLow); #ifdef __cplusplus struct IUnityInterface { }; #else typedef void IUnityInterface; #endif typedef struct IUnityInterfaces { // Returns an interface matching the guid. // Returns nullptr if the given interface is unavailable in the active Unity runtime. IUnityInterface* (UNITY_INTERFACE_API * GetInterface)(UnityInterfaceGUID guid); // Registers a new interface. void(UNITY_INTERFACE_API * RegisterInterface)(UnityInterfaceGUID guid, IUnityInterface * ptr); // Split APIs for C IUnityInterface* (UNITY_INTERFACE_API * GetInterfaceSplit)(unsigned long long guidHigh, unsigned long long guidLow); void(UNITY_INTERFACE_API * RegisterInterfaceSplit)(unsigned long long guidHigh, unsigned long long guidLow, IUnityInterface * ptr); #ifdef __cplusplus // Helper for GetInterface. template INTERFACE* Get() { return static_cast(GetInterface(GetUnityInterfaceGUID())); } // Helper for RegisterInterface. template void Register(IUnityInterface* ptr) { RegisterInterface(GetUnityInterfaceGUID(), ptr); } #endif } IUnityInterfaces; #ifdef __cplusplus extern "C" { #endif // If exported by a plugin, this function will be called when the plugin is loaded. void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityPluginLoad(IUnityInterfaces* unityInterfaces); // If exported by a plugin, this function will be called when the plugin is about to be unloaded. void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityPluginUnload(); #ifdef __cplusplus } #endif struct RenderSurfaceBase; typedef struct RenderSurfaceBase* UnityRenderBuffer; typedef unsigned int UnityTextureID; ================================================ FILE: Plugin/Unity/IUnityRenderingExtensions.h ================================================ // Unity Native Plugin API copyright © 2015 Unity Technologies ApS // // Licensed under the Unity Companion License for Unity - dependent projects--see[Unity Companion License](http://www.unity3d.com/legal/licenses/Unity_Companion_License). // // Unless expressly provided otherwise, the Software under this license is made available strictly on an “AS IS” BASIS WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED.Please review the license for details on these and other terms and conditions. #pragma once #include "IUnityGraphics.h" #include /* Low-level Native Plugin Rendering Extensions ============================================ On top of the Low-level native plugin interface, Unity also supports low level rendering extensions that can receive callbacks when certain events happen. This is mostly used to implement and control low-level rendering in your plugin and enable it to work with Unity’s multithreaded rendering. Due to the low-level nature of this extension the plugin might need to be preloaded before the devices get created. Currently the convention is name-based namely the plugin name must be prefixed by “GfxPlugin”. Example: GfxPluginMyFancyNativePlugin. // Native plugin code example enum PluginCustomCommands { kPluginCustomCommandDownscale = kUnityRenderingExtUserEventsStart, kPluginCustomCommandUpscale, // insert your own events here kPluginCustomCommandCount }; static IUnityInterfaces* s_UnityInterfaces = NULL; extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityPluginLoad(IUnityInterfaces* unityInterfaces) { // initialization code here... } extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityRenderingExtEvent(UnityRenderingExtEventType event, void* data) { switch (event) { case kUnityRenderingExtEventBeforeDrawCall: // do some stuff break; case kUnityRenderingExtEventAfterDrawCall: // undo some stuff break; case kPluginCustomCommandDownscale: // downscale some stuff break; case kPluginCustomCommandUpscale: // upscale some stuff break; } } */ // These events will be propagated to all plugins that implement void UnityRenderingExtEvent(UnityRenderingExtEventType event, void* data); typedef enum UnityRenderingExtEventType { kUnityRenderingExtEventSetStereoTarget, // issued during SetStereoTarget and carrying the current 'eye' index as parameter kUnityRenderingExtEventSetStereoEye, // issued during stereo rendering at the beginning of each eye's rendering loop. It carries the current 'eye' index as parameter kUnityRenderingExtEventStereoRenderingDone, // issued after the rendering has finished kUnityRenderingExtEventBeforeDrawCall, // issued during BeforeDrawCall and carrying UnityRenderingExtBeforeDrawCallParams as parameter kUnityRenderingExtEventAfterDrawCall, // issued during AfterDrawCall. This event doesn't carry any parameters kUnityRenderingExtEventCustomGrab, // issued during GrabIntoRenderTexture since we can't simply copy the resources // when custom rendering is used - we need to let plugin handle this. It carries over // a UnityRenderingExtCustomBlitParams params = { X, source, dest, 0, 0 } ( X means it's irrelevant ) kUnityRenderingExtEventCustomBlit, // issued by plugin to insert custom blits. It carries over UnityRenderingExtCustomBlitParams as param. kUnityRenderingExtEventUpdateTextureBegin, // Deprecated. kUnityRenderingExtEventUpdateTextureEnd, // Deprecated. kUnityRenderingExtEventUpdateTextureBeginV1 = kUnityRenderingExtEventUpdateTextureBegin, // Deprecated. Issued to update a texture. It carries over UnityRenderingExtTextureUpdateParamsV1 kUnityRenderingExtEventUpdateTextureEndV1 = kUnityRenderingExtEventUpdateTextureEnd, // Deprecated. Issued to signal the plugin that the texture update has finished. It carries over the same UnityRenderingExtTextureUpdateParamsV1 as kUnityRenderingExtEventUpdateTextureBeginV1 kUnityRenderingExtEventUpdateTextureBeginV2, // Issued to update a texture. It carries over UnityRenderingExtTextureUpdateParamsV2 kUnityRenderingExtEventUpdateTextureEndV2, // Issued to signal the plugin that the texture update has finished. It carries over the same UnityRenderingExtTextureUpdateParamsV2 as kUnityRenderingExtEventUpdateTextureBeginV2 // keep this last kUnityRenderingExtEventCount, kUnityRenderingExtUserEventsStart = kUnityRenderingExtEventCount } UnityRenderingExtEventType; typedef enum UnityRenderingExtCustomBlitCommands { kUnityRenderingExtCustomBlitVRFlush, // This event is mostly used in multi GPU configurations ( SLI, etc ) in order to allow the plugin to flush all GPU's targets // keep this last kUnityRenderingExtCustomBlitCount, kUnityRenderingExtUserCustomBlitStart = kUnityRenderingExtCustomBlitCount } UnityRenderingExtCustomBlitCommands; /* This will be propagated to all plugins implementing UnityRenderingExtQuery. */ typedef enum UnityRenderingExtQueryType { kUnityRenderingExtQueryOverrideViewport = 1 << 0, // The plugin handles setting up the viewport rects. Unity will skip its internal SetViewport calls kUnityRenderingExtQueryOverrideScissor = 1 << 1, // The plugin handles setting up the scissor rects. Unity will skip its internal SetScissor calls kUnityRenderingExtQueryOverrideVROcclussionMesh = 1 << 2, // The plugin handles its own VR occlusion mesh rendering. Unity will skip rendering its internal VR occlusion mask kUnityRenderingExtQueryOverrideVRSinglePass = 1 << 3, // The plugin uses its own single pass stereo technique. Unity will only traverse and render the render node graph once. // and it will clear the whole render target not just per-eye on demand. kUnityRenderingExtQueryKeepOriginalDoubleWideWidth_DEPRECATED = 1 << 4, // Instructs unity to keep the original double wide width. By default unity will try and have a power-of-two width for mip-mapping requirements. kUnityRenderingExtQueryRequestVRFlushCallback = 1 << 5, // Instructs unity to provide callbacks when the VR eye textures need flushing. Useful for multi GPU synchronization. kUnityRenderingExtQueryOverridePresentFrame = 1 << 6, // The plugin handles it's own SwapChain Present. Unity will skip its internal Present calls } UnityRenderingExtQueryType; typedef enum UnityRenderingExtTextureFormat { kUnityRenderingExtFormatNone = 0, kUnityRenderingExtFormatFirst = kUnityRenderingExtFormatNone, // sRGB formats kUnityRenderingExtFormatR8_SRGB, kUnityRenderingExtFormatR8G8_SRGB, kUnityRenderingExtFormatR8G8B8_SRGB, kUnityRenderingExtFormatR8G8B8A8_SRGB, // 8 bit integer formats kUnityRenderingExtFormatR8_UNorm, kUnityRenderingExtFormatR8G8_UNorm, kUnityRenderingExtFormatR8G8B8_UNorm, kUnityRenderingExtFormatR8G8B8A8_UNorm, kUnityRenderingExtFormatR8_SNorm, kUnityRenderingExtFormatR8G8_SNorm, kUnityRenderingExtFormatR8G8B8_SNorm, kUnityRenderingExtFormatR8G8B8A8_SNorm, kUnityRenderingExtFormatR8_UInt, kUnityRenderingExtFormatR8G8_UInt, kUnityRenderingExtFormatR8G8B8_UInt, kUnityRenderingExtFormatR8G8B8A8_UInt, kUnityRenderingExtFormatR8_SInt, kUnityRenderingExtFormatR8G8_SInt, kUnityRenderingExtFormatR8G8B8_SInt, kUnityRenderingExtFormatR8G8B8A8_SInt, // 16 bit integer formats kUnityRenderingExtFormatR16_UNorm, kUnityRenderingExtFormatR16G16_UNorm, kUnityRenderingExtFormatR16G16B16_UNorm, kUnityRenderingExtFormatR16G16B16A16_UNorm, kUnityRenderingExtFormatR16_SNorm, kUnityRenderingExtFormatR16G16_SNorm, kUnityRenderingExtFormatR16G16B16_SNorm, kUnityRenderingExtFormatR16G16B16A16_SNorm, kUnityRenderingExtFormatR16_UInt, kUnityRenderingExtFormatR16G16_UInt, kUnityRenderingExtFormatR16G16B16_UInt, kUnityRenderingExtFormatR16G16B16A16_UInt, kUnityRenderingExtFormatR16_SInt, kUnityRenderingExtFormatR16G16_SInt, kUnityRenderingExtFormatR16G16B16_SInt, kUnityRenderingExtFormatR16G16B16A16_SInt, // 32 bit integer formats kUnityRenderingExtFormatR32_UInt, kUnityRenderingExtFormatR32G32_UInt, kUnityRenderingExtFormatR32G32B32_UInt, kUnityRenderingExtFormatR32G32B32A32_UInt, kUnityRenderingExtFormatR32_SInt, kUnityRenderingExtFormatR32G32_SInt, kUnityRenderingExtFormatR32G32B32_SInt, kUnityRenderingExtFormatR32G32B32A32_SInt, // HDR formats kUnityRenderingExtFormatR16_SFloat, kUnityRenderingExtFormatR16G16_SFloat, kUnityRenderingExtFormatR16G16B16_SFloat, kUnityRenderingExtFormatR16G16B16A16_SFloat, kUnityRenderingExtFormatR32_SFloat, kUnityRenderingExtFormatR32G32_SFloat, kUnityRenderingExtFormatR32G32B32_SFloat, kUnityRenderingExtFormatR32G32B32A32_SFloat, // Luminance and Alpha format kUnityRenderingExtFormatL8_UNorm, kUnityRenderingExtFormatA8_UNorm, kUnityRenderingExtFormatA16_UNorm, // BGR formats kUnityRenderingExtFormatB8G8R8_SRGB, kUnityRenderingExtFormatB8G8R8A8_SRGB, kUnityRenderingExtFormatB8G8R8_UNorm, kUnityRenderingExtFormatB8G8R8A8_UNorm, kUnityRenderingExtFormatB8G8R8_SNorm, kUnityRenderingExtFormatB8G8R8A8_SNorm, kUnityRenderingExtFormatB8G8R8_UInt, kUnityRenderingExtFormatB8G8R8A8_UInt, kUnityRenderingExtFormatB8G8R8_SInt, kUnityRenderingExtFormatB8G8R8A8_SInt, // 16 bit packed formats kUnityRenderingExtFormatR4G4B4A4_UNormPack16, kUnityRenderingExtFormatB4G4R4A4_UNormPack16, kUnityRenderingExtFormatR5G6B5_UNormPack16, kUnityRenderingExtFormatB5G6R5_UNormPack16, kUnityRenderingExtFormatR5G5B5A1_UNormPack16, kUnityRenderingExtFormatB5G5R5A1_UNormPack16, kUnityRenderingExtFormatA1R5G5B5_UNormPack16, // Packed formats kUnityRenderingExtFormatE5B9G9R9_UFloatPack32, kUnityRenderingExtFormatB10G11R11_UFloatPack32, kUnityRenderingExtFormatA2B10G10R10_UNormPack32, kUnityRenderingExtFormatA2B10G10R10_UIntPack32, kUnityRenderingExtFormatA2B10G10R10_SIntPack32, kUnityRenderingExtFormatA2R10G10B10_UNormPack32, kUnityRenderingExtFormatA2R10G10B10_UIntPack32, kUnityRenderingExtFormatA2R10G10B10_SIntPack32, kUnityRenderingExtFormatA2R10G10B10_XRSRGBPack32, kUnityRenderingExtFormatA2R10G10B10_XRUNormPack32, kUnityRenderingExtFormatR10G10B10_XRSRGBPack32, kUnityRenderingExtFormatR10G10B10_XRUNormPack32, kUnityRenderingExtFormatA10R10G10B10_XRSRGBPack32, kUnityRenderingExtFormatA10R10G10B10_XRUNormPack32, // ARGB formats... TextureFormat legacy kUnityRenderingExtFormatA8R8G8B8_SRGB, kUnityRenderingExtFormatA8R8G8B8_UNorm, kUnityRenderingExtFormatA32R32G32B32_SFloat, // Depth Stencil for formats kUnityRenderingExtFormatD16_UNorm, kUnityRenderingExtFormatD24_UNorm, kUnityRenderingExtFormatD24_UNorm_S8_UInt, kUnityRenderingExtFormatD32_SFloat, kUnityRenderingExtFormatD32_SFloat_S8_UInt, kUnityRenderingExtFormatS8_UInt, // Compression formats kUnityRenderingExtFormatRGBA_DXT1_SRGB, kUnityRenderingExtFormatRGBA_DXT1_UNorm, kUnityRenderingExtFormatRGBA_DXT3_SRGB, kUnityRenderingExtFormatRGBA_DXT3_UNorm, kUnityRenderingExtFormatRGBA_DXT5_SRGB, kUnityRenderingExtFormatRGBA_DXT5_UNorm, kUnityRenderingExtFormatR_BC4_UNorm, kUnityRenderingExtFormatR_BC4_SNorm, kUnityRenderingExtFormatRG_BC5_UNorm, kUnityRenderingExtFormatRG_BC5_SNorm, kUnityRenderingExtFormatRGB_BC6H_UFloat, kUnityRenderingExtFormatRGB_BC6H_SFloat, kUnityRenderingExtFormatRGBA_BC7_SRGB, kUnityRenderingExtFormatRGBA_BC7_UNorm, kUnityRenderingExtFormatRGB_PVRTC_2Bpp_SRGB, kUnityRenderingExtFormatRGB_PVRTC_2Bpp_UNorm, kUnityRenderingExtFormatRGB_PVRTC_4Bpp_SRGB, kUnityRenderingExtFormatRGB_PVRTC_4Bpp_UNorm, kUnityRenderingExtFormatRGBA_PVRTC_2Bpp_SRGB, kUnityRenderingExtFormatRGBA_PVRTC_2Bpp_UNorm, kUnityRenderingExtFormatRGBA_PVRTC_4Bpp_SRGB, kUnityRenderingExtFormatRGBA_PVRTC_4Bpp_UNorm, kUnityRenderingExtFormatRGB_ETC_UNorm, kUnityRenderingExtFormatRGB_ETC2_SRGB, kUnityRenderingExtFormatRGB_ETC2_UNorm, kUnityRenderingExtFormatRGB_A1_ETC2_SRGB, kUnityRenderingExtFormatRGB_A1_ETC2_UNorm, kUnityRenderingExtFormatRGBA_ETC2_SRGB, kUnityRenderingExtFormatRGBA_ETC2_UNorm, kUnityRenderingExtFormatR_EAC_UNorm, kUnityRenderingExtFormatR_EAC_SNorm, kUnityRenderingExtFormatRG_EAC_UNorm, kUnityRenderingExtFormatRG_EAC_SNorm, kUnityRenderingExtFormatRGBA_ASTC4X4_SRGB, kUnityRenderingExtFormatRGBA_ASTC4X4_UNorm, kUnityRenderingExtFormatRGBA_ASTC5X5_SRGB, kUnityRenderingExtFormatRGBA_ASTC5X5_UNorm, kUnityRenderingExtFormatRGBA_ASTC6X6_SRGB, kUnityRenderingExtFormatRGBA_ASTC6X6_UNorm, kUnityRenderingExtFormatRGBA_ASTC8X8_SRGB, kUnityRenderingExtFormatRGBA_ASTC8X8_UNorm, kUnityRenderingExtFormatRGBA_ASTC10X10_SRGB, kUnityRenderingExtFormatRGBA_ASTC10X10_UNorm, kUnityRenderingExtFormatRGBA_ASTC12X12_SRGB, kUnityRenderingExtFormatRGBA_ASTC12X12_UNorm, // Video formats kUnityRenderingExtFormatYUV2, // Obsoleted //kUnityRenderingExtFormatDepthAuto_removed_donotuse = 142, //kUnityRenderingExtFormatShadowAuto_removed_donotuse = 143, //kUnityRenderingExtFormatVideoAuto_removed_donotuse = 144, // ASTC hdr profile kUnityRenderingExtFormatRGBA_ASTC4X4_UFloat = 145, kUnityRenderingExtFormatRGBA_ASTC5X5_UFloat, kUnityRenderingExtFormatRGBA_ASTC6X6_UFloat, kUnityRenderingExtFormatRGBA_ASTC8X8_UFloat, kUnityRenderingExtFormatRGBA_ASTC10X10_UFloat, kUnityRenderingExtFormatRGBA_ASTC12X12_UFloat, kUnityRenderingExtFormatLast = kUnityRenderingExtFormatRGBA_ASTC12X12_UFloat, // Remove? } UnityRenderingExtTextureFormat; typedef struct UnityRenderingExtBeforeDrawCallParams { void* vertexShader; // bound vertex shader (platform dependent) void* fragmentShader; // bound fragment shader (platform dependent) void* geometryShader; // bound geometry shader (platform dependent) void* hullShader; // bound hull shader (platform dependent) void* domainShader; // bound domain shader (platform dependent) int eyeIndex; // the index of the current stereo "eye" being currently rendered. } UnityRenderingExtBeforeDrawCallParams; typedef struct UnityRenderingExtCustomBlitParams { UnityTextureID source; // source texture UnityRenderBuffer destination; // destination surface unsigned int command; // command for the custom blit - could be any UnityRenderingExtCustomBlitCommands command or custom ones. unsigned int commandParam; // custom parameters for the command unsigned int commandFlags; // custom flags for the command } UnityRenderingExtCustomBlitParams; // Deprecated. Use UnityRenderingExtTextureUpdateParamsV2 and CommandBuffer.IssuePluginCustomTextureUpdateV2 instead. // Only supports DX11, GLES, Metal typedef struct UnityRenderingExtTextureUpdateParamsV1 { void* texData; // source data for the texture update. Must be set by the plugin unsigned int userData; // user defined data. Set by the plugin unsigned int textureID; // texture ID of the texture to be updated. UnityRenderingExtTextureFormat format; // format of the texture to be updated unsigned int width; // width of the texture unsigned int height; // height of the texture unsigned int bpp; // texture bytes per pixel. } UnityRenderingExtTextureUpdateParamsV1; // Deprecated. Use UnityRenderingExtTextureUpdateParamsV2 and CommandBuffer.IssuePluginCustomTextureUpdateV2 instead. // Only supports DX11, GLES, Metal typedef UnityRenderingExtTextureUpdateParamsV1 UnityRenderingExtTextureUpdateParams; // Type of the "data" parameter passed when callbacks registered with CommandBuffer.IssuePluginCustomTextureUpdateV2 are called. // Supports DX11, GLES, Metal, and Switch (also possibly PS4, PSVita in the future) typedef struct UnityRenderingExtTextureUpdateParamsV2 { void* texData; // source data for the texture update. Must be set by the plugin intptr_t textureID; // texture ID of the texture to be updated. unsigned int userData; // user defined data. Set by the plugin UnityRenderingExtTextureFormat format; // format of the texture to be updated unsigned int width; // width of the texture unsigned int height; // height of the texture unsigned int bpp; // texture bytes per pixel. } UnityRenderingExtTextureUpdateParamsV2; // Certain Unity APIs (GL.IssuePluginEventAndData, CommandBuffer.IssuePluginEventAndData) can callback into native plugins. // Provide them with an address to a function of this signature. typedef void (UNITY_INTERFACE_API * UnityRenderingEventAndData)(int eventId, void* data); #ifdef __cplusplus extern "C" { #endif // If exported by a plugin, this function will be called for all the events in UnityRenderingExtEventType void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityRenderingExtEvent(UnityRenderingExtEventType event, void* data); // If exported by a plugin, this function will be called to query the plugin for the queries in UnityRenderingExtQueryType bool UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityRenderingExtQuery(UnityRenderingExtQueryType query); #ifdef __cplusplus } #endif ================================================ FILE: Plugin/build.macos.sh ================================================ #!/bin/sh set -exuo pipefail RPATH_FLAGS="-install_name @rpath/KlakHap.bundle" VER_FLAGS="-current_version 1.0.0 -compatibility_version 1.0.0" make ARCH=arm64 SO_ARGS="$RPATH_FLAGS $VER_FLAGS" -f Makefile.macos make ARCH=x86_64 SO_ARGS="$RPATH_FLAGS $VER_FLAGS" -f Makefile.macos lipo -create -output KlakHap.bundle \ build-macOS-arm64/libKlakHap.dylib \ build-macOS-x86_64/libKlakHap.dylib cp KlakHap.bundle ../Packages/jp.keijiro.klak.hap/Plugin/MacOS/ ================================================ FILE: ProjectSettings/AudioManager.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!11 &1 AudioManager: m_ObjectHideFlags: 0 serializedVersion: 2 m_Volume: 1 Rolloff Scale: 1 Doppler Factor: 1 Default Speaker Mode: 2 m_SampleRate: 0 m_DSPBufferSize: 1024 m_VirtualVoiceCount: 512 m_RealVoiceCount: 32 m_EnableOutputSuspension: 1 m_SpatializerPlugin: m_AmbisonicDecoderPlugin: m_DisableAudio: 0 m_VirtualizeEffects: 1 m_RequestedDSPBufferSize: 0 m_AudioFoundation: 0 m_OutputChannelLayout: 2 m_OutputSamplingRate: 48000 ================================================ FILE: ProjectSettings/ClusterInputManager.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!236 &1 ClusterInputManager: m_ObjectHideFlags: 0 m_Inputs: [] ================================================ FILE: ProjectSettings/DynamicsManager.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!55 &1 PhysicsManager: m_ObjectHideFlags: 0 serializedVersion: 22 m_Gravity: {x: 0, y: -9.81, z: 0} m_DefaultMaterial: {fileID: 0} m_BounceThreshold: 2 m_DefaultMaxDepenetrationVelocity: 10 m_SleepThreshold: 0.005 m_DefaultContactOffset: 0.01 m_DefaultSolverIterations: 6 m_DefaultSolverVelocityIterations: 1 m_QueriesHitBackfaces: 0 m_QueriesHitTriggers: 1 m_EnableAdaptiveForce: 0 m_ClothInterCollisionDistance: 0.1 m_ClothInterCollisionStiffness: 0.2 m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff m_SimulationMode: 0 m_AutoSyncTransforms: 0 m_ReuseCollisionCallbacks: 1 m_InvokeCollisionCallbacks: 1 m_ClothInterCollisionSettingsToggle: 0 m_ClothGravity: {x: 0, y: -9.81, z: 0} m_ContactPairsMode: 0 m_BroadphaseType: 0 m_WorldBounds: m_Center: {x: 0, y: 0, z: 0} m_Extent: {x: 256, y: 256, z: 256} m_WorldSubdivisions: 8 m_FrictionType: 0 m_EnableEnhancedDeterminism: 0 m_ImprovedPatchFriction: 0 m_GenerateOnTriggerStayEvents: 1 m_SolverType: 0 m_DefaultMaxAngularSpeed: 50 m_ScratchBufferChunkCount: 4 m_CurrentBackendId: 4072204805 m_FastMotionThreshold: 3.4028235e+38 m_SceneBuffersReleaseInterval: 0 m_ReleaseSceneBuffers: 0 m_LogVerbosity: 3 ================================================ FILE: ProjectSettings/EditorBuildSettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!1045 &1 EditorBuildSettings: m_ObjectHideFlags: 0 serializedVersion: 2 m_Scenes: [] m_configObjects: {} m_UseUCBPForAssetBundles: 0 ================================================ FILE: ProjectSettings/EditorSettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!159 &1 EditorSettings: m_ObjectHideFlags: 0 serializedVersion: 15 m_SerializationMode: 2 m_LineEndingsForNewScripts: 1 m_DefaultBehaviorMode: 0 m_PrefabRegularEnvironment: {fileID: 0} m_PrefabUIEnvironment: {fileID: 0} m_SpritePackerMode: 0 m_SpritePackerCacheSize: 10 m_SpritePackerPaddingPower: 1 m_Bc7TextureCompressor: 0 m_EtcTextureCompressorBehavior: 1 m_EtcTextureFastCompressor: 1 m_EtcTextureNormalCompressor: 2 m_EtcTextureBestCompressor: 4 m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmdef;asmref;rsp;java;cpp;c;mm;m;h m_ProjectGenerationRootNamespace: m_EnableTextureStreamingInEditMode: 1 m_EnableTextureStreamingInPlayMode: 1 m_EnableEditorAsyncCPUTextureLoading: 0 m_AsyncShaderCompilation: 1 m_PrefabModeAllowAutoSave: 1 m_EnterPlayModeOptionsEnabled: 1 m_EnterPlayModeOptions: 3 m_GameObjectNamingDigits: 1 m_GameObjectNamingScheme: 0 m_AssetNamingUsesSpace: 1 m_InspectorUseIMGUIDefaultInspector: 0 m_UseLegacyProbeSampleCount: 0 m_SerializeInlineMappingsOnOneLine: 1 m_DisableCookiesInLightmapper: 0 m_ShadowmaskStitching: 1 m_AssetPipelineMode: 1 m_RefreshImportMode: 0 m_CacheServerMode: 0 m_CacheServerEndpoint: m_CacheServerNamespacePrefix: default m_CacheServerEnableDownload: 1 m_CacheServerEnableUpload: 1 m_CacheServerEnableAuth: 0 m_CacheServerEnableTls: 0 m_CacheServerValidationMode: 2 m_CacheServerDownloadBatchSize: 128 m_EnableEnlightenBakedGI: 0 m_ReferencedClipsExactNaming: 1 m_ForceAssetUnloadAndGCOnSceneLoad: 1 ================================================ FILE: ProjectSettings/GraphicsSettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!30 &1 GraphicsSettings: m_ObjectHideFlags: 0 serializedVersion: 16 m_Deferred: m_Mode: 1 m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0} m_DeferredReflections: m_Mode: 1 m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0} m_ScreenSpaceShadows: m_Mode: 1 m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0} m_DepthNormals: m_Mode: 1 m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0} m_MotionVectors: m_Mode: 1 m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0} m_LightHalo: m_Mode: 1 m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0} m_LensFlare: m_Mode: 1 m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0} m_VideoShadersIncludeMode: 2 m_AlwaysIncludedShaders: - {fileID: 7, guid: 0000000000000000f000000000000000, type: 0} - {fileID: 15104, guid: 0000000000000000f000000000000000, type: 0} - {fileID: 15105, guid: 0000000000000000f000000000000000, type: 0} - {fileID: 15106, guid: 0000000000000000f000000000000000, type: 0} - {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0} - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0} - {fileID: 10783, guid: 0000000000000000f000000000000000, type: 0} m_PreloadedShaders: [] m_PreloadShadersBatchTimeLimit: -1 m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0} m_CustomRenderPipeline: {fileID: 11400000, guid: 0f2789a7f25c049d88040a322e46da7c, type: 2} m_TransparencySortMode: 0 m_TransparencySortAxis: {x: 0, y: 0, z: 1} m_DefaultRenderingPath: 1 m_DefaultMobileRenderingPath: 1 m_TierSettings: [] m_LightmapStripping: 0 m_FogStripping: 0 m_InstancingStripping: 0 m_BrgStripping: 0 m_LightmapKeepPlain: 1 m_LightmapKeepDirCombined: 1 m_LightmapKeepDynamicPlain: 1 m_LightmapKeepDynamicDirCombined: 1 m_LightmapKeepShadowMask: 1 m_LightmapKeepSubtractive: 1 m_FogKeepLinear: 1 m_FogKeepExp: 1 m_FogKeepExp2: 1 m_AlbedoSwatchInfos: [] m_RenderPipelineGlobalSettingsMap: UnityEngine.Rendering.Universal.UniversalRenderPipeline: {fileID: 11400000, guid: a0e1abbc8745e464ab699ff4e71959f6, type: 2} m_ShaderBuildSettings: keywordDeclarationOverrides: [] m_LightsUseLinearIntensity: 1 m_LightsUseColorTemperature: 1 m_LogWhenShaderIsCompiled: 0 m_LightProbeOutsideHullStrategy: 1 m_CameraRelativeLightCulling: 0 m_CameraRelativeShadowCulling: 0 ================================================ FILE: ProjectSettings/InputManager.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!13 &1 InputManager: m_ObjectHideFlags: 0 serializedVersion: 2 m_Axes: - serializedVersion: 3 m_Name: Horizontal descriptiveName: descriptiveNegativeName: negativeButton: left positiveButton: right altNegativeButton: a altPositiveButton: d gravity: 3 dead: 0.001 sensitivity: 3 snap: 1 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Vertical descriptiveName: descriptiveNegativeName: negativeButton: down positiveButton: up altNegativeButton: s altPositiveButton: w gravity: 3 dead: 0.001 sensitivity: 3 snap: 1 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Fire1 descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: left ctrl altNegativeButton: altPositiveButton: mouse 0 gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Fire2 descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: left alt altNegativeButton: altPositiveButton: mouse 1 gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Fire3 descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: left shift altNegativeButton: altPositiveButton: mouse 2 gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Jump descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: space altNegativeButton: altPositiveButton: gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Mouse X descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: altNegativeButton: altPositiveButton: gravity: 0 dead: 0 sensitivity: 0.1 snap: 0 invert: 0 type: 1 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Mouse Y descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: altNegativeButton: altPositiveButton: gravity: 0 dead: 0 sensitivity: 0.1 snap: 0 invert: 0 type: 1 axis: 1 joyNum: 0 - serializedVersion: 3 m_Name: Mouse ScrollWheel descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: altNegativeButton: altPositiveButton: gravity: 0 dead: 0 sensitivity: 0.1 snap: 0 invert: 0 type: 1 axis: 2 joyNum: 0 - serializedVersion: 3 m_Name: Horizontal descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: altNegativeButton: altPositiveButton: gravity: 0 dead: 0.19 sensitivity: 1 snap: 0 invert: 0 type: 2 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Vertical descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: altNegativeButton: altPositiveButton: gravity: 0 dead: 0.19 sensitivity: 1 snap: 0 invert: 1 type: 2 axis: 1 joyNum: 0 - serializedVersion: 3 m_Name: Fire1 descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: joystick button 0 altNegativeButton: altPositiveButton: gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Fire2 descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: joystick button 1 altNegativeButton: altPositiveButton: gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Fire3 descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: joystick button 2 altNegativeButton: altPositiveButton: gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Jump descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: joystick button 3 altNegativeButton: altPositiveButton: gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Submit descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: return altNegativeButton: altPositiveButton: joystick button 0 gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Submit descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: enter altNegativeButton: altPositiveButton: space gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 - serializedVersion: 3 m_Name: Cancel descriptiveName: descriptiveNegativeName: negativeButton: positiveButton: escape altNegativeButton: altPositiveButton: joystick button 1 gravity: 1000 dead: 0.001 sensitivity: 1000 snap: 0 invert: 0 type: 0 axis: 0 joyNum: 0 m_UsePhysicalKeys: 1 ================================================ FILE: ProjectSettings/MemorySettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!387306366 &1 MemorySettings: m_ObjectHideFlags: 0 m_EditorMemorySettings: m_MainAllocatorBlockSize: -1 m_ThreadAllocatorBlockSize: -1 m_MainGfxBlockSize: -1 m_ThreadGfxBlockSize: -1 m_CacheBlockSize: -1 m_TypetreeBlockSize: -1 m_ProfilerBlockSize: -1 m_ProfilerEditorBlockSize: -1 m_BucketAllocatorGranularity: -1 m_BucketAllocatorBucketsCount: -1 m_BucketAllocatorBlockSize: -1 m_BucketAllocatorBlockCount: -1 m_ProfilerBucketAllocatorGranularity: -1 m_ProfilerBucketAllocatorBucketsCount: -1 m_ProfilerBucketAllocatorBlockSize: -1 m_ProfilerBucketAllocatorBlockCount: -1 m_TempAllocatorSizeMain: -1 m_JobTempAllocatorBlockSize: -1 m_BackgroundJobTempAllocatorBlockSize: -1 m_JobTempAllocatorReducedBlockSize: -1 m_TempAllocatorSizeGIBakingWorker: -1 m_TempAllocatorSizeNavMeshWorker: -1 m_TempAllocatorSizeAudioWorker: -1 m_TempAllocatorSizeCloudWorker: -1 m_TempAllocatorSizeGfx: -1 m_TempAllocatorSizeJobWorker: -1 m_TempAllocatorSizeBackgroundWorker: -1 m_TempAllocatorSizePreloadManager: -1 m_PlatformMemorySettings: {} ================================================ FILE: ProjectSettings/MultiplayerManager.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!655991488 &1 MultiplayerManager: m_ObjectHideFlags: 0 m_EnableMultiplayerRoles: 0 m_EnablePlayModeLocalDeployment: 0 m_EnablePlayModeRemoteDeployment: 0 m_StrippingTypes: {} ================================================ FILE: ProjectSettings/NavMeshAreas.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!126 &1 NavMeshProjectSettings: m_ObjectHideFlags: 0 serializedVersion: 2 areas: - name: Walkable cost: 1 - name: Not Walkable cost: 1 - name: Jump cost: 2 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 - name: cost: 1 m_LastAgentTypeID: -887442657 m_Settings: - serializedVersion: 3 agentTypeID: 0 agentRadius: 0.5 agentHeight: 2 agentSlope: 45 agentClimb: 0.75 ledgeDropHeight: 0 maxJumpAcrossDistance: 0 minRegionArea: 2 manualCellSize: 0 cellSize: 0.16666667 manualTileSize: 0 tileSize: 256 buildHeightMesh: 0 maxJobWorkers: 0 preserveTilesOutsideBounds: 0 debug: m_Flags: 0 m_SettingNames: - Humanoid ================================================ FILE: ProjectSettings/PackageManagerSettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!114 &1 MonoBehaviour: m_ObjectHideFlags: 53 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 13964, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: UnityEditor.dll::UnityEditor.PackageManager.UI.Internal.PackageManagerProjectSettings m_EnablePreReleasePackages: 1 m_AdvancedSettingsExpanded: 1 m_ScopedRegistriesSettingsExpanded: 1 m_SeeAllPackageVersions: 0 m_DismissPreviewPackagesInUse: 0 oneTimeWarningShown: 1 oneTimePackageErrorsPopUpShown: 0 m_Registries: - m_Id: main m_Name: m_Url: https://packages.unity.com m_Scopes: [] m_IsDefault: 1 m_Capabilities: 7 m_ConfigSource: 0 m_Compliance: m_Status: 0 m_Violations: [] - m_Id: scoped:project:Keijiro m_Name: Keijiro m_Url: https://registry.npmjs.com m_Scopes: - jp.keijiro m_IsDefault: 0 m_Capabilities: 0 m_ConfigSource: 4 m_Compliance: m_Status: 0 m_Violations: [] m_UserSelectedRegistryName: Keijiro m_UserAddingNewScopedRegistry: 0 m_RegistryInfoDraft: m_Modified: 0 m_ErrorMessage: m_UserModificationsInstanceId: -894 m_OriginalInstanceId: -896 m_LoadAssets: 0 ================================================ FILE: ProjectSettings/Packages/com.unity.dedicated-server/MultiplayerRolesSettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!114 &1 MonoBehaviour: m_ObjectHideFlags: 53 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 15023, guid: 0000000000000000e000000000000000, type: 0} m_Name: m_EditorClassIdentifier: UnityEditor.MultiplayerModule.dll::UnityEditor.Multiplayer.Internal.MultiplayerRolesSettings m_MultiplayerRoleForClassicProfile: m_Keys: [] m_Values: ================================================ FILE: ProjectSettings/Physics2DSettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!19 &1 Physics2DSettings: m_ObjectHideFlags: 0 serializedVersion: 11 m_Gravity: {x: 0, y: -9.81} m_DefaultMaterial: {fileID: 0} m_VelocityIterations: 8 m_PositionIterations: 3 m_BounceThreshold: 1 m_MaxLinearCorrection: 0.2 m_MaxAngularCorrection: 8 m_MaxTranslationSpeed: 100 m_MaxRotationSpeed: 360 m_BaumgarteScale: 0.2 m_BaumgarteTimeOfImpactScale: 0.75 m_TimeToSleep: 0.5 m_LinearSleepTolerance: 0.01 m_AngularSleepTolerance: 2 m_DefaultContactOffset: 0.01 m_ContactThreshold: 0 m_JobOptions: serializedVersion: 2 useMultithreading: 0 useConsistencySorting: 0 m_InterpolationPosesPerJob: 100 m_NewContactsPerJob: 30 m_CollideContactsPerJob: 100 m_ClearFlagsPerJob: 200 m_ClearBodyForcesPerJob: 200 m_SyncDiscreteFixturesPerJob: 50 m_SyncContinuousFixturesPerJob: 50 m_FindNearestContactsPerJob: 100 m_UpdateTriggerContactsPerJob: 100 m_IslandSolverCostThreshold: 100 m_IslandSolverBodyCostScale: 1 m_IslandSolverContactCostScale: 10 m_IslandSolverJointCostScale: 10 m_IslandSolverBodiesPerJob: 50 m_IslandSolverContactsPerJob: 50 m_SimulationMode: 0 m_SimulationLayers: serializedVersion: 2 m_Bits: 4294967295 m_MaxSubStepCount: 4 m_MinSubStepFPS: 30 m_UseSubStepping: 0 m_UseSubStepContacts: 0 m_QueriesHitTriggers: 1 m_QueriesStartInColliders: 1 m_CallbacksOnDisable: 1 m_ReuseCollisionCallbacks: 1 m_AutoSyncTransforms: 0 m_GizmoOptions: 10 m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff m_PhysicsLowLevelSettings: {fileID: 0} ================================================ FILE: ProjectSettings/PresetManager.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!1386491679 &1 PresetManager: m_ObjectHideFlags: 0 serializedVersion: 2 m_DefaultPresets: {} ================================================ FILE: ProjectSettings/ProjectSettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!129 &1 PlayerSettings: m_ObjectHideFlags: 0 serializedVersion: 28 productGUID: 912d5b78b8f784c9187b110aa6c731a2 AndroidProfiler: 0 AndroidFilterTouchesWhenObscured: 0 AndroidEnableSustainedPerformanceMode: 0 defaultScreenOrientation: 4 targetDevice: 2 useOnDemandResources: 0 accelerometerFrequency: 60 companyName: Keijiro productName: KlakHap defaultCursor: {fileID: 0} cursorHotspot: {x: 0, y: 0} m_SplashScreenBackgroundColor: {r: 0.12156863, g: 0.12156863, b: 0.1254902, a: 1} m_ShowUnitySplashScreen: 0 m_ShowUnitySplashLogo: 1 m_SplashScreenOverlayOpacity: 1 m_SplashScreenAnimation: 1 m_SplashScreenLogoStyle: 1 m_SplashScreenDrawMode: 0 m_SplashScreenBackgroundAnimationZoom: 1 m_SplashScreenLogoAnimationZoom: 1 m_SplashScreenBackgroundLandscapeAspect: 1 m_SplashScreenBackgroundPortraitAspect: 1 m_SplashScreenBackgroundLandscapeUvs: serializedVersion: 2 x: 0 y: 0 width: 1 height: 1 m_SplashScreenBackgroundPortraitUvs: serializedVersion: 2 x: 0 y: 0 width: 1 height: 1 m_SplashScreenLogos: [] m_VirtualRealitySplashScreen: {fileID: 0} m_HolographicTrackingLossScreen: {fileID: 0} defaultScreenWidth: 1920 defaultScreenHeight: 1080 defaultScreenWidthWeb: 960 defaultScreenHeightWeb: 600 m_StereoRenderingPath: 0 m_ActiveColorSpace: 1 unsupportedMSAAFallback: 0 m_SpriteBatchMaxVertexCount: 65535 m_SpriteBatchVertexThreshold: 300 m_MTRendering: 1 mipStripping: 0 numberOfMipsStripped: 0 numberOfMipsStrippedPerMipmapLimitGroup: {} m_StackTraceTypes: 010000000100000001000000010000000100000001000000 iosShowActivityIndicatorOnLoading: -1 androidShowActivityIndicatorOnLoading: -1 iosUseCustomAppBackgroundBehavior: 0 allowedAutorotateToPortrait: 1 allowedAutorotateToPortraitUpsideDown: 1 allowedAutorotateToLandscapeRight: 1 allowedAutorotateToLandscapeLeft: 1 useOSAutorotation: 1 use32BitDisplayBuffer: 1 preserveFramebufferAlpha: 0 disableDepthAndStencilBuffers: 0 androidStartInFullscreen: 1 androidRenderOutsideSafeArea: 1 androidUseSwappy: 1 androidDisplayOptions: 1 androidBlitType: 0 androidResizeableActivity: 1 androidDefaultWindowWidth: 1920 androidDefaultWindowHeight: 1080 androidMinimumWindowWidth: 400 androidMinimumWindowHeight: 300 androidFullscreenMode: 1 androidAutoRotationBehavior: 1 androidPredictiveBackSupport: 1 androidApplicationEntry: 2 defaultIsNativeResolution: 1 macRetinaSupport: 1 runInBackground: 1 muteOtherAudioSources: 0 Prepare IOS For Recording: 0 Force IOS Speakers When Recording: 0 audioSpatialExperience: 0 deferSystemGesturesMode: 0 hideHomeButton: 0 submitAnalytics: 1 usePlayerLog: 1 dedicatedServerOptimizations: 1 bakeCollisionMeshes: 0 forceSingleInstance: 0 useFlipModelSwapchain: 1 resizableWindow: 0 useMacAppStoreValidation: 0 macAppStoreCategory: public.app-category.games gpuSkinning: 0 meshDeformation: 0 xboxPIXTextureCapture: 0 xboxEnableAvatar: 0 xboxEnableKinect: 0 xboxEnableKinectAutoTracking: 0 xboxEnableFitness: 0 visibleInBackground: 1 allowFullscreenSwitch: 1 fullscreenMode: 3 xboxSpeechDB: 0 xboxEnableHeadOrientation: 0 xboxEnableGuest: 0 xboxEnablePIXSampling: 0 metalFramebufferOnly: 0 xboxOneResolution: 0 xboxOneSResolution: 0 xboxOneXResolution: 3 xboxOneMonoLoggingLevel: 0 xboxOneLoggingLevel: 1 xboxOneDisableEsram: 0 xboxOneEnableTypeOptimization: 0 xboxOnePresentImmediateThreshold: 0 switchQueueCommandMemory: 1048576 switchQueueControlMemory: 16384 switchQueueComputeMemory: 262144 switchNVNShaderPoolsGranularity: 33554432 switchNVNDefaultPoolsGranularity: 16777216 switchNVNOtherPoolsGranularity: 16777216 switchGpuScratchPoolGranularity: 2097152 switchAllowGpuScratchShrinking: 0 switchNVNMaxPublicTextureIDCount: 0 switchNVNMaxPublicSamplerIDCount: 0 switchMaxWorkerMultiple: 8 switchNVNGraphicsFirmwareMemory: 32 switchGraphicsJobsSyncAfterKick: 1 vulkanNumSwapchainBuffers: 3 vulkanEnableSetSRGBWrite: 0 vulkanEnablePreTransform: 0 vulkanEnableLateAcquireNextImage: 0 vulkanEnableCommandBufferRecycling: 1 loadStoreDebugModeEnabled: 0 visionOSBundleVersion: 1.0 tvOSBundleVersion: 1.0 bundleVersion: 1.0 preloadedAssets: [] metroInputSource: 0 wsaTransparentSwapchain: 0 m_HolographicPauseOnTrackingLoss: 1 xboxOneDisableKinectGpuReservation: 1 xboxOneEnable7thCore: 1 vrSettings: enable360StereoCapture: 0 isWsaHolographicRemotingEnabled: 0 enableFrameTimingStats: 0 enableOpenGLProfilerGPURecorders: 1 allowHDRDisplaySupport: 0 useHDRDisplay: 0 hdrBitDepth: 0 m_ColorGamuts: 00000000 targetPixelDensity: 30 resolutionScalingMode: 0 resetResolutionOnWindowResize: 0 androidSupportedAspectRatio: 1 androidMaxAspectRatio: 2.4 androidMinAspectRatio: 1 applicationIdentifier: Standalone: com.Keijiro.KlakHap buildNumber: Standalone: 0 VisionOS: 0 iPhone: 0 tvOS: 0 overrideDefaultApplicationIdentifier: 0 AndroidBundleVersionCode: 1 AndroidMinSdkVersion: 25 AndroidTargetSdkVersion: 0 AndroidPreferredInstallLocation: 1 AndroidPreferredDataLocation: 1 aotOptions: stripEngineCode: 1 iPhoneStrippingLevel: 0 iPhoneScriptCallOptimization: 0 ForceInternetPermission: 0 ForceSDCardPermission: 0 CreateWallpaper: 0 androidSplitApplicationBinary: 0 keepLoadedShadersAlive: 0 StripUnusedMeshComponents: 0 strictShaderVariantMatching: 0 VertexChannelCompressionMask: 4054 iPhoneSdkVersion: 988 iOSSimulatorArchitecture: 0 iOSTargetOSVersionString: 15.0 tvOSSdkVersion: 0 tvOSSimulatorArchitecture: 0 tvOSRequireExtendedGameController: 0 tvOSTargetOSVersionString: 15.0 VisionOSSdkVersion: 0 VisionOSTargetOSVersionString: 1.0 uIPrerenderedIcon: 0 uIRequiresPersistentWiFi: 0 uIRequiresFullScreen: 1 uIStatusBarHidden: 1 uIExitOnSuspend: 0 uIStatusBarStyle: 0 appleTVSplashScreen: {fileID: 0} appleTVSplashScreen2x: {fileID: 0} tvOSSmallIconLayers: [] tvOSSmallIconLayers2x: [] tvOSLargeIconLayers: [] tvOSLargeIconLayers2x: [] tvOSTopShelfImageLayers: [] tvOSTopShelfImageLayers2x: [] tvOSTopShelfImageWideLayers: [] tvOSTopShelfImageWideLayers2x: [] iOSLaunchScreenType: 0 iOSLaunchScreenPortrait: {fileID: 0} iOSLaunchScreenLandscape: {fileID: 0} iOSLaunchScreenBackgroundColor: serializedVersion: 2 rgba: 0 iOSLaunchScreenFillPct: 100 iOSLaunchScreenSize: 100 iOSLaunchScreeniPadType: 0 iOSLaunchScreeniPadImage: {fileID: 0} iOSLaunchScreeniPadBackgroundColor: serializedVersion: 2 rgba: 0 iOSLaunchScreeniPadFillPct: 100 iOSLaunchScreeniPadSize: 100 iOSLaunchScreenCustomStoryboardPath: iOSLaunchScreeniPadCustomStoryboardPath: iOSDeviceRequirements: [] iOSURLSchemes: [] macOSURLSchemes: [] iOSBackgroundModes: 0 iOSMetalForceHardShadows: 0 metalEditorSupport: 1 metalAPIValidation: 1 metalCompileShaderBinary: 0 iOSRenderExtraFrameOnPause: 0 iosCopyPluginsCodeInsteadOfSymlink: 0 appleDeveloperTeamID: iOSManualSigningProvisioningProfileID: tvOSManualSigningProvisioningProfileID: VisionOSManualSigningProvisioningProfileID: iOSManualSigningProvisioningProfileType: 0 tvOSManualSigningProvisioningProfileType: 0 VisionOSManualSigningProvisioningProfileType: 0 appleEnableAutomaticSigning: 0 iOSRequireARKit: 0 iOSAutomaticallyDetectAndAddCapabilities: 1 appleEnableProMotion: 0 shaderPrecisionModel: 0 clonedFromGUID: 00000000000000000000000000000000 templatePackageId: templateDefaultScene: useCustomMainManifest: 0 useCustomLauncherManifest: 0 useCustomMainGradleTemplate: 0 useCustomLauncherGradleManifest: 0 useCustomBaseGradleTemplate: 0 useCustomGradlePropertiesTemplate: 0 useCustomGradleSettingsTemplate: 0 useCustomProguardFile: 0 AndroidTargetArchitectures: 2 AndroidAllowedArchitectures: -1 AndroidSplashScreenScale: 0 androidSplashScreen: {fileID: 0} AndroidKeystoreName: AndroidKeyaliasName: AndroidEnableArmv9SecurityFeatures: 0 AndroidEnableArm64MTE: 0 AndroidBuildApkPerCpuArchitecture: 0 AndroidTVCompatibility: 0 AndroidIsGame: 1 androidAppCategory: 3 useAndroidAppCategory: 1 androidAppCategoryOther: AndroidEnableTango: 0 androidEnableBanner: 1 androidUseLowAccuracyLocation: 0 androidUseCustomKeystore: 0 m_AndroidBanners: - width: 320 height: 180 banner: {fileID: 0} androidGamepadSupportLevel: 0 AndroidMinifyRelease: 0 AndroidMinifyDebug: 0 AndroidValidateAppBundleSize: 1 AndroidAppBundleSizeToValidate: 200 AndroidReportGooglePlayAppDependencies: 1 androidSymbolsSizeThreshold: 800 m_BuildTargetIcons: [] m_BuildTargetPlatformIcons: - m_BuildTarget: Android m_Icons: - m_Textures: [] m_Width: 432 m_Height: 432 m_Kind: 2 m_SubKind: - m_Textures: [] m_Width: 324 m_Height: 324 m_Kind: 2 m_SubKind: - m_Textures: [] m_Width: 216 m_Height: 216 m_Kind: 2 m_SubKind: - m_Textures: [] m_Width: 162 m_Height: 162 m_Kind: 2 m_SubKind: - m_Textures: [] m_Width: 108 m_Height: 108 m_Kind: 2 m_SubKind: - m_Textures: [] m_Width: 81 m_Height: 81 m_Kind: 2 m_SubKind: - m_Textures: [] m_Width: 192 m_Height: 192 m_Kind: 1 m_SubKind: - m_Textures: [] m_Width: 144 m_Height: 144 m_Kind: 1 m_SubKind: - m_Textures: [] m_Width: 96 m_Height: 96 m_Kind: 1 m_SubKind: - m_Textures: [] m_Width: 72 m_Height: 72 m_Kind: 1 m_SubKind: - m_Textures: [] m_Width: 48 m_Height: 48 m_Kind: 1 m_SubKind: - m_Textures: [] m_Width: 36 m_Height: 36 m_Kind: 1 m_SubKind: - m_Textures: [] m_Width: 192 m_Height: 192 m_Kind: 0 m_SubKind: - m_Textures: [] m_Width: 144 m_Height: 144 m_Kind: 0 m_SubKind: - m_Textures: [] m_Width: 96 m_Height: 96 m_Kind: 0 m_SubKind: - m_Textures: [] m_Width: 72 m_Height: 72 m_Kind: 0 m_SubKind: - m_Textures: [] m_Width: 48 m_Height: 48 m_Kind: 0 m_SubKind: - m_Textures: [] m_Width: 36 m_Height: 36 m_Kind: 0 m_SubKind: m_BuildTargetBatching: [] m_BuildTargetShaderSettings: [] m_BuildTargetGraphicsJobs: [] m_BuildTargetGraphicsJobMode: [] m_BuildTargetGraphicsAPIs: [] m_BuildTargetVRSettings: [] m_DefaultShaderChunkSizeInMB: 16 m_DefaultShaderChunkCount: 0 openGLRequireES31: 0 openGLRequireES31AEP: 0 openGLRequireES32: 0 m_TemplateCustomTags: {} mobileMTRendering: Android: 1 VisionOS: 1 iPhone: 1 tvOS: 1 m_BuildTargetGroupLightmapEncodingQuality: [] m_BuildTargetGroupHDRCubemapEncodingQuality: [] m_BuildTargetGroupLightmapSettings: [] m_BuildTargetGroupLoadStoreDebugModeSettings: [] m_BuildTargetNormalMapEncoding: [] m_BuildTargetDefaultTextureCompressionFormat: [] playModeTestRunnerEnabled: 0 runPlayModeTestAsEditModeTest: 0 actionOnDotNetUnhandledException: 1 editorGfxJobOverride: 1 enableInternalProfiler: 0 logObjCUncaughtExceptions: 1 enableCrashReportAPI: 0 cameraUsageDescription: locationUsageDescription: microphoneUsageDescription: bluetoothUsageDescription: macOSTargetOSVersion: 12.0 switchNMETAOverride: switchNetLibKey: switchSocketMemoryPoolSize: 6144 switchSocketAllocatorPoolSize: 128 switchSocketConcurrencyLimit: 14 switchScreenResolutionBehavior: 2 switchUseCPUProfiler: 0 switchEnableFileSystemTrace: 0 switchLTOSetting: 0 switchApplicationID: 0x01004b9000490000 switchNSODependencies: switchCompilerFlags: switchTitleNames_0: switchTitleNames_1: switchTitleNames_2: switchTitleNames_3: switchTitleNames_4: switchTitleNames_5: switchTitleNames_6: switchTitleNames_7: switchTitleNames_8: switchTitleNames_9: switchTitleNames_10: switchTitleNames_11: switchTitleNames_12: switchTitleNames_13: switchTitleNames_14: switchTitleNames_15: switchPublisherNames_0: switchPublisherNames_1: switchPublisherNames_2: switchPublisherNames_3: switchPublisherNames_4: switchPublisherNames_5: switchPublisherNames_6: switchPublisherNames_7: switchPublisherNames_8: switchPublisherNames_9: switchPublisherNames_10: switchPublisherNames_11: switchPublisherNames_12: switchPublisherNames_13: switchPublisherNames_14: switchPublisherNames_15: switchIcons_0: {fileID: 0} switchIcons_1: {fileID: 0} switchIcons_2: {fileID: 0} switchIcons_3: {fileID: 0} switchIcons_4: {fileID: 0} switchIcons_5: {fileID: 0} switchIcons_6: {fileID: 0} switchIcons_7: {fileID: 0} switchIcons_8: {fileID: 0} switchIcons_9: {fileID: 0} switchIcons_10: {fileID: 0} switchIcons_11: {fileID: 0} switchIcons_12: {fileID: 0} switchIcons_13: {fileID: 0} switchIcons_14: {fileID: 0} switchIcons_15: {fileID: 0} switchSmallIcons_0: {fileID: 0} switchSmallIcons_1: {fileID: 0} switchSmallIcons_2: {fileID: 0} switchSmallIcons_3: {fileID: 0} switchSmallIcons_4: {fileID: 0} switchSmallIcons_5: {fileID: 0} switchSmallIcons_6: {fileID: 0} switchSmallIcons_7: {fileID: 0} switchSmallIcons_8: {fileID: 0} switchSmallIcons_9: {fileID: 0} switchSmallIcons_10: {fileID: 0} switchSmallIcons_11: {fileID: 0} switchSmallIcons_12: {fileID: 0} switchSmallIcons_13: {fileID: 0} switchSmallIcons_14: {fileID: 0} switchSmallIcons_15: {fileID: 0} switchManualHTML: switchAccessibleURLs: switchLegalInformation: switchMainThreadStackSize: 1048576 switchPresenceGroupId: switchLogoHandling: 0 switchReleaseVersion: 0 switchDisplayVersion: 1.0.0 switchStartupUserAccount: 0 switchSupportedLanguagesMask: 0 switchLogoType: 0 switchApplicationErrorCodeCategory: switchUserAccountSaveDataSize: 0 switchUserAccountSaveDataJournalSize: 0 switchApplicationAttribute: 0 switchCardSpecSize: -1 switchCardSpecClock: -1 switchRatingsMask: 0 switchRatingsInt_0: 0 switchRatingsInt_1: 0 switchRatingsInt_2: 0 switchRatingsInt_3: 0 switchRatingsInt_4: 0 switchRatingsInt_5: 0 switchRatingsInt_6: 0 switchRatingsInt_7: 0 switchRatingsInt_8: 0 switchRatingsInt_9: 0 switchRatingsInt_10: 0 switchRatingsInt_11: 0 switchRatingsInt_12: 0 switchLocalCommunicationIds_0: switchLocalCommunicationIds_1: switchLocalCommunicationIds_2: switchLocalCommunicationIds_3: switchLocalCommunicationIds_4: switchLocalCommunicationIds_5: switchLocalCommunicationIds_6: switchLocalCommunicationIds_7: switchParentalControl: 0 switchAllowsScreenshot: 1 switchAllowsVideoCapturing: 1 switchAllowsRuntimeAddOnContentInstall: 0 switchDataLossConfirmation: 0 switchUserAccountLockEnabled: 0 switchSystemResourceMemory: 16777216 switchSupportedNpadStyles: 22 switchNativeFsCacheSize: 32 switchIsHoldTypeHorizontal: 1 switchSupportedNpadCount: 8 switchEnableTouchScreen: 1 switchSocketConfigEnabled: 0 switchTcpInitialSendBufferSize: 32 switchTcpInitialReceiveBufferSize: 64 switchTcpAutoSendBufferSizeMax: 256 switchTcpAutoReceiveBufferSizeMax: 256 switchUdpSendBufferSize: 9 switchUdpReceiveBufferSize: 42 switchSocketBufferEfficiency: 4 switchSocketInitializeEnabled: 1 switchNetworkInterfaceManagerInitializeEnabled: 1 switchDisableHTCSPlayerConnection: 0 switchUseNewStyleFilepaths: 1 switchUseLegacyFmodPriorities: 0 switchUseMicroSleepForYield: 1 switchEnableRamDiskSupport: 0 switchMicroSleepForYieldTime: 25 switchRamDiskSpaceSize: 12 switchUpgradedPlayerSettingsToNMETA: 0 ps4NPAgeRating: 12 ps4NPTitleSecret: ps4NPTrophyPackPath: ps4ParentalLevel: 11 ps4ContentID: ED1633-NPXX51362_00-0000000000000000 ps4Category: 0 ps4MasterVersion: 01.00 ps4AppVersion: 01.00 ps4AppType: 0 ps4ParamSfxPath: ps4VideoOutPixelFormat: 0 ps4VideoOutInitialWidth: 1920 ps4VideoOutBaseModeInitialWidth: 1920 ps4VideoOutReprojectionRate: 60 ps4PronunciationXMLPath: ps4PronunciationSIGPath: ps4BackgroundImagePath: ps4StartupImagePath: ps4StartupImagesFolder: ps4IconImagesFolder: ps4SaveDataImagePath: ps4SdkOverride: ps4BGMPath: ps4ShareFilePath: ps4ShareOverlayImagePath: ps4PrivacyGuardImagePath: ps4ExtraSceSysFile: ps4NPtitleDatPath: ps4RemotePlayKeyAssignment: -1 ps4RemotePlayKeyMappingDir: ps4PlayTogetherPlayerCount: 0 ps4EnterButtonAssignment: 2 ps4ApplicationParam1: 0 ps4ApplicationParam2: 0 ps4ApplicationParam3: 0 ps4ApplicationParam4: 0 ps4DownloadDataSize: 0 ps4GarlicHeapSize: 2048 ps4ProGarlicHeapSize: 2560 playerPrefsMaxSize: 32768 ps4Passcode: jx5SBNX6qZpkMcX5Y7A9NxEsBwNSYYCK ps4pnSessions: 1 ps4pnPresence: 1 ps4pnFriends: 1 ps4pnGameCustomData: 1 playerPrefsSupport: 0 enableApplicationExit: 0 resetTempFolder: 1 restrictedAudioUsageRights: 0 ps4UseResolutionFallback: 0 ps4ReprojectionSupport: 0 ps4UseAudio3dBackend: 0 ps4UseLowGarlicFragmentationMode: 1 ps4SocialScreenEnabled: 0 ps4ScriptOptimizationLevel: 2 ps4Audio3dVirtualSpeakerCount: 14 ps4attribCpuUsage: 0 ps4PatchPkgPath: ps4PatchLatestPkgPath: ps4PatchChangeinfoPath: ps4PatchDayOne: 0 ps4attribUserManagement: 0 ps4attribMoveSupport: 0 ps4attrib3DSupport: 0 ps4attribShareSupport: 0 ps4attribExclusiveVR: 0 ps4disableAutoHideSplash: 0 ps4videoRecordingFeaturesUsed: 0 ps4contentSearchFeaturesUsed: 0 ps4CompatibilityPS5: 0 ps4AllowPS5Detection: 0 ps4GPU800MHz: 1 ps4attribEyeToEyeDistanceSettingVR: 0 ps4IncludedModules: [] ps4attribVROutputEnabled: 0 monoEnv: splashScreenBackgroundSourceLandscape: {fileID: 0} splashScreenBackgroundSourcePortrait: {fileID: 0} blurSplashScreenBackground: 1 spritePackerPolicy: webGLMemorySize: 32 webGLExceptionSupport: 1 webGLNameFilesAsHashes: 0 webGLShowDiagnostics: 0 webGLDataCaching: 1 webGLDebugSymbols: 0 webGLEmscriptenArgs: webGLModulesDirectory: webGLTemplate: APPLICATION:Default webGLAnalyzeBuildSize: 0 webGLUseEmbeddedResources: 0 webGLCompressionFormat: 1 webGLWasmArithmeticExceptions: 0 webGLLinkerTarget: 1 webGLThreadsSupport: 0 webGLDecompressionFallback: 0 webGLInitialMemorySize: 32 webGLMaximumMemorySize: 2048 webGLMemoryGrowthMode: 2 webGLMemoryLinearGrowthStep: 16 webGLMemoryGeometricGrowthStep: 0.2 webGLMemoryGeometricGrowthCap: 96 webGLPowerPreference: 2 webGLWebAssemblyTable: 0 webGLWebAssemblyBigInt: 0 webGLCloseOnQuit: 0 webWasm2023: 0 webEnableSubmoduleStrippingCompatibility: 0 scriptingDefineSymbols: {} additionalCompilerArguments: {} platformArchitecture: {} scriptingBackend: {} il2cppCompilerConfiguration: {} il2cppCodeGeneration: {} il2cppStacktraceInformation: {} managedStrippingLevel: {} incrementalIl2cppBuild: {} suppressCommonWarnings: 0 allowUnsafeCode: 0 useDeterministicCompilation: 1 additionalIl2CppArgs: scriptingRuntimeVersion: 1 gcIncremental: 1 gcWBarrierValidation: 0 apiCompatibilityLevelPerPlatform: {} editorAssembliesCompatibilityLevel: 1 m_RenderingPath: 1 m_MobileRenderingPath: 1 metroPackageName: KlakHap metroPackageVersion: metroCertificatePath: metroCertificatePassword: metroCertificateSubject: metroCertificateIssuer: metroCertificateNotAfter: 0000000000000000 metroApplicationDescription: KlakHap wsaImages: {} metroTileShortName: metroTileShowName: 0 metroMediumTileShowName: 0 metroLargeTileShowName: 0 metroWideTileShowName: 0 metroSupportStreamingInstall: 0 metroLastRequiredScene: 0 metroDefaultTileSize: 1 metroTileForegroundText: 2 metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0} metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, a: 1} metroSplashScreenUseBackgroundColor: 0 syncCapabilities: 0 platformCapabilities: {} metroTargetDeviceFamilies: {} metroFTAName: metroFTAFileTypes: [] metroProtocolName: vcxProjDefaultLanguage: XboxOneProductId: XboxOneUpdateKey: XboxOneSandboxId: XboxOneContentId: XboxOneTitleId: XboxOneSCId: XboxOneGameOsOverridePath: XboxOnePackagingOverridePath: XboxOneAppManifestOverridePath: XboxOneVersion: 1.0.0.0 XboxOnePackageEncryption: 0 XboxOnePackageUpdateGranularity: 2 XboxOneDescription: XboxOneLanguage: - enus XboxOneCapability: [] XboxOneGameRating: {} XboxOneIsContentPackage: 0 XboxOneEnhancedXboxCompatibilityMode: 0 XboxOneEnableGPUVariability: 1 XboxOneSockets: {} XboxOneSplashScreen: {fileID: 0} XboxOneAllowedProductIds: [] XboxOnePersistentLocalStorageSize: 0 XboxOneXTitleMemory: 8 XboxOneOverrideIdentityName: XboxOneOverrideIdentityPublisher: vrEditorSettings: {} cloudServicesEnabled: {} luminIcon: m_Name: m_ModelFolderPath: m_PortalFolderPath: luminCert: m_CertPath: m_SignPackage: 1 luminIsChannelApp: 0 luminVersion: m_VersionCode: 1 m_VersionName: hmiPlayerDataPath: hmiForceSRGBBlit: 0 embeddedLinuxEnableGamepadInput: 0 hmiCpuConfiguration: hmiLogStartupTiming: 0 qnxGraphicConfPath: apiCompatibilityLevel: 6 captureStartupLogs: {} activeInputHandler: 1 windowsGamepadBackendHint: 0 cloudProjectId: framebufferDepthMemorylessMode: 0 qualitySettingsNames: [] projectName: organizationId: cloudEnabled: 0 legacyClampBlendShapeWeights: 0 hmiLoadingImage: {fileID: 0} platformRequiresReadableAssets: 0 virtualTexturingSupportEnabled: 0 insecureHttpOption: 0 androidVulkanDenyFilterList: [] androidVulkanAllowFilterList: [] androidVulkanDeviceFilterListAsset: {fileID: 0} d3d12DeviceFilterListAsset: {fileID: 0} allowedHttpConnections: 3 ================================================ FILE: ProjectSettings/ProjectVersion.txt ================================================ m_EditorVersion: 6000.3.6f1 m_EditorVersionWithRevision: 6000.3.6f1 (bbb010bdb8a3) ================================================ FILE: ProjectSettings/QualitySettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!47 &1 QualitySettings: m_ObjectHideFlags: 0 serializedVersion: 5 m_CurrentQuality: 0 m_QualitySettings: - serializedVersion: 5 name: High pixelLightCount: 2 shadows: 2 shadowResolution: 1 shadowProjection: 1 shadowCascades: 2 shadowDistance: 40 shadowNearPlaneOffset: 3 shadowCascade2Split: 0.33333334 shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} shadowmaskMode: 1 skinWeights: 2 globalTextureMipmapLimit: 0 textureMipmapLimitSettings: [] anisotropicTextures: 1 antiAliasing: 0 softParticles: 0 softVegetation: 1 realtimeReflectionProbes: 1 billboardsFaceCameraPosition: 1 useLegacyDetailDistribution: 0 adaptiveVsync: 0 vSyncCount: 1 realtimeGICPUUsage: 50 adaptiveVsyncExtraA: 0 adaptiveVsyncExtraB: 0 lodBias: 1 meshLodThreshold: 1 maximumLODLevel: 0 enableLODCrossFade: 1 streamingMipmapsActive: 0 streamingMipmapsAddAllCameras: 1 streamingMipmapsMemoryBudget: 512 streamingMipmapsRenderersPerFrame: 512 streamingMipmapsMaxLevelReduction: 2 streamingMipmapsMaxFileIORequests: 1024 particleRaycastBudget: 256 asyncUploadTimeSlice: 2 asyncUploadBufferSize: 16 asyncUploadPersistentBuffer: 1 resolutionScalingFixedDPIFactor: 1 customRenderPipeline: {fileID: 0} terrainQualityOverrides: 0 terrainPixelError: 1 terrainDetailDensityScale: 1 terrainBasemapDistance: 1000 terrainDetailDistance: 80 terrainTreeDistance: 5000 terrainBillboardStart: 50 terrainFadeLength: 5 terrainMaxTrees: 50 excludedTargetPlatforms: [] m_TextureMipmapLimitGroupNames: [] m_PerPlatformDefaultQuality: Android: 0 EmbeddedLinux: 0 GameCoreScarlett: 0 GameCoreXboxOne: 0 Kepler: 0 LinuxHeadlessSimulation: 0 Nintendo Switch: 0 Nintendo Switch 2: 0 PS4: 0 PS5: 0 QNX: 0 Server: 0 Standalone: 0 VisionOS: 0 WebGL: 0 Windows Store Apps: 0 XboxOne: 0 iPhone: 0 tvOS: 0 ================================================ FILE: ProjectSettings/SceneTemplateSettings.json ================================================ { "templatePinStates": [], "dependencyTypeInfos": [ { "userAdded": false, "type": "UnityEngine.AnimationClip", "defaultInstantiationMode": 0 }, { "userAdded": false, "type": "UnityEditor.Animations.AnimatorController", "defaultInstantiationMode": 0 }, { "userAdded": false, "type": "UnityEngine.AnimatorOverrideController", "defaultInstantiationMode": 0 }, { "userAdded": false, "type": "UnityEditor.Audio.AudioMixerController", "defaultInstantiationMode": 0 }, { "userAdded": false, "type": "UnityEngine.ComputeShader", "defaultInstantiationMode": 1 }, { "userAdded": false, "type": "UnityEngine.Cubemap", "defaultInstantiationMode": 0 }, { "userAdded": false, "type": "UnityEngine.GameObject", "defaultInstantiationMode": 0 }, { "userAdded": false, "type": "UnityEditor.LightingDataAsset", "defaultInstantiationMode": 0 }, { "userAdded": false, "type": "UnityEngine.LightingSettings", "defaultInstantiationMode": 0 }, { "userAdded": false, "type": "UnityEngine.Material", "defaultInstantiationMode": 0 }, { "userAdded": false, "type": "UnityEditor.MonoScript", "defaultInstantiationMode": 1 }, { "userAdded": false, "type": "UnityEngine.PhysicsMaterial", "defaultInstantiationMode": 0 }, { "userAdded": false, "type": "UnityEngine.PhysicsMaterial2D", "defaultInstantiationMode": 0 }, { "userAdded": false, "type": "UnityEngine.Rendering.PostProcessing.PostProcessProfile", "defaultInstantiationMode": 0 }, { "userAdded": false, "type": "UnityEngine.Rendering.PostProcessing.PostProcessResources", "defaultInstantiationMode": 0 }, { "userAdded": false, "type": "UnityEngine.Rendering.VolumeProfile", "defaultInstantiationMode": 0 }, { "userAdded": false, "type": "UnityEditor.SceneAsset", "defaultInstantiationMode": 1 }, { "userAdded": false, "type": "UnityEngine.Shader", "defaultInstantiationMode": 1 }, { "userAdded": false, "type": "UnityEngine.ShaderVariantCollection", "defaultInstantiationMode": 1 }, { "userAdded": false, "type": "UnityEngine.Texture", "defaultInstantiationMode": 0 }, { "userAdded": false, "type": "UnityEngine.Texture2D", "defaultInstantiationMode": 0 }, { "userAdded": false, "type": "UnityEngine.Timeline.TimelineAsset", "defaultInstantiationMode": 0 } ], "defaultDependencyTypeInfo": { "userAdded": false, "type": "", "defaultInstantiationMode": 1 }, "newSceneOverride": 0 } ================================================ FILE: ProjectSettings/ShaderGraphSettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!114 &1 MonoBehaviour: m_ObjectHideFlags: 53 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: de02f9e1d18f588468e474319d09a723, type: 3} m_Name: m_EditorClassIdentifier: Unity.ShaderGraph.Editor::UnityEditor.ShaderGraph.ShaderGraphProjectSettings shaderVariantLimit: 2048 overrideShaderVariantLimit: 0 customInterpolatorErrorThreshold: 32 customInterpolatorWarningThreshold: 16 customHeatmapValues: {fileID: 0} ================================================ FILE: ProjectSettings/TagManager.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!78 &1 TagManager: serializedVersion: 3 tags: [] layers: - Default - TransparentFX - Ignore Raycast - - Water - UI - - - - - - - - - - - - - - - - - - - - - - - - - - m_SortingLayers: - name: Default uniqueID: 0 locked: 0 m_RenderingLayers: - Default ================================================ FILE: ProjectSettings/TimeManager.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!5 &1 TimeManager: m_ObjectHideFlags: 0 serializedVersion: 2 Fixed Timestep: m_Count: 2822399 m_Rate: m_Denominator: 1 m_Numerator: 141120000 Maximum Allowed Timestep: 0.33333334 m_TimeScale: 1 Maximum Particle Timestep: 0.03 ================================================ FILE: ProjectSettings/URPProjectSettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!114 &1 MonoBehaviour: m_ObjectHideFlags: 61 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 0} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: 247994e1f5a72c2419c26a37e9334c01, type: 3} m_Name: m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.UniversalProjectSettings m_LastMaterialVersion: 10 m_ProjectSettingFolderPath: URPDefaultResources ================================================ FILE: ProjectSettings/UnityConnectSettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!310 &1 UnityConnectSettings: m_ObjectHideFlags: 0 serializedVersion: 1 m_Enabled: 0 m_TestMode: 0 m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events m_EventUrl: https://cdp.cloud.unity3d.com/v1/events m_ConfigUrl: https://config.uca.cloud.unity3d.com m_DashboardUrl: https://dashboard.unity3d.com m_TestInitMode: 0 InsightsSettings: m_EngineDiagnosticsEnabled: 0 m_Enabled: 0 CrashReportingSettings: serializedVersion: 2 m_EventUrl: https://perf-events.cloud.unity3d.com m_EnableCloudDiagnosticsReporting: 0 m_LogBufferSize: 10 m_CaptureEditorExceptions: 1 UnityPurchasingSettings: m_Enabled: 0 m_TestMode: 0 UnityAnalyticsSettings: m_Enabled: 0 m_TestMode: 0 m_InitializeOnStartup: 1 m_PackageRequiringCoreStatsPresent: 0 UnityAdsSettings: m_Enabled: 0 m_InitializeOnStartup: 1 m_TestMode: 0 m_IosGameId: m_AndroidGameId: m_GameIds: {} m_GameId: PerformanceReportingSettings: m_Enabled: 0 ================================================ FILE: ProjectSettings/VFXManager.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!937362698 &1 VFXManager: m_ObjectHideFlags: 0 m_IndirectShader: {fileID: 0} m_CopyBufferShader: {fileID: 0} m_PrefixSumShader: {fileID: 0} m_SortShader: {fileID: 0} m_StripUpdateShader: {fileID: 0} m_EmptyShader: {fileID: 0} m_RenderPipeSettingsPath: m_FixedTimeStep: 0.016666668 m_MaxDeltaTime: 0.05 m_MaxScrubTime: 30 m_MaxCapacity: 100000000 m_CompiledVersion: 0 m_RuntimeVersion: 0 m_RuntimeResources: {fileID: 0} m_BatchEmptyLifetime: 300 ================================================ FILE: ProjectSettings/VersionControlSettings.asset ================================================ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!890905787 &1 VersionControlSettings: m_ObjectHideFlags: 0 m_Mode: Visible Meta Files m_TrackPackagesOutsideProject: 0 ================================================ FILE: README.md ================================================ # KlakHap ![GIF](https://i.imgur.com/exuJAIA.gif) **KlakHap** is a Unity plugin for playing back video streams encoded with the [HAP video codecs]. HAP is a fast and high-quality video codec often used in real-time interactive applications. From the HAP Codecs website: > The HAP codecs are designed to fit the needs of a variety of real-time video > workflows where ultra high resolution video is needed such as live event > production, set design, 360 video for gaming, projection mapping and creative > coding. KlakHap provides decoded frames as textures that you can use anywhere in Unity's rendering pipeline: apply them to a material, present full-screen video, animate a UI element, and more. Thanks to the efficient design and implementation of the HAP codecs, it can adjust playback time and speed dynamically without hiccups. [HAP video codecs]: https://hap.video/ # System requirements - Unity 2022.3 or later Currently, KlakHap supports only 64-bit desktop platforms (Windows, macOS, and Linux). # Supported formats KlakHap supports **HAP**, **HAP Alpha**, and **HAP Q**. **HAP Q Alpha** is not supported. KlakHap only supports the QuickTime File Format as a container, i.e., `.mov` files. # How to install Install the KlakHap package (`jp.keijiro.klak.hap`) from the "Keijiro" scoped registry in Package Manager. Follow [these instructions] to add the registry to your project. [these instructions]: https://gist.github.com/keijiro/f8c7e8ff29bfe63d86b888901b82644c # How to specify a video file There are two ways to specify a video file in the plugin: - **Streaming Assets Mode**: Put a video file in the [Streaming Assets] directory and specify its file name. - **Local File System Mode**: Put a video file somewhere in local drive and specify its full path. The former method is recommended when the video file ships with the application. The latter method is useful when you need to play external content. [Streaming Assets]: https://docs.unity3d.com/Manual/StreamingAssets.html # Hap Player component ![Inspector](https://i.imgur.com/pIACL4W.png) **File Path** and **Path Mode** specify the source video file. See the previous section for details. **Time**, **Speed** and **Loop** are used to set the initial playback state. You can also change these values during playback. **Target Texture** stores decoded frames in a render texture. Note that this allocates a small amount of GPU time for data transfer. **Target Renderer** applies the decoded texture to a specific material property. Although this is the most performant way to render video frames, it requires a few extra steps to render correctly. Keep the following points in mind: - UV coordinate incompatibility: Decoded textures are upside down due to differences in UV coordinate conventions between Unity and HAP. You can fix this using a vertically inverted texture scale/offset. You can also use the `Klak/Hap` shader for this purpose. - Color space conversion for HAP Q: [YCoCg conversion] must be added to a shader when using HAP Q. You can also use the `Klak/HAP Q` shader for this purpose. [YCoCg conversion]: https://gist.github.com/dlublin/90f879cfe027ebf5792bdadf2c911bb5 # How to control playback `HapPlayer` provides only a few properties and methods for controlling playback. This is an intentional design choice; I avoid ambiguous methods like `Play`, `Stop`, and `Pause`. Use the basic properties and methods instead. - To jump to a specific point: Assign a time in seconds to `time`. - To jump to a specific frame: Calculate the time in seconds using `frameCount` and `streamDuration`, then assign it to `time`. - To reverse the playback direction: Assign a negative value to `speed`. - To pause: Assign `0` to `speed`. - To resume: Assign `1` to `speed`. - To stop: Assign `false` to `enabled`. - To close the video file: Destroy the `HapPlayer` component. - To open another video file: Call `AddComponent`, then call `Open`. # Timeline support ![GIF](https://i.imgur.com/efrvvye.gif) The HAP Player component implements the [ITimeControl] interface, which allows it to control playback time from a Control Track in a [Timeline]. You can create a control track by dragging and dropping a HAP Player game object into the Timeline Editor, or manually create a Control Track/Clip and set the source game object. [ITimeControl]: https://docs.unity3d.com/ScriptReference/Timeline.ITimeControl.html [Timeline]: https://docs.unity3d.com/Manual/TimelineSection.html # Platform differences (internal latency) On Windows, KlakHap uses the [Custom Texture Update] feature to hide the synchronization point in the background thread. It guarantees exact-frame playback with minimal load on the main thread. On macOS and Linux, the Custom Texture Update feature is unavailable for this purpose[^1]. Instead, KlakHap delays synchronization to the next frame to avoid main thread stalls. In other words, it guarantees exact-frame playback but adds a single-frame latency. You can turn off this behavior by adding `HAP_NO_DELAY` to the [Scripting Define Symbols] in the project settings. This stalls the main thread for every frame decoding. It significantly slows down the application but is useful when exact frame matching is essential (e.g., [volumetric video playback] with Alembic animation). [Custom Texture Update]: https://github.com/keijiro/TextureUpdateExample [Scripting Define Symbols]: https://docs.unity3d.com/Manual/CustomScriptingSymbols.html [volumetric video playback]: https://github.com/keijiro/Abcvfx [^1]: The Custom Texture Update feature is available even on macOS/Linux but doesn't support compressed texture formats, which are essential for HAP decoding.