// Copyright (c) 2022 Vuplex Inc. All rights reserved.
//
// Licensed under the Vuplex Commercial Software Library License, you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// https://vuplex.com/commercial-library-license
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#if UNITY_ANDROID && !UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using UnityEngine.Rendering;
namespace Vuplex.WebView.Internal {
///
/// Static utility methods used internally by 3D WebView on Android.
///
public static class VXAndroidUtils {
public static Material CreateAndroidMaterial() {
if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Vulkan) {
return VXUtils.CreateDefaultMaterial();
}
var singlePassStereoRenderingEnabled = false;
// For some headsets like HTC Vive Focus, XRSettings.eyeTextureDesc.vrUsage returns a value
// other than VRTextureUsage.TwoEyes, so the VUPLEX_FORCE_SINGLE_PASS scripting symbol can be
// used to force single pass in that scenario.
#if VUPLEX_FORCE_SINGLE_PASS
singlePassStereoRenderingEnabled = VXUtils.XRSettings.enabled;
#elif UNITY_2017_2_OR_NEWER
singlePassStereoRenderingEnabled = VXUtils.XRSettings.enabled && VXUtils.XRSettings.eyeTextureDesc.vrUsage == VRTextureUsage.TwoEyes;
#endif
var materialName = singlePassStereoRenderingEnabled ? "AndroidSinglePassViewportMaterial"
: "AndroidViewportMaterial";
// Construct a new material because Resources.Load() returns a singleton.
return new Material(Resources.Load(materialName));
}
public static byte[] ConvertFromJavaByteArray(AndroidJavaObject arrayObject) {
// Unity 2019.1 and newer logs a warning that converting from byte[] is obsolete
// but older versions are incapable of converting from sbyte[].
#if UNITY_2019_1_OR_NEWER
return (byte[])(Array)AndroidJNIHelper.ConvertFromJNIArray(arrayObject.GetRawObject());
#else
return AndroidJNIHelper.ConvertFromJNIArray(arrayObject.GetRawObject());
#endif
}
public static void ThrowVulkanExtensionException() {
throw new InvalidOperationException("The Vulkan Graphics API is in use, but this device does not support the VK_ANDROID_external_memory_android_hardware_buffer Vulkan API required by 3D WebView. Please switch to the OpenGLES Graphics API in Player Settings. For more info, see this page: https://support.vuplex.com/articles/vulkan");
}
public static AndroidJavaObject ToJavaMap(Dictionary dictionary) {
AndroidJavaObject map = new AndroidJavaObject("java.util.HashMap");
IntPtr putMethod = AndroidJNIHelper.GetMethodID(map.GetRawClass(), "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
foreach (var entry in dictionary) {
AndroidJNI.CallObjectMethod(
map.GetRawObject(),
putMethod,
AndroidJNIHelper.CreateJNIArgArray(new object[] { entry.Key, entry.Value })
);
}
return map;
}
public static AndroidJavaObject ToJavaObject(IntPtr jobject) {
if (jobject == IntPtr.Zero) {
return null;
}
return (AndroidJavaObject)_androidJavaObjectIntPtrConstructor.Invoke(new object[] { jobject });
}
// Get a reference to AndroidJavaObject's hidden constructor that takes
// the IntPtr for a jobject as a parameter.
readonly static ConstructorInfo _androidJavaObjectIntPtrConstructor = typeof(AndroidJavaObject).GetConstructor(
BindingFlags.Instance | BindingFlags.NonPublic,
null,
new []{ typeof(IntPtr) },
null
);
}
}
#endif