diff --git a/CMakeLists.txt b/CMakeLists.txt index 09e8aaf1ac..352038ad7f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -400,6 +400,13 @@ else() option(FILAMENT_ENABLE_MATDBG "Enable the material debugger" OFF) endif() +# Only optimize materials in Release mode (so error message lines match the source code) +if (CMAKE_BUILD_TYPE MATCHES Release) + option(FILAMENT_DISABLE_MATOPT "Disable material optimizations" OFF) +else() + option(FILAMENT_DISABLE_MATOPT "Disable material optimizations" ON) +endif() + # ================================================================================================== # Material compilation flags # ================================================================================================== @@ -423,8 +430,7 @@ if (FILAMENT_SUPPORTS_METAL) set(MATC_API_FLAGS ${MATC_API_FLAGS} -a metal) endif() -# Only optimize materials in Release mode (so error message lines match the source code) -if (NOT CMAKE_BUILD_TYPE MATCHES Release) +if (FILAMENT_DISABLE_MATOPT) set(MATC_OPT_FLAGS -g) endif() diff --git a/README.md b/README.md index 6985714aa9..7acd6e06c0 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ repositories { } dependencies { - implementation 'com.google.android.filament:filament-android:1.9.19' + implementation 'com.google.android.filament:filament-android:1.9.20' } ``` @@ -63,7 +63,7 @@ A much smaller alternative to `filamat-android` that can only generate OpenGL sh iOS projects can use CocoaPods to install the latest release: ``` -pod 'Filament', '~> 1.9.19' +pod 'Filament', '~> 1.9.20' ``` ### Snapshots diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index b8c3a73bd9..60ed56fc22 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -5,6 +5,20 @@ A new header is inserted each time a *tag* is created. ## Next release (main branch) +## v1.9.20 + +Android: Fix VSM. +engine: Introduce BufferObject API. +engine: Add new isTextureSwizzleSupported API on Texture. +engine: Add support to Metal and Vulkan backends for texture swizzling. +engine: Add new DoF settings (native/half res, gather kernel ring counts, CoC radius clamp). +engine: DoF quality and performance improvements. +engine: Fix high-quality upsampling issue with SSAO. +Java: Expose `TransformManager.getParent(int)`. +samples: Add Metal and Vulkan backend support to Suzanne sample. +WebGL: expose fitIntoUnitCube to JS. +WebGL: support for multiple elements. + ## v1.9.19 - engine: Fix Metal bug when setGeometryAt is called multiple times. diff --git a/android/filament-android/CMakeLists.txt b/android/filament-android/CMakeLists.txt index 3a38131b5a..8c89028b85 100644 --- a/android/filament-android/CMakeLists.txt +++ b/android/filament-android/CMakeLists.txt @@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.6) option(FILAMENT_SUPPORTS_VULKAN "Enables Vulkan on Android" OFF) option(FILAMENT_ENABLE_MATDBG "Enables Material debugger" OFF) +option(FILAMENT_DISABLE_MATOPT "Disables material optimizations" OFF) set(FILAMENT_DIR ${FILAMENT_DIST_DIR}) @@ -55,6 +56,7 @@ set(VERSION_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/libfilament-jni.map") set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} -Wl,--version-script=${VERSION_SCRIPT}") add_library(filament-jni SHARED + src/main/cpp/BufferObject.cpp src/main/cpp/Camera.cpp src/main/cpp/Colors.cpp src/main/cpp/ColorGrading.cpp diff --git a/android/filament-android/src/main/cpp/BufferObject.cpp b/android/filament-android/src/main/cpp/BufferObject.cpp new file mode 100644 index 0000000000..e86d81bd5c --- /dev/null +++ b/android/filament-android/src/main/cpp/BufferObject.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +#include + +#include +#include +#include + +#include + +#include + +#include "common/CallbackUtils.h" +#include "common/NioUtils.h" + +using namespace filament; +using namespace backend; + +extern "C" JNIEXPORT jlong JNICALL +Java_com_google_android_filament_BufferObject_nCreateBuilder(JNIEnv *env, jclass type) { + return (jlong) new BufferObject::Builder(); +} + +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_BufferObject_nDestroyBuilder(JNIEnv *env, jclass type, + jlong nativeBuilder) { + BufferObject::Builder* builder = (BufferObject::Builder *) nativeBuilder; + delete builder; +} + +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_BufferObject_nBuilderSize(JNIEnv *env, jclass type, + jlong nativeBuilder, jint byteCount) { + BufferObject::Builder* builder = (BufferObject::Builder *) nativeBuilder; + builder->size((uint32_t) byteCount); +} + +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_BufferObject_nBuilderBindingType(JNIEnv *env, jclass type, + jlong nativeBuilder, jint bindingType) { + using BindingType = BufferObject::BindingType; + BufferObject::Builder* builder = (BufferObject::Builder *) nativeBuilder; + BindingType types[] = {BindingType::VERTEX}; + builder->bindingType(types[bindingType]); +} + +extern "C" JNIEXPORT jlong JNICALL +Java_com_google_android_filament_BufferObject_nBuilderBuild(JNIEnv *env, jclass type, + jlong nativeBuilder, jlong nativeEngine) { + BufferObject::Builder* builder = (BufferObject::Builder *) nativeBuilder; + Engine *engine = (Engine *) nativeEngine; + return (jlong) builder->build(*engine); +} + +extern "C" JNIEXPORT jint JNICALL +Java_com_google_android_filament_BufferObject_nGetByteCount(JNIEnv *env, jclass type, + jlong nativeBufferObject) { + BufferObject *bufferObject = (BufferObject *) nativeBufferObject; + return (jint) bufferObject->getByteCount(); +} + +extern "C" JNIEXPORT int JNICALL +Java_com_google_android_filament_BufferObject_nSetBuffer(JNIEnv *env, jclass type, + jlong nativeBufferObject, jlong nativeEngine, jobject buffer, int remaining, + jint destOffsetInBytes, jint count, + jobject handler, jobject runnable) { + BufferObject *bufferObject = (BufferObject *) nativeBufferObject; + Engine *engine = (Engine *) nativeEngine; + + AutoBuffer nioBuffer(env, buffer, count); + void* data = nioBuffer.getData(); + size_t sizeInBytes = nioBuffer.getSize(); + if (sizeInBytes > (remaining << nioBuffer.getShift())) { + // BufferOverflowException + return -1; + } + + auto* callback = JniBufferCallback::make(engine, env, handler, runnable, std::move(nioBuffer)); + + BufferDescriptor desc(data, sizeInBytes, &JniBufferCallback::invoke, callback); + + bufferObject->setBuffer(*engine, std::move(desc), (uint32_t) destOffsetInBytes); + + return 0; +} diff --git a/android/filament-android/src/main/cpp/Texture.cpp b/android/filament-android/src/main/cpp/Texture.cpp index 519c4b9d4d..ba91618afb 100644 --- a/android/filament-android/src/main/cpp/Texture.cpp +++ b/android/filament-android/src/main/cpp/Texture.cpp @@ -51,6 +51,13 @@ Java_com_google_android_filament_Texture_nIsTextureFormatSupported(JNIEnv*, jcla (Texture::InternalFormat) internalFormat); } +extern "C" JNIEXPORT jboolean JNICALL +Java_com_google_android_filament_Texture_nIsTextureSwizzleSupported(JNIEnv*, jclass, + jlong nativeEngine) { + Engine *engine = (Engine *) nativeEngine; + return (jboolean) Texture::isTextureSwizzleSupported(*engine); +} + // Texture::Builder... extern "C" JNIEXPORT jlong JNICALL diff --git a/android/filament-android/src/main/cpp/TransformManager.cpp b/android/filament-android/src/main/cpp/TransformManager.cpp index b329776289..c4b2d3895d 100644 --- a/android/filament-android/src/main/cpp/TransformManager.cpp +++ b/android/filament-android/src/main/cpp/TransformManager.cpp @@ -86,6 +86,13 @@ Java_com_google_android_filament_TransformManager_nSetParent(JNIEnv*, jclass, (TransformManager::Instance) newParent); } +extern "C" JNIEXPORT jint JNICALL +Java_com_google_android_filament_TransformManager_nGetParent(JNIEnv*, jclass, + jlong nativeTransformManager, jint i) { + TransformManager* tm = (TransformManager*) nativeTransformManager; + return tm->getParent((TransformManager::Instance) i).getId(); +} + extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_TransformManager_nSetTransform(JNIEnv* env, jclass, jlong nativeTransformManager, jint i, diff --git a/android/filament-android/src/main/cpp/VertexBuffer.cpp b/android/filament-android/src/main/cpp/VertexBuffer.cpp index 60d613ed75..ed9460e17a 100644 --- a/android/filament-android/src/main/cpp/VertexBuffer.cpp +++ b/android/filament-android/src/main/cpp/VertexBuffer.cpp @@ -50,6 +50,13 @@ Java_com_google_android_filament_VertexBuffer_nBuilderVertexCount(JNIEnv *env, j builder->vertexCount((uint32_t) vertexCount); } +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_VertexBuffer_nBuilderEnableBufferObjects(JNIEnv *env, jclass type, + jlong nativeBuilder, jboolean enabled) { + VertexBuffer::Builder* builder = (VertexBuffer::Builder *) nativeBuilder; + builder->enableBufferObjects(enabled); +} + extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_VertexBuffer_nBuilderBufferCount(JNIEnv *env, jclass type, jlong nativeBuilder, jint bufferCount) { @@ -115,6 +122,15 @@ Java_com_google_android_filament_VertexBuffer_nSetBufferAt(JNIEnv *env, jclass t return 0; } +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_VertexBuffer_nSetBufferObjectAt(JNIEnv *env, jclass type, + jlong nativeVertexBuffer, jlong nativeEngine, jint bufferIndex, jlong nativeBufferObject) { + VertexBuffer *vertexBuffer = (VertexBuffer *) nativeVertexBuffer; + Engine *engine = (Engine *) nativeEngine; + BufferObject *bufferObject = (BufferObject *) nativeBufferObject; + vertexBuffer->setBufferObjectAt(*engine, (uint8_t) bufferIndex, bufferObject); +} + extern "C" [[deprecated]] JNIEXPORT void JNICALL Java_com_google_android_filament_VertexBuffer_nPopulateTangentQuaternions(JNIEnv *env, jclass type, jint quatType, jint quatCount, jobject outBuffer, jint outRemaining, diff --git a/android/filament-android/src/main/cpp/View.cpp b/android/filament-android/src/main/cpp/View.cpp index 541adb0eed..bdad7b0fae 100644 --- a/android/filament-android/src/main/cpp/View.cpp +++ b/android/filament-android/src/main/cpp/View.cpp @@ -300,7 +300,9 @@ Java_com_google_android_filament_View_nSetBlendMode(JNIEnv *, jclass , jlong nat extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_View_nSetDepthOfFieldOptions(JNIEnv *, jclass , - jlong nativeView, jfloat focusDistance, jfloat cocScale, jfloat maxApertureDiameter, jboolean enabled, jint filter) { + jlong nativeView, jfloat focusDistance, jfloat cocScale, jfloat maxApertureDiameter, jboolean enabled, jint filter, + jboolean nativeResolution, jint foregroundRingCount, jint backgroundRingCount, jint fastGatherRingCount, + jint maxForegroundCOC, jint maxBackgroundCOC) { View* view = (View*) nativeView; View::DepthOfFieldOptions::Filter eFilter{}; if (filter == 1) { @@ -308,7 +310,14 @@ Java_com_google_android_filament_View_nSetDepthOfFieldOptions(JNIEnv *, jclass , eFilter = View::DepthOfFieldOptions::Filter::MEDIAN; } view->setDepthOfFieldOptions({.focusDistance = focusDistance, .cocScale = cocScale, - .maxApertureDiameter = maxApertureDiameter, .enabled = (bool)enabled, .filter = eFilter}); + .maxApertureDiameter = maxApertureDiameter, .enabled = (bool)enabled, .filter = eFilter, + .nativeResolution = (bool)nativeResolution, + .foregroundRingCount = (uint8_t)foregroundRingCount, + .backgroundRingCount = (uint8_t)backgroundRingCount, + .fastGatherRingCount = (uint8_t)fastGatherRingCount, + .maxForegroundCOC = (uint8_t)maxForegroundCOC, + .maxBackgroundCOC = (uint8_t)maxBackgroundCOC, + }); } extern "C" diff --git a/android/filament-android/src/main/java/com/google/android/filament/BufferObject.java b/android/filament-android/src/main/java/com/google/android/filament/BufferObject.java new file mode 100644 index 0000000000..7fc63e3652 --- /dev/null +++ b/android/filament-android/src/main/java/com/google/android/filament/BufferObject.java @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +package com.google.android.filament; + +import androidx.annotation.IntRange; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.nio.Buffer; +import java.nio.BufferOverflowException; + +/** + * A generic GPU buffer containing data. + * + * Usage of this BufferObject is optional. For simple use cases it is not necessary. It is useful + * only when you need to share data between multiple VertexBuffer instances. It also allows you to + * efficiently swap-out the buffers in VertexBuffer. + * + * NOTE: For now this is only used for vertex data, but in the future we may use it for other things + * (e.g. compute). + * + * @see VertexBuffer + */ +public class BufferObject { + private long mNativeObject; + + private BufferObject(long nativeBufferObject) { + mNativeObject = nativeBufferObject; + } + + public static class Builder { + @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) + // Keep to finalize native resources + private final BuilderFinalizer mFinalizer; + private final long mNativeBuilder; + + public enum BindingType { + VERTEX, + } + + public Builder() { + mNativeBuilder = nCreateBuilder(); + mFinalizer = new BuilderFinalizer(mNativeBuilder); + } + + /** + * Size of the buffer in bytes. + * + * @param byteCount Maximum number of bytes the BufferObject can hold. + * @return A reference to this Builder for chaining calls. + */ + @NonNull + public Builder size(@IntRange(from = 1) int byteCount) { + nBuilderSize(mNativeBuilder, byteCount); + return this; + } + + /** + * The binding type for this buffer object. (defaults to VERTEX) + * + * @param BindingType Distinguishes between SSBO, VBO, etc. For now this must be VERTEX. + * @return A reference to this Builder for chaining calls. + */ + @NonNull + public Builder bindingType(@NonNull BindingType bindingType) { + nBuilderBindingType(mNativeBuilder, bindingType.ordinal()); + return this; + } + + /** + * Creates and returns the BufferObject object. After creation, the buffer + * is uninitialized. Use {@link #setBuffer} to initialize the BufferObject. + * + * @param engine reference to the {@link Engine} to associate this BufferObject + * with + * + * @return the newly created BufferObject object + * + * @exception IllegalStateException if the BufferObject could not be created + * + * @see #setBuffer + */ + @NonNull + public BufferObject build(@NonNull Engine engine) { + long nativeBufferObject = nBuilderBuild(mNativeBuilder, engine.getNativeObject()); + if (nativeBufferObject == 0) + throw new IllegalStateException("Couldn't create BufferObject"); + return new BufferObject(nativeBufferObject); + } + + private static class BuilderFinalizer { + private final long mNativeObject; + + BuilderFinalizer(long nativeObject) { + mNativeObject = nativeObject; + } + + @Override + public void finalize() { + try { + super.finalize(); + } catch (Throwable t) { // Ignore + } finally { + nDestroyBuilder(mNativeObject); + } + } + } + } + + /** + * Returns the size of this BufferObject in elements. + * + * @return the number of bytes the BufferObject holds + */ + @IntRange(from = 0) + public int getByteCount() { + return nGetByteCount(getNativeObject()); + } + + /** + * Asynchronously copy-initializes this BufferObject from the data provided. + * + * @param engine reference to the {@link Engine} to associate this + * BufferObject with + * @param buffer a CPU-side {@link Buffer} with the data used to initialize the + * BufferObject. + */ + public void setBuffer(@NonNull Engine engine, @NonNull Buffer buffer) { + setBuffer(engine, buffer, 0, 0, null, null); + } + + /** + * Asynchronously copy-initializes a region of this BufferObject from the data + * provided. + * + * @param engine reference to the {@link Engine} to associate this + * BufferObject with + * @param buffer a CPU-side {@link Buffer} with the data used to initialize the + * BufferObject. + * @param destOffsetInBytes offset in bytes into the BufferObject + * @param count number of bytes to consume, defaults to + * buffer.remaining() + */ + public void setBuffer(@NonNull Engine engine, @NonNull Buffer buffer, + @IntRange(from = 0) int destOffsetInBytes, @IntRange(from = 0) int count) { + setBuffer(engine, buffer, destOffsetInBytes, count, null, null); + } + + /** + * Asynchronously copy-initializes a region of this BufferObject from the data + * provided. + * + * @param engine reference to the {@link Engine} to associate this + * BufferObject with + * @param buffer a CPU-side {@link Buffer} with the data used to initialize the + * BufferObject. + * @param destOffsetInBytes offset in bytes into the BufferObject + * @param count number of bytes to consume, defaults to + * buffer.remaining() + * @param handler an {@link java.util.concurrent.Executor Executor}. On Android this + * can also be a {@link android.os.Handler Handler}. + * @param callback a callback executed by handler when buffer + * is no longer needed. + */ + public void setBuffer(@NonNull Engine engine, @NonNull Buffer buffer, + @IntRange(from = 0) int destOffsetInBytes, @IntRange(from = 0) int count, + @Nullable Object handler, @Nullable Runnable callback) { + int result = nSetBuffer(getNativeObject(), engine.getNativeObject(), buffer, buffer.remaining(), + destOffsetInBytes, count == 0 ? buffer.remaining() : count, handler, callback); + if (result < 0) { + throw new BufferOverflowException(); + } + } + + public long getNativeObject() { + if (mNativeObject == 0) { + throw new IllegalStateException("Calling method on destroyed BufferObject"); + } + return mNativeObject; + } + + void clearNativeObject() { + mNativeObject = 0; + } + + private static native long nCreateBuilder(); + private static native void nDestroyBuilder(long nativeBuilder); + private static native void nBuilderSize(long nativeBuilder, int byteCount); + private static native void nBuilderBindingType(long nativeBuilder, int bindingType); + private static native long nBuilderBuild(long nativeBuilder, long nativeEngine); + + private static native int nGetByteCount(long nativeBufferObject); + private static native int nSetBuffer(long nativeBufferObject, long nativeEngine, + Buffer buffer, int remaining, + int destOffsetInBytes, int count, Object handler, Runnable callback); +} diff --git a/android/filament-android/src/main/java/com/google/android/filament/Texture.java b/android/filament-android/src/main/java/com/google/android/filament/Texture.java index 60ab2905c1..a971fc142d 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/Texture.java +++ b/android/filament-android/src/main/java/com/google/android/filament/Texture.java @@ -562,6 +562,17 @@ public class Texture { return nIsTextureFormatSupported(engine.getNativeObject(), format.ordinal()); } + /** + * Checks whether texture swizzling is supported in this {@link Engine}. + * This depends on the selected backend. + * + * @param engine {@link Engine} + * @return true if texture swizzling. + */ + public static boolean isTextureSwizzleSupported(@NonNull Engine engine) { + return nIsTextureSwizzleSupported(engine.getNativeObject()); + } + /** * Use Builder to construct a Texture object instance. */ @@ -1137,6 +1148,7 @@ public class Texture { } private static native boolean nIsTextureFormatSupported(long nativeEngine, int internalFormat); + private static native boolean nIsTextureSwizzleSupported(long nativeEngine); private static native long nCreateBuilder(); private static native void nDestroyBuilder(long nativeBuilder); diff --git a/android/filament-android/src/main/java/com/google/android/filament/TransformManager.java b/android/filament-android/src/main/java/com/google/android/filament/TransformManager.java index 3611ee6c1e..e67f882e80 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/TransformManager.java +++ b/android/filament-android/src/main/java/com/google/android/filament/TransformManager.java @@ -140,6 +140,19 @@ import androidx.annotation.Size; nSetParent(mNativeObject, i, newParent); } + /** + * Returns the actual parent entity of an {@link EntityInstance} originally defined + * by {@link #setParent(int, int)}. + * + * @param i the {@link EntityInstance} of the transform component to get the parent from. + * @return the parent {@link Entity}. + * @see #getInstance + */ + @Entity + public int getParent(@EntityInstance int i) { + return nGetParent(mNativeObject, i); + } + /** * Sets a local transform of a transform component. *

This operation can be slow if the hierarchy of transform is too deep, and this @@ -241,6 +254,7 @@ import androidx.annotation.Size; private static native int nCreateArray(long mNativeObject, int entity, int parent, float[] localTransform); private static native void nDestroy(long nativeTransformManager, int entity); private static native void nSetParent(long nativeTransformManager, int i, int newParent); + private static native int nGetParent(long nativeTransformManager, int i); private static native void nSetTransform(long nativeTransformManager, int i, float[] localTransform); private static native void nGetTransform(long nativeTransformManager, int i, float[] outLocalTransform); private static native void nGetWorldTransform(long nativeTransformManager, int i, float[] outWorldTransform); diff --git a/android/filament-android/src/main/java/com/google/android/filament/VertexBuffer.java b/android/filament-android/src/main/java/com/google/android/filament/VertexBuffer.java index 6d56e12cba..0ff6f52c2e 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/VertexBuffer.java +++ b/android/filament-android/src/main/java/com/google/android/filament/VertexBuffer.java @@ -170,6 +170,21 @@ public class VertexBuffer { return this; } + /** + * Allows buffers to be swapped out and shared using BufferObject. + * + * If buffer objects mode is enabled, clients must call setBufferObjectAt rather than + * setBufferAt. This allows sharing of data between VertexBuffer objects, but it may + * slightly increase the memory footprint of Filament's internal bookkeeping. + * + * @param enabled If true, enables buffer object mode. False by default. + */ + @NonNull + public Builder enableBufferObjects(boolean enabled) { + nBuilderEnableBufferObjects(mNativeBuilder, enabled); + return this; + } + /** * Defines how many buffers will be created in this vertex buffer set. These buffers are * later referenced by index from 0 to bufferCount - 1. @@ -397,6 +412,23 @@ public class VertexBuffer { throw new BufferOverflowException(); } } + + /** + * Swaps in the given buffer object. + * + * To use this, you must first call enableBufferObjects() on the Builder. + * + * @param engine Reference to the filament::Engine to associate this VertexBuffer with. + * @param bufferIndex Index of the buffer to initialize. Must be between 0 + * and Builder::bufferCount() - 1. + * @param bufferObject The handle to the GPU data that will be used in this buffer slot. + */ + public void setBufferObjectAt(@NonNull Engine engine, int bufferIndex, + @NonNull BufferObject bufferObject) { + nSetBufferObjectAt(getNativeObject(), engine.getNativeObject(), bufferIndex, + bufferObject.getNativeObject()); + } + /** * Convenience function that consumes normal vectors (and, optionally, tangent vectors) and * produces quaternions that can be passed into a TANGENTS buffer. @@ -443,6 +475,7 @@ public class VertexBuffer { private static native long nCreateBuilder(); private static native void nDestroyBuilder(long nativeBuilder); private static native void nBuilderVertexCount(long nativeBuilder, int vertexCount); + private static native void nBuilderEnableBufferObjects(long nativeBuilder, boolean enabled); private static native void nBuilderBufferCount(long nativeBuilder, int bufferCount); private static native void nBuilderAttribute(long nativeBuilder, int attribute, int bufferIndex, int attributeType, int byteOffset, int byteStride); @@ -455,6 +488,9 @@ public class VertexBuffer { int bufferIndex, Buffer buffer, int remaining, int destOffsetInBytes, int count, Object handler, Runnable callback); + private static native void nSetBufferObjectAt(long nativeVertexBuffer, long nativeEngine, + int bufferIndex, long nativeBufferObject); + private static native void nPopulateTangentQuaternions(int quatType, int quatCount, Buffer outBuffer, int outRemaining, int outStride, Buffer normals, int normalsRemaining, int normalsStride, Buffer tangents, int tangentsRemaining, int tangentsStride); diff --git a/android/filament-android/src/main/java/com/google/android/filament/View.java b/android/filament-android/src/main/java/com/google/android/filament/View.java index ab12538005..8affc43ca9 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/View.java +++ b/android/filament-android/src/main/java/com/google/android/filament/View.java @@ -266,7 +266,7 @@ public class View { /** * Options for Temporal Anti-aliasing (TAA) - * @see View#setTemporalAntiAliasingOptions() + * @see View#setTemporalAntiAliasingOptions */ public static class TemporalAntiAliasingOptions { /** reconstruction filter width typically between 0 (sharper, aliased) and 1 (smoother) */ @@ -465,6 +465,55 @@ public class View { /** filter to use for filling gaps in the kernel */ @NonNull public Filter filter = Filter.MEDIAN; + + /** perform DoF processing at native resolution */ + public boolean nativeResolution = false; + + /** + *

Number of of rings used by the foreground kernel. The number of rings affects quality + * and performance. The actual number of sample per pixel is defined + * as (ringCount * 2 - 1)^2. Here are a few commonly used values:

+ * 3 rings : 25 ( 5x 5 grid) + * 4 rings : 49 ( 7x 7 grid) + * 5 rings : 81 ( 9x 9 grid) + * 17 rings : 1089 (33x33 grid) + * + *

With a maximum circle-of-confusion of 32, it is never necessary to use more than 17 rings.

+ * + *

Usually all three settings below are set to the same value, however, it is often + * acceptable to use a lower ring count for the "fast tiles", which improves performance. + * Fast tiles are regions of the screen where every pixels have a similar + * circle-of-confusion radius.

+ * + *

A value of 0 means default, which is 5 on desktop and 3 on mobile.

+ */ + public int foregroundRingCount = 0; + + /** + * Number of of rings used by the background kernel. The number of rings affects quality + * and performance. + * @see #foregroundRingCount + */ + public int backgroundRingCount = 0; + + /** + * Number of of rings used by the fast gather kernel. The number of rings affects quality + * and performance. + * @see #foregroundRingCount + */ + public int fastGatherRingCount = 0; + + /** + * maximum circle-of-confusion in pixels for the foreground, must be in [0, 32] range. + * A value of 0 means default, which is 32 on desktop and 24 on mobile. + */ + public int maxForegroundCOC = 0; + + /** + * maximum circle-of-confusion in pixels for the background, must be in [0, 32] range. + * A value of 0 means default, which is 32 on desktop and 24 on mobile. + */ + public int maxBackgroundCOC = 0; }; /** @@ -1393,7 +1442,10 @@ public class View { */ public void setDepthOfFieldOptions(@NonNull DepthOfFieldOptions options) { mDepthOfFieldOptions = options; - nSetDepthOfFieldOptions(getNativeObject(), options.focusDistance, options.cocScale, options.maxApertureDiameter, options.enabled, options.filter.ordinal()); + nSetDepthOfFieldOptions(getNativeObject(), options.focusDistance, options.cocScale, + options.maxApertureDiameter, options.enabled, options.filter.ordinal(), + options.nativeResolution, options.foregroundRingCount, options.backgroundRingCount, + options.fastGatherRingCount, options.maxForegroundCOC, options.maxBackgroundCOC); } /** @@ -1452,7 +1504,8 @@ public class View { private static native void nSetBloomOptions(long nativeView, long dirtNativeObject, float dirtStrength, float strength, int resolution, float anamorphism, int levels, int blendMode, boolean threshold, boolean enabled, float highlight); private static native void nSetFogOptions(long nativeView, float distance, float maximumOpacity, float height, float heightFalloff, float v, float v1, float v2, float density, float inScatteringStart, float inScatteringSize, boolean fogColorFromIbl, boolean enabled); private static native void nSetBlendMode(long nativeView, int blendMode); - private static native void nSetDepthOfFieldOptions(long nativeView, float focusDistance, float cocScale, float maxApertureDiameter, boolean enabled, int filter); + private static native void nSetDepthOfFieldOptions(long nativeView, float focusDistance, float cocScale, float maxApertureDiameter, boolean enabled, int filter, + boolean nativeResolution, int foregroundRingCount, int backgroundRingCount, int fastGatherRingCount, int maxForegroundCOC, int maxBackgroundCOC); private static native void nSetVignetteOptions(long nativeView, float midPoint, float roundness, float feather, float r, float g, float b, float a, boolean enabled); private static native void nSetTemporalAntiAliasingOptions(long nativeView, float feedback, float filterWidth, boolean enabled); private static native boolean nIsShadowingEnabled(long nativeView); diff --git a/android/gradle.properties b/android/gradle.properties index 8d3b35e378..d42fab15b8 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.google.android.filament -VERSION_NAME=1.9.19 +VERSION_NAME=1.9.20 POM_DESCRIPTION=Real-time physically based rendering engine for Android. diff --git a/build.sh b/build.sh index 5c89b80042..b953601045 100755 --- a/build.sh +++ b/build.sh @@ -17,6 +17,8 @@ function print_help { echo " Generate .tgz build archives, implies -i." echo " -c" echo " Clean build directories." + echo " -d" + echo " Enable matdbg and disable material optimization." echo " -f" echo " Always invoke CMake before incremental builds." echo " -i" @@ -80,6 +82,27 @@ function print_help { echo "" } +function print_matdbg_help { + echo "matdbg is enabled in the build, but some extra steps are needed." + echo "" + echo "FOR DESKTOP BUILDS:" + echo "" + echo "Please set the port environment variable before launching. e.g., on macOS do:" + echo " export FILAMENT_MATDBG_PORT=8080" + echo "" + echo "FOR ANDROID BUILDS:" + echo "" + echo "1) The most reliable way to enable matdbg is to bypass Gradle and" + echo " directly modify the appropriate two lines in the following file:" + echo " ./android/filament-android/CMakeLists.txt" + echo "" + echo "2) The port number is hardcoded to 8081 so you will need to do:" + echo " adb forward tcp:8081 tcp:8081" + echo "" + echo "3) Be sure to enable INTERNET permission in your app's manifest file." + echo "" +} + # Requirements CMAKE_MAJOR=3 CMAKE_MINOR=10 @@ -122,6 +145,8 @@ VULKAN_ANDROID_GRADLE_OPTION="" SWIFTSHADER_OPTION="-DFILAMENT_USE_SWIFTSHADER=OFF" +MATDBG_OPTION="-DFILAMENT_ENABLE_MATDBG=OFF" + IOS_BUILD_SIMULATOR=false BUILD_UNIVERSAL_LIBRARIES=false @@ -174,6 +199,7 @@ function build_desktop_target { -DCMAKE_INSTALL_PREFIX="../${lc_target}/filament" \ -DFILAMENT_ENABLE_JAVA="${FILAMENT_ENABLE_JAVA}" \ ${SWIFTSHADER_OPTION} \ + ${MATDBG_OPTION} \ ${deployment_target} \ ${architectures} \ ../.. @@ -298,6 +324,7 @@ function build_android_target { -DCMAKE_BUILD_TYPE="$1" \ -DCMAKE_INSTALL_PREFIX="../android-${lc_target}/filament" \ -DCMAKE_TOOLCHAIN_FILE="../../build/toolchain-${arch}-linux-android.cmake" \ + ${MATDBG_OPTION} \ ${VULKAN_ANDROID_OPTION} \ ../.. fi @@ -484,6 +511,7 @@ function build_ios_target { -DPLATFORM_NAME="${platform}" \ -DIOS=1 \ -DCMAKE_TOOLCHAIN_FILE=../../third_party/clang/iOS.cmake \ + ${MATDBG_OPTION} \ ../.. fi @@ -654,7 +682,7 @@ function run_tests { pushd "$(dirname "$0")" > /dev/null -while getopts ":hacfijmp:q:uvslwt" opt; do +while getopts ":hacfijmp:q:uvslwtd" opt; do case ${opt} in h) print_help @@ -667,6 +695,10 @@ while getopts ":hacfijmp:q:uvslwt" opt; do c) ISSUE_CLEAN=true ;; + d) + PRINT_MATDBG_HELP=true + MATDBG_OPTION="-DFILAMENT_ENABLE_MATDBG=ON, -DFILAMENT_DISABLE_MATOPT=ON" + ;; f) ISSUE_CMAKE_ALWAYS=true ;; @@ -825,3 +857,7 @@ fi if [[ "${RUN_TESTS}" == "true" ]]; then run_tests fi + +if [[ "${PRINT_MATDBG_HELP}" == "true" ]]; then + print_matdbg_help +fi diff --git a/docs/remote/filament.js b/docs/remote/filament.js index 013ebbb32a..23adc9dcbb 100644 --- a/docs/remote/filament.js +++ b/docs/remote/filament.js @@ -6,7 +6,7 @@ var Filament = (function() { function(Filament) { Filament = Filament || {}; -var Module=typeof Filament!=="undefined"?Filament:{};var readyPromiseResolve,readyPromiseReject;Module["ready"]=new Promise(function(resolve,reject){readyPromiseResolve=resolve;readyPromiseReject=reject});var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var arguments_=[];var thisProgram="./this.program";var quit_=function(status,toThrow){throw toThrow};var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var ENVIRONMENT_IS_NODE=false;var ENVIRONMENT_IS_SHELL=false;ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_WORKER=typeof importScripts==="function";ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string";ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;var nodeFS;var nodePath;if(ENVIRONMENT_IS_NODE){if(ENVIRONMENT_IS_WORKER){scriptDirectory=require("path").dirname(scriptDirectory)+"/"}else{scriptDirectory=__dirname+"/"}read_=function shell_read(filename,binary){if(!nodeFS)nodeFS=require("fs");if(!nodePath)nodePath=require("path");filename=nodePath["normalize"](filename);return nodeFS["readFileSync"](filename,binary?null:"utf8")};readBinary=function readBinary(filename){var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};if(process["argv"].length>1){thisProgram=process["argv"][1].replace(/\\/g,"/")}arguments_=process["argv"].slice(2);process["on"]("uncaughtException",function(ex){if(!(ex instanceof ExitStatus)){throw ex}});process["on"]("unhandledRejection",abort);quit_=function(status){process["exit"](status)};Module["inspect"]=function(){return"[Emscripten Module object]"}}else if(ENVIRONMENT_IS_SHELL){if(typeof read!="undefined"){read_=function shell_read(f){return read(f)}}readBinary=function readBinary(f){var data;if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){arguments_=scriptArgs}else if(typeof arguments!="undefined"){arguments_=arguments}if(typeof quit==="function"){quit_=function(status){quit(status)}}if(typeof print!=="undefined"){if(typeof console==="undefined")console={};console.log=print;console.warn=console.error=typeof printErr!=="undefined"?printErr:print}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!=="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptDir){scriptDirectory=_scriptDir}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=function(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=function(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=function(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}setWindowTitle=function(title){document.title=title}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.warn.bind(console);for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var STACK_ALIGN=16;function alignMemory(size,factor){if(!factor)factor=STACK_ALIGN;return Math.ceil(size/factor)*factor}var tempRet0=0;var setTempRet0=function(value){tempRet0=value};var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime;if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(typeof WebAssembly!=="object"){abort("no native wasm support detected")}var wasmMemory;var ABORT=false;var EXITSTATUS;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf8"):undefined;function UTF8ArrayToString(heap,idx,maxBytesToRead){var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heap[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heap.subarray&&UTF8Decoder){return UTF8Decoder.decode(heap.subarray(idx,endPtr))}else{var str="";while(idx>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function stringToUTF8Array(str,heap,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}function lengthBytesUTF8(str){var len=0;for(var i=0;i=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127)++len;else if(u<=2047)len+=2;else if(u<=65535)len+=3;else len+=4}return len}var UTF16Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf-16le"):undefined;function UTF16ToString(ptr,maxBytesToRead){var endPtr=ptr;var idx=endPtr>>1;var maxIdx=idx+maxBytesToRead/2;while(!(idx>=maxIdx)&&HEAPU16[idx])++idx;endPtr=idx<<1;if(endPtr-ptr>32&&UTF16Decoder){return UTF16Decoder.decode(HEAPU8.subarray(ptr,endPtr))}else{var str="";for(var i=0;!(i>=maxBytesToRead/2);++i){var codeUnit=HEAP16[ptr+i*2>>1];if(codeUnit==0)break;str+=String.fromCharCode(codeUnit)}return str}}function stringToUTF16(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite>1]=codeUnit;outPtr+=2}HEAP16[outPtr>>1]=0;return outPtr-startPtr}function lengthBytesUTF16(str){return str.length*2}function UTF32ToString(ptr,maxBytesToRead){var i=0;var str="";while(!(i>=maxBytesToRead/4)){var utf32=HEAP32[ptr+i*4>>2];if(utf32==0)break;++i;if(utf32>=65536){var ch=utf32-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}else{str+=String.fromCharCode(utf32)}}return str}function stringToUTF32(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i=55296&&codeUnit<=57343){var trailSurrogate=str.charCodeAt(++i);codeUnit=65536+((codeUnit&1023)<<10)|trailSurrogate&1023}HEAP32[outPtr>>2]=codeUnit;outPtr+=4;if(outPtr+4>endPtr)break}HEAP32[outPtr>>2]=0;return outPtr-startPtr}function lengthBytesUTF32(str){var len=0;for(var i=0;i=55296&&codeUnit<=57343)++i;len+=4}return len}function writeArrayToMemory(array,buffer){HEAP8.set(array,buffer)}function writeAsciiToMemory(str,buffer,dontAddNull){for(var i=0;i>0]=str.charCodeAt(i)}if(!dontAddNull)HEAP8[buffer>>0]=0}function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}return x}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var INITIAL_MEMORY=Module["INITIAL_MEMORY"]||16777216;var wasmTable;var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;__ATINIT__.push({func:function(){___wasm_call_ctors()}});function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;if(!Module["noFSInit"]&&!FS.init.initialized)FS.init();TTY.init();callRuntimeCallbacks(__ATINIT__)}function preMain(){FS.ignorePermissions=false;callRuntimeCallbacks(__ATMAIN__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function getUniqueRunDependency(id){return id}function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}what+="";err(what);ABORT=true;EXITSTATUS=1;what="abort("+what+"). Build with -s ASSERTIONS=1 for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}function hasPrefix(str,prefix){return String.prototype.startsWith?str.startsWith(prefix):str.indexOf(prefix)===0}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return hasPrefix(filename,dataURIPrefix)}var fileURIPrefix="file://";function isFileURI(filename){return hasPrefix(filename,fileURIPrefix)}var wasmBinaryFile="filament.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(file){try{if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)){if(typeof fetch==="function"&&!isFileURI(wasmBinaryFile)){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary(wasmBinaryFile)})}else{if(readAsync){return new Promise(function(resolve,reject){readAsync(wasmBinaryFile,function(response){resolve(new Uint8Array(response))},reject)})}}}return Promise.resolve().then(function(){return getBinary(wasmBinaryFile)})}function createWasm(){var info={"a":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;wasmMemory=Module["asm"]["Zg"];updateGlobalBufferAndViews(wasmMemory.buffer);wasmTable=Module["asm"]["_g"];removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");function receiveInstantiatedSource(output){receiveInstance(output["instance"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&!isFileURI(wasmBinaryFile)&&typeof fetch==="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiatedSource,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(receiveInstantiatedSource)})})}else{return instantiateArrayBuffer(receiveInstantiatedSource)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync().catch(readyPromiseReject);return{}}var tempDouble;var tempI64;var ASM_CONSTS={10126:function(){const handle=GL.registerContext(Filament.glContext,Filament.glOptions);GL.makeContextCurrent(handle)},2419420:function(){GL.currArrayBuffer=GLctx.currentArrayBufferBinding}};function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback(Module);continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){wasmTable.get(func)()}else{wasmTable.get(func)(callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}function setErrNo(value){HEAP32[___errno_location()>>2]=value;return value}var PATH={splitPath:function(filename){var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:function(parts,allowAboveRoot){var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:function(path){var isAbsolute=path.charAt(0)==="/",trailingSlash=path.substr(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(function(p){return!!p}),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:function(path){var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.substr(0,dir.length-1)}return root+dir},basename:function(path){if(path==="/")return"/";path=PATH.normalize(path);path=path.replace(/\/$/,"");var lastSlash=path.lastIndexOf("/");if(lastSlash===-1)return path;return path.substr(lastSlash+1)},extname:function(path){return PATH.splitPath(path)[3]},join:function(){var paths=Array.prototype.slice.call(arguments,0);return PATH.normalize(paths.join("/"))},join2:function(l,r){return PATH.normalize(l+"/"+r)}};function getRandomDevice(){if(typeof crypto==="object"&&typeof crypto["getRandomValues"]==="function"){var randomBuffer=new Uint8Array(1);return function(){crypto.getRandomValues(randomBuffer);return randomBuffer[0]}}else if(ENVIRONMENT_IS_NODE){try{var crypto_module=require("crypto");return function(){return crypto_module["randomBytes"](1)[0]}}catch(e){}}return function(){abort("randomDevice")}}var PATH_FS={resolve:function(){var resolvedPath="",resolvedAbsolute=false;for(var i=arguments.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?arguments[i]:FS.cwd();if(typeof path!=="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=path.charAt(0)==="/"}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(function(p){return!!p}),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:function(from,to){from=PATH_FS.resolve(from).substr(1);to=PATH_FS.resolve(to).substr(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i0){result=buf.slice(0,bytesRead).toString("utf-8")}else{result=null}}else if(typeof window!="undefined"&&typeof window.prompt=="function"){result=window.prompt("Input: ");if(result!==null){result+="\n"}}else if(typeof readline=="function"){result=readline();if(result!==null){result+="\n"}}if(!result){return null}tty.input=intArrayFromString(result,true)}return tty.input.shift()},put_char:function(tty,val){if(val===null||val===10){out(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},flush:function(tty){if(tty.output&&tty.output.length>0){out(UTF8ArrayToString(tty.output,0));tty.output=[]}}},default_tty1_ops:{put_char:function(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},flush:function(tty){if(tty.output&&tty.output.length>0){err(UTF8ArrayToString(tty.output,0));tty.output=[]}}}};function mmapAlloc(size){var alignedSize=alignMemory(size,16384);var ptr=_malloc(alignedSize);while(size=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity>>0);if(prevCapacity!=0)newCapacity=Math.max(newCapacity,256);var oldContents=node.contents;node.contents=new Uint8Array(newCapacity);if(node.usedBytes>0)node.contents.set(oldContents.subarray(0,node.usedBytes),0);return},resizeFileStorage:function(node,newSize){if(node.usedBytes==newSize)return;if(newSize==0){node.contents=null;node.usedBytes=0;return}if(!node.contents||node.contents.subarray){var oldContents=node.contents;node.contents=new Uint8Array(newSize);if(oldContents){node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)))}node.usedBytes=newSize;return}if(!node.contents)node.contents=[];if(node.contents.length>newSize)node.contents.length=newSize;else while(node.contents.length=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);if(size>8&&contents.subarray){buffer.set(contents.subarray(position,position+size),offset)}else{for(var i=0;i0||position+length8){throw new FS.ErrnoError(32)}var parts=PATH.normalizeArray(path.split("/").filter(function(p){return!!p}),false);var current=FS.root;var current_path="/";for(var i=0;i40){throw new FS.ErrnoError(32)}}}}return{path:current_path,node:current}},getPath:function(node){var path;while(true){if(FS.isRoot(node)){var mount=node.mount.mountpoint;if(!path)return mount;return mount[mount.length-1]!=="/"?mount+"/"+path:mount+path}path=path?node.name+"/"+path:node.name;node=node.parent}},hashName:function(parentid,name){var hash=0;for(var i=0;i>>0)%FS.nameTable.length},hashAddNode:function(node){var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode:function(node){var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode:function(parent,name){var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode,parent)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode:function(parent,name,mode,rdev){var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode:function(node){FS.hashRemoveNode(node)},isRoot:function(node){return node===node.parent},isMountpoint:function(node){return!!node.mounted},isFile:function(mode){return(mode&61440)===32768},isDir:function(mode){return(mode&61440)===16384},isLink:function(mode){return(mode&61440)===40960},isChrdev:function(mode){return(mode&61440)===8192},isBlkdev:function(mode){return(mode&61440)===24576},isFIFO:function(mode){return(mode&61440)===4096},isSocket:function(mode){return(mode&49152)===49152},flagModes:{"r":0,"r+":2,"w":577,"w+":578,"a":1089,"a+":1090},modeStringToFlags:function(str){var flags=FS.flagModes[str];if(typeof flags==="undefined"){throw new Error("Unknown file open mode: "+str)}return flags},flagsToPermissionString:function(flag){var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions:function(node,perms){if(FS.ignorePermissions){return 0}if(perms.indexOf("r")!==-1&&!(node.mode&292)){return 2}else if(perms.indexOf("w")!==-1&&!(node.mode&146)){return 2}else if(perms.indexOf("x")!==-1&&!(node.mode&73)){return 2}return 0},mayLookup:function(dir){var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate:function(dir,name){try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete:function(dir,name,isdir){var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else{if(FS.isDir(node.mode)){return 31}}return 0},mayOpen:function(node,flags){if(!node){return 44}if(FS.isLink(node.mode)){return 32}else if(FS.isDir(node.mode)){if(FS.flagsToPermissionString(flags)!=="r"||flags&512){return 31}}return FS.nodePermissions(node,FS.flagsToPermissionString(flags))},MAX_OPEN_FDS:4096,nextfd:function(fd_start,fd_end){fd_start=fd_start||0;fd_end=fd_end||FS.MAX_OPEN_FDS;for(var fd=fd_start;fd<=fd_end;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStream:function(fd){return FS.streams[fd]},createStream:function(stream,fd_start,fd_end){if(!FS.FSStream){FS.FSStream=function(){};FS.FSStream.prototype={object:{get:function(){return this.node},set:function(val){this.node=val}},isRead:{get:function(){return(this.flags&2097155)!==1}},isWrite:{get:function(){return(this.flags&2097155)!==0}},isAppend:{get:function(){return this.flags&1024}}}}var newStream=new FS.FSStream;for(var p in stream){newStream[p]=stream[p]}stream=newStream;var fd=FS.nextfd(fd_start,fd_end);stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream:function(fd){FS.streams[fd]=null},chrdev_stream_ops:{open:function(stream){var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;if(stream.stream_ops.open){stream.stream_ops.open(stream)}},llseek:function(){throw new FS.ErrnoError(70)}},major:function(dev){return dev>>8},minor:function(dev){return dev&255},makedev:function(ma,mi){return ma<<8|mi},registerDevice:function(dev,ops){FS.devices[dev]={stream_ops:ops}},getDevice:function(dev){return FS.devices[dev]},getMounts:function(mount){var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push.apply(check,m.mounts)}return mounts},syncfs:function(populate,callback){if(typeof populate==="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){err("warning: "+FS.syncFSRequests+" FS.syncfs operations in flight at once, probably just doing extra work")}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null)}}mounts.forEach(function(mount){if(!mount.type.syncfs){return done(null)}mount.type.syncfs(mount,populate,done)})},mount:function(type,opts,mountpoint){var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type:type,opts:opts,mountpoint:mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount:function(mountpoint){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);Object.keys(FS.nameTable).forEach(function(hash){var current=FS.nameTable[hash];while(current){var next=current.name_next;if(mounts.indexOf(current.mount)!==-1){FS.destroyNode(current)}current=next}});node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup:function(parent,name){return parent.node_ops.lookup(parent,name)},mknod:function(path,mode,dev){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name||name==="."||name===".."){throw new FS.ErrnoError(28)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},create:function(path,mode){mode=mode!==undefined?mode:438;mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir:function(path,mode){mode=mode!==undefined?mode:511;mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree:function(path,mode){var dirs=path.split("/");var d="";for(var i=0;ithis.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]};LazyUint8Array.prototype.setDataGetter=function LazyUint8Array_setDataGetter(getter){this.getter=getter};LazyUint8Array.prototype.cacheLength=function LazyUint8Array_cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=function(from,to){if(from>to)throw new Error("invalid range ("+from+", "+to+") or no bytes requested!");if(to>datalength-1)throw new Error("only "+datalength+" bytes available! programmer error!");var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);if(typeof Uint8Array!="undefined")xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}else{return intArrayFromString(xhr.responseText||"",true)}};var lazyArray=this;lazyArray.setDataGetter(function(chunkNum){var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]==="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]==="undefined")throw new Error("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true};if(typeof XMLHttpRequest!=="undefined"){if(!ENVIRONMENT_IS_WORKER)throw"Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var lazyArray=new LazyUint8Array;Object.defineProperties(lazyArray,{length:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._length}},chunkSize:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}});var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url:url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};var keys=Object.keys(node.stream_ops);keys.forEach(function(key){var fn=node.stream_ops[key];stream_ops[key]=function forceLoadLazyFile(){FS.forceLoadFile(node);return fn.apply(null,arguments)}});stream_ops.read=function stream_ops_read(stream,buffer,offset,length,position){FS.forceLoadFile(node);var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i>2]=stat.dev;HEAP32[buf+4>>2]=0;HEAP32[buf+8>>2]=stat.ino;HEAP32[buf+12>>2]=stat.mode;HEAP32[buf+16>>2]=stat.nlink;HEAP32[buf+20>>2]=stat.uid;HEAP32[buf+24>>2]=stat.gid;HEAP32[buf+28>>2]=stat.rdev;HEAP32[buf+32>>2]=0;tempI64=[stat.size>>>0,(tempDouble=stat.size,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+40>>2]=tempI64[0],HEAP32[buf+44>>2]=tempI64[1];HEAP32[buf+48>>2]=4096;HEAP32[buf+52>>2]=stat.blocks;HEAP32[buf+56>>2]=stat.atime.getTime()/1e3|0;HEAP32[buf+60>>2]=0;HEAP32[buf+64>>2]=stat.mtime.getTime()/1e3|0;HEAP32[buf+68>>2]=0;HEAP32[buf+72>>2]=stat.ctime.getTime()/1e3|0;HEAP32[buf+76>>2]=0;tempI64=[stat.ino>>>0,(tempDouble=stat.ino,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+80>>2]=tempI64[0],HEAP32[buf+84>>2]=tempI64[1];return 0},doMsync:function(addr,stream,len,flags,offset){var buffer=HEAPU8.slice(addr,addr+len);FS.msync(stream,buffer,offset,len,flags)},doMkdir:function(path,mode){path=PATH.normalize(path);if(path[path.length-1]==="/")path=path.substr(0,path.length-1);FS.mkdir(path,mode,0);return 0},doMknod:function(path,mode,dev){switch(mode&61440){case 32768:case 8192:case 24576:case 4096:case 49152:break;default:return-28}FS.mknod(path,mode,dev);return 0},doReadlink:function(path,buf,bufsize){if(bufsize<=0)return-28;var ret=FS.readlink(path);var len=Math.min(bufsize,lengthBytesUTF8(ret));var endChar=HEAP8[buf+len];stringToUTF8(ret,buf,bufsize+1);HEAP8[buf+len]=endChar;return len},doAccess:function(path,amode){if(amode&~7){return-28}var node;var lookup=FS.lookupPath(path,{follow:true});node=lookup.node;if(!node){return-44}var perms="";if(amode&4)perms+="r";if(amode&2)perms+="w";if(amode&1)perms+="x";if(perms&&FS.nodePermissions(node,perms)){return-2}return 0},doDup:function(path,flags,suggestFD){var suggest=FS.getStream(suggestFD);if(suggest)FS.close(suggest);return FS.open(path,flags,0,suggestFD,suggestFD).fd},doReadv:function(stream,iov,iovcnt,offset){var ret=0;for(var i=0;i>2];var len=HEAP32[iov+(i*8+4)>>2];var curr=FS.read(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2];var len=HEAP32[iov+(i*8+4)>>2];var curr=FS.write(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr}return ret},varargs:undefined,get:function(){SYSCALLS.varargs+=4;var ret=HEAP32[SYSCALLS.varargs-4>>2];return ret},getStr:function(ptr){var ret=UTF8ToString(ptr);return ret},getStreamFromFD:function(fd){var stream=FS.getStream(fd);if(!stream)throw new FS.ErrnoError(8);return stream},get64:function(low,high){return low}};function ___sys_fcntl64(fd,cmd,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:{var arg=SYSCALLS.get();if(arg<0){return-28}var newStream;newStream=FS.open(stream.path,stream.flags,0,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=SYSCALLS.get();stream.flags|=arg;return 0}case 12:{var arg=SYSCALLS.get();var offset=0;HEAP16[arg+offset>>1]=2;return 0}case 13:case 14:return 0;case 16:case 8:return-28;case 9:setErrNo(28);return-1;default:{return-28}}}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___sys_ioctl(fd,op,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(op){case 21509:case 21505:{if(!stream.tty)return-59;return 0}case 21510:case 21511:case 21512:case 21506:case 21507:case 21508:{if(!stream.tty)return-59;return 0}case 21519:{if(!stream.tty)return-59;var argp=SYSCALLS.get();HEAP32[argp>>2]=0;return 0}case 21520:{if(!stream.tty)return-59;return-28}case 21531:{var argp=SYSCALLS.get();return FS.ioctl(stream,op,argp)}case 21523:{if(!stream.tty)return-59;return 0}case 21524:{if(!stream.tty)return-59;return 0}default:abort("bad ioctl syscall "+op)}}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___sys_open(path,flags,varargs){SYSCALLS.varargs=varargs;try{var pathname=SYSCALLS.getStr(path);var mode=varargs?SYSCALLS.get():0;var stream=FS.open(pathname,flags,mode);return stream.fd}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}var tupleRegistrations={};function runDestructors(destructors){while(destructors.length){var ptr=destructors.pop();var del=destructors.pop();del(ptr)}}function simpleReadValueFromPointer(pointer){return this["fromWireType"](HEAPU32[pointer>>2])}var awaitingDependencies={};var registeredTypes={};var typeDependencies={};var char_0=48;var char_9=57;function makeLegalFunctionName(name){if(undefined===name){return"_unknown"}name=name.replace(/[^a-zA-Z0-9_]/g,"$");var f=name.charCodeAt(0);if(f>=char_0&&f<=char_9){return"_"+name}else{return name}}function createNamedFunction(name,body){name=makeLegalFunctionName(name);return new Function("body","return function "+name+"() {\n"+' "use strict";'+" return body.apply(this, arguments);\n"+"};\n")(body)}function extendError(baseErrorType,errorName){var errorClass=createNamedFunction(errorName,function(message){this.name=errorName;this.message=message;var stack=new Error(message).stack;if(stack!==undefined){this.stack=this.toString()+"\n"+stack.replace(/^Error(:[^\n]*)?\n/,"")}});errorClass.prototype=Object.create(baseErrorType.prototype);errorClass.prototype.constructor=errorClass;errorClass.prototype.toString=function(){if(this.message===undefined){return this.name}else{return this.name+": "+this.message}};return errorClass}var InternalError=undefined;function throwInternalError(message){throw new InternalError(message)}function whenDependentTypesAreResolved(myTypes,dependentTypes,getTypeConverters){myTypes.forEach(function(type){typeDependencies[type]=dependentTypes});function onComplete(typeConverters){var myTypeConverters=getTypeConverters(typeConverters);if(myTypeConverters.length!==myTypes.length){throwInternalError("Mismatched type converter count")}for(var i=0;i>shift])},destructorFunction:null})}function ClassHandle_isAliasOf(other){if(!(this instanceof ClassHandle)){return false}if(!(other instanceof ClassHandle)){return false}var leftClass=this.$$.ptrType.registeredClass;var left=this.$$.ptr;var rightClass=other.$$.ptrType.registeredClass;var right=other.$$.ptr;while(leftClass.baseClass){left=leftClass.upcast(left);leftClass=leftClass.baseClass}while(rightClass.baseClass){right=rightClass.upcast(right);rightClass=rightClass.baseClass}return leftClass===rightClass&&left===right}function shallowCopyInternalPointer(o){return{count:o.count,deleteScheduled:o.deleteScheduled,preservePointerOnDelete:o.preservePointerOnDelete,ptr:o.ptr,ptrType:o.ptrType,smartPtr:o.smartPtr,smartPtrType:o.smartPtrType}}function throwInstanceAlreadyDeleted(obj){function getInstanceTypeName(handle){return handle.$$.ptrType.registeredClass.name}throwBindingError(getInstanceTypeName(obj)+" instance already deleted")}var finalizationGroup=false;function detachFinalizer(handle){}function runDestructor($$){if($$.smartPtr){$$.smartPtrType.rawDestructor($$.smartPtr)}else{$$.ptrType.registeredClass.rawDestructor($$.ptr)}}function releaseClassHandle($$){$$.count.value-=1;var toDelete=0===$$.count.value;if(toDelete){runDestructor($$)}}function attachFinalizer(handle){if("undefined"===typeof FinalizationGroup){attachFinalizer=function(handle){return handle};return handle}finalizationGroup=new FinalizationGroup(function(iter){for(var result=iter.next();!result.done;result=iter.next()){var $$=result.value;if(!$$.ptr){console.warn("object already deleted: "+$$.ptr)}else{releaseClassHandle($$)}}});attachFinalizer=function(handle){finalizationGroup.register(handle,handle.$$,handle.$$);return handle};detachFinalizer=function(handle){finalizationGroup.unregister(handle.$$)};return attachFinalizer(handle)}function ClassHandle_clone(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.preservePointerOnDelete){this.$$.count.value+=1;return this}else{var clone=attachFinalizer(Object.create(Object.getPrototypeOf(this),{$$:{value:shallowCopyInternalPointer(this.$$)}}));clone.$$.count.value+=1;clone.$$.deleteScheduled=false;return clone}}function ClassHandle_delete(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}detachFinalizer(this);releaseClassHandle(this.$$);if(!this.$$.preservePointerOnDelete){this.$$.smartPtr=undefined;this.$$.ptr=undefined}}function ClassHandle_isDeleted(){return!this.$$.ptr}var delayFunction=undefined;var deletionQueue=[];function flushPendingDeletes(){while(deletionQueue.length){var obj=deletionQueue.pop();obj.$$.deleteScheduled=false;obj["delete"]()}}function ClassHandle_deleteLater(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}deletionQueue.push(this);if(deletionQueue.length===1&&delayFunction){delayFunction(flushPendingDeletes)}this.$$.deleteScheduled=true;return this}function init_ClassHandle(){ClassHandle.prototype["isAliasOf"]=ClassHandle_isAliasOf;ClassHandle.prototype["clone"]=ClassHandle_clone;ClassHandle.prototype["delete"]=ClassHandle_delete;ClassHandle.prototype["isDeleted"]=ClassHandle_isDeleted;ClassHandle.prototype["deleteLater"]=ClassHandle_deleteLater}function ClassHandle(){}var registeredPointers={};function ensureOverloadTable(proto,methodName,humanName){if(undefined===proto[methodName].overloadTable){var prevFunc=proto[methodName];proto[methodName]=function(){if(!proto[methodName].overloadTable.hasOwnProperty(arguments.length)){throwBindingError("Function '"+humanName+"' called with an invalid number of arguments ("+arguments.length+") - expects one of ("+proto[methodName].overloadTable+")!")}return proto[methodName].overloadTable[arguments.length].apply(this,arguments)};proto[methodName].overloadTable=[];proto[methodName].overloadTable[prevFunc.argCount]=prevFunc}}function exposePublicSymbol(name,value,numArguments){if(Module.hasOwnProperty(name)){if(undefined===numArguments||undefined!==Module[name].overloadTable&&undefined!==Module[name].overloadTable[numArguments]){throwBindingError("Cannot register public name '"+name+"' twice")}ensureOverloadTable(Module,name,name);if(Module.hasOwnProperty(numArguments)){throwBindingError("Cannot register multiple overloads of a function with the same number of arguments ("+numArguments+")!")}Module[name].overloadTable[numArguments]=value}else{Module[name]=value;if(undefined!==numArguments){Module[name].numArguments=numArguments}}}function RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast){this.name=name;this.constructor=constructor;this.instancePrototype=instancePrototype;this.rawDestructor=rawDestructor;this.baseClass=baseClass;this.getActualType=getActualType;this.upcast=upcast;this.downcast=downcast;this.pureVirtualFunctions=[]}function upcastPointer(ptr,ptrClass,desiredClass){while(ptrClass!==desiredClass){if(!ptrClass.upcast){throwBindingError("Expected null or instance of "+desiredClass.name+", got an instance of "+ptrClass.name)}ptr=ptrClass.upcast(ptr);ptrClass=ptrClass.baseClass}return ptr}function constNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}return 0}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function genericPointerToWireType(destructors,handle){var ptr;if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}if(this.isSmartPointer){ptr=this.rawConstructor();if(destructors!==null){destructors.push(this.rawDestructor,ptr)}return ptr}else{return 0}}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}if(!this.isConst&&handle.$$.ptrType.isConst){throwBindingError("Cannot convert argument of type "+(handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name)+" to parameter type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);if(this.isSmartPointer){if(undefined===handle.$$.smartPtr){throwBindingError("Passing raw pointer to smart pointer is illegal")}switch(this.sharingPolicy){case 0:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{throwBindingError("Cannot convert argument of type "+(handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name)+" to parameter type "+this.name)}break;case 1:ptr=handle.$$.smartPtr;break;case 2:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{var clonedHandle=handle["clone"]();ptr=this.rawShare(ptr,__emval_register(function(){clonedHandle["delete"]()}));if(destructors!==null){destructors.push(this.rawDestructor,ptr)}}break;default:throwBindingError("Unsupporting sharing policy")}}return ptr}function nonConstNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}return 0}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}if(handle.$$.ptrType.isConst){throwBindingError("Cannot convert argument of type "+handle.$$.ptrType.name+" to parameter type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function RegisteredPointer_getPointee(ptr){if(this.rawGetPointee){ptr=this.rawGetPointee(ptr)}return ptr}function RegisteredPointer_destructor(ptr){if(this.rawDestructor){this.rawDestructor(ptr)}}function RegisteredPointer_deleteObject(handle){if(handle!==null){handle["delete"]()}}function downcastPointer(ptr,ptrClass,desiredClass){if(ptrClass===desiredClass){return ptr}if(undefined===desiredClass.baseClass){return null}var rv=downcastPointer(ptr,ptrClass,desiredClass.baseClass);if(rv===null){return null}return desiredClass.downcast(rv)}function getInheritedInstanceCount(){return Object.keys(registeredInstances).length}function getLiveInheritedInstances(){var rv=[];for(var k in registeredInstances){if(registeredInstances.hasOwnProperty(k)){rv.push(registeredInstances[k])}}return rv}function setDelayFunction(fn){delayFunction=fn;if(deletionQueue.length&&delayFunction){delayFunction(flushPendingDeletes)}}function init_embind(){Module["getInheritedInstanceCount"]=getInheritedInstanceCount;Module["getLiveInheritedInstances"]=getLiveInheritedInstances;Module["flushPendingDeletes"]=flushPendingDeletes;Module["setDelayFunction"]=setDelayFunction}var registeredInstances={};function getBasestPointer(class_,ptr){if(ptr===undefined){throwBindingError("ptr should not be undefined")}while(class_.baseClass){ptr=class_.upcast(ptr);class_=class_.baseClass}return ptr}function getInheritedInstance(class_,ptr){ptr=getBasestPointer(class_,ptr);return registeredInstances[ptr]}function makeClassHandle(prototype,record){if(!record.ptrType||!record.ptr){throwInternalError("makeClassHandle requires ptr and ptrType")}var hasSmartPtrType=!!record.smartPtrType;var hasSmartPtr=!!record.smartPtr;if(hasSmartPtrType!==hasSmartPtr){throwInternalError("Both smartPtrType and smartPtr must be specified")}record.count={value:1};return attachFinalizer(Object.create(prototype,{$$:{value:record}}))}function RegisteredPointer_fromWireType(ptr){var rawPointer=this.getPointee(ptr);if(!rawPointer){this.destructor(ptr);return null}var registeredInstance=getInheritedInstance(this.registeredClass,rawPointer);if(undefined!==registeredInstance){if(0===registeredInstance.$$.count.value){registeredInstance.$$.ptr=rawPointer;registeredInstance.$$.smartPtr=ptr;return registeredInstance["clone"]()}else{var rv=registeredInstance["clone"]();this.destructor(ptr);return rv}}function makeDefaultHandle(){if(this.isSmartPointer){return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this.pointeeType,ptr:rawPointer,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this,ptr:ptr})}}var actualType=this.registeredClass.getActualType(rawPointer);var registeredPointerRecord=registeredPointers[actualType];if(!registeredPointerRecord){return makeDefaultHandle.call(this)}var toType;if(this.isConst){toType=registeredPointerRecord.constPointerType}else{toType=registeredPointerRecord.pointerType}var dp=downcastPointer(rawPointer,this.registeredClass,toType.registeredClass);if(dp===null){return makeDefaultHandle.call(this)}if(this.isSmartPointer){return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp})}}function init_RegisteredPointer(){RegisteredPointer.prototype.getPointee=RegisteredPointer_getPointee;RegisteredPointer.prototype.destructor=RegisteredPointer_destructor;RegisteredPointer.prototype["argPackAdvance"]=8;RegisteredPointer.prototype["readValueFromPointer"]=simpleReadValueFromPointer;RegisteredPointer.prototype["deleteObject"]=RegisteredPointer_deleteObject;RegisteredPointer.prototype["fromWireType"]=RegisteredPointer_fromWireType}function RegisteredPointer(name,registeredClass,isReference,isConst,isSmartPointer,pointeeType,sharingPolicy,rawGetPointee,rawConstructor,rawShare,rawDestructor){this.name=name;this.registeredClass=registeredClass;this.isReference=isReference;this.isConst=isConst;this.isSmartPointer=isSmartPointer;this.pointeeType=pointeeType;this.sharingPolicy=sharingPolicy;this.rawGetPointee=rawGetPointee;this.rawConstructor=rawConstructor;this.rawShare=rawShare;this.rawDestructor=rawDestructor;if(!isSmartPointer&®isteredClass.baseClass===undefined){if(isConst){this["toWireType"]=constNoSmartPtrRawPointerToWireType;this.destructorFunction=null}else{this["toWireType"]=nonConstNoSmartPtrRawPointerToWireType;this.destructorFunction=null}}else{this["toWireType"]=genericPointerToWireType}}function replacePublicSymbol(name,value,numArguments){if(!Module.hasOwnProperty(name)){throwInternalError("Replacing nonexistant public symbol")}if(undefined!==Module[name].overloadTable&&undefined!==numArguments){Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}}function dynCallLegacy(sig,ptr,args){if(args&&args.length){return Module["dynCall_"+sig].apply(null,[ptr].concat(args))}return Module["dynCall_"+sig].call(null,ptr)}function dynCall(sig,ptr,args){if(sig.indexOf("j")!=-1){return dynCallLegacy(sig,ptr,args)}return wasmTable.get(ptr).apply(null,args)}function getDynCaller(sig,ptr){assert(sig.indexOf("j")>=0,"getDynCaller should only be called with i64 sigs");var argCache=[];return function(){argCache.length=arguments.length;for(var i=0;i0?", ":"")+argsListWired}invokerFnBody+=(returns?"var rv = ":"")+"invoker(fn"+(argsListWired.length>0?", ":"")+argsListWired+");\n";if(needsDestructorStack){invokerFnBody+="runDestructors(destructors);\n"}else{for(var i=isClassMethodFunc?1:2;i>2)+i])}return array}function __embind_register_class_class_function(rawClassType,methodName,argCount,rawArgTypesAddr,invokerSignature,rawInvoker,fn){var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);methodName=readLatin1String(methodName);rawInvoker=embind__requireFunction(invokerSignature,rawInvoker);whenDependentTypesAreResolved([],[rawClassType],function(classType){classType=classType[0];var humanName=classType.name+"."+methodName;function unboundTypesHandler(){throwUnboundTypeError("Cannot call "+humanName+" due to unbound types",rawArgTypes)}var proto=classType.registeredClass.constructor;if(undefined===proto[methodName]){unboundTypesHandler.argCount=argCount-1;proto[methodName]=unboundTypesHandler}else{ensureOverloadTable(proto,methodName,humanName);proto[methodName].overloadTable[argCount-1]=unboundTypesHandler}whenDependentTypesAreResolved([],rawArgTypes,function(argTypes){var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));var func=craftInvokerFunction(humanName,invokerArgsArray,null,rawInvoker,fn);if(undefined===proto[methodName].overloadTable){func.argCount=argCount-1;proto[methodName]=func}else{proto[methodName].overloadTable[argCount-1]=func}return[]});return[]})}function __embind_register_class_constructor(rawClassType,argCount,rawArgTypesAddr,invokerSignature,invoker,rawConstructor){assert(argCount>0);var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);invoker=embind__requireFunction(invokerSignature,invoker);var args=[rawConstructor];var destructors=[];whenDependentTypesAreResolved([],[rawClassType],function(classType){classType=classType[0];var humanName="constructor "+classType.name;if(undefined===classType.registeredClass.constructor_body){classType.registeredClass.constructor_body=[]}if(undefined!==classType.registeredClass.constructor_body[argCount-1]){throw new BindingError("Cannot register multiple constructors with identical number of parameters ("+(argCount-1)+") for class '"+classType.name+"'! Overload resolution is currently only performed using the parameter count, not actual type info!")}classType.registeredClass.constructor_body[argCount-1]=function unboundTypeHandler(){throwUnboundTypeError("Cannot construct "+classType.name+" due to unbound types",rawArgTypes)};whenDependentTypesAreResolved([],rawArgTypes,function(argTypes){classType.registeredClass.constructor_body[argCount-1]=function constructor_body(){if(arguments.length!==argCount-1){throwBindingError(humanName+" called with "+arguments.length+" arguments, expected "+(argCount-1))}destructors.length=0;args.length=argCount;for(var i=1;i4&&0===--emval_handle_array[handle].refcount){emval_handle_array[handle]=undefined;emval_free_list.push(handle)}}function count_emval_handles(){var count=0;for(var i=5;i>1])};case 2:return function(pointer){var heap=signed?HEAP32:HEAPU32;return this["fromWireType"](heap[pointer>>2])};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_enum(rawType,name,size,isSigned){var shift=getShiftFromSize(size);name=readLatin1String(name);function ctor(){}ctor.values={};registerType(rawType,{name:name,constructor:ctor,"fromWireType":function(c){return this.constructor.values[c]},"toWireType":function(destructors,c){return c.value},"argPackAdvance":8,"readValueFromPointer":enumReadValueFromPointer(name,shift,isSigned),destructorFunction:null});exposePublicSymbol(name,ctor)}function requireRegisteredType(rawType,humanName){var impl=registeredTypes[rawType];if(undefined===impl){throwBindingError(humanName+" has unknown type "+getTypeName(rawType))}return impl}function __embind_register_enum_value(rawEnumType,name,enumValue){var enumType=requireRegisteredType(rawEnumType,"enum");name=readLatin1String(name);var Enum=enumType.constructor;var Value=Object.create(enumType.constructor.prototype,{value:{value:enumValue},constructor:{value:createNamedFunction(enumType.name+"_"+name,function(){})}});Enum.values[enumValue]=Value;Enum[name]=Value}function _embind_repr(v){if(v===null){return"null"}var t=typeof v;if(t==="object"||t==="array"||t==="function"){return v.toString()}else{return""+v}}function floatReadValueFromPointer(name,shift){switch(shift){case 2:return function(pointer){return this["fromWireType"](HEAPF32[pointer>>2])};case 3:return function(pointer){return this["fromWireType"](HEAPF64[pointer>>3])};default:throw new TypeError("Unknown float type: "+name)}}function __embind_register_float(rawType,name,size){var shift=getShiftFromSize(size);name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(value){return value},"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}return value},"argPackAdvance":8,"readValueFromPointer":floatReadValueFromPointer(name,shift),destructorFunction:null})}function __embind_register_function(name,argCount,rawArgTypesAddr,signature,rawInvoker,fn){var argTypes=heap32VectorToArray(argCount,rawArgTypesAddr);name=readLatin1String(name);rawInvoker=embind__requireFunction(signature,rawInvoker);exposePublicSymbol(name,function(){throwUnboundTypeError("Cannot call "+name+" due to unbound types",argTypes)},argCount-1);whenDependentTypesAreResolved([],argTypes,function(argTypes){var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));replacePublicSymbol(name,craftInvokerFunction(name,invokerArgsArray,null,rawInvoker,fn),argCount-1);return[]})}function integerReadValueFromPointer(name,shift,signed){switch(shift){case 0:return signed?function readS8FromPointer(pointer){return HEAP8[pointer]}:function readU8FromPointer(pointer){return HEAPU8[pointer]};case 1:return signed?function readS16FromPointer(pointer){return HEAP16[pointer>>1]}:function readU16FromPointer(pointer){return HEAPU16[pointer>>1]};case 2:return signed?function readS32FromPointer(pointer){return HEAP32[pointer>>2]}:function readU32FromPointer(pointer){return HEAPU32[pointer>>2]};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_integer(primitiveType,name,size,minRange,maxRange){name=readLatin1String(name);if(maxRange===-1){maxRange=4294967295}var shift=getShiftFromSize(size);var fromWireType=function(value){return value};if(minRange===0){var bitshift=32-8*size;fromWireType=function(value){return value<>>bitshift}}var isUnsignedType=name.indexOf("unsigned")!=-1;registerType(primitiveType,{name:name,"fromWireType":fromWireType,"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}if(valuemaxRange){throw new TypeError('Passing a number "'+_embind_repr(value)+'" from JS side to C/C++ side to an argument of type "'+name+'", which is outside the valid range ['+minRange+", "+maxRange+"]!")}return isUnsignedType?value>>>0:value|0},"argPackAdvance":8,"readValueFromPointer":integerReadValueFromPointer(name,shift,minRange!==0),destructorFunction:null})}function __embind_register_memory_view(rawType,dataTypeIndex,name){var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array];var TA=typeMapping[dataTypeIndex];function decodeMemoryView(handle){handle=handle>>2;var heap=HEAPU32;var size=heap[handle];var data=heap[handle+1];return new TA(buffer,data,size)}name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":decodeMemoryView,"argPackAdvance":8,"readValueFromPointer":decodeMemoryView},{ignoreDuplicateRegistrations:true})}function __embind_register_std_string(rawType,name){name=readLatin1String(name);var stdStringIsUTF8=name==="std::string";registerType(rawType,{name:name,"fromWireType":function(value){var length=HEAPU32[value>>2];var str;if(stdStringIsUTF8){var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i;if(i==length||HEAPU8[currentBytePtr]==0){var maxRead=currentBytePtr-decodeStartPtr;var stringSegment=UTF8ToString(decodeStartPtr,maxRead);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+1}}}else{var a=new Array(length);for(var i=0;i>2]=length;if(stdStringIsUTF8&&valueIsOfTypeString){stringToUTF8(value,ptr+4,length+1)}else{if(valueIsOfTypeString){for(var i=0;i255){_free(ptr);throwBindingError("String has UTF-16 code units that do not fit in 8 bits")}HEAPU8[ptr+4+i]=charCode}}else{for(var i=0;i>2];var HEAP=getHeap();var str;var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i*charSize;if(i==length||HEAP[currentBytePtr>>shift]==0){var maxReadBytes=currentBytePtr-decodeStartPtr;var stringSegment=decodeString(decodeStartPtr,maxReadBytes);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+charSize}}_free(value);return str},"toWireType":function(destructors,value){if(!(typeof value==="string")){throwBindingError("Cannot pass non-string to C++ string type "+name)}var length=lengthBytesUTF(value);var ptr=_malloc(4+length+charSize);HEAPU32[ptr>>2]=length>>shift;encodeString(value,ptr+4,length+charSize);if(destructors!==null){destructors.push(_free,ptr)}return ptr},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:function(ptr){_free(ptr)}})}function __embind_register_value_array(rawType,name,constructorSignature,rawConstructor,destructorSignature,rawDestructor){tupleRegistrations[rawType]={name:readLatin1String(name),rawConstructor:embind__requireFunction(constructorSignature,rawConstructor),rawDestructor:embind__requireFunction(destructorSignature,rawDestructor),elements:[]}}function __embind_register_value_array_element(rawTupleType,getterReturnType,getterSignature,getter,getterContext,setterArgumentType,setterSignature,setter,setterContext){tupleRegistrations[rawTupleType].elements.push({getterReturnType:getterReturnType,getter:embind__requireFunction(getterSignature,getter),getterContext:getterContext,setterArgumentType:setterArgumentType,setter:embind__requireFunction(setterSignature,setter),setterContext:setterContext})}function __embind_register_value_object(rawType,name,constructorSignature,rawConstructor,destructorSignature,rawDestructor){structRegistrations[rawType]={name:readLatin1String(name),rawConstructor:embind__requireFunction(constructorSignature,rawConstructor),rawDestructor:embind__requireFunction(destructorSignature,rawDestructor),fields:[]}}function __embind_register_value_object_field(structType,fieldName,getterReturnType,getterSignature,getter,getterContext,setterArgumentType,setterSignature,setter,setterContext){structRegistrations[structType].fields.push({fieldName:readLatin1String(fieldName),getterReturnType:getterReturnType,getter:embind__requireFunction(getterSignature,getter),getterContext:getterContext,setterArgumentType:setterArgumentType,setter:embind__requireFunction(setterSignature,setter),setterContext:setterContext})}function __embind_register_void(rawType,name){name=readLatin1String(name);registerType(rawType,{isVoid:true,name:name,"argPackAdvance":0,"fromWireType":function(){return undefined},"toWireType":function(destructors,o){return undefined}})}function requireHandle(handle){if(!handle){throwBindingError("Cannot use deleted val. handle = "+handle)}return emval_handle_array[handle].value}function __emval_as(handle,returnType,destructorsRef){handle=requireHandle(handle);returnType=requireRegisteredType(returnType,"emval::as");var destructors=[];var rd=__emval_register(destructors);HEAP32[destructorsRef>>2]=rd;return returnType["toWireType"](destructors,handle)}function __emval_get_property(handle,key){handle=requireHandle(handle);key=requireHandle(key);return __emval_register(handle[key])}function __emval_incref(handle){if(handle>4){emval_handle_array[handle].refcount+=1}}var emval_symbols={};function getStringOrSymbol(address){var symbol=emval_symbols[address];if(symbol===undefined){return readLatin1String(address)}else{return symbol}}function __emval_new_cstring(v){return __emval_register(getStringOrSymbol(v))}function __emval_run_destructors(handle){var destructors=emval_handle_array[handle].value;runDestructors(destructors);__emval_decref(handle)}function __emval_take_value(type,argv){type=requireRegisteredType(type,"_emval_take_value");var v=type["readValueFromPointer"](argv);return __emval_register(v)}function _abort(){abort()}var _emscripten_get_now;if(ENVIRONMENT_IS_NODE){_emscripten_get_now=function(){var t=process["hrtime"]();return t[0]*1e3+t[1]/1e6}}else if(typeof dateNow!=="undefined"){_emscripten_get_now=dateNow}else _emscripten_get_now=function(){return performance.now()};var _emscripten_get_now_is_monotonic=true;function _clock_gettime(clk_id,tp){var now;if(clk_id===0){now=Date.now()}else if((clk_id===1||clk_id===4)&&_emscripten_get_now_is_monotonic){now=_emscripten_get_now()}else{setErrNo(28);return-1}HEAP32[tp>>2]=now/1e3|0;HEAP32[tp+4>>2]=now%1e3*1e3*1e3|0;return 0}function _emscripten_asm_const_int(code,sigPtr,argbuf){var args=readAsmConstArgs(sigPtr,argbuf);return ASM_CONSTS[code].apply(null,args)}function __webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance(ctx){return!!(ctx.dibvbi=ctx.getExtension("WEBGL_draw_instanced_base_vertex_base_instance"))}function __webgl_enable_WEBGL_multi_draw_instanced_base_vertex_base_instance(ctx){return!!(ctx.mdibvbi=ctx.getExtension("WEBGL_multi_draw_instanced_base_vertex_base_instance"))}function __webgl_enable_WEBGL_multi_draw(ctx){return!!(ctx.multiDrawWebgl=ctx.getExtension("WEBGL_multi_draw"))}var GL={counter:1,buffers:[],mappedBuffers:{},programs:[],framebuffers:[],renderbuffers:[],textures:[],uniforms:[],shaders:[],vaos:[],contexts:[],offscreenCanvases:{},timerQueriesEXT:[],queries:[],samplers:[],transformFeedbacks:[],syncs:[],byteSizeByTypeRoot:5120,byteSizeByType:[1,1,2,2,4,4,4,2,3,4,8],programInfos:{},stringCache:{},stringiCache:{},unpackAlignment:4,recordError:function recordError(errorCode){if(!GL.lastError){GL.lastError=errorCode}},getNewId:function(table){var ret=GL.counter++;for(var i=table.length;i>1;var quadIndexes=new Uint16Array(numIndexes);var i=0,v=0;while(1){quadIndexes[i++]=v;if(i>=numIndexes)break;quadIndexes[i++]=v+1;if(i>=numIndexes)break;quadIndexes[i++]=v+2;if(i>=numIndexes)break;quadIndexes[i++]=v;if(i>=numIndexes)break;quadIndexes[i++]=v+2;if(i>=numIndexes)break;quadIndexes[i++]=v+3;if(i>=numIndexes)break;v+=4}context.GLctx.bufferData(34963,quadIndexes,35044);context.GLctx.bindBuffer(34963,null)}},getTempVertexBuffer:function getTempVertexBuffer(sizeBytes){var idx=GL.log2ceilLookup(sizeBytes);var ringbuffer=GL.currentContext.tempVertexBuffers1[idx];var nextFreeBufferIndex=GL.currentContext.tempVertexBufferCounters1[idx];GL.currentContext.tempVertexBufferCounters1[idx]=GL.currentContext.tempVertexBufferCounters1[idx]+1&GL.numTempVertexBuffersPerSize-1;var vbo=ringbuffer[nextFreeBufferIndex];if(vbo){return vbo}var prevVBO=GLctx.getParameter(34964);ringbuffer[nextFreeBufferIndex]=GLctx.createBuffer();GLctx.bindBuffer(34962,ringbuffer[nextFreeBufferIndex]);GLctx.bufferData(34962,1<>2]:-1;source+=UTF8ToString(HEAP32[string+i*4>>2],len<0?undefined:len)}return source},calcBufLength:function calcBufLength(size,type,stride,count){if(stride>0){return count*stride}var typeSize=GL.byteSizeByType[type-GL.byteSizeByTypeRoot];return size*typeSize*count},usedTempBuffers:[],preDrawHandleClientVertexAttribBindings:function preDrawHandleClientVertexAttribBindings(count){GL.resetBufferBinding=false;for(var i=0;i>2)}function _emscripten_glClearBufferiv(buffer,drawbuffer,value){GLctx["clearBufferiv"](buffer,drawbuffer,HEAP32,value>>2)}function _emscripten_glClearBufferuiv(buffer,drawbuffer,value){GLctx["clearBufferuiv"](buffer,drawbuffer,HEAPU32,value>>2)}function _emscripten_glClearColor(x0,x1,x2,x3){GLctx["clearColor"](x0,x1,x2,x3)}function _emscripten_glClearDepthf(x0){GLctx["clearDepth"](x0)}function _emscripten_glClearStencil(x0){GLctx["clearStencil"](x0)}function convertI32PairToI53(lo,hi){return(lo>>>0)+hi*4294967296}function _emscripten_glClientWaitSync(sync,flags,timeoutLo,timeoutHi){return GLctx.clientWaitSync(GL.syncs[sync],flags,convertI32PairToI53(timeoutLo,timeoutHi))}function _emscripten_glColorMask(red,green,blue,alpha){GLctx.colorMask(!!red,!!green,!!blue,!!alpha)}function _emscripten_glCompileShader(shader){GLctx.compileShader(GL.shaders[shader])}function _emscripten_glCompressedTexImage2D(target,level,internalFormat,width,height,border,imageSize,data){if(true){if(GLctx.currentPixelUnpackBufferBinding){GLctx["compressedTexImage2D"](target,level,internalFormat,width,height,border,imageSize,data)}else{GLctx["compressedTexImage2D"](target,level,internalFormat,width,height,border,HEAPU8,data,imageSize)}return}GLctx["compressedTexImage2D"](target,level,internalFormat,width,height,border,data?HEAPU8.subarray(data,data+imageSize):null)}function _emscripten_glCompressedTexImage3D(target,level,internalFormat,width,height,depth,border,imageSize,data){if(GLctx.currentPixelUnpackBufferBinding){GLctx["compressedTexImage3D"](target,level,internalFormat,width,height,depth,border,imageSize,data)}else{GLctx["compressedTexImage3D"](target,level,internalFormat,width,height,depth,border,HEAPU8,data,imageSize)}}function _emscripten_glCompressedTexSubImage2D(target,level,xoffset,yoffset,width,height,format,imageSize,data){if(true){if(GLctx.currentPixelUnpackBufferBinding){GLctx["compressedTexSubImage2D"](target,level,xoffset,yoffset,width,height,format,imageSize,data)}else{GLctx["compressedTexSubImage2D"](target,level,xoffset,yoffset,width,height,format,HEAPU8,data,imageSize)}return}GLctx["compressedTexSubImage2D"](target,level,xoffset,yoffset,width,height,format,data?HEAPU8.subarray(data,data+imageSize):null)}function _emscripten_glCompressedTexSubImage3D(target,level,xoffset,yoffset,zoffset,width,height,depth,format,imageSize,data){if(GLctx.currentPixelUnpackBufferBinding){GLctx["compressedTexSubImage3D"](target,level,xoffset,yoffset,zoffset,width,height,depth,format,imageSize,data)}else{GLctx["compressedTexSubImage3D"](target,level,xoffset,yoffset,zoffset,width,height,depth,format,HEAPU8,data,imageSize)}}function _emscripten_glCopyBufferSubData(x0,x1,x2,x3,x4){GLctx["copyBufferSubData"](x0,x1,x2,x3,x4)}function _emscripten_glCopyTexImage2D(x0,x1,x2,x3,x4,x5,x6,x7){GLctx["copyTexImage2D"](x0,x1,x2,x3,x4,x5,x6,x7)}function _emscripten_glCopyTexSubImage2D(x0,x1,x2,x3,x4,x5,x6,x7){GLctx["copyTexSubImage2D"](x0,x1,x2,x3,x4,x5,x6,x7)}function _emscripten_glCopyTexSubImage3D(x0,x1,x2,x3,x4,x5,x6,x7,x8){GLctx["copyTexSubImage3D"](x0,x1,x2,x3,x4,x5,x6,x7,x8)}function _emscripten_glCreateProgram(){var id=GL.getNewId(GL.programs);var program=GLctx.createProgram();program.name=id;GL.programs[id]=program;return id}function _emscripten_glCreateShader(shaderType){var id=GL.getNewId(GL.shaders);GL.shaders[id]=GLctx.createShader(shaderType);return id}function _emscripten_glCullFace(x0){GLctx["cullFace"](x0)}function _emscripten_glDeleteBuffers(n,buffers){for(var i=0;i>2];var buffer=GL.buffers[id];if(!buffer)continue;GLctx.deleteBuffer(buffer);buffer.name=0;GL.buffers[id]=null;if(id==GLctx.currentArrayBufferBinding)GLctx.currentArrayBufferBinding=0;if(id==GLctx.currentElementArrayBufferBinding)GLctx.currentElementArrayBufferBinding=0;if(id==GLctx.currentPixelPackBufferBinding)GLctx.currentPixelPackBufferBinding=0;if(id==GLctx.currentPixelUnpackBufferBinding)GLctx.currentPixelUnpackBufferBinding=0}}function _emscripten_glDeleteFramebuffers(n,framebuffers){for(var i=0;i>2];var framebuffer=GL.framebuffers[id];if(!framebuffer)continue;GLctx.deleteFramebuffer(framebuffer);framebuffer.name=0;GL.framebuffers[id]=null}}function _emscripten_glDeleteProgram(id){if(!id)return;var program=GL.programs[id];if(!program){GL.recordError(1281);return}GLctx.deleteProgram(program);program.name=0;GL.programs[id]=null;GL.programInfos[id]=null}function _emscripten_glDeleteQueries(n,ids){for(var i=0;i>2];var query=GL.queries[id];if(!query)continue;GLctx["deleteQuery"](query);GL.queries[id]=null}}function _emscripten_glDeleteQueriesEXT(n,ids){for(var i=0;i>2];var query=GL.timerQueriesEXT[id];if(!query)continue;GLctx.disjointTimerQueryExt["deleteQueryEXT"](query);GL.timerQueriesEXT[id]=null}}function _emscripten_glDeleteRenderbuffers(n,renderbuffers){for(var i=0;i>2];var renderbuffer=GL.renderbuffers[id];if(!renderbuffer)continue;GLctx.deleteRenderbuffer(renderbuffer);renderbuffer.name=0;GL.renderbuffers[id]=null}}function _emscripten_glDeleteSamplers(n,samplers){for(var i=0;i>2];var sampler=GL.samplers[id];if(!sampler)continue;GLctx["deleteSampler"](sampler);sampler.name=0;GL.samplers[id]=null}}function _emscripten_glDeleteShader(id){if(!id)return;var shader=GL.shaders[id];if(!shader){GL.recordError(1281);return}GLctx.deleteShader(shader);GL.shaders[id]=null}function _emscripten_glDeleteSync(id){if(!id)return;var sync=GL.syncs[id];if(!sync){GL.recordError(1281);return}GLctx.deleteSync(sync);sync.name=0;GL.syncs[id]=null}function _emscripten_glDeleteTextures(n,textures){for(var i=0;i>2];var texture=GL.textures[id];if(!texture)continue;GLctx.deleteTexture(texture);texture.name=0;GL.textures[id]=null}}function _emscripten_glDeleteTransformFeedbacks(n,ids){for(var i=0;i>2];var transformFeedback=GL.transformFeedbacks[id];if(!transformFeedback)continue;GLctx["deleteTransformFeedback"](transformFeedback);transformFeedback.name=0;GL.transformFeedbacks[id]=null}}function _emscripten_glDeleteVertexArrays(n,vaos){for(var i=0;i>2];GLctx["deleteVertexArray"](GL.vaos[id]);GL.vaos[id]=null}}function _emscripten_glDeleteVertexArraysOES(n,vaos){for(var i=0;i>2];GLctx["deleteVertexArray"](GL.vaos[id]);GL.vaos[id]=null}}function _emscripten_glDepthFunc(x0){GLctx["depthFunc"](x0)}function _emscripten_glDepthMask(flag){GLctx.depthMask(!!flag)}function _emscripten_glDepthRangef(x0,x1){GLctx["depthRange"](x0,x1)}function _emscripten_glDetachShader(program,shader){GLctx.detachShader(GL.programs[program],GL.shaders[shader])}function _emscripten_glDisable(x0){GLctx["disable"](x0)}function _emscripten_glDisableVertexAttribArray(index){var cb=GL.currentContext.clientBuffers[index];cb.enabled=false;GLctx.disableVertexAttribArray(index)}function _emscripten_glDrawArrays(mode,first,count){GL.preDrawHandleClientVertexAttribBindings(first+count);GLctx.drawArrays(mode,first,count);GL.postDrawHandleClientVertexAttribBindings()}function _emscripten_glDrawArraysInstanced(mode,first,count,primcount){GLctx["drawArraysInstanced"](mode,first,count,primcount)}function _emscripten_glDrawArraysInstancedANGLE(mode,first,count,primcount){GLctx["drawArraysInstanced"](mode,first,count,primcount)}function _emscripten_glDrawArraysInstancedARB(mode,first,count,primcount){GLctx["drawArraysInstanced"](mode,first,count,primcount)}function _emscripten_glDrawArraysInstancedEXT(mode,first,count,primcount){GLctx["drawArraysInstanced"](mode,first,count,primcount)}function _emscripten_glDrawArraysInstancedNV(mode,first,count,primcount){GLctx["drawArraysInstanced"](mode,first,count,primcount)}var tempFixedLengthArray=[];function _emscripten_glDrawBuffers(n,bufs){var bufArray=tempFixedLengthArray[n];for(var i=0;i>2]}GLctx["drawBuffers"](bufArray)}function _emscripten_glDrawBuffersEXT(n,bufs){var bufArray=tempFixedLengthArray[n];for(var i=0;i>2]}GLctx["drawBuffers"](bufArray)}function _emscripten_glDrawBuffersWEBGL(n,bufs){var bufArray=tempFixedLengthArray[n];for(var i=0;i>2]}GLctx["drawBuffers"](bufArray)}function _emscripten_glDrawElements(mode,count,type,indices){var buf;if(!GLctx.currentElementArrayBufferBinding){var size=GL.calcBufLength(1,type,0,count);buf=GL.getTempIndexBuffer(size);GLctx.bindBuffer(34963,buf);GLctx.bufferSubData(34963,0,HEAPU8.subarray(indices,indices+size));indices=0}GL.preDrawHandleClientVertexAttribBindings(count);GLctx.drawElements(mode,count,type,indices);GL.postDrawHandleClientVertexAttribBindings(count);if(!GLctx.currentElementArrayBufferBinding){GLctx.bindBuffer(34963,null)}}function _emscripten_glDrawElementsInstanced(mode,count,type,indices,primcount){GLctx["drawElementsInstanced"](mode,count,type,indices,primcount)}function _emscripten_glDrawElementsInstancedANGLE(mode,count,type,indices,primcount){GLctx["drawElementsInstanced"](mode,count,type,indices,primcount)}function _emscripten_glDrawElementsInstancedARB(mode,count,type,indices,primcount){GLctx["drawElementsInstanced"](mode,count,type,indices,primcount)}function _emscripten_glDrawElementsInstancedEXT(mode,count,type,indices,primcount){GLctx["drawElementsInstanced"](mode,count,type,indices,primcount)}function _emscripten_glDrawElementsInstancedNV(mode,count,type,indices,primcount){GLctx["drawElementsInstanced"](mode,count,type,indices,primcount)}function _glDrawElements(mode,count,type,indices){var buf;if(!GLctx.currentElementArrayBufferBinding){var size=GL.calcBufLength(1,type,0,count);buf=GL.getTempIndexBuffer(size);GLctx.bindBuffer(34963,buf);GLctx.bufferSubData(34963,0,HEAPU8.subarray(indices,indices+size));indices=0}GL.preDrawHandleClientVertexAttribBindings(count);GLctx.drawElements(mode,count,type,indices);GL.postDrawHandleClientVertexAttribBindings(count);if(!GLctx.currentElementArrayBufferBinding){GLctx.bindBuffer(34963,null)}}function _emscripten_glDrawRangeElements(mode,start,end,count,type,indices){_glDrawElements(mode,count,type,indices)}function _emscripten_glEnable(x0){GLctx["enable"](x0)}function _emscripten_glEnableVertexAttribArray(index){var cb=GL.currentContext.clientBuffers[index];cb.enabled=true;GLctx.enableVertexAttribArray(index)}function _emscripten_glEndQuery(x0){GLctx["endQuery"](x0)}function _emscripten_glEndQueryEXT(target){GLctx.disjointTimerQueryExt["endQueryEXT"](target)}function _emscripten_glEndTransformFeedback(){GLctx["endTransformFeedback"]()}function _emscripten_glFenceSync(condition,flags){var sync=GLctx.fenceSync(condition,flags);if(sync){var id=GL.getNewId(GL.syncs);sync.name=id;GL.syncs[id]=sync;return id}else{return 0}}function _emscripten_glFinish(){GLctx["finish"]()}function _emscripten_glFlush(){GLctx["flush"]()}function emscriptenWebGLGetBufferBinding(target){switch(target){case 34962:target=34964;break;case 34963:target=34965;break;case 35051:target=35053;break;case 35052:target=35055;break;case 35982:target=35983;break;case 36662:target=36662;break;case 36663:target=36663;break;case 35345:target=35368;break}var buffer=GLctx.getParameter(target);if(buffer)return buffer.name|0;else return 0}function emscriptenWebGLValidateMapBufferTarget(target){switch(target){case 34962:case 34963:case 36662:case 36663:case 35051:case 35052:case 35882:case 35982:case 35345:return true;default:return false}}function _emscripten_glFlushMappedBufferRange(target,offset,length){if(!emscriptenWebGLValidateMapBufferTarget(target)){GL.recordError(1280);err("GL_INVALID_ENUM in glFlushMappedBufferRange");return}var mapping=GL.mappedBuffers[emscriptenWebGLGetBufferBinding(target)];if(!mapping){GL.recordError(1282);err("buffer was never mapped in glFlushMappedBufferRange");return}if(!(mapping.access&16)){GL.recordError(1282);err("buffer was not mapped with GL_MAP_FLUSH_EXPLICIT_BIT in glFlushMappedBufferRange");return}if(offset<0||length<0||offset+length>mapping.length){GL.recordError(1281);err("invalid range in glFlushMappedBufferRange");return}GLctx.bufferSubData(target,mapping.offset,HEAPU8.subarray(mapping.mem+offset,mapping.mem+offset+length))}function _emscripten_glFramebufferRenderbuffer(target,attachment,renderbuffertarget,renderbuffer){GLctx.framebufferRenderbuffer(target,attachment,renderbuffertarget,GL.renderbuffers[renderbuffer])}function _emscripten_glFramebufferTexture2D(target,attachment,textarget,texture,level){GLctx.framebufferTexture2D(target,attachment,textarget,GL.textures[texture],level)}function _emscripten_glFramebufferTextureLayer(target,attachment,texture,level,layer){GLctx.framebufferTextureLayer(target,attachment,GL.textures[texture],level,layer)}function _emscripten_glFrontFace(x0){GLctx["frontFace"](x0)}function __glGenObject(n,buffers,createFunction,objectTable){for(var i=0;i>2]=id}}function _emscripten_glGenBuffers(n,buffers){__glGenObject(n,buffers,"createBuffer",GL.buffers)}function _emscripten_glGenFramebuffers(n,ids){__glGenObject(n,ids,"createFramebuffer",GL.framebuffers)}function _emscripten_glGenQueries(n,ids){__glGenObject(n,ids,"createQuery",GL.queries)}function _emscripten_glGenQueriesEXT(n,ids){for(var i=0;i>2]=0;return}var id=GL.getNewId(GL.timerQueriesEXT);query.name=id;GL.timerQueriesEXT[id]=query;HEAP32[ids+i*4>>2]=id}}function _emscripten_glGenRenderbuffers(n,renderbuffers){__glGenObject(n,renderbuffers,"createRenderbuffer",GL.renderbuffers)}function _emscripten_glGenSamplers(n,samplers){__glGenObject(n,samplers,"createSampler",GL.samplers)}function _emscripten_glGenTextures(n,textures){__glGenObject(n,textures,"createTexture",GL.textures)}function _emscripten_glGenTransformFeedbacks(n,ids){__glGenObject(n,ids,"createTransformFeedback",GL.transformFeedbacks)}function _emscripten_glGenVertexArrays(n,arrays){__glGenObject(n,arrays,"createVertexArray",GL.vaos)}function _emscripten_glGenVertexArraysOES(n,arrays){__glGenObject(n,arrays,"createVertexArray",GL.vaos)}function _emscripten_glGenerateMipmap(x0){GLctx["generateMipmap"](x0)}function __glGetActiveAttribOrUniform(funcName,program,index,bufSize,length,size,type,name){program=GL.programs[program];var info=GLctx[funcName](program,index);if(info){var numBytesWrittenExclNull=name&&stringToUTF8(info.name,name,bufSize);if(length)HEAP32[length>>2]=numBytesWrittenExclNull;if(size)HEAP32[size>>2]=info.size;if(type)HEAP32[type>>2]=info.type}}function _emscripten_glGetActiveAttrib(program,index,bufSize,length,size,type,name){__glGetActiveAttribOrUniform("getActiveAttrib",program,index,bufSize,length,size,type,name)}function _emscripten_glGetActiveUniform(program,index,bufSize,length,size,type,name){__glGetActiveAttribOrUniform("getActiveUniform",program,index,bufSize,length,size,type,name)}function _emscripten_glGetActiveUniformBlockName(program,uniformBlockIndex,bufSize,length,uniformBlockName){program=GL.programs[program];var result=GLctx["getActiveUniformBlockName"](program,uniformBlockIndex);if(!result)return;if(uniformBlockName&&bufSize>0){var numBytesWrittenExclNull=stringToUTF8(result,uniformBlockName,bufSize);if(length)HEAP32[length>>2]=numBytesWrittenExclNull}else{if(length)HEAP32[length>>2]=0}}function _emscripten_glGetActiveUniformBlockiv(program,uniformBlockIndex,pname,params){if(!params){GL.recordError(1281);return}program=GL.programs[program];switch(pname){case 35393:var name=GLctx["getActiveUniformBlockName"](program,uniformBlockIndex);HEAP32[params>>2]=name.length+1;return;default:var result=GLctx["getActiveUniformBlockParameter"](program,uniformBlockIndex,pname);if(!result)return;if(typeof result=="number"){HEAP32[params>>2]=result}else{for(var i=0;i>2]=result[i]}}}}function _emscripten_glGetActiveUniformsiv(program,uniformCount,uniformIndices,pname,params){if(!params){GL.recordError(1281);return}if(uniformCount>0&&uniformIndices==0){GL.recordError(1281);return}program=GL.programs[program];var ids=[];for(var i=0;i>2])}var result=GLctx["getActiveUniforms"](program,ids,pname);if(!result)return;var len=result.length;for(var i=0;i>2]=result[i]}}function _emscripten_glGetAttachedShaders(program,maxCount,count,shaders){var result=GLctx.getAttachedShaders(GL.programs[program]);var len=result.length;if(len>maxCount){len=maxCount}HEAP32[count>>2]=len;for(var i=0;i>2]=id}}function _emscripten_glGetAttribLocation(program,name){return GLctx.getAttribLocation(GL.programs[program],UTF8ToString(name))}function writeI53ToI64(ptr,num){HEAPU32[ptr>>2]=num;HEAPU32[ptr+4>>2]=(num-HEAPU32[ptr>>2])/4294967296}function emscriptenWebGLGet(name_,p,type){if(!p){GL.recordError(1281);return}var ret=undefined;switch(name_){case 36346:ret=1;break;case 36344:if(type!=0&&type!=1){GL.recordError(1280)}return;case 34814:case 36345:ret=0;break;case 34466:var formats=GLctx.getParameter(34467);ret=formats?formats.length:0;break;case 33309:if(GL.currentContext.version<2){GL.recordError(1282);return}var exts=GLctx.getSupportedExtensions()||[];ret=2*exts.length;break;case 33307:case 33308:if(GL.currentContext.version<2){GL.recordError(1280);return}ret=name_==33307?3:0;break}if(ret===undefined){var result=GLctx.getParameter(name_);switch(typeof result){case"number":ret=result;break;case"boolean":ret=result?1:0;break;case"string":GL.recordError(1280);return;case"object":if(result===null){switch(name_){case 34964:case 35725:case 34965:case 36006:case 36007:case 32873:case 34229:case 36662:case 36663:case 35053:case 35055:case 36010:case 35097:case 35869:case 32874:case 36389:case 35983:case 35368:case 34068:{ret=0;break}default:{GL.recordError(1280);return}}}else if(result instanceof Float32Array||result instanceof Uint32Array||result instanceof Int32Array||result instanceof Array){for(var i=0;i>2]=result[i];break;case 2:HEAPF32[p+i*4>>2]=result[i];break;case 4:HEAP8[p+i>>0]=result[i]?1:0;break}}return}else{try{ret=result.name|0}catch(e){GL.recordError(1280);err("GL_INVALID_ENUM in glGet"+type+"v: Unknown object returned from WebGL getParameter("+name_+")! (error: "+e+")");return}}break;default:GL.recordError(1280);err("GL_INVALID_ENUM in glGet"+type+"v: Native code calling glGet"+type+"v("+name_+") and it returns "+result+" of type "+typeof result+"!");return}}switch(type){case 1:writeI53ToI64(p,ret);break;case 0:HEAP32[p>>2]=ret;break;case 2:HEAPF32[p>>2]=ret;break;case 4:HEAP8[p>>0]=ret?1:0;break}}function _emscripten_glGetBooleanv(name_,p){emscriptenWebGLGet(name_,p,4)}function _emscripten_glGetBufferParameteri64v(target,value,data){if(!data){GL.recordError(1281);return}writeI53ToI64(data,GLctx.getBufferParameter(target,value))}function _emscripten_glGetBufferParameteriv(target,value,data){if(!data){GL.recordError(1281);return}HEAP32[data>>2]=GLctx.getBufferParameter(target,value)}function _emscripten_glGetBufferPointerv(target,pname,params){if(pname==35005){var ptr=0;var mappedBuffer=GL.mappedBuffers[emscriptenWebGLGetBufferBinding(target)];if(mappedBuffer){ptr=mappedBuffer.mem}HEAP32[params>>2]=ptr}else{GL.recordError(1280);err("GL_INVALID_ENUM in glGetBufferPointerv")}}function _emscripten_glGetError(){var error=GLctx.getError()||GL.lastError;GL.lastError=0;return error}function _emscripten_glGetFloatv(name_,p){emscriptenWebGLGet(name_,p,2)}function _emscripten_glGetFragDataLocation(program,name){return GLctx["getFragDataLocation"](GL.programs[program],UTF8ToString(name))}function _emscripten_glGetFramebufferAttachmentParameteriv(target,attachment,pname,params){var result=GLctx.getFramebufferAttachmentParameter(target,attachment,pname);if(result instanceof WebGLRenderbuffer||result instanceof WebGLTexture){result=result.name|0}HEAP32[params>>2]=result}function emscriptenWebGLGetIndexed(target,index,data,type){if(!data){GL.recordError(1281);return}var result=GLctx["getIndexedParameter"](target,index);var ret;switch(typeof result){case"boolean":ret=result?1:0;break;case"number":ret=result;break;case"object":if(result===null){switch(target){case 35983:case 35368:ret=0;break;default:{GL.recordError(1280);return}}}else if(result instanceof WebGLBuffer){ret=result.name|0}else{GL.recordError(1280);return}break;default:GL.recordError(1280);return}switch(type){case 1:writeI53ToI64(data,ret);break;case 0:HEAP32[data>>2]=ret;break;case 2:HEAPF32[data>>2]=ret;break;case 4:HEAP8[data>>0]=ret?1:0;break;default:throw"internal emscriptenWebGLGetIndexed() error, bad type: "+type}}function _emscripten_glGetInteger64i_v(target,index,data){emscriptenWebGLGetIndexed(target,index,data,1)}function _emscripten_glGetInteger64v(name_,p){emscriptenWebGLGet(name_,p,1)}function _emscripten_glGetIntegeri_v(target,index,data){emscriptenWebGLGetIndexed(target,index,data,0)}function _emscripten_glGetIntegerv(name_,p){emscriptenWebGLGet(name_,p,0)}function _emscripten_glGetInternalformativ(target,internalformat,pname,bufSize,params){if(bufSize<0){GL.recordError(1281);return}if(!params){GL.recordError(1281);return}var ret=GLctx["getInternalformatParameter"](target,internalformat,pname);if(ret===null)return;for(var i=0;i>2]=ret[i]}}function _emscripten_glGetProgramBinary(program,bufSize,length,binaryFormat,binary){GL.recordError(1282)}function _emscripten_glGetProgramInfoLog(program,maxLength,length,infoLog){var log=GLctx.getProgramInfoLog(GL.programs[program]);if(log===null)log="(unknown error)";var numBytesWrittenExclNull=maxLength>0&&infoLog?stringToUTF8(log,infoLog,maxLength):0;if(length)HEAP32[length>>2]=numBytesWrittenExclNull}function _emscripten_glGetProgramiv(program,pname,p){if(!p){GL.recordError(1281);return}if(program>=GL.counter){GL.recordError(1281);return}var ptable=GL.programInfos[program];if(!ptable){GL.recordError(1282);return}if(pname==35716){var log=GLctx.getProgramInfoLog(GL.programs[program]);if(log===null)log="(unknown error)";HEAP32[p>>2]=log.length+1}else if(pname==35719){HEAP32[p>>2]=ptable.maxUniformLength}else if(pname==35722){if(ptable.maxAttributeLength==-1){program=GL.programs[program];var numAttribs=GLctx.getProgramParameter(program,35721);ptable.maxAttributeLength=0;for(var i=0;i>2]=ptable.maxAttributeLength}else if(pname==35381){if(ptable.maxUniformBlockNameLength==-1){program=GL.programs[program];var numBlocks=GLctx.getProgramParameter(program,35382);ptable.maxUniformBlockNameLength=0;for(var i=0;i>2]=ptable.maxUniformBlockNameLength}else{HEAP32[p>>2]=GLctx.getProgramParameter(GL.programs[program],pname)}}function _emscripten_glGetQueryObjecti64vEXT(id,pname,params){if(!params){GL.recordError(1281);return}var query=GL.timerQueriesEXT[id];var param=GLctx.disjointTimerQueryExt["getQueryObjectEXT"](query,pname);var ret;if(typeof param=="boolean"){ret=param?1:0}else{ret=param}writeI53ToI64(params,ret)}function _emscripten_glGetQueryObjectivEXT(id,pname,params){if(!params){GL.recordError(1281);return}var query=GL.timerQueriesEXT[id];var param=GLctx.disjointTimerQueryExt["getQueryObjectEXT"](query,pname);var ret;if(typeof param=="boolean"){ret=param?1:0}else{ret=param}HEAP32[params>>2]=ret}function _emscripten_glGetQueryObjectui64vEXT(id,pname,params){if(!params){GL.recordError(1281);return}var query=GL.timerQueriesEXT[id];var param=GLctx.disjointTimerQueryExt["getQueryObjectEXT"](query,pname);var ret;if(typeof param=="boolean"){ret=param?1:0}else{ret=param}writeI53ToI64(params,ret)}function _emscripten_glGetQueryObjectuiv(id,pname,params){if(!params){GL.recordError(1281);return}var query=GL.queries[id];var param=GLctx["getQueryParameter"](query,pname);var ret;if(typeof param=="boolean"){ret=param?1:0}else{ret=param}HEAP32[params>>2]=ret}function _emscripten_glGetQueryObjectuivEXT(id,pname,params){if(!params){GL.recordError(1281);return}var query=GL.timerQueriesEXT[id];var param=GLctx.disjointTimerQueryExt["getQueryObjectEXT"](query,pname);var ret;if(typeof param=="boolean"){ret=param?1:0}else{ret=param}HEAP32[params>>2]=ret}function _emscripten_glGetQueryiv(target,pname,params){if(!params){GL.recordError(1281);return}HEAP32[params>>2]=GLctx["getQuery"](target,pname)}function _emscripten_glGetQueryivEXT(target,pname,params){if(!params){GL.recordError(1281);return}HEAP32[params>>2]=GLctx.disjointTimerQueryExt["getQueryEXT"](target,pname)}function _emscripten_glGetRenderbufferParameteriv(target,pname,params){if(!params){GL.recordError(1281);return}HEAP32[params>>2]=GLctx.getRenderbufferParameter(target,pname)}function _emscripten_glGetSamplerParameterfv(sampler,pname,params){if(!params){GL.recordError(1281);return}sampler=GL.samplers[sampler];HEAPF32[params>>2]=GLctx["getSamplerParameter"](sampler,pname)}function _emscripten_glGetSamplerParameteriv(sampler,pname,params){if(!params){GL.recordError(1281);return}sampler=GL.samplers[sampler];HEAP32[params>>2]=GLctx["getSamplerParameter"](sampler,pname)}function _emscripten_glGetShaderInfoLog(shader,maxLength,length,infoLog){var log=GLctx.getShaderInfoLog(GL.shaders[shader]);if(log===null)log="(unknown error)";var numBytesWrittenExclNull=maxLength>0&&infoLog?stringToUTF8(log,infoLog,maxLength):0;if(length)HEAP32[length>>2]=numBytesWrittenExclNull}function _emscripten_glGetShaderPrecisionFormat(shaderType,precisionType,range,precision){var result=GLctx.getShaderPrecisionFormat(shaderType,precisionType);HEAP32[range>>2]=result.rangeMin;HEAP32[range+4>>2]=result.rangeMax;HEAP32[precision>>2]=result.precision}function _emscripten_glGetShaderSource(shader,bufSize,length,source){var result=GLctx.getShaderSource(GL.shaders[shader]);if(!result)return;var numBytesWrittenExclNull=bufSize>0&&source?stringToUTF8(result,source,bufSize):0;if(length)HEAP32[length>>2]=numBytesWrittenExclNull}function _emscripten_glGetShaderiv(shader,pname,p){if(!p){GL.recordError(1281);return}if(pname==35716){var log=GLctx.getShaderInfoLog(GL.shaders[shader]);if(log===null)log="(unknown error)";var logLength=log?log.length+1:0;HEAP32[p>>2]=logLength}else if(pname==35720){var source=GLctx.getShaderSource(GL.shaders[shader]);var sourceLength=source?source.length+1:0;HEAP32[p>>2]=sourceLength}else{HEAP32[p>>2]=GLctx.getShaderParameter(GL.shaders[shader],pname)}}function stringToNewUTF8(jsString){var length=lengthBytesUTF8(jsString)+1;var cString=_malloc(length);stringToUTF8(jsString,cString,length);return cString}function _emscripten_glGetString(name_){if(GL.stringCache[name_])return GL.stringCache[name_];var ret;switch(name_){case 7939:var exts=GLctx.getSupportedExtensions()||[];exts=exts.concat(exts.map(function(e){return"GL_"+e}));ret=stringToNewUTF8(exts.join(" "));break;case 7936:case 7937:case 37445:case 37446:var s=GLctx.getParameter(name_);if(!s){GL.recordError(1280)}ret=stringToNewUTF8(s);break;case 7938:var glVersion=GLctx.getParameter(7938);if(true)glVersion="OpenGL ES 3.0 ("+glVersion+")";else{glVersion="OpenGL ES 2.0 ("+glVersion+")"}ret=stringToNewUTF8(glVersion);break;case 35724:var glslVersion=GLctx.getParameter(35724);var ver_re=/^WebGL GLSL ES ([0-9]\.[0-9][0-9]?)(?:$| .*)/;var ver_num=glslVersion.match(ver_re);if(ver_num!==null){if(ver_num[1].length==3)ver_num[1]=ver_num[1]+"0";glslVersion="OpenGL ES GLSL ES "+ver_num[1]+" ("+glslVersion+")"}ret=stringToNewUTF8(glslVersion);break;default:GL.recordError(1280);return 0}GL.stringCache[name_]=ret;return ret}function _emscripten_glGetStringi(name,index){if(GL.currentContext.version<2){GL.recordError(1282);return 0}var stringiCache=GL.stringiCache[name];if(stringiCache){if(index<0||index>=stringiCache.length){GL.recordError(1281);return 0}return stringiCache[index]}switch(name){case 7939:var exts=GLctx.getSupportedExtensions()||[];exts=exts.concat(exts.map(function(e){return"GL_"+e}));exts=exts.map(function(e){return stringToNewUTF8(e)});stringiCache=GL.stringiCache[name]=exts;if(index<0||index>=stringiCache.length){GL.recordError(1281);return 0}return stringiCache[index];default:GL.recordError(1280);return 0}}function _emscripten_glGetSynciv(sync,pname,bufSize,length,values){if(bufSize<0){GL.recordError(1281);return}if(!values){GL.recordError(1281);return}var ret=GLctx.getSyncParameter(GL.syncs[sync],pname);HEAP32[length>>2]=ret;if(ret!==null&&length)HEAP32[length>>2]=1}function _emscripten_glGetTexParameterfv(target,pname,params){if(!params){GL.recordError(1281);return}HEAPF32[params>>2]=GLctx.getTexParameter(target,pname)}function _emscripten_glGetTexParameteriv(target,pname,params){if(!params){GL.recordError(1281);return}HEAP32[params>>2]=GLctx.getTexParameter(target,pname)}function _emscripten_glGetTransformFeedbackVarying(program,index,bufSize,length,size,type,name){program=GL.programs[program];var info=GLctx["getTransformFeedbackVarying"](program,index);if(!info)return;if(name&&bufSize>0){var numBytesWrittenExclNull=stringToUTF8(info.name,name,bufSize);if(length)HEAP32[length>>2]=numBytesWrittenExclNull}else{if(length)HEAP32[length>>2]=0}if(size)HEAP32[size>>2]=info.size;if(type)HEAP32[type>>2]=info.type}function _emscripten_glGetUniformBlockIndex(program,uniformBlockName){return GLctx["getUniformBlockIndex"](GL.programs[program],UTF8ToString(uniformBlockName))}function _emscripten_glGetUniformIndices(program,uniformCount,uniformNames,uniformIndices){if(!uniformIndices){GL.recordError(1281);return}if(uniformCount>0&&(uniformNames==0||uniformIndices==0)){GL.recordError(1281);return}program=GL.programs[program];var names=[];for(var i=0;i>2]));var result=GLctx["getUniformIndices"](program,names);if(!result)return;var len=result.length;for(var i=0;i>2]=result[i]}}function jstoi_q(str){return parseInt(str)}function _emscripten_glGetUniformLocation(program,name){name=UTF8ToString(name);var arrayIndex=0;if(name[name.length-1]=="]"){var leftBrace=name.lastIndexOf("[");arrayIndex=name[leftBrace+1]!="]"?jstoi_q(name.slice(leftBrace+1)):0;name=name.slice(0,leftBrace)}var uniformInfo=GL.programInfos[program]&&GL.programInfos[program].uniforms[name];if(uniformInfo&&arrayIndex>=0&&arrayIndex>2]=data;break;case 2:HEAPF32[params>>2]=data;break}}else{for(var i=0;i>2]=data[i];break;case 2:HEAPF32[params+i*4>>2]=data[i];break}}}}function _emscripten_glGetUniformfv(program,location,params){emscriptenWebGLGetUniform(program,location,params,2)}function _emscripten_glGetUniformiv(program,location,params){emscriptenWebGLGetUniform(program,location,params,0)}function _emscripten_glGetUniformuiv(program,location,params){emscriptenWebGLGetUniform(program,location,params,0)}function emscriptenWebGLGetVertexAttrib(index,pname,params,type){if(!params){GL.recordError(1281);return}if(GL.currentContext.clientBuffers[index].enabled){err("glGetVertexAttrib*v on client-side array: not supported, bad data returned")}var data=GLctx.getVertexAttrib(index,pname);if(pname==34975){HEAP32[params>>2]=data&&data["name"]}else if(typeof data=="number"||typeof data=="boolean"){switch(type){case 0:HEAP32[params>>2]=data;break;case 2:HEAPF32[params>>2]=data;break;case 5:HEAP32[params>>2]=Math.fround(data);break}}else{for(var i=0;i>2]=data[i];break;case 2:HEAPF32[params+i*4>>2]=data[i];break;case 5:HEAP32[params+i*4>>2]=Math.fround(data[i]);break}}}}function _emscripten_glGetVertexAttribIiv(index,pname,params){emscriptenWebGLGetVertexAttrib(index,pname,params,0)}function _emscripten_glGetVertexAttribIuiv(index,pname,params){emscriptenWebGLGetVertexAttrib(index,pname,params,0)}function _emscripten_glGetVertexAttribPointerv(index,pname,pointer){if(!pointer){GL.recordError(1281);return}if(GL.currentContext.clientBuffers[index].enabled){err("glGetVertexAttribPointer on client-side array: not supported, bad data returned")}HEAP32[pointer>>2]=GLctx.getVertexAttribOffset(index,pname)}function _emscripten_glGetVertexAttribfv(index,pname,params){emscriptenWebGLGetVertexAttrib(index,pname,params,2)}function _emscripten_glGetVertexAttribiv(index,pname,params){emscriptenWebGLGetVertexAttrib(index,pname,params,5)}function _emscripten_glHint(x0,x1){GLctx["hint"](x0,x1)}function _emscripten_glInvalidateFramebuffer(target,numAttachments,attachments){var list=tempFixedLengthArray[numAttachments];for(var i=0;i>2]}GLctx["invalidateFramebuffer"](target,list)}function _emscripten_glInvalidateSubFramebuffer(target,numAttachments,attachments,x,y,width,height){var list=tempFixedLengthArray[numAttachments];for(var i=0;i>2]}GLctx["invalidateSubFramebuffer"](target,list,x,y,width,height)}function _emscripten_glIsBuffer(buffer){var b=GL.buffers[buffer];if(!b)return 0;return GLctx.isBuffer(b)}function _emscripten_glIsEnabled(x0){return GLctx["isEnabled"](x0)}function _emscripten_glIsFramebuffer(framebuffer){var fb=GL.framebuffers[framebuffer];if(!fb)return 0;return GLctx.isFramebuffer(fb)}function _emscripten_glIsProgram(program){program=GL.programs[program];if(!program)return 0;return GLctx.isProgram(program)}function _emscripten_glIsQuery(id){var query=GL.queries[id];if(!query)return 0;return GLctx["isQuery"](query)}function _emscripten_glIsQueryEXT(id){var query=GL.timerQueriesEXT[id];if(!query)return 0;return GLctx.disjointTimerQueryExt["isQueryEXT"](query)}function _emscripten_glIsRenderbuffer(renderbuffer){var rb=GL.renderbuffers[renderbuffer];if(!rb)return 0;return GLctx.isRenderbuffer(rb)}function _emscripten_glIsSampler(id){var sampler=GL.samplers[id];if(!sampler)return 0;return GLctx["isSampler"](sampler)}function _emscripten_glIsShader(shader){var s=GL.shaders[shader];if(!s)return 0;return GLctx.isShader(s)}function _emscripten_glIsSync(sync){return GLctx.isSync(GL.syncs[sync])}function _emscripten_glIsTexture(id){var texture=GL.textures[id];if(!texture)return 0;return GLctx.isTexture(texture)}function _emscripten_glIsTransformFeedback(id){return GLctx["isTransformFeedback"](GL.transformFeedbacks[id])}function _emscripten_glIsVertexArray(array){var vao=GL.vaos[array];if(!vao)return 0;return GLctx["isVertexArray"](vao)}function _emscripten_glIsVertexArrayOES(array){var vao=GL.vaos[array];if(!vao)return 0;return GLctx["isVertexArray"](vao)}function _emscripten_glLineWidth(x0){GLctx["lineWidth"](x0)}function _emscripten_glLinkProgram(program){GLctx.linkProgram(GL.programs[program]);GL.populateUniformTable(program)}function _emscripten_glMapBufferRange(target,offset,length,access){if(access!=26&&access!=10){err("glMapBufferRange is only supported when access is MAP_WRITE|INVALIDATE_BUFFER");return 0}if(!emscriptenWebGLValidateMapBufferTarget(target)){GL.recordError(1280);err("GL_INVALID_ENUM in glMapBufferRange");return 0}var mem=_malloc(length);if(!mem)return 0;GL.mappedBuffers[emscriptenWebGLGetBufferBinding(target)]={offset:offset,length:length,mem:mem,access:access};return mem}function _emscripten_glPauseTransformFeedback(){GLctx["pauseTransformFeedback"]()}function _emscripten_glPixelStorei(pname,param){if(pname==3317){GL.unpackAlignment=param}GLctx.pixelStorei(pname,param)}function _emscripten_glPolygonOffset(x0,x1){GLctx["polygonOffset"](x0,x1)}function _emscripten_glProgramBinary(program,binaryFormat,binary,length){GL.recordError(1280)}function _emscripten_glProgramParameteri(program,pname,value){GL.recordError(1280)}function _emscripten_glQueryCounterEXT(id,target){GLctx.disjointTimerQueryExt["queryCounterEXT"](GL.timerQueriesEXT[id],target)}function _emscripten_glReadBuffer(x0){GLctx["readBuffer"](x0)}function computeUnpackAlignedImageSize(width,height,sizePerPixel,alignment){function roundedToNextMultipleOf(x,y){return x+y-1&-y}var plainRowSize=width*sizePerPixel;var alignedRowSize=roundedToNextMultipleOf(plainRowSize,alignment);return height*alignedRowSize}function __colorChannelsInGlTextureFormat(format){var colorChannels={5:3,6:4,8:2,29502:3,29504:4,26917:2,26918:2,29846:3,29847:4};return colorChannels[format-6402]||1}function heapObjectForWebGLType(type){type-=5120;if(type==0)return HEAP8;if(type==1)return HEAPU8;if(type==2)return HEAP16;if(type==4)return HEAP32;if(type==6)return HEAPF32;if(type==5||type==28922||type==28520||type==30779||type==30782)return HEAPU32;return HEAPU16}function heapAccessShiftForWebGLHeap(heap){return 31-Math.clz32(heap.BYTES_PER_ELEMENT)}function emscriptenWebGLGetTexPixelData(type,format,width,height,pixels,internalFormat){var heap=heapObjectForWebGLType(type);var shift=heapAccessShiftForWebGLHeap(heap);var byteSize=1<>shift,pixels+bytes>>shift)}function _emscripten_glReadPixels(x,y,width,height,format,type,pixels){if(true){if(GLctx.currentPixelPackBufferBinding){GLctx.readPixels(x,y,width,height,format,type,pixels)}else{var heap=heapObjectForWebGLType(type);GLctx.readPixels(x,y,width,height,format,type,heap,pixels>>heapAccessShiftForWebGLHeap(heap))}return}var pixelData=emscriptenWebGLGetTexPixelData(type,format,width,height,pixels,format);if(!pixelData){GL.recordError(1280);return}GLctx.readPixels(x,y,width,height,format,type,pixelData)}function _emscripten_glReleaseShaderCompiler(){}function _emscripten_glRenderbufferStorage(x0,x1,x2,x3){GLctx["renderbufferStorage"](x0,x1,x2,x3)}function _emscripten_glRenderbufferStorageMultisample(x0,x1,x2,x3,x4){GLctx["renderbufferStorageMultisample"](x0,x1,x2,x3,x4)}function _emscripten_glResumeTransformFeedback(){GLctx["resumeTransformFeedback"]()}function _emscripten_glSampleCoverage(value,invert){GLctx.sampleCoverage(value,!!invert)}function _emscripten_glSamplerParameterf(sampler,pname,param){GLctx["samplerParameterf"](GL.samplers[sampler],pname,param)}function _emscripten_glSamplerParameterfv(sampler,pname,params){var param=HEAPF32[params>>2];GLctx["samplerParameterf"](GL.samplers[sampler],pname,param)}function _emscripten_glSamplerParameteri(sampler,pname,param){GLctx["samplerParameteri"](GL.samplers[sampler],pname,param)}function _emscripten_glSamplerParameteriv(sampler,pname,params){var param=HEAP32[params>>2];GLctx["samplerParameteri"](GL.samplers[sampler],pname,param)}function _emscripten_glScissor(x0,x1,x2,x3){GLctx["scissor"](x0,x1,x2,x3)}function _emscripten_glShaderBinary(){GL.recordError(1280)}function _emscripten_glShaderSource(shader,count,string,length){var source=GL.getSource(shader,count,string,length);GLctx.shaderSource(GL.shaders[shader],source)}function _emscripten_glStencilFunc(x0,x1,x2){GLctx["stencilFunc"](x0,x1,x2)}function _emscripten_glStencilFuncSeparate(x0,x1,x2,x3){GLctx["stencilFuncSeparate"](x0,x1,x2,x3)}function _emscripten_glStencilMask(x0){GLctx["stencilMask"](x0)}function _emscripten_glStencilMaskSeparate(x0,x1){GLctx["stencilMaskSeparate"](x0,x1)}function _emscripten_glStencilOp(x0,x1,x2){GLctx["stencilOp"](x0,x1,x2)}function _emscripten_glStencilOpSeparate(x0,x1,x2,x3){GLctx["stencilOpSeparate"](x0,x1,x2,x3)}function _emscripten_glTexImage2D(target,level,internalFormat,width,height,border,format,type,pixels){if(true){if(GLctx.currentPixelUnpackBufferBinding){GLctx.texImage2D(target,level,internalFormat,width,height,border,format,type,pixels)}else if(pixels){var heap=heapObjectForWebGLType(type);GLctx.texImage2D(target,level,internalFormat,width,height,border,format,type,heap,pixels>>heapAccessShiftForWebGLHeap(heap))}else{GLctx.texImage2D(target,level,internalFormat,width,height,border,format,type,null)}return}GLctx.texImage2D(target,level,internalFormat,width,height,border,format,type,pixels?emscriptenWebGLGetTexPixelData(type,format,width,height,pixels,internalFormat):null)}function _emscripten_glTexImage3D(target,level,internalFormat,width,height,depth,border,format,type,pixels){if(GLctx.currentPixelUnpackBufferBinding){GLctx["texImage3D"](target,level,internalFormat,width,height,depth,border,format,type,pixels)}else if(pixels){var heap=heapObjectForWebGLType(type);GLctx["texImage3D"](target,level,internalFormat,width,height,depth,border,format,type,heap,pixels>>heapAccessShiftForWebGLHeap(heap))}else{GLctx["texImage3D"](target,level,internalFormat,width,height,depth,border,format,type,null)}}function _emscripten_glTexParameterf(x0,x1,x2){GLctx["texParameterf"](x0,x1,x2)}function _emscripten_glTexParameterfv(target,pname,params){var param=HEAPF32[params>>2];GLctx.texParameterf(target,pname,param)}function _emscripten_glTexParameteri(x0,x1,x2){GLctx["texParameteri"](x0,x1,x2)}function _emscripten_glTexParameteriv(target,pname,params){var param=HEAP32[params>>2];GLctx.texParameteri(target,pname,param)}function _emscripten_glTexStorage2D(x0,x1,x2,x3,x4){GLctx["texStorage2D"](x0,x1,x2,x3,x4)}function _emscripten_glTexStorage3D(x0,x1,x2,x3,x4,x5){GLctx["texStorage3D"](x0,x1,x2,x3,x4,x5)}function _emscripten_glTexSubImage2D(target,level,xoffset,yoffset,width,height,format,type,pixels){if(true){if(GLctx.currentPixelUnpackBufferBinding){GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,pixels)}else if(pixels){var heap=heapObjectForWebGLType(type);GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,heap,pixels>>heapAccessShiftForWebGLHeap(heap))}else{GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,null)}return}var pixelData=null;if(pixels)pixelData=emscriptenWebGLGetTexPixelData(type,format,width,height,pixels,0);GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,pixelData)}function _emscripten_glTexSubImage3D(target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,pixels){if(GLctx.currentPixelUnpackBufferBinding){GLctx["texSubImage3D"](target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,pixels)}else if(pixels){var heap=heapObjectForWebGLType(type);GLctx["texSubImage3D"](target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,heap,pixels>>heapAccessShiftForWebGLHeap(heap))}else{GLctx["texSubImage3D"](target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,null)}}function _emscripten_glTransformFeedbackVaryings(program,count,varyings,bufferMode){program=GL.programs[program];var vars=[];for(var i=0;i>2]));GLctx["transformFeedbackVaryings"](program,vars,bufferMode)}function _emscripten_glUniform1f(location,v0){GLctx.uniform1f(GL.uniforms[location],v0)}function _emscripten_glUniform1fv(location,count,value){GLctx.uniform1fv(GL.uniforms[location],HEAPF32,value>>2,count)}function _emscripten_glUniform1i(location,v0){GLctx.uniform1i(GL.uniforms[location],v0)}function _emscripten_glUniform1iv(location,count,value){GLctx.uniform1iv(GL.uniforms[location],HEAP32,value>>2,count)}function _emscripten_glUniform1ui(location,v0){GLctx.uniform1ui(GL.uniforms[location],v0)}function _emscripten_glUniform1uiv(location,count,value){GLctx.uniform1uiv(GL.uniforms[location],HEAPU32,value>>2,count)}function _emscripten_glUniform2f(location,v0,v1){GLctx.uniform2f(GL.uniforms[location],v0,v1)}function _emscripten_glUniform2fv(location,count,value){GLctx.uniform2fv(GL.uniforms[location],HEAPF32,value>>2,count*2)}function _emscripten_glUniform2i(location,v0,v1){GLctx.uniform2i(GL.uniforms[location],v0,v1)}function _emscripten_glUniform2iv(location,count,value){GLctx.uniform2iv(GL.uniforms[location],HEAP32,value>>2,count*2)}function _emscripten_glUniform2ui(location,v0,v1){GLctx.uniform2ui(GL.uniforms[location],v0,v1)}function _emscripten_glUniform2uiv(location,count,value){GLctx.uniform2uiv(GL.uniforms[location],HEAPU32,value>>2,count*2)}function _emscripten_glUniform3f(location,v0,v1,v2){GLctx.uniform3f(GL.uniforms[location],v0,v1,v2)}function _emscripten_glUniform3fv(location,count,value){GLctx.uniform3fv(GL.uniforms[location],HEAPF32,value>>2,count*3)}function _emscripten_glUniform3i(location,v0,v1,v2){GLctx.uniform3i(GL.uniforms[location],v0,v1,v2)}function _emscripten_glUniform3iv(location,count,value){GLctx.uniform3iv(GL.uniforms[location],HEAP32,value>>2,count*3)}function _emscripten_glUniform3ui(location,v0,v1,v2){GLctx.uniform3ui(GL.uniforms[location],v0,v1,v2)}function _emscripten_glUniform3uiv(location,count,value){GLctx.uniform3uiv(GL.uniforms[location],HEAPU32,value>>2,count*3)}function _emscripten_glUniform4f(location,v0,v1,v2,v3){GLctx.uniform4f(GL.uniforms[location],v0,v1,v2,v3)}function _emscripten_glUniform4fv(location,count,value){GLctx.uniform4fv(GL.uniforms[location],HEAPF32,value>>2,count*4)}function _emscripten_glUniform4i(location,v0,v1,v2,v3){GLctx.uniform4i(GL.uniforms[location],v0,v1,v2,v3)}function _emscripten_glUniform4iv(location,count,value){GLctx.uniform4iv(GL.uniforms[location],HEAP32,value>>2,count*4)}function _emscripten_glUniform4ui(location,v0,v1,v2,v3){GLctx.uniform4ui(GL.uniforms[location],v0,v1,v2,v3)}function _emscripten_glUniform4uiv(location,count,value){GLctx.uniform4uiv(GL.uniforms[location],HEAPU32,value>>2,count*4)}function _emscripten_glUniformBlockBinding(program,uniformBlockIndex,uniformBlockBinding){program=GL.programs[program];GLctx["uniformBlockBinding"](program,uniformBlockIndex,uniformBlockBinding)}function _emscripten_glUniformMatrix2fv(location,count,transpose,value){GLctx.uniformMatrix2fv(GL.uniforms[location],!!transpose,HEAPF32,value>>2,count*4)}function _emscripten_glUniformMatrix2x3fv(location,count,transpose,value){GLctx.uniformMatrix2x3fv(GL.uniforms[location],!!transpose,HEAPF32,value>>2,count*6)}function _emscripten_glUniformMatrix2x4fv(location,count,transpose,value){GLctx.uniformMatrix2x4fv(GL.uniforms[location],!!transpose,HEAPF32,value>>2,count*8)}function _emscripten_glUniformMatrix3fv(location,count,transpose,value){GLctx.uniformMatrix3fv(GL.uniforms[location],!!transpose,HEAPF32,value>>2,count*9)}function _emscripten_glUniformMatrix3x2fv(location,count,transpose,value){GLctx.uniformMatrix3x2fv(GL.uniforms[location],!!transpose,HEAPF32,value>>2,count*6)}function _emscripten_glUniformMatrix3x4fv(location,count,transpose,value){GLctx.uniformMatrix3x4fv(GL.uniforms[location],!!transpose,HEAPF32,value>>2,count*12)}function _emscripten_glUniformMatrix4fv(location,count,transpose,value){GLctx.uniformMatrix4fv(GL.uniforms[location],!!transpose,HEAPF32,value>>2,count*16)}function _emscripten_glUniformMatrix4x2fv(location,count,transpose,value){GLctx.uniformMatrix4x2fv(GL.uniforms[location],!!transpose,HEAPF32,value>>2,count*8)}function _emscripten_glUniformMatrix4x3fv(location,count,transpose,value){GLctx.uniformMatrix4x3fv(GL.uniforms[location],!!transpose,HEAPF32,value>>2,count*12)}function _emscripten_glUnmapBuffer(target){if(!emscriptenWebGLValidateMapBufferTarget(target)){GL.recordError(1280);err("GL_INVALID_ENUM in glUnmapBuffer");return 0}var buffer=emscriptenWebGLGetBufferBinding(target);var mapping=GL.mappedBuffers[buffer];if(!mapping){GL.recordError(1282);err("buffer was never mapped in glUnmapBuffer");return 0}GL.mappedBuffers[buffer]=null;if(!(mapping.access&16))if(true){GLctx.bufferSubData(target,mapping.offset,HEAPU8,mapping.mem,mapping.length)}else{GLctx.bufferSubData(target,mapping.offset,HEAPU8.subarray(mapping.mem,mapping.mem+mapping.length))}_free(mapping.mem);return 1}function _emscripten_glUseProgram(program){GLctx.useProgram(GL.programs[program])}function _emscripten_glValidateProgram(program){GLctx.validateProgram(GL.programs[program])}function _emscripten_glVertexAttrib1f(x0,x1){GLctx["vertexAttrib1f"](x0,x1)}function _emscripten_glVertexAttrib1fv(index,v){GLctx.vertexAttrib1f(index,HEAPF32[v>>2])}function _emscripten_glVertexAttrib2f(x0,x1,x2){GLctx["vertexAttrib2f"](x0,x1,x2)}function _emscripten_glVertexAttrib2fv(index,v){GLctx.vertexAttrib2f(index,HEAPF32[v>>2],HEAPF32[v+4>>2])}function _emscripten_glVertexAttrib3f(x0,x1,x2,x3){GLctx["vertexAttrib3f"](x0,x1,x2,x3)}function _emscripten_glVertexAttrib3fv(index,v){GLctx.vertexAttrib3f(index,HEAPF32[v>>2],HEAPF32[v+4>>2],HEAPF32[v+8>>2])}function _emscripten_glVertexAttrib4f(x0,x1,x2,x3,x4){GLctx["vertexAttrib4f"](x0,x1,x2,x3,x4)}function _emscripten_glVertexAttrib4fv(index,v){GLctx.vertexAttrib4f(index,HEAPF32[v>>2],HEAPF32[v+4>>2],HEAPF32[v+8>>2],HEAPF32[v+12>>2])}function _emscripten_glVertexAttribDivisor(index,divisor){GLctx["vertexAttribDivisor"](index,divisor)}function _emscripten_glVertexAttribDivisorANGLE(index,divisor){GLctx["vertexAttribDivisor"](index,divisor)}function _emscripten_glVertexAttribDivisorARB(index,divisor){GLctx["vertexAttribDivisor"](index,divisor)}function _emscripten_glVertexAttribDivisorEXT(index,divisor){GLctx["vertexAttribDivisor"](index,divisor)}function _emscripten_glVertexAttribDivisorNV(index,divisor){GLctx["vertexAttribDivisor"](index,divisor)}function _emscripten_glVertexAttribI4i(x0,x1,x2,x3,x4){GLctx["vertexAttribI4i"](x0,x1,x2,x3,x4)}function _emscripten_glVertexAttribI4iv(index,v){GLctx.vertexAttribI4i(index,HEAP32[v>>2],HEAP32[v+4>>2],HEAP32[v+8>>2],HEAP32[v+12>>2])}function _emscripten_glVertexAttribI4ui(x0,x1,x2,x3,x4){GLctx["vertexAttribI4ui"](x0,x1,x2,x3,x4)}function _emscripten_glVertexAttribI4uiv(index,v){GLctx.vertexAttribI4ui(index,HEAPU32[v>>2],HEAPU32[v+4>>2],HEAPU32[v+8>>2],HEAPU32[v+12>>2])}function _emscripten_glVertexAttribIPointer(index,size,type,stride,ptr){var cb=GL.currentContext.clientBuffers[index];if(!GLctx.currentArrayBufferBinding){cb.size=size;cb.type=type;cb.normalized=false;cb.stride=stride;cb.ptr=ptr;cb.clientside=true;cb.vertexAttribPointerAdaptor=function(index,size,type,normalized,stride,ptr){this.vertexAttribIPointer(index,size,type,stride,ptr)};return}cb.clientside=false;GLctx["vertexAttribIPointer"](index,size,type,stride,ptr)}function _emscripten_glVertexAttribPointer(index,size,type,normalized,stride,ptr){var cb=GL.currentContext.clientBuffers[index];if(!GLctx.currentArrayBufferBinding){cb.size=size;cb.type=type;cb.normalized=normalized;cb.stride=stride;cb.ptr=ptr;cb.clientside=true;cb.vertexAttribPointerAdaptor=function(index,size,type,normalized,stride,ptr){this.vertexAttribPointer(index,size,type,normalized,stride,ptr)};return}cb.clientside=false;GLctx.vertexAttribPointer(index,size,type,!!normalized,stride,ptr)}function _emscripten_glViewport(x0,x1,x2,x3){GLctx["viewport"](x0,x1,x2,x3)}function _emscripten_glWaitSync(sync,flags,timeoutLo,timeoutHi){GLctx.waitSync(GL.syncs[sync],flags,convertI32PairToI53(timeoutLo,timeoutHi))}function _emscripten_memcpy_big(dest,src,num){HEAPU8.copyWithin(dest,src,src+num)}function _emscripten_get_heap_size(){return HEAPU8.length}function emscripten_realloc_buffer(size){try{wasmMemory.grow(size-buffer.byteLength+65535>>>16);updateGlobalBufferAndViews(wasmMemory.buffer);return 1}catch(e){}}function _emscripten_resize_heap(requestedSize){requestedSize=requestedSize>>>0;var oldSize=_emscripten_get_heap_size();var maxHeapSize=2147483648;if(requestedSize>maxHeapSize){return false}var minHeapSize=16777216;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(minHeapSize,requestedSize,overGrownHeapSize),65536));var replacement=emscripten_realloc_buffer(newSize);if(replacement){return true}}return false}var ENV={};function getExecutableName(){return thisProgram||"./this.program"}function getEnvStrings(){if(!getEnvStrings.strings){var lang=(typeof navigator==="object"&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8";var env={"USER":"web_user","LOGNAME":"web_user","PATH":"/","PWD":"/","HOME":"/home/web_user","LANG":lang,"_":getExecutableName()};for(var x in ENV){env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(x+"="+env[x])}getEnvStrings.strings=strings}return getEnvStrings.strings}function _environ_get(__environ,environ_buf){try{var bufSize=0;getEnvStrings().forEach(function(string,i){var ptr=environ_buf+bufSize;HEAP32[__environ+i*4>>2]=ptr;writeAsciiToMemory(string,ptr);bufSize+=string.length+1});return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _environ_sizes_get(penviron_count,penviron_buf_size){try{var strings=getEnvStrings();HEAP32[penviron_count>>2]=strings.length;var bufSize=0;strings.forEach(function(string){bufSize+=string.length+1});HEAP32[penviron_buf_size>>2]=bufSize;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_read(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=SYSCALLS.doReadv(stream,iov,iovcnt);HEAP32[pnum>>2]=num;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){try{var stream=SYSCALLS.getStreamFromFD(fd);var HIGH_OFFSET=4294967296;var offset=offset_high*HIGH_OFFSET+(offset_low>>>0);var DOUBLE_LIMIT=9007199254740992;if(offset<=-DOUBLE_LIMIT||offset>=DOUBLE_LIMIT){return-61}FS.llseek(stream,offset,whence);tempI64=[stream.position>>>0,(tempDouble=stream.position,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[newOffset>>2]=tempI64[0],HEAP32[newOffset+4>>2]=tempI64[1];if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_write(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=SYSCALLS.doWritev(stream,iov,iovcnt);HEAP32[pnum>>2]=num;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _getentropy(buffer,size){if(!_getentropy.randomDevice){_getentropy.randomDevice=getRandomDevice()}for(var i=0;i>0]=_getentropy.randomDevice()}return 0}function _glActiveTexture(x0){GLctx["activeTexture"](x0)}function _glAttachShader(program,shader){GLctx.attachShader(GL.programs[program],GL.shaders[shader])}function _glBeginQuery(target,id){GLctx["beginQuery"](target,GL.queries[id])}function _glBindBuffer(target,buffer){if(target==34962){GLctx.currentArrayBufferBinding=buffer}else if(target==34963){GLctx.currentElementArrayBufferBinding=buffer}if(target==35051){GLctx.currentPixelPackBufferBinding=buffer}else if(target==35052){GLctx.currentPixelUnpackBufferBinding=buffer}GLctx.bindBuffer(target,GL.buffers[buffer])}function _glBindBufferRange(target,index,buffer,offset,ptrsize){GLctx["bindBufferRange"](target,index,GL.buffers[buffer],offset,ptrsize)}function _glBindFramebuffer(target,framebuffer){GLctx.bindFramebuffer(target,GL.framebuffers[framebuffer])}function _glBindRenderbuffer(target,renderbuffer){GLctx.bindRenderbuffer(target,GL.renderbuffers[renderbuffer])}function _glBindSampler(unit,sampler){GLctx["bindSampler"](unit,GL.samplers[sampler])}function _glBindTexture(target,texture){GLctx.bindTexture(target,GL.textures[texture])}function _glBindVertexArray(vao){GLctx["bindVertexArray"](GL.vaos[vao]);var ibo=GLctx.getParameter(34965);GLctx.currentElementArrayBufferBinding=ibo?ibo.name|0:0}function _glBlendEquationSeparate(x0,x1){GLctx["blendEquationSeparate"](x0,x1)}function _glBlendFuncSeparate(x0,x1,x2,x3){GLctx["blendFuncSeparate"](x0,x1,x2,x3)}function _glBlitFramebuffer(x0,x1,x2,x3,x4,x5,x6,x7,x8,x9){GLctx["blitFramebuffer"](x0,x1,x2,x3,x4,x5,x6,x7,x8,x9)}function _glBufferData(target,size,data,usage){if(true){if(data){GLctx.bufferData(target,HEAPU8,usage,data,size)}else{GLctx.bufferData(target,size,usage)}}else{GLctx.bufferData(target,data?HEAPU8.subarray(data,data+size):size,usage)}}function _glBufferSubData(target,offset,size,data){if(true){GLctx.bufferSubData(target,offset,HEAPU8,data,size);return}GLctx.bufferSubData(target,offset,HEAPU8.subarray(data,data+size))}function _glClearBufferfi(x0,x1,x2,x3){GLctx["clearBufferfi"](x0,x1,x2,x3)}function _glClearBufferfv(buffer,drawbuffer,value){GLctx["clearBufferfv"](buffer,drawbuffer,HEAPF32,value>>2)}function _glClearBufferiv(buffer,drawbuffer,value){GLctx["clearBufferiv"](buffer,drawbuffer,HEAP32,value>>2)}function _glClientWaitSync(sync,flags,timeoutLo,timeoutHi){return GLctx.clientWaitSync(GL.syncs[sync],flags,convertI32PairToI53(timeoutLo,timeoutHi))}function _glColorMask(red,green,blue,alpha){GLctx.colorMask(!!red,!!green,!!blue,!!alpha)}function _glCompileShader(shader){GLctx.compileShader(GL.shaders[shader])}function _glCompressedTexSubImage2D(target,level,xoffset,yoffset,width,height,format,imageSize,data){if(true){if(GLctx.currentPixelUnpackBufferBinding){GLctx["compressedTexSubImage2D"](target,level,xoffset,yoffset,width,height,format,imageSize,data)}else{GLctx["compressedTexSubImage2D"](target,level,xoffset,yoffset,width,height,format,HEAPU8,data,imageSize)}return}GLctx["compressedTexSubImage2D"](target,level,xoffset,yoffset,width,height,format,data?HEAPU8.subarray(data,data+imageSize):null)}function _glCompressedTexSubImage3D(target,level,xoffset,yoffset,zoffset,width,height,depth,format,imageSize,data){if(GLctx.currentPixelUnpackBufferBinding){GLctx["compressedTexSubImage3D"](target,level,xoffset,yoffset,zoffset,width,height,depth,format,imageSize,data)}else{GLctx["compressedTexSubImage3D"](target,level,xoffset,yoffset,zoffset,width,height,depth,format,HEAPU8,data,imageSize)}}function _glCreateProgram(){var id=GL.getNewId(GL.programs);var program=GLctx.createProgram();program.name=id;GL.programs[id]=program;return id}function _glCreateShader(shaderType){var id=GL.getNewId(GL.shaders);GL.shaders[id]=GLctx.createShader(shaderType);return id}function _glCullFace(x0){GLctx["cullFace"](x0)}function _glDeleteBuffers(n,buffers){for(var i=0;i>2];var buffer=GL.buffers[id];if(!buffer)continue;GLctx.deleteBuffer(buffer);buffer.name=0;GL.buffers[id]=null;if(id==GLctx.currentArrayBufferBinding)GLctx.currentArrayBufferBinding=0;if(id==GLctx.currentElementArrayBufferBinding)GLctx.currentElementArrayBufferBinding=0;if(id==GLctx.currentPixelPackBufferBinding)GLctx.currentPixelPackBufferBinding=0;if(id==GLctx.currentPixelUnpackBufferBinding)GLctx.currentPixelUnpackBufferBinding=0}}function _glDeleteFramebuffers(n,framebuffers){for(var i=0;i>2];var framebuffer=GL.framebuffers[id];if(!framebuffer)continue;GLctx.deleteFramebuffer(framebuffer);framebuffer.name=0;GL.framebuffers[id]=null}}function _glDeleteProgram(id){if(!id)return;var program=GL.programs[id];if(!program){GL.recordError(1281);return}GLctx.deleteProgram(program);program.name=0;GL.programs[id]=null;GL.programInfos[id]=null}function _glDeleteQueries(n,ids){for(var i=0;i>2];var query=GL.queries[id];if(!query)continue;GLctx["deleteQuery"](query);GL.queries[id]=null}}function _glDeleteRenderbuffers(n,renderbuffers){for(var i=0;i>2];var renderbuffer=GL.renderbuffers[id];if(!renderbuffer)continue;GLctx.deleteRenderbuffer(renderbuffer);renderbuffer.name=0;GL.renderbuffers[id]=null}}function _glDeleteSamplers(n,samplers){for(var i=0;i>2];var sampler=GL.samplers[id];if(!sampler)continue;GLctx["deleteSampler"](sampler);sampler.name=0;GL.samplers[id]=null}}function _glDeleteShader(id){if(!id)return;var shader=GL.shaders[id];if(!shader){GL.recordError(1281);return}GLctx.deleteShader(shader);GL.shaders[id]=null}function _glDeleteSync(id){if(!id)return;var sync=GL.syncs[id];if(!sync){GL.recordError(1281);return}GLctx.deleteSync(sync);sync.name=0;GL.syncs[id]=null}function _glDeleteTextures(n,textures){for(var i=0;i>2];var texture=GL.textures[id];if(!texture)continue;GLctx.deleteTexture(texture);texture.name=0;GL.textures[id]=null}}function _glDeleteVertexArrays(n,vaos){for(var i=0;i>2];GLctx["deleteVertexArray"](GL.vaos[id]);GL.vaos[id]=null}}function _glDepthFunc(x0){GLctx["depthFunc"](x0)}function _glDepthMask(flag){GLctx.depthMask(!!flag)}function _glDepthRangef(x0,x1){GLctx["depthRange"](x0,x1)}function _glDetachShader(program,shader){GLctx.detachShader(GL.programs[program],GL.shaders[shader])}function _glDisable(x0){GLctx["disable"](x0)}function _glDisableVertexAttribArray(index){var cb=GL.currentContext.clientBuffers[index];cb.enabled=false;GLctx.disableVertexAttribArray(index)}function _glDrawArrays(mode,first,count){GL.preDrawHandleClientVertexAttribBindings(first+count);GLctx.drawArrays(mode,first,count);GL.postDrawHandleClientVertexAttribBindings()}function _glDrawBuffers(n,bufs){var bufArray=tempFixedLengthArray[n];for(var i=0;i>2]}GLctx["drawBuffers"](bufArray)}function _glDrawRangeElements(mode,start,end,count,type,indices){_glDrawElements(mode,count,type,indices)}function _glEnable(x0){GLctx["enable"](x0)}function _glEnableVertexAttribArray(index){var cb=GL.currentContext.clientBuffers[index];cb.enabled=true;GLctx.enableVertexAttribArray(index)}function _glEndQuery(x0){GLctx["endQuery"](x0)}function _glFenceSync(condition,flags){var sync=GLctx.fenceSync(condition,flags);if(sync){var id=GL.getNewId(GL.syncs);sync.name=id;GL.syncs[id]=sync;return id}else{return 0}}function _glFinish(){GLctx["finish"]()}function _glFlush(){GLctx["flush"]()}function _glFramebufferRenderbuffer(target,attachment,renderbuffertarget,renderbuffer){GLctx.framebufferRenderbuffer(target,attachment,renderbuffertarget,GL.renderbuffers[renderbuffer])}function _glFramebufferTexture2D(target,attachment,textarget,texture,level){GLctx.framebufferTexture2D(target,attachment,textarget,GL.textures[texture],level)}function _glFramebufferTextureLayer(target,attachment,texture,level,layer){GLctx.framebufferTextureLayer(target,attachment,GL.textures[texture],level,layer)}function _glFrontFace(x0){GLctx["frontFace"](x0)}function _glGenBuffers(n,buffers){__glGenObject(n,buffers,"createBuffer",GL.buffers)}function _glGenFramebuffers(n,ids){__glGenObject(n,ids,"createFramebuffer",GL.framebuffers)}function _glGenQueries(n,ids){__glGenObject(n,ids,"createQuery",GL.queries)}function _glGenRenderbuffers(n,renderbuffers){__glGenObject(n,renderbuffers,"createRenderbuffer",GL.renderbuffers)}function _glGenSamplers(n,samplers){__glGenObject(n,samplers,"createSampler",GL.samplers)}function _glGenTextures(n,textures){__glGenObject(n,textures,"createTexture",GL.textures)}function _glGenVertexArrays(n,arrays){__glGenObject(n,arrays,"createVertexArray",GL.vaos)}function _glGenerateMipmap(x0){GLctx["generateMipmap"](x0)}function _glGetError(){var error=GLctx.getError()||GL.lastError;GL.lastError=0;return error}function _glGetFloatv(name_,p){emscriptenWebGLGet(name_,p,2)}function _glGetIntegerv(name_,p){emscriptenWebGLGet(name_,p,0)}function _glGetProgramInfoLog(program,maxLength,length,infoLog){var log=GLctx.getProgramInfoLog(GL.programs[program]);if(log===null)log="(unknown error)";var numBytesWrittenExclNull=maxLength>0&&infoLog?stringToUTF8(log,infoLog,maxLength):0;if(length)HEAP32[length>>2]=numBytesWrittenExclNull}function _glGetProgramiv(program,pname,p){if(!p){GL.recordError(1281);return}if(program>=GL.counter){GL.recordError(1281);return}var ptable=GL.programInfos[program];if(!ptable){GL.recordError(1282);return}if(pname==35716){var log=GLctx.getProgramInfoLog(GL.programs[program]);if(log===null)log="(unknown error)";HEAP32[p>>2]=log.length+1}else if(pname==35719){HEAP32[p>>2]=ptable.maxUniformLength}else if(pname==35722){if(ptable.maxAttributeLength==-1){program=GL.programs[program];var numAttribs=GLctx.getProgramParameter(program,35721);ptable.maxAttributeLength=0;for(var i=0;i>2]=ptable.maxAttributeLength}else if(pname==35381){if(ptable.maxUniformBlockNameLength==-1){program=GL.programs[program];var numBlocks=GLctx.getProgramParameter(program,35382);ptable.maxUniformBlockNameLength=0;for(var i=0;i>2]=ptable.maxUniformBlockNameLength}else{HEAP32[p>>2]=GLctx.getProgramParameter(GL.programs[program],pname)}}function _glGetQueryObjectuiv(id,pname,params){if(!params){GL.recordError(1281);return}var query=GL.queries[id];var param=GLctx["getQueryParameter"](query,pname);var ret;if(typeof param=="boolean"){ret=param?1:0}else{ret=param}HEAP32[params>>2]=ret}function _glGetShaderInfoLog(shader,maxLength,length,infoLog){var log=GLctx.getShaderInfoLog(GL.shaders[shader]);if(log===null)log="(unknown error)";var numBytesWrittenExclNull=maxLength>0&&infoLog?stringToUTF8(log,infoLog,maxLength):0;if(length)HEAP32[length>>2]=numBytesWrittenExclNull}function _glGetShaderiv(shader,pname,p){if(!p){GL.recordError(1281);return}if(pname==35716){var log=GLctx.getShaderInfoLog(GL.shaders[shader]);if(log===null)log="(unknown error)";var logLength=log?log.length+1:0;HEAP32[p>>2]=logLength}else if(pname==35720){var source=GLctx.getShaderSource(GL.shaders[shader]);var sourceLength=source?source.length+1:0;HEAP32[p>>2]=sourceLength}else{HEAP32[p>>2]=GLctx.getShaderParameter(GL.shaders[shader],pname)}}function _glGetString(name_){if(GL.stringCache[name_])return GL.stringCache[name_];var ret;switch(name_){case 7939:var exts=GLctx.getSupportedExtensions()||[];exts=exts.concat(exts.map(function(e){return"GL_"+e}));ret=stringToNewUTF8(exts.join(" "));break;case 7936:case 7937:case 37445:case 37446:var s=GLctx.getParameter(name_);if(!s){GL.recordError(1280)}ret=stringToNewUTF8(s);break;case 7938:var glVersion=GLctx.getParameter(7938);if(true)glVersion="OpenGL ES 3.0 ("+glVersion+")";else{glVersion="OpenGL ES 2.0 ("+glVersion+")"}ret=stringToNewUTF8(glVersion);break;case 35724:var glslVersion=GLctx.getParameter(35724);var ver_re=/^WebGL GLSL ES ([0-9]\.[0-9][0-9]?)(?:$| .*)/;var ver_num=glslVersion.match(ver_re);if(ver_num!==null){if(ver_num[1].length==3)ver_num[1]=ver_num[1]+"0";glslVersion="OpenGL ES GLSL ES "+ver_num[1]+" ("+glslVersion+")"}ret=stringToNewUTF8(glslVersion);break;default:GL.recordError(1280);return 0}GL.stringCache[name_]=ret;return ret}function _glGetStringi(name,index){if(GL.currentContext.version<2){GL.recordError(1282);return 0}var stringiCache=GL.stringiCache[name];if(stringiCache){if(index<0||index>=stringiCache.length){GL.recordError(1281);return 0}return stringiCache[index]}switch(name){case 7939:var exts=GLctx.getSupportedExtensions()||[];exts=exts.concat(exts.map(function(e){return"GL_"+e}));exts=exts.map(function(e){return stringToNewUTF8(e)});stringiCache=GL.stringiCache[name]=exts;if(index<0||index>=stringiCache.length){GL.recordError(1281);return 0}return stringiCache[index];default:GL.recordError(1280);return 0}}function _glGetUniformBlockIndex(program,uniformBlockName){return GLctx["getUniformBlockIndex"](GL.programs[program],UTF8ToString(uniformBlockName))}function _glGetUniformLocation(program,name){name=UTF8ToString(name);var arrayIndex=0;if(name[name.length-1]=="]"){var leftBrace=name.lastIndexOf("[");arrayIndex=name[leftBrace+1]!="]"?jstoi_q(name.slice(leftBrace+1)):0;name=name.slice(0,leftBrace)}var uniformInfo=GL.programInfos[program]&&GL.programInfos[program].uniforms[name];if(uniformInfo&&arrayIndex>=0&&arrayIndex>2]}GLctx["invalidateFramebuffer"](target,list)}function _glIsEnabled(x0){return GLctx["isEnabled"](x0)}function _glLinkProgram(program){GLctx.linkProgram(GL.programs[program]);GL.populateUniformTable(program)}function _glMapBufferRange(target,offset,length,access){if(access!=26&&access!=10){err("glMapBufferRange is only supported when access is MAP_WRITE|INVALIDATE_BUFFER");return 0}if(!emscriptenWebGLValidateMapBufferTarget(target)){GL.recordError(1280);err("GL_INVALID_ENUM in glMapBufferRange");return 0}var mem=_malloc(length);if(!mem)return 0;GL.mappedBuffers[emscriptenWebGLGetBufferBinding(target)]={offset:offset,length:length,mem:mem,access:access};return mem}function _glPixelStorei(pname,param){if(pname==3317){GL.unpackAlignment=param}GLctx.pixelStorei(pname,param)}function _glPolygonOffset(x0,x1){GLctx["polygonOffset"](x0,x1)}function _glReadPixels(x,y,width,height,format,type,pixels){if(true){if(GLctx.currentPixelPackBufferBinding){GLctx.readPixels(x,y,width,height,format,type,pixels)}else{var heap=heapObjectForWebGLType(type);GLctx.readPixels(x,y,width,height,format,type,heap,pixels>>heapAccessShiftForWebGLHeap(heap))}return}var pixelData=emscriptenWebGLGetTexPixelData(type,format,width,height,pixels,format);if(!pixelData){GL.recordError(1280);return}GLctx.readPixels(x,y,width,height,format,type,pixelData)}function _glRenderbufferStorage(x0,x1,x2,x3){GLctx["renderbufferStorage"](x0,x1,x2,x3)}function _glRenderbufferStorageMultisample(x0,x1,x2,x3,x4){GLctx["renderbufferStorageMultisample"](x0,x1,x2,x3,x4)}function _glSamplerParameteri(sampler,pname,param){GLctx["samplerParameteri"](GL.samplers[sampler],pname,param)}function _glScissor(x0,x1,x2,x3){GLctx["scissor"](x0,x1,x2,x3)}function _glShaderSource(shader,count,string,length){var source=GL.getSource(shader,count,string,length);GLctx.shaderSource(GL.shaders[shader],source)}function _glTexParameteri(x0,x1,x2){GLctx["texParameteri"](x0,x1,x2)}function _glTexStorage2D(x0,x1,x2,x3,x4){GLctx["texStorage2D"](x0,x1,x2,x3,x4)}function _glTexStorage3D(x0,x1,x2,x3,x4,x5){GLctx["texStorage3D"](x0,x1,x2,x3,x4,x5)}function _glTexSubImage2D(target,level,xoffset,yoffset,width,height,format,type,pixels){if(true){if(GLctx.currentPixelUnpackBufferBinding){GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,pixels)}else if(pixels){var heap=heapObjectForWebGLType(type);GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,heap,pixels>>heapAccessShiftForWebGLHeap(heap))}else{GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,null)}return}var pixelData=null;if(pixels)pixelData=emscriptenWebGLGetTexPixelData(type,format,width,height,pixels,0);GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,pixelData)}function _glTexSubImage3D(target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,pixels){if(GLctx.currentPixelUnpackBufferBinding){GLctx["texSubImage3D"](target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,pixels)}else if(pixels){var heap=heapObjectForWebGLType(type);GLctx["texSubImage3D"](target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,heap,pixels>>heapAccessShiftForWebGLHeap(heap))}else{GLctx["texSubImage3D"](target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,null)}}function _glUniform1i(location,v0){GLctx.uniform1i(GL.uniforms[location],v0)}function _glUniformBlockBinding(program,uniformBlockIndex,uniformBlockBinding){program=GL.programs[program];GLctx["uniformBlockBinding"](program,uniformBlockIndex,uniformBlockBinding)}function _glUnmapBuffer(target){if(!emscriptenWebGLValidateMapBufferTarget(target)){GL.recordError(1280);err("GL_INVALID_ENUM in glUnmapBuffer");return 0}var buffer=emscriptenWebGLGetBufferBinding(target);var mapping=GL.mappedBuffers[buffer];if(!mapping){GL.recordError(1282);err("buffer was never mapped in glUnmapBuffer");return 0}GL.mappedBuffers[buffer]=null;if(!(mapping.access&16))if(true){GLctx.bufferSubData(target,mapping.offset,HEAPU8,mapping.mem,mapping.length)}else{GLctx.bufferSubData(target,mapping.offset,HEAPU8.subarray(mapping.mem,mapping.mem+mapping.length))}_free(mapping.mem);return 1}function _glUseProgram(program){GLctx.useProgram(GL.programs[program])}function _glVertexAttrib4f(x0,x1,x2,x3,x4){GLctx["vertexAttrib4f"](x0,x1,x2,x3,x4)}function _glVertexAttribI4ui(x0,x1,x2,x3,x4){GLctx["vertexAttribI4ui"](x0,x1,x2,x3,x4)}function _glVertexAttribIPointer(index,size,type,stride,ptr){var cb=GL.currentContext.clientBuffers[index];if(!GLctx.currentArrayBufferBinding){cb.size=size;cb.type=type;cb.normalized=false;cb.stride=stride;cb.ptr=ptr;cb.clientside=true;cb.vertexAttribPointerAdaptor=function(index,size,type,normalized,stride,ptr){this.vertexAttribIPointer(index,size,type,stride,ptr)};return}cb.clientside=false;GLctx["vertexAttribIPointer"](index,size,type,stride,ptr)}function _glVertexAttribPointer(index,size,type,normalized,stride,ptr){var cb=GL.currentContext.clientBuffers[index];if(!GLctx.currentArrayBufferBinding){cb.size=size;cb.type=type;cb.normalized=normalized;cb.stride=stride;cb.ptr=ptr;cb.clientside=true;cb.vertexAttribPointerAdaptor=function(index,size,type,normalized,stride,ptr){this.vertexAttribPointer(index,size,type,normalized,stride,ptr)};return}cb.clientside=false;GLctx.vertexAttribPointer(index,size,type,!!normalized,stride,ptr)}function _glViewport(x0,x1,x2,x3){GLctx["viewport"](x0,x1,x2,x3)}function _glWaitSync(sync,flags,timeoutLo,timeoutHi){GLctx.waitSync(GL.syncs[sync],flags,convertI32PairToI53(timeoutLo,timeoutHi))}function _pthread_create(){return 6}function _pthread_join(){return 28}function _setTempRet0($i){setTempRet0($i|0)}function __isLeapYear(year){return year%4===0&&(year%100!==0||year%400===0)}function __arraySum(array,index){var sum=0;for(var i=0;i<=index;sum+=array[i++]){}return sum}var __MONTH_DAYS_LEAP=[31,29,31,30,31,30,31,31,30,31,30,31];var __MONTH_DAYS_REGULAR=[31,28,31,30,31,30,31,31,30,31,30,31];function __addDays(date,days){var newDate=new Date(date.getTime());while(days>0){var leap=__isLeapYear(newDate.getFullYear());var currentMonth=newDate.getMonth();var daysInCurrentMonth=(leap?__MONTH_DAYS_LEAP:__MONTH_DAYS_REGULAR)[currentMonth];if(days>daysInCurrentMonth-newDate.getDate()){days-=daysInCurrentMonth-newDate.getDate()+1;newDate.setDate(1);if(currentMonth<11){newDate.setMonth(currentMonth+1)}else{newDate.setMonth(0);newDate.setFullYear(newDate.getFullYear()+1)}}else{newDate.setDate(newDate.getDate()+days);return newDate}}return newDate}function _strftime(s,maxsize,format,tm){var tm_zone=HEAP32[tm+40>>2];var date={tm_sec:HEAP32[tm>>2],tm_min:HEAP32[tm+4>>2],tm_hour:HEAP32[tm+8>>2],tm_mday:HEAP32[tm+12>>2],tm_mon:HEAP32[tm+16>>2],tm_year:HEAP32[tm+20>>2],tm_wday:HEAP32[tm+24>>2],tm_yday:HEAP32[tm+28>>2],tm_isdst:HEAP32[tm+32>>2],tm_gmtoff:HEAP32[tm+36>>2],tm_zone:tm_zone?UTF8ToString(tm_zone):""};var pattern=UTF8ToString(format);var EXPANSION_RULES_1={"%c":"%a %b %d %H:%M:%S %Y","%D":"%m/%d/%y","%F":"%Y-%m-%d","%h":"%b","%r":"%I:%M:%S %p","%R":"%H:%M","%T":"%H:%M:%S","%x":"%m/%d/%y","%X":"%H:%M:%S","%Ec":"%c","%EC":"%C","%Ex":"%m/%d/%y","%EX":"%H:%M:%S","%Ey":"%y","%EY":"%Y","%Od":"%d","%Oe":"%e","%OH":"%H","%OI":"%I","%Om":"%m","%OM":"%M","%OS":"%S","%Ou":"%u","%OU":"%U","%OV":"%V","%Ow":"%w","%OW":"%W","%Oy":"%y"};for(var rule in EXPANSION_RULES_1){pattern=pattern.replace(new RegExp(rule,"g"),EXPANSION_RULES_1[rule])}var WEEKDAYS=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];var MONTHS=["January","February","March","April","May","June","July","August","September","October","November","December"];function leadingSomething(value,digits,character){var str=typeof value==="number"?value.toString():value||"";while(str.length0?1:0}var compare;if((compare=sgn(date1.getFullYear()-date2.getFullYear()))===0){if((compare=sgn(date1.getMonth()-date2.getMonth()))===0){compare=sgn(date1.getDate()-date2.getDate())}}return compare}function getFirstWeekStartDate(janFourth){switch(janFourth.getDay()){case 0:return new Date(janFourth.getFullYear()-1,11,29);case 1:return janFourth;case 2:return new Date(janFourth.getFullYear(),0,3);case 3:return new Date(janFourth.getFullYear(),0,2);case 4:return new Date(janFourth.getFullYear(),0,1);case 5:return new Date(janFourth.getFullYear()-1,11,31);case 6:return new Date(janFourth.getFullYear()-1,11,30)}}function getWeekBasedYear(date){var thisDate=__addDays(new Date(date.tm_year+1900,0,1),date.tm_yday);var janFourthThisYear=new Date(thisDate.getFullYear(),0,4);var janFourthNextYear=new Date(thisDate.getFullYear()+1,0,4);var firstWeekStartThisYear=getFirstWeekStartDate(janFourthThisYear);var firstWeekStartNextYear=getFirstWeekStartDate(janFourthNextYear);if(compareByDay(firstWeekStartThisYear,thisDate)<=0){if(compareByDay(firstWeekStartNextYear,thisDate)<=0){return thisDate.getFullYear()+1}else{return thisDate.getFullYear()}}else{return thisDate.getFullYear()-1}}var EXPANSION_RULES_2={"%a":function(date){return WEEKDAYS[date.tm_wday].substring(0,3)},"%A":function(date){return WEEKDAYS[date.tm_wday]},"%b":function(date){return MONTHS[date.tm_mon].substring(0,3)},"%B":function(date){return MONTHS[date.tm_mon]},"%C":function(date){var year=date.tm_year+1900;return leadingNulls(year/100|0,2)},"%d":function(date){return leadingNulls(date.tm_mday,2)},"%e":function(date){return leadingSomething(date.tm_mday,2," ")},"%g":function(date){return getWeekBasedYear(date).toString().substring(2)},"%G":function(date){return getWeekBasedYear(date)},"%H":function(date){return leadingNulls(date.tm_hour,2)},"%I":function(date){var twelveHour=date.tm_hour;if(twelveHour==0)twelveHour=12;else if(twelveHour>12)twelveHour-=12;return leadingNulls(twelveHour,2)},"%j":function(date){return leadingNulls(date.tm_mday+__arraySum(__isLeapYear(date.tm_year+1900)?__MONTH_DAYS_LEAP:__MONTH_DAYS_REGULAR,date.tm_mon-1),3)},"%m":function(date){return leadingNulls(date.tm_mon+1,2)},"%M":function(date){return leadingNulls(date.tm_min,2)},"%n":function(){return"\n"},"%p":function(date){if(date.tm_hour>=0&&date.tm_hour<12){return"AM"}else{return"PM"}},"%S":function(date){return leadingNulls(date.tm_sec,2)},"%t":function(){return"\t"},"%u":function(date){return date.tm_wday||7},"%U":function(date){var janFirst=new Date(date.tm_year+1900,0,1);var firstSunday=janFirst.getDay()===0?janFirst:__addDays(janFirst,7-janFirst.getDay());var endDate=new Date(date.tm_year+1900,date.tm_mon,date.tm_mday);if(compareByDay(firstSunday,endDate)<0){var februaryFirstUntilEndMonth=__arraySum(__isLeapYear(endDate.getFullYear())?__MONTH_DAYS_LEAP:__MONTH_DAYS_REGULAR,endDate.getMonth()-1)-31;var firstSundayUntilEndJanuary=31-firstSunday.getDate();var days=firstSundayUntilEndJanuary+februaryFirstUntilEndMonth+endDate.getDate();return leadingNulls(Math.ceil(days/7),2)}return compareByDay(firstSunday,janFirst)===0?"01":"00"},"%V":function(date){var janFourthThisYear=new Date(date.tm_year+1900,0,4);var janFourthNextYear=new Date(date.tm_year+1901,0,4);var firstWeekStartThisYear=getFirstWeekStartDate(janFourthThisYear);var firstWeekStartNextYear=getFirstWeekStartDate(janFourthNextYear);var endDate=__addDays(new Date(date.tm_year+1900,0,1),date.tm_yday);if(compareByDay(endDate,firstWeekStartThisYear)<0){return"53"}if(compareByDay(firstWeekStartNextYear,endDate)<=0){return"01"}var daysDifference;if(firstWeekStartThisYear.getFullYear()=0;off=Math.abs(off)/60;off=off/60*100+off%60;return(ahead?"+":"-")+String("0000"+off).slice(-4)},"%Z":function(date){return date.tm_zone},"%%":function(){return"%"}};for(var rule in EXPANSION_RULES_2){if(pattern.indexOf(rule)>=0){pattern=pattern.replace(new RegExp(rule,"g"),EXPANSION_RULES_2[rule](date))}}var bytes=intArrayFromString(pattern,false);if(bytes.length>maxsize){return 0}writeArrayToMemory(bytes,s);return bytes.length-1}function _strftime_l(s,maxsize,format,tm){return _strftime(s,maxsize,format,tm)}function _sysconf(name){switch(name){case 30:return 16384;case 85:var maxHeapSize=2147483648;return maxHeapSize/16384;case 132:case 133:case 12:case 137:case 138:case 15:case 235:case 16:case 17:case 18:case 19:case 20:case 149:case 13:case 10:case 236:case 153:case 9:case 21:case 22:case 159:case 154:case 14:case 77:case 78:case 139:case 82:case 68:case 67:case 164:case 11:case 29:case 47:case 48:case 95:case 52:case 51:case 46:return 200809;case 27:case 246:case 127:case 128:case 23:case 24:case 160:case 161:case 181:case 182:case 242:case 183:case 184:case 243:case 244:case 245:case 165:case 178:case 179:case 49:case 50:case 168:case 169:case 175:case 170:case 171:case 172:case 97:case 76:case 32:case 173:case 35:case 80:case 81:case 79:return-1;case 176:case 177:case 7:case 155:case 8:case 157:case 125:case 126:case 92:case 93:case 129:case 130:case 131:case 94:case 91:return 1;case 74:case 60:case 69:case 70:case 4:return 1024;case 31:case 42:case 72:return 32;case 87:case 26:case 33:return 2147483647;case 34:case 1:return 47839;case 38:case 36:return 99;case 43:case 37:return 2048;case 0:return 2097152;case 3:return 65536;case 28:return 32768;case 44:return 32767;case 75:return 16384;case 39:return 1e3;case 89:return 700;case 71:return 256;case 40:return 255;case 2:return 100;case 180:return 64;case 25:return 20;case 5:return 16;case 6:return 6;case 73:return 4;case 84:{if(typeof navigator==="object")return navigator["hardwareConcurrency"]||1;return 1}}setErrNo(28);return-1}var readAsmConstArgsArray=[];function readAsmConstArgs(sigPtr,buf){readAsmConstArgsArray.length=0;var ch;buf>>=2;while(ch=HEAPU8[sigPtr++]){var double=ch<105;if(double&&buf&1)buf++;readAsmConstArgsArray.push(double?HEAPF64[buf++>>1]:HEAP32[buf]);++buf}return readAsmConstArgsArray}var FSNode=function(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.mounted=null;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.node_ops={};this.stream_ops={};this.rdev=rdev};var readMode=292|73;var writeMode=146;Object.defineProperties(FSNode.prototype,{read:{get:function(){return(this.mode&readMode)===readMode},set:function(val){val?this.mode|=readMode:this.mode&=~readMode}},write:{get:function(){return(this.mode&writeMode)===writeMode},set:function(val){val?this.mode|=writeMode:this.mode&=~writeMode}},isFolder:{get:function(){return FS.isDir(this.mode)}},isDevice:{get:function(){return FS.isChrdev(this.mode)}}});FS.FSNode=FSNode;FS.staticInit();InternalError=Module["InternalError"]=extendError(Error,"InternalError");embind_init_charCodes();BindingError=Module["BindingError"]=extendError(Error,"BindingError");init_ClassHandle();init_RegisteredPointer();init_embind();UnboundTypeError=Module["UnboundTypeError"]=extendError(Error,"UnboundTypeError");init_emval();var GLctx;for(var i=0;i<32;++i)tempFixedLengthArray.push(new Array(i));function intArrayFromString(stringy,dontAddNull,length){var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array}var asmLibraryArg={"Ha":___sys_fcntl64,"Dg":___sys_ioctl,"Eg":___sys_open,"v":__embind_finalize_value_array,"z":__embind_finalize_value_object,"Hg":__embind_register_bool,"d":__embind_register_class,"n":__embind_register_class_class_function,"m":__embind_register_class_constructor,"a":__embind_register_class_function,"u":__embind_register_class_property,"Gg":__embind_register_emval,"i":__embind_register_enum,"b":__embind_register_enum_value,"Ja":__embind_register_float,"La":__embind_register_function,"D":__embind_register_integer,"x":__embind_register_memory_view,"Ka":__embind_register_std_string,"ja":__embind_register_std_wstring,"w":__embind_register_value_array,"f":__embind_register_value_array_element,"A":__embind_register_value_object,"e":__embind_register_value_object_field,"Ig":__embind_register_void,"q":__emval_as,"g":__emval_decref,"r":__emval_get_property,"ha":__emval_incref,"G":__emval_new_cstring,"p":__emval_run_destructors,"s":__emval_take_value,"c":_abort,"wg":_clock_gettime,"Ga":_emscripten_asm_const_int,"ag":_emscripten_glActiveTexture,"$f":_emscripten_glAttachShader,"cd":_emscripten_glBeginQuery,"qg":_emscripten_glBeginQueryEXT,"Gc":_emscripten_glBeginTransformFeedback,"_f":_emscripten_glBindAttribLocation,"Zf":_emscripten_glBindBuffer,"Dc":_emscripten_glBindBufferBase,"Ec":_emscripten_glBindBufferRange,"Yf":_emscripten_glBindFramebuffer,"Xf":_emscripten_glBindRenderbuffer,"Kb":_emscripten_glBindSampler,"Wf":_emscripten_glBindTexture,"Cb":_emscripten_glBindTransformFeedback,"Lc":_emscripten_glBindVertexArray,"ig":_emscripten_glBindVertexArrayOES,"Vf":_emscripten_glBlendColor,"Uf":_emscripten_glBlendEquation,"Tf":_emscripten_glBlendEquationSeparate,"Sf":_emscripten_glBlendFunc,"Rf":_emscripten_glBlendFuncSeparate,"Rc":_emscripten_glBlitFramebuffer,"Qf":_emscripten_glBufferData,"Pf":_emscripten_glBufferSubData,"Of":_emscripten_glCheckFramebufferStatus,"Nf":_emscripten_glClear,"gc":_emscripten_glClearBufferfi,"hc":_emscripten_glClearBufferfv,"jc":_emscripten_glClearBufferiv,"ic":_emscripten_glClearBufferuiv,"Mf":_emscripten_glClearColor,"Lf":_emscripten_glClearDepthf,"Kf":_emscripten_glClearStencil,"Ub":_emscripten_glClientWaitSync,"Jf":_emscripten_glColorMask,"If":_emscripten_glCompileShader,"Hf":_emscripten_glCompressedTexImage2D,"hd":_emscripten_glCompressedTexImage3D,"Gf":_emscripten_glCompressedTexSubImage2D,"gd":_emscripten_glCompressedTexSubImage3D,"ec":_emscripten_glCopyBufferSubData,"Ff":_emscripten_glCopyTexImage2D,"Ef":_emscripten_glCopyTexSubImage2D,"jd":_emscripten_glCopyTexSubImage3D,"Df":_emscripten_glCreateProgram,"Cf":_emscripten_glCreateShader,"Bf":_emscripten_glCullFace,"Af":_emscripten_glDeleteBuffers,"zf":_emscripten_glDeleteFramebuffers,"yf":_emscripten_glDeleteProgram,"ed":_emscripten_glDeleteQueries,"sg":_emscripten_glDeleteQueriesEXT,"xf":_emscripten_glDeleteRenderbuffers,"Mb":_emscripten_glDeleteSamplers,"wf":_emscripten_glDeleteShader,"Vb":_emscripten_glDeleteSync,"vf":_emscripten_glDeleteTextures,"Bb":_emscripten_glDeleteTransformFeedbacks,"Kc":_emscripten_glDeleteVertexArrays,"hg":_emscripten_glDeleteVertexArraysOES,"uf":_emscripten_glDepthFunc,"tf":_emscripten_glDepthMask,"sf":_emscripten_glDepthRangef,"rf":_emscripten_glDetachShader,"qf":_emscripten_glDisable,"pf":_emscripten_glDisableVertexAttribArray,"of":_emscripten_glDrawArrays,"Zb":_emscripten_glDrawArraysInstanced,"dg":_emscripten_glDrawArraysInstancedANGLE,"lb":_emscripten_glDrawArraysInstancedARB,"qd":_emscripten_glDrawArraysInstancedEXT,"mb":_emscripten_glDrawArraysInstancedNV,"Yc":_emscripten_glDrawBuffers,"od":_emscripten_glDrawBuffersEXT,"eg":_emscripten_glDrawBuffersWEBGL,"nf":_emscripten_glDrawElements,"Yb":_emscripten_glDrawElementsInstanced,"cg":_emscripten_glDrawElementsInstancedANGLE,"jb":_emscripten_glDrawElementsInstancedARB,"kb":_emscripten_glDrawElementsInstancedEXT,"pd":_emscripten_glDrawElementsInstancedNV,"md":_emscripten_glDrawRangeElements,"mf":_emscripten_glEnable,"lf":_emscripten_glEnableVertexAttribArray,"bd":_emscripten_glEndQuery,"pg":_emscripten_glEndQueryEXT,"Fc":_emscripten_glEndTransformFeedback,"Xb":_emscripten_glFenceSync,"kf":_emscripten_glFinish,"jf":_emscripten_glFlush,"Mc":_emscripten_glFlushMappedBufferRange,"hf":_emscripten_glFramebufferRenderbuffer,"gf":_emscripten_glFramebufferTexture2D,"Oc":_emscripten_glFramebufferTextureLayer,"ff":_emscripten_glFrontFace,"ef":_emscripten_glGenBuffers,"cf":_emscripten_glGenFramebuffers,"fd":_emscripten_glGenQueries,"tg":_emscripten_glGenQueriesEXT,"bf":_emscripten_glGenRenderbuffers,"Nb":_emscripten_glGenSamplers,"af":_emscripten_glGenTextures,"Ab":_emscripten_glGenTransformFeedbacks,"Jc":_emscripten_glGenVertexArrays,"gg":_emscripten_glGenVertexArraysOES,"df":_emscripten_glGenerateMipmap,"$e":_emscripten_glGetActiveAttrib,"_e":_emscripten_glGetActiveUniform,"$b":_emscripten_glGetActiveUniformBlockName,"ac":_emscripten_glGetActiveUniformBlockiv,"cc":_emscripten_glGetActiveUniformsiv,"Ze":_emscripten_glGetAttachedShaders,"Ye":_emscripten_glGetAttribLocation,"Xe":_emscripten_glGetBooleanv,"Ob":_emscripten_glGetBufferParameteri64v,"We":_emscripten_glGetBufferParameteriv,"Zc":_emscripten_glGetBufferPointerv,"Ve":_emscripten_glGetError,"Ue":_emscripten_glGetFloatv,"sc":_emscripten_glGetFragDataLocation,"Te":_emscripten_glGetFramebufferAttachmentParameteriv,"Pb":_emscripten_glGetInteger64i_v,"Rb":_emscripten_glGetInteger64v,"Hc":_emscripten_glGetIntegeri_v,"Se":_emscripten_glGetIntegerv,"pb":_emscripten_glGetInternalformativ,"wb":_emscripten_glGetProgramBinary,"Qe":_emscripten_glGetProgramInfoLog,"Re":_emscripten_glGetProgramiv,"kg":_emscripten_glGetQueryObjecti64vEXT,"mg":_emscripten_glGetQueryObjectivEXT,"jg":_emscripten_glGetQueryObjectui64vEXT,"$c":_emscripten_glGetQueryObjectuiv,"lg":_emscripten_glGetQueryObjectuivEXT,"ad":_emscripten_glGetQueryiv,"ng":_emscripten_glGetQueryivEXT,"Pe":_emscripten_glGetRenderbufferParameteriv,"Eb":_emscripten_glGetSamplerParameterfv,"Fb":_emscripten_glGetSamplerParameteriv,"Ne":_emscripten_glGetShaderInfoLog,"Me":_emscripten_glGetShaderPrecisionFormat,"Le":_emscripten_glGetShaderSource,"Oe":_emscripten_glGetShaderiv,"Ke":_emscripten_glGetString,"fc":_emscripten_glGetStringi,"Qb":_emscripten_glGetSynciv,"Je":_emscripten_glGetTexParameterfv,"Ie":_emscripten_glGetTexParameteriv,"Bc":_emscripten_glGetTransformFeedbackVarying,"bc":_emscripten_glGetUniformBlockIndex,"dc":_emscripten_glGetUniformIndices,"Fe":_emscripten_glGetUniformLocation,"He":_emscripten_glGetUniformfv,"Ge":_emscripten_glGetUniformiv,"tc":_emscripten_glGetUniformuiv,"zc":_emscripten_glGetVertexAttribIiv,"yc":_emscripten_glGetVertexAttribIuiv,"Ce":_emscripten_glGetVertexAttribPointerv,"Ee":_emscripten_glGetVertexAttribfv,"De":_emscripten_glGetVertexAttribiv,"Be":_emscripten_glHint,"tb":_emscripten_glInvalidateFramebuffer,"sb":_emscripten_glInvalidateSubFramebuffer,"Ae":_emscripten_glIsBuffer,"ze":_emscripten_glIsEnabled,"ye":_emscripten_glIsFramebuffer,"xe":_emscripten_glIsProgram,"dd":_emscripten_glIsQuery,"rg":_emscripten_glIsQueryEXT,"we":_emscripten_glIsRenderbuffer,"Lb":_emscripten_glIsSampler,"ve":_emscripten_glIsShader,"Wb":_emscripten_glIsSync,"ue":_emscripten_glIsTexture,"zb":_emscripten_glIsTransformFeedback,"Ic":_emscripten_glIsVertexArray,"fg":_emscripten_glIsVertexArrayOES,"te":_emscripten_glLineWidth,"se":_emscripten_glLinkProgram,"Nc":_emscripten_glMapBufferRange,"yb":_emscripten_glPauseTransformFeedback,"re":_emscripten_glPixelStorei,"qe":_emscripten_glPolygonOffset,"vb":_emscripten_glProgramBinary,"ub":_emscripten_glProgramParameteri,"og":_emscripten_glQueryCounterEXT,"nd":_emscripten_glReadBuffer,"pe":_emscripten_glReadPixels,"oe":_emscripten_glReleaseShaderCompiler,"ne":_emscripten_glRenderbufferStorage,"Qc":_emscripten_glRenderbufferStorageMultisample,"xb":_emscripten_glResumeTransformFeedback,"me":_emscripten_glSampleCoverage,"Hb":_emscripten_glSamplerParameterf,"Gb":_emscripten_glSamplerParameterfv,"Jb":_emscripten_glSamplerParameteri,"Ib":_emscripten_glSamplerParameteriv,"le":_emscripten_glScissor,"ke":_emscripten_glShaderBinary,"je":_emscripten_glShaderSource,"ie":_emscripten_glStencilFunc,"he":_emscripten_glStencilFuncSeparate,"ge":_emscripten_glStencilMask,"fe":_emscripten_glStencilMaskSeparate,"ee":_emscripten_glStencilOp,"de":_emscripten_glStencilOpSeparate,"ce":_emscripten_glTexImage2D,"ld":_emscripten_glTexImage3D,"be":_emscripten_glTexParameterf,"ae":_emscripten_glTexParameterfv,"$d":_emscripten_glTexParameteri,"_d":_emscripten_glTexParameteriv,"rb":_emscripten_glTexStorage2D,"qb":_emscripten_glTexStorage3D,"Zd":_emscripten_glTexSubImage2D,"kd":_emscripten_glTexSubImage3D,"Cc":_emscripten_glTransformFeedbackVaryings,"Yd":_emscripten_glUniform1f,"Xd":_emscripten_glUniform1fv,"Wd":_emscripten_glUniform1i,"Vd":_emscripten_glUniform1iv,"rc":_emscripten_glUniform1ui,"nc":_emscripten_glUniform1uiv,"Ud":_emscripten_glUniform2f,"Td":_emscripten_glUniform2fv,"Sd":_emscripten_glUniform2i,"Rd":_emscripten_glUniform2iv,"qc":_emscripten_glUniform2ui,"mc":_emscripten_glUniform2uiv,"Qd":_emscripten_glUniform3f,"Pd":_emscripten_glUniform3fv,"Nd":_emscripten_glUniform3i,"Md":_emscripten_glUniform3iv,"pc":_emscripten_glUniform3ui,"lc":_emscripten_glUniform3uiv,"Ld":_emscripten_glUniform4f,"Kd":_emscripten_glUniform4fv,"Jd":_emscripten_glUniform4i,"Id":_emscripten_glUniform4iv,"oc":_emscripten_glUniform4ui,"kc":_emscripten_glUniform4uiv,"_b":_emscripten_glUniformBlockBinding,"Hd":_emscripten_glUniformMatrix2fv,"Xc":_emscripten_glUniformMatrix2x3fv,"Vc":_emscripten_glUniformMatrix2x4fv,"Gd":_emscripten_glUniformMatrix3fv,"Wc":_emscripten_glUniformMatrix3x2fv,"Tc":_emscripten_glUniformMatrix3x4fv,"Fd":_emscripten_glUniformMatrix4fv,"Uc":_emscripten_glUniformMatrix4x2fv,"Sc":_emscripten_glUniformMatrix4x3fv,"_c":_emscripten_glUnmapBuffer,"Ed":_emscripten_glUseProgram,"Dd":_emscripten_glValidateProgram,"Cd":_emscripten_glVertexAttrib1f,"Bd":_emscripten_glVertexAttrib1fv,"Ad":_emscripten_glVertexAttrib2f,"zd":_emscripten_glVertexAttrib2fv,"yd":_emscripten_glVertexAttrib3f,"xd":_emscripten_glVertexAttrib3fv,"wd":_emscripten_glVertexAttrib4f,"vd":_emscripten_glVertexAttrib4fv,"Db":_emscripten_glVertexAttribDivisor,"bg":_emscripten_glVertexAttribDivisorANGLE,"nb":_emscripten_glVertexAttribDivisorARB,"rd":_emscripten_glVertexAttribDivisorEXT,"ob":_emscripten_glVertexAttribDivisorNV,"xc":_emscripten_glVertexAttribI4i,"vc":_emscripten_glVertexAttribI4iv,"wc":_emscripten_glVertexAttribI4ui,"uc":_emscripten_glVertexAttribI4uiv,"Ac":_emscripten_glVertexAttribIPointer,"ud":_emscripten_glVertexAttribPointer,"sd":_emscripten_glViewport,"Tb":_emscripten_glWaitSync,"ug":_emscripten_memcpy_big,"vg":_emscripten_resize_heap,"zg":_environ_get,"Ag":_environ_sizes_get,"ia":_fd_close,"Fg":_fd_read,"gb":_fd_seek,"Ia":_fd_write,"xg":_getentropy,"h":_glActiveTexture,"T":_glAttachShader,"Xg":_glBeginQuery,"C":_glBindBuffer,"ra":_glBindBufferRange,"l":_glBindFramebuffer,"H":_glBindRenderbuffer,"U":_glBindSampler,"k":_glBindTexture,"N":_glBindVertexArray,"Tg":_glBlendEquationSeparate,"Sg":_glBlendFuncSeparate,"pa":_glBlitFramebuffer,"M":_glBufferData,"Y":_glBufferSubData,"Mg":_glClearBufferfi,"O":_glClearBufferfv,"Lg":_glClearBufferiv,"hb":_glClientWaitSync,"la":_glColorMask,"ba":_glCompileShader,"Na":_glCompressedTexSubImage2D,"Ma":_glCompressedTexSubImage3D,"Aa":_glCreateProgram,"da":_glCreateShader,"ka":_glCullFace,"Ca":_glDeleteBuffers,"S":_glDeleteFramebuffers,"$":_glDeleteProgram,"$a":_glDeleteQueries,"E":_glDeleteRenderbuffers,"na":_glDeleteSamplers,"L":_glDeleteShader,"F":_glDeleteSync,"J":_glDeleteTextures,"Sb":_glDeleteVertexArrays,"Rg":_glDepthFunc,"Qg":_glDepthMask,"Za":_glDepthRangef,"_":_glDetachShader,"j":_glDisable,"sa":_glDisableVertexAttribArray,"Ra":_glDrawArrays,"Ta":_glDrawBuffers,"Ua":_glDrawRangeElements,"B":_glEnable,"ta":_glEnableVertexAttribArray,"Wg":_glEndQuery,"Z":_glFenceSync,"Da":_glFinish,"Ea":_glFlush,"P":_glFramebufferRenderbuffer,"Q":_glFramebufferTexture2D,"Qa":_glFramebufferTextureLayer,"Ug":_glFrontFace,"W":_glGenBuffers,"R":_glGenFramebuffers,"ab":_glGenQueries,"ga":_glGenRenderbuffers,"oa":_glGenSamplers,"I":_glGenTextures,"fb":_glGenVertexArrays,"_a":_glGenerateMipmap,"Sa":_glGetError,"td":_glGetFloatv,"o":_glGetIntegerv,"cb":_glGetProgramInfoLog,"ya":_glGetProgramiv,"Vg":_glGetQueryObjectuiv,"bb":_glGetShaderInfoLog,"aa":_glGetShaderiv,"V":_glGetString,"Od":_glGetStringi,"eb":_glGetUniformBlockIndex,"xa":_glGetUniformLocation,"Yg":_glGetVertexAttribiv,"id":_glHint,"va":_glInvalidateFramebuffer,"ma":_glIsEnabled,"za":_glLinkProgram,"Kg":_glMapBufferRange,"Pc":_glPixelStorei,"Va":_glPolygonOffset,"qa":_glReadPixels,"ea":_glRenderbufferStorage,"fa":_glRenderbufferStorageMultisample,"t":_glSamplerParameteri,"Ng":_glScissor,"ca":_glShaderSource,"y":_glTexParameteri,"Pg":_glTexStorage2D,"Og":_glTexStorage3D,"Pa":_glTexSubImage2D,"Oa":_glTexSubImage3D,"wa":_glUniform1i,"db":_glUniformBlockBinding,"Jg":_glUnmapBuffer,"K":_glUseProgram,"Wa":_glVertexAttrib4f,"Xa":_glVertexAttribI4ui,"Ya":_glVertexAttribIPointer,"ua":_glVertexAttribPointer,"X":_glViewport,"ib":_glWaitSync,"Fa":_pthread_create,"Cg":_pthread_join,"Ba":_setTempRet0,"yg":_strftime_l,"Bg":_sysconf};var asm=createWasm();var ___wasm_call_ctors=Module["___wasm_call_ctors"]=function(){return(___wasm_call_ctors=Module["___wasm_call_ctors"]=Module["asm"]["$g"]).apply(null,arguments)};var _free=Module["_free"]=function(){return(_free=Module["_free"]=Module["asm"]["ah"]).apply(null,arguments)};var _malloc=Module["_malloc"]=function(){return(_malloc=Module["_malloc"]=Module["asm"]["bh"]).apply(null,arguments)};var ___errno_location=Module["___errno_location"]=function(){return(___errno_location=Module["___errno_location"]=Module["asm"]["ch"]).apply(null,arguments)};var ___getTypeName=Module["___getTypeName"]=function(){return(___getTypeName=Module["___getTypeName"]=Module["asm"]["dh"]).apply(null,arguments)};var ___embind_register_native_and_builtin_types=Module["___embind_register_native_and_builtin_types"]=function(){return(___embind_register_native_and_builtin_types=Module["___embind_register_native_and_builtin_types"]=Module["asm"]["eh"]).apply(null,arguments)};var dynCall_viijii=Module["dynCall_viijii"]=function(){return(dynCall_viijii=Module["dynCall_viijii"]=Module["asm"]["fh"]).apply(null,arguments)};var dynCall_jii=Module["dynCall_jii"]=function(){return(dynCall_jii=Module["dynCall_jii"]=Module["asm"]["gh"]).apply(null,arguments)};var dynCall_iiij=Module["dynCall_iiij"]=function(){return(dynCall_iiij=Module["dynCall_iiij"]=Module["asm"]["hh"]).apply(null,arguments)};var dynCall_vij=Module["dynCall_vij"]=function(){return(dynCall_vij=Module["dynCall_vij"]=Module["asm"]["ih"]).apply(null,arguments)};var dynCall_jiji=Module["dynCall_jiji"]=function(){return(dynCall_jiji=Module["dynCall_jiji"]=Module["asm"]["jh"]).apply(null,arguments)};var dynCall_iiiiij=Module["dynCall_iiiiij"]=function(){return(dynCall_iiiiij=Module["dynCall_iiiiij"]=Module["asm"]["kh"]).apply(null,arguments)};var dynCall_iiiiijj=Module["dynCall_iiiiijj"]=function(){return(dynCall_iiiiijj=Module["dynCall_iiiiijj"]=Module["asm"]["lh"]).apply(null,arguments)};var dynCall_iiiiiijj=Module["dynCall_iiiiiijj"]=function(){return(dynCall_iiiiiijj=Module["dynCall_iiiiiijj"]=Module["asm"]["mh"]).apply(null,arguments)};var calledRun;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(args){args=args||arguments_;if(runDependencies>0){return}preRun();if(runDependencies>0)return;function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();preMain();readyPromiseResolve(Module);if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}noExitRuntime=true;run(); +var Module=typeof Filament!=="undefined"?Filament:{};var readyPromiseResolve,readyPromiseReject;Module["ready"]=new Promise(function(resolve,reject){readyPromiseResolve=resolve;readyPromiseReject=reject});var moduleOverrides={};var key;for(key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var arguments_=[];var thisProgram="./this.program";var quit_=function(status,toThrow){throw toThrow};var ENVIRONMENT_IS_WEB=false;var ENVIRONMENT_IS_WORKER=false;var ENVIRONMENT_IS_NODE=false;var ENVIRONMENT_IS_SHELL=false;ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_WORKER=typeof importScripts==="function";ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string";ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER;var scriptDirectory="";function locateFile(path){if(Module["locateFile"]){return Module["locateFile"](path,scriptDirectory)}return scriptDirectory+path}var read_,readAsync,readBinary,setWindowTitle;var nodeFS;var nodePath;if(ENVIRONMENT_IS_NODE){if(ENVIRONMENT_IS_WORKER){scriptDirectory=require("path").dirname(scriptDirectory)+"/"}else{scriptDirectory=__dirname+"/"}read_=function shell_read(filename,binary){if(!nodeFS)nodeFS=require("fs");if(!nodePath)nodePath=require("path");filename=nodePath["normalize"](filename);return nodeFS["readFileSync"](filename,binary?null:"utf8")};readBinary=function readBinary(filename){var ret=read_(filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};if(process["argv"].length>1){thisProgram=process["argv"][1].replace(/\\/g,"/")}arguments_=process["argv"].slice(2);process["on"]("uncaughtException",function(ex){if(!(ex instanceof ExitStatus)){throw ex}});process["on"]("unhandledRejection",abort);quit_=function(status){process["exit"](status)};Module["inspect"]=function(){return"[Emscripten Module object]"}}else if(ENVIRONMENT_IS_SHELL){if(typeof read!="undefined"){read_=function shell_read(f){return read(f)}}readBinary=function readBinary(f){var data;if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){arguments_=scriptArgs}else if(typeof arguments!="undefined"){arguments_=arguments}if(typeof quit==="function"){quit_=function(status){quit(status)}}if(typeof print!=="undefined"){if(typeof console==="undefined")console={};console.log=print;console.warn=console.error=typeof printErr!=="undefined"?printErr:print}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!=="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptDir){scriptDirectory=_scriptDir}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=function(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=function(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=function(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}setWindowTitle=function(title){document.title=title}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.warn.bind(console);for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var STACK_ALIGN=16;function alignMemory(size,factor){if(!factor)factor=STACK_ALIGN;return Math.ceil(size/factor)*factor}var tempRet0=0;var setTempRet0=function(value){tempRet0=value};var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime;if(Module["noExitRuntime"])noExitRuntime=Module["noExitRuntime"];if(typeof WebAssembly!=="object"){abort("no native wasm support detected")}var wasmMemory;var ABORT=false;var EXITSTATUS;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf8"):undefined;function UTF8ArrayToString(heap,idx,maxBytesToRead){var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heap[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heap.subarray&&UTF8Decoder){return UTF8Decoder.decode(heap.subarray(idx,endPtr))}else{var str="";while(idx>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function stringToUTF8Array(str,heap,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}function lengthBytesUTF8(str){var len=0;for(var i=0;i=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127)++len;else if(u<=2047)len+=2;else if(u<=65535)len+=3;else len+=4}return len}var UTF16Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf-16le"):undefined;function UTF16ToString(ptr,maxBytesToRead){var endPtr=ptr;var idx=endPtr>>1;var maxIdx=idx+maxBytesToRead/2;while(!(idx>=maxIdx)&&HEAPU16[idx])++idx;endPtr=idx<<1;if(endPtr-ptr>32&&UTF16Decoder){return UTF16Decoder.decode(HEAPU8.subarray(ptr,endPtr))}else{var str="";for(var i=0;!(i>=maxBytesToRead/2);++i){var codeUnit=HEAP16[ptr+i*2>>1];if(codeUnit==0)break;str+=String.fromCharCode(codeUnit)}return str}}function stringToUTF16(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite>1]=codeUnit;outPtr+=2}HEAP16[outPtr>>1]=0;return outPtr-startPtr}function lengthBytesUTF16(str){return str.length*2}function UTF32ToString(ptr,maxBytesToRead){var i=0;var str="";while(!(i>=maxBytesToRead/4)){var utf32=HEAP32[ptr+i*4>>2];if(utf32==0)break;++i;if(utf32>=65536){var ch=utf32-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}else{str+=String.fromCharCode(utf32)}}return str}function stringToUTF32(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i=55296&&codeUnit<=57343){var trailSurrogate=str.charCodeAt(++i);codeUnit=65536+((codeUnit&1023)<<10)|trailSurrogate&1023}HEAP32[outPtr>>2]=codeUnit;outPtr+=4;if(outPtr+4>endPtr)break}HEAP32[outPtr>>2]=0;return outPtr-startPtr}function lengthBytesUTF32(str){var len=0;for(var i=0;i=55296&&codeUnit<=57343)++i;len+=4}return len}function writeArrayToMemory(array,buffer){HEAP8.set(array,buffer)}function writeAsciiToMemory(str,buffer,dontAddNull){for(var i=0;i>0]=str.charCodeAt(i)}if(!dontAddNull)HEAP8[buffer>>0]=0}function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}return x}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var INITIAL_MEMORY=Module["INITIAL_MEMORY"]||16777216;var wasmTable;var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;__ATINIT__.push({func:function(){___wasm_call_ctors()}});function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;if(!Module["noFSInit"]&&!FS.init.initialized)FS.init();TTY.init();callRuntimeCallbacks(__ATINIT__)}function preMain(){FS.ignorePermissions=false;callRuntimeCallbacks(__ATMAIN__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function getUniqueRunDependency(id){return id}function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}what+="";err(what);ABORT=true;EXITSTATUS=1;what="abort("+what+"). Build with -s ASSERTIONS=1 for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}function hasPrefix(str,prefix){return String.prototype.startsWith?str.startsWith(prefix):str.indexOf(prefix)===0}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return hasPrefix(filename,dataURIPrefix)}var fileURIPrefix="file://";function isFileURI(filename){return hasPrefix(filename,fileURIPrefix)}var wasmBinaryFile="filament.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(file){try{if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)){if(typeof fetch==="function"&&!isFileURI(wasmBinaryFile)){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary(wasmBinaryFile)})}else{if(readAsync){return new Promise(function(resolve,reject){readAsync(wasmBinaryFile,function(response){resolve(new Uint8Array(response))},reject)})}}}return Promise.resolve().then(function(){return getBinary(wasmBinaryFile)})}function createWasm(){var info={"a":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;wasmMemory=Module["asm"]["Zg"];updateGlobalBufferAndViews(wasmMemory.buffer);wasmTable=Module["asm"]["_g"];removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");function receiveInstantiatedSource(output){receiveInstance(output["instance"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){return WebAssembly.instantiate(binary,info)}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&!isFileURI(wasmBinaryFile)&&typeof fetch==="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiatedSource,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(receiveInstantiatedSource)})})}else{return instantiateArrayBuffer(receiveInstantiatedSource)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync().catch(readyPromiseReject);return{}}var tempDouble;var tempI64;var ASM_CONSTS={10520:function(){const handle=GL.registerContext(Filament.glContext,Filament.glOptions);GL.makeContextCurrent(handle)}};function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback(Module);continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){wasmTable.get(func)()}else{wasmTable.get(func)(callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}function setErrNo(value){HEAP32[___errno_location()>>2]=value;return value}var PATH={splitPath:function(filename){var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:function(parts,allowAboveRoot){var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:function(path){var isAbsolute=path.charAt(0)==="/",trailingSlash=path.substr(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(function(p){return!!p}),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:function(path){var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.substr(0,dir.length-1)}return root+dir},basename:function(path){if(path==="/")return"/";path=PATH.normalize(path);path=path.replace(/\/$/,"");var lastSlash=path.lastIndexOf("/");if(lastSlash===-1)return path;return path.substr(lastSlash+1)},extname:function(path){return PATH.splitPath(path)[3]},join:function(){var paths=Array.prototype.slice.call(arguments,0);return PATH.normalize(paths.join("/"))},join2:function(l,r){return PATH.normalize(l+"/"+r)}};function getRandomDevice(){if(typeof crypto==="object"&&typeof crypto["getRandomValues"]==="function"){var randomBuffer=new Uint8Array(1);return function(){crypto.getRandomValues(randomBuffer);return randomBuffer[0]}}else if(ENVIRONMENT_IS_NODE){try{var crypto_module=require("crypto");return function(){return crypto_module["randomBytes"](1)[0]}}catch(e){}}return function(){abort("randomDevice")}}var PATH_FS={resolve:function(){var resolvedPath="",resolvedAbsolute=false;for(var i=arguments.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?arguments[i]:FS.cwd();if(typeof path!=="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=path.charAt(0)==="/"}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(function(p){return!!p}),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:function(from,to){from=PATH_FS.resolve(from).substr(1);to=PATH_FS.resolve(to).substr(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i0){result=buf.slice(0,bytesRead).toString("utf-8")}else{result=null}}else if(typeof window!="undefined"&&typeof window.prompt=="function"){result=window.prompt("Input: ");if(result!==null){result+="\n"}}else if(typeof readline=="function"){result=readline();if(result!==null){result+="\n"}}if(!result){return null}tty.input=intArrayFromString(result,true)}return tty.input.shift()},put_char:function(tty,val){if(val===null||val===10){out(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},flush:function(tty){if(tty.output&&tty.output.length>0){out(UTF8ArrayToString(tty.output,0));tty.output=[]}}},default_tty1_ops:{put_char:function(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},flush:function(tty){if(tty.output&&tty.output.length>0){err(UTF8ArrayToString(tty.output,0));tty.output=[]}}}};function mmapAlloc(size){var alignedSize=alignMemory(size,16384);var ptr=_malloc(alignedSize);while(size=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity>>0);if(prevCapacity!=0)newCapacity=Math.max(newCapacity,256);var oldContents=node.contents;node.contents=new Uint8Array(newCapacity);if(node.usedBytes>0)node.contents.set(oldContents.subarray(0,node.usedBytes),0);return},resizeFileStorage:function(node,newSize){if(node.usedBytes==newSize)return;if(newSize==0){node.contents=null;node.usedBytes=0;return}if(!node.contents||node.contents.subarray){var oldContents=node.contents;node.contents=new Uint8Array(newSize);if(oldContents){node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)))}node.usedBytes=newSize;return}if(!node.contents)node.contents=[];if(node.contents.length>newSize)node.contents.length=newSize;else while(node.contents.length=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);if(size>8&&contents.subarray){buffer.set(contents.subarray(position,position+size),offset)}else{for(var i=0;i0||position+length8){throw new FS.ErrnoError(32)}var parts=PATH.normalizeArray(path.split("/").filter(function(p){return!!p}),false);var current=FS.root;var current_path="/";for(var i=0;i40){throw new FS.ErrnoError(32)}}}}return{path:current_path,node:current}},getPath:function(node){var path;while(true){if(FS.isRoot(node)){var mount=node.mount.mountpoint;if(!path)return mount;return mount[mount.length-1]!=="/"?mount+"/"+path:mount+path}path=path?node.name+"/"+path:node.name;node=node.parent}},hashName:function(parentid,name){var hash=0;for(var i=0;i>>0)%FS.nameTable.length},hashAddNode:function(node){var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode:function(node){var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode:function(parent,name){var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode,parent)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode:function(parent,name,mode,rdev){var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode:function(node){FS.hashRemoveNode(node)},isRoot:function(node){return node===node.parent},isMountpoint:function(node){return!!node.mounted},isFile:function(mode){return(mode&61440)===32768},isDir:function(mode){return(mode&61440)===16384},isLink:function(mode){return(mode&61440)===40960},isChrdev:function(mode){return(mode&61440)===8192},isBlkdev:function(mode){return(mode&61440)===24576},isFIFO:function(mode){return(mode&61440)===4096},isSocket:function(mode){return(mode&49152)===49152},flagModes:{"r":0,"r+":2,"w":577,"w+":578,"a":1089,"a+":1090},modeStringToFlags:function(str){var flags=FS.flagModes[str];if(typeof flags==="undefined"){throw new Error("Unknown file open mode: "+str)}return flags},flagsToPermissionString:function(flag){var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions:function(node,perms){if(FS.ignorePermissions){return 0}if(perms.indexOf("r")!==-1&&!(node.mode&292)){return 2}else if(perms.indexOf("w")!==-1&&!(node.mode&146)){return 2}else if(perms.indexOf("x")!==-1&&!(node.mode&73)){return 2}return 0},mayLookup:function(dir){var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate:function(dir,name){try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete:function(dir,name,isdir){var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else{if(FS.isDir(node.mode)){return 31}}return 0},mayOpen:function(node,flags){if(!node){return 44}if(FS.isLink(node.mode)){return 32}else if(FS.isDir(node.mode)){if(FS.flagsToPermissionString(flags)!=="r"||flags&512){return 31}}return FS.nodePermissions(node,FS.flagsToPermissionString(flags))},MAX_OPEN_FDS:4096,nextfd:function(fd_start,fd_end){fd_start=fd_start||0;fd_end=fd_end||FS.MAX_OPEN_FDS;for(var fd=fd_start;fd<=fd_end;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStream:function(fd){return FS.streams[fd]},createStream:function(stream,fd_start,fd_end){if(!FS.FSStream){FS.FSStream=function(){};FS.FSStream.prototype={object:{get:function(){return this.node},set:function(val){this.node=val}},isRead:{get:function(){return(this.flags&2097155)!==1}},isWrite:{get:function(){return(this.flags&2097155)!==0}},isAppend:{get:function(){return this.flags&1024}}}}var newStream=new FS.FSStream;for(var p in stream){newStream[p]=stream[p]}stream=newStream;var fd=FS.nextfd(fd_start,fd_end);stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream:function(fd){FS.streams[fd]=null},chrdev_stream_ops:{open:function(stream){var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;if(stream.stream_ops.open){stream.stream_ops.open(stream)}},llseek:function(){throw new FS.ErrnoError(70)}},major:function(dev){return dev>>8},minor:function(dev){return dev&255},makedev:function(ma,mi){return ma<<8|mi},registerDevice:function(dev,ops){FS.devices[dev]={stream_ops:ops}},getDevice:function(dev){return FS.devices[dev]},getMounts:function(mount){var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push.apply(check,m.mounts)}return mounts},syncfs:function(populate,callback){if(typeof populate==="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){err("warning: "+FS.syncFSRequests+" FS.syncfs operations in flight at once, probably just doing extra work")}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null)}}mounts.forEach(function(mount){if(!mount.type.syncfs){return done(null)}mount.type.syncfs(mount,populate,done)})},mount:function(type,opts,mountpoint){var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type:type,opts:opts,mountpoint:mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount:function(mountpoint){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);Object.keys(FS.nameTable).forEach(function(hash){var current=FS.nameTable[hash];while(current){var next=current.name_next;if(mounts.indexOf(current.mount)!==-1){FS.destroyNode(current)}current=next}});node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup:function(parent,name){return parent.node_ops.lookup(parent,name)},mknod:function(path,mode,dev){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name||name==="."||name===".."){throw new FS.ErrnoError(28)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},create:function(path,mode){mode=mode!==undefined?mode:438;mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir:function(path,mode){mode=mode!==undefined?mode:511;mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree:function(path,mode){var dirs=path.split("/");var d="";for(var i=0;ithis.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]};LazyUint8Array.prototype.setDataGetter=function LazyUint8Array_setDataGetter(getter){this.getter=getter};LazyUint8Array.prototype.cacheLength=function LazyUint8Array_cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=function(from,to){if(from>to)throw new Error("invalid range ("+from+", "+to+") or no bytes requested!");if(to>datalength-1)throw new Error("only "+datalength+" bytes available! programmer error!");var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);if(typeof Uint8Array!="undefined")xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}else{return intArrayFromString(xhr.responseText||"",true)}};var lazyArray=this;lazyArray.setDataGetter(function(chunkNum){var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]==="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]==="undefined")throw new Error("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true};if(typeof XMLHttpRequest!=="undefined"){if(!ENVIRONMENT_IS_WORKER)throw"Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var lazyArray=new LazyUint8Array;Object.defineProperties(lazyArray,{length:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._length}},chunkSize:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}});var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url:url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};var keys=Object.keys(node.stream_ops);keys.forEach(function(key){var fn=node.stream_ops[key];stream_ops[key]=function forceLoadLazyFile(){FS.forceLoadFile(node);return fn.apply(null,arguments)}});stream_ops.read=function stream_ops_read(stream,buffer,offset,length,position){FS.forceLoadFile(node);var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i>2]=stat.dev;HEAP32[buf+4>>2]=0;HEAP32[buf+8>>2]=stat.ino;HEAP32[buf+12>>2]=stat.mode;HEAP32[buf+16>>2]=stat.nlink;HEAP32[buf+20>>2]=stat.uid;HEAP32[buf+24>>2]=stat.gid;HEAP32[buf+28>>2]=stat.rdev;HEAP32[buf+32>>2]=0;tempI64=[stat.size>>>0,(tempDouble=stat.size,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+40>>2]=tempI64[0],HEAP32[buf+44>>2]=tempI64[1];HEAP32[buf+48>>2]=4096;HEAP32[buf+52>>2]=stat.blocks;HEAP32[buf+56>>2]=stat.atime.getTime()/1e3|0;HEAP32[buf+60>>2]=0;HEAP32[buf+64>>2]=stat.mtime.getTime()/1e3|0;HEAP32[buf+68>>2]=0;HEAP32[buf+72>>2]=stat.ctime.getTime()/1e3|0;HEAP32[buf+76>>2]=0;tempI64=[stat.ino>>>0,(tempDouble=stat.ino,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+80>>2]=tempI64[0],HEAP32[buf+84>>2]=tempI64[1];return 0},doMsync:function(addr,stream,len,flags,offset){var buffer=HEAPU8.slice(addr,addr+len);FS.msync(stream,buffer,offset,len,flags)},doMkdir:function(path,mode){path=PATH.normalize(path);if(path[path.length-1]==="/")path=path.substr(0,path.length-1);FS.mkdir(path,mode,0);return 0},doMknod:function(path,mode,dev){switch(mode&61440){case 32768:case 8192:case 24576:case 4096:case 49152:break;default:return-28}FS.mknod(path,mode,dev);return 0},doReadlink:function(path,buf,bufsize){if(bufsize<=0)return-28;var ret=FS.readlink(path);var len=Math.min(bufsize,lengthBytesUTF8(ret));var endChar=HEAP8[buf+len];stringToUTF8(ret,buf,bufsize+1);HEAP8[buf+len]=endChar;return len},doAccess:function(path,amode){if(amode&~7){return-28}var node;var lookup=FS.lookupPath(path,{follow:true});node=lookup.node;if(!node){return-44}var perms="";if(amode&4)perms+="r";if(amode&2)perms+="w";if(amode&1)perms+="x";if(perms&&FS.nodePermissions(node,perms)){return-2}return 0},doDup:function(path,flags,suggestFD){var suggest=FS.getStream(suggestFD);if(suggest)FS.close(suggest);return FS.open(path,flags,0,suggestFD,suggestFD).fd},doReadv:function(stream,iov,iovcnt,offset){var ret=0;for(var i=0;i>2];var len=HEAP32[iov+(i*8+4)>>2];var curr=FS.read(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2];var len=HEAP32[iov+(i*8+4)>>2];var curr=FS.write(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr}return ret},varargs:undefined,get:function(){SYSCALLS.varargs+=4;var ret=HEAP32[SYSCALLS.varargs-4>>2];return ret},getStr:function(ptr){var ret=UTF8ToString(ptr);return ret},getStreamFromFD:function(fd){var stream=FS.getStream(fd);if(!stream)throw new FS.ErrnoError(8);return stream},get64:function(low,high){return low}};function ___sys_fcntl64(fd,cmd,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:{var arg=SYSCALLS.get();if(arg<0){return-28}var newStream;newStream=FS.open(stream.path,stream.flags,0,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=SYSCALLS.get();stream.flags|=arg;return 0}case 12:{var arg=SYSCALLS.get();var offset=0;HEAP16[arg+offset>>1]=2;return 0}case 13:case 14:return 0;case 16:case 8:return-28;case 9:setErrNo(28);return-1;default:{return-28}}}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___sys_ioctl(fd,op,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(op){case 21509:case 21505:{if(!stream.tty)return-59;return 0}case 21510:case 21511:case 21512:case 21506:case 21507:case 21508:{if(!stream.tty)return-59;return 0}case 21519:{if(!stream.tty)return-59;var argp=SYSCALLS.get();HEAP32[argp>>2]=0;return 0}case 21520:{if(!stream.tty)return-59;return-28}case 21531:{var argp=SYSCALLS.get();return FS.ioctl(stream,op,argp)}case 21523:{if(!stream.tty)return-59;return 0}case 21524:{if(!stream.tty)return-59;return 0}default:abort("bad ioctl syscall "+op)}}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___sys_open(path,flags,varargs){SYSCALLS.varargs=varargs;try{var pathname=SYSCALLS.getStr(path);var mode=varargs?SYSCALLS.get():0;var stream=FS.open(pathname,flags,mode);return stream.fd}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}var tupleRegistrations={};function runDestructors(destructors){while(destructors.length){var ptr=destructors.pop();var del=destructors.pop();del(ptr)}}function simpleReadValueFromPointer(pointer){return this["fromWireType"](HEAPU32[pointer>>2])}var awaitingDependencies={};var registeredTypes={};var typeDependencies={};var char_0=48;var char_9=57;function makeLegalFunctionName(name){if(undefined===name){return"_unknown"}name=name.replace(/[^a-zA-Z0-9_]/g,"$");var f=name.charCodeAt(0);if(f>=char_0&&f<=char_9){return"_"+name}else{return name}}function createNamedFunction(name,body){name=makeLegalFunctionName(name);return new Function("body","return function "+name+"() {\n"+' "use strict";'+" return body.apply(this, arguments);\n"+"};\n")(body)}function extendError(baseErrorType,errorName){var errorClass=createNamedFunction(errorName,function(message){this.name=errorName;this.message=message;var stack=new Error(message).stack;if(stack!==undefined){this.stack=this.toString()+"\n"+stack.replace(/^Error(:[^\n]*)?\n/,"")}});errorClass.prototype=Object.create(baseErrorType.prototype);errorClass.prototype.constructor=errorClass;errorClass.prototype.toString=function(){if(this.message===undefined){return this.name}else{return this.name+": "+this.message}};return errorClass}var InternalError=undefined;function throwInternalError(message){throw new InternalError(message)}function whenDependentTypesAreResolved(myTypes,dependentTypes,getTypeConverters){myTypes.forEach(function(type){typeDependencies[type]=dependentTypes});function onComplete(typeConverters){var myTypeConverters=getTypeConverters(typeConverters);if(myTypeConverters.length!==myTypes.length){throwInternalError("Mismatched type converter count")}for(var i=0;i>shift])},destructorFunction:null})}function ClassHandle_isAliasOf(other){if(!(this instanceof ClassHandle)){return false}if(!(other instanceof ClassHandle)){return false}var leftClass=this.$$.ptrType.registeredClass;var left=this.$$.ptr;var rightClass=other.$$.ptrType.registeredClass;var right=other.$$.ptr;while(leftClass.baseClass){left=leftClass.upcast(left);leftClass=leftClass.baseClass}while(rightClass.baseClass){right=rightClass.upcast(right);rightClass=rightClass.baseClass}return leftClass===rightClass&&left===right}function shallowCopyInternalPointer(o){return{count:o.count,deleteScheduled:o.deleteScheduled,preservePointerOnDelete:o.preservePointerOnDelete,ptr:o.ptr,ptrType:o.ptrType,smartPtr:o.smartPtr,smartPtrType:o.smartPtrType}}function throwInstanceAlreadyDeleted(obj){function getInstanceTypeName(handle){return handle.$$.ptrType.registeredClass.name}throwBindingError(getInstanceTypeName(obj)+" instance already deleted")}var finalizationGroup=false;function detachFinalizer(handle){}function runDestructor($$){if($$.smartPtr){$$.smartPtrType.rawDestructor($$.smartPtr)}else{$$.ptrType.registeredClass.rawDestructor($$.ptr)}}function releaseClassHandle($$){$$.count.value-=1;var toDelete=0===$$.count.value;if(toDelete){runDestructor($$)}}function attachFinalizer(handle){if("undefined"===typeof FinalizationGroup){attachFinalizer=function(handle){return handle};return handle}finalizationGroup=new FinalizationGroup(function(iter){for(var result=iter.next();!result.done;result=iter.next()){var $$=result.value;if(!$$.ptr){console.warn("object already deleted: "+$$.ptr)}else{releaseClassHandle($$)}}});attachFinalizer=function(handle){finalizationGroup.register(handle,handle.$$,handle.$$);return handle};detachFinalizer=function(handle){finalizationGroup.unregister(handle.$$)};return attachFinalizer(handle)}function ClassHandle_clone(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.preservePointerOnDelete){this.$$.count.value+=1;return this}else{var clone=attachFinalizer(Object.create(Object.getPrototypeOf(this),{$$:{value:shallowCopyInternalPointer(this.$$)}}));clone.$$.count.value+=1;clone.$$.deleteScheduled=false;return clone}}function ClassHandle_delete(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}detachFinalizer(this);releaseClassHandle(this.$$);if(!this.$$.preservePointerOnDelete){this.$$.smartPtr=undefined;this.$$.ptr=undefined}}function ClassHandle_isDeleted(){return!this.$$.ptr}var delayFunction=undefined;var deletionQueue=[];function flushPendingDeletes(){while(deletionQueue.length){var obj=deletionQueue.pop();obj.$$.deleteScheduled=false;obj["delete"]()}}function ClassHandle_deleteLater(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}deletionQueue.push(this);if(deletionQueue.length===1&&delayFunction){delayFunction(flushPendingDeletes)}this.$$.deleteScheduled=true;return this}function init_ClassHandle(){ClassHandle.prototype["isAliasOf"]=ClassHandle_isAliasOf;ClassHandle.prototype["clone"]=ClassHandle_clone;ClassHandle.prototype["delete"]=ClassHandle_delete;ClassHandle.prototype["isDeleted"]=ClassHandle_isDeleted;ClassHandle.prototype["deleteLater"]=ClassHandle_deleteLater}function ClassHandle(){}var registeredPointers={};function ensureOverloadTable(proto,methodName,humanName){if(undefined===proto[methodName].overloadTable){var prevFunc=proto[methodName];proto[methodName]=function(){if(!proto[methodName].overloadTable.hasOwnProperty(arguments.length)){throwBindingError("Function '"+humanName+"' called with an invalid number of arguments ("+arguments.length+") - expects one of ("+proto[methodName].overloadTable+")!")}return proto[methodName].overloadTable[arguments.length].apply(this,arguments)};proto[methodName].overloadTable=[];proto[methodName].overloadTable[prevFunc.argCount]=prevFunc}}function exposePublicSymbol(name,value,numArguments){if(Module.hasOwnProperty(name)){if(undefined===numArguments||undefined!==Module[name].overloadTable&&undefined!==Module[name].overloadTable[numArguments]){throwBindingError("Cannot register public name '"+name+"' twice")}ensureOverloadTable(Module,name,name);if(Module.hasOwnProperty(numArguments)){throwBindingError("Cannot register multiple overloads of a function with the same number of arguments ("+numArguments+")!")}Module[name].overloadTable[numArguments]=value}else{Module[name]=value;if(undefined!==numArguments){Module[name].numArguments=numArguments}}}function RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast){this.name=name;this.constructor=constructor;this.instancePrototype=instancePrototype;this.rawDestructor=rawDestructor;this.baseClass=baseClass;this.getActualType=getActualType;this.upcast=upcast;this.downcast=downcast;this.pureVirtualFunctions=[]}function upcastPointer(ptr,ptrClass,desiredClass){while(ptrClass!==desiredClass){if(!ptrClass.upcast){throwBindingError("Expected null or instance of "+desiredClass.name+", got an instance of "+ptrClass.name)}ptr=ptrClass.upcast(ptr);ptrClass=ptrClass.baseClass}return ptr}function constNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}return 0}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function genericPointerToWireType(destructors,handle){var ptr;if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}if(this.isSmartPointer){ptr=this.rawConstructor();if(destructors!==null){destructors.push(this.rawDestructor,ptr)}return ptr}else{return 0}}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}if(!this.isConst&&handle.$$.ptrType.isConst){throwBindingError("Cannot convert argument of type "+(handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name)+" to parameter type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);if(this.isSmartPointer){if(undefined===handle.$$.smartPtr){throwBindingError("Passing raw pointer to smart pointer is illegal")}switch(this.sharingPolicy){case 0:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{throwBindingError("Cannot convert argument of type "+(handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name)+" to parameter type "+this.name)}break;case 1:ptr=handle.$$.smartPtr;break;case 2:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{var clonedHandle=handle["clone"]();ptr=this.rawShare(ptr,__emval_register(function(){clonedHandle["delete"]()}));if(destructors!==null){destructors.push(this.rawDestructor,ptr)}}break;default:throwBindingError("Unsupporting sharing policy")}}return ptr}function nonConstNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}return 0}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}if(handle.$$.ptrType.isConst){throwBindingError("Cannot convert argument of type "+handle.$$.ptrType.name+" to parameter type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function RegisteredPointer_getPointee(ptr){if(this.rawGetPointee){ptr=this.rawGetPointee(ptr)}return ptr}function RegisteredPointer_destructor(ptr){if(this.rawDestructor){this.rawDestructor(ptr)}}function RegisteredPointer_deleteObject(handle){if(handle!==null){handle["delete"]()}}function downcastPointer(ptr,ptrClass,desiredClass){if(ptrClass===desiredClass){return ptr}if(undefined===desiredClass.baseClass){return null}var rv=downcastPointer(ptr,ptrClass,desiredClass.baseClass);if(rv===null){return null}return desiredClass.downcast(rv)}function getInheritedInstanceCount(){return Object.keys(registeredInstances).length}function getLiveInheritedInstances(){var rv=[];for(var k in registeredInstances){if(registeredInstances.hasOwnProperty(k)){rv.push(registeredInstances[k])}}return rv}function setDelayFunction(fn){delayFunction=fn;if(deletionQueue.length&&delayFunction){delayFunction(flushPendingDeletes)}}function init_embind(){Module["getInheritedInstanceCount"]=getInheritedInstanceCount;Module["getLiveInheritedInstances"]=getLiveInheritedInstances;Module["flushPendingDeletes"]=flushPendingDeletes;Module["setDelayFunction"]=setDelayFunction}var registeredInstances={};function getBasestPointer(class_,ptr){if(ptr===undefined){throwBindingError("ptr should not be undefined")}while(class_.baseClass){ptr=class_.upcast(ptr);class_=class_.baseClass}return ptr}function getInheritedInstance(class_,ptr){ptr=getBasestPointer(class_,ptr);return registeredInstances[ptr]}function makeClassHandle(prototype,record){if(!record.ptrType||!record.ptr){throwInternalError("makeClassHandle requires ptr and ptrType")}var hasSmartPtrType=!!record.smartPtrType;var hasSmartPtr=!!record.smartPtr;if(hasSmartPtrType!==hasSmartPtr){throwInternalError("Both smartPtrType and smartPtr must be specified")}record.count={value:1};return attachFinalizer(Object.create(prototype,{$$:{value:record}}))}function RegisteredPointer_fromWireType(ptr){var rawPointer=this.getPointee(ptr);if(!rawPointer){this.destructor(ptr);return null}var registeredInstance=getInheritedInstance(this.registeredClass,rawPointer);if(undefined!==registeredInstance){if(0===registeredInstance.$$.count.value){registeredInstance.$$.ptr=rawPointer;registeredInstance.$$.smartPtr=ptr;return registeredInstance["clone"]()}else{var rv=registeredInstance["clone"]();this.destructor(ptr);return rv}}function makeDefaultHandle(){if(this.isSmartPointer){return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this.pointeeType,ptr:rawPointer,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this,ptr:ptr})}}var actualType=this.registeredClass.getActualType(rawPointer);var registeredPointerRecord=registeredPointers[actualType];if(!registeredPointerRecord){return makeDefaultHandle.call(this)}var toType;if(this.isConst){toType=registeredPointerRecord.constPointerType}else{toType=registeredPointerRecord.pointerType}var dp=downcastPointer(rawPointer,this.registeredClass,toType.registeredClass);if(dp===null){return makeDefaultHandle.call(this)}if(this.isSmartPointer){return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp})}}function init_RegisteredPointer(){RegisteredPointer.prototype.getPointee=RegisteredPointer_getPointee;RegisteredPointer.prototype.destructor=RegisteredPointer_destructor;RegisteredPointer.prototype["argPackAdvance"]=8;RegisteredPointer.prototype["readValueFromPointer"]=simpleReadValueFromPointer;RegisteredPointer.prototype["deleteObject"]=RegisteredPointer_deleteObject;RegisteredPointer.prototype["fromWireType"]=RegisteredPointer_fromWireType}function RegisteredPointer(name,registeredClass,isReference,isConst,isSmartPointer,pointeeType,sharingPolicy,rawGetPointee,rawConstructor,rawShare,rawDestructor){this.name=name;this.registeredClass=registeredClass;this.isReference=isReference;this.isConst=isConst;this.isSmartPointer=isSmartPointer;this.pointeeType=pointeeType;this.sharingPolicy=sharingPolicy;this.rawGetPointee=rawGetPointee;this.rawConstructor=rawConstructor;this.rawShare=rawShare;this.rawDestructor=rawDestructor;if(!isSmartPointer&®isteredClass.baseClass===undefined){if(isConst){this["toWireType"]=constNoSmartPtrRawPointerToWireType;this.destructorFunction=null}else{this["toWireType"]=nonConstNoSmartPtrRawPointerToWireType;this.destructorFunction=null}}else{this["toWireType"]=genericPointerToWireType}}function replacePublicSymbol(name,value,numArguments){if(!Module.hasOwnProperty(name)){throwInternalError("Replacing nonexistant public symbol")}if(undefined!==Module[name].overloadTable&&undefined!==numArguments){Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}}function dynCallLegacy(sig,ptr,args){if(args&&args.length){return Module["dynCall_"+sig].apply(null,[ptr].concat(args))}return Module["dynCall_"+sig].call(null,ptr)}function dynCall(sig,ptr,args){if(sig.indexOf("j")!=-1){return dynCallLegacy(sig,ptr,args)}return wasmTable.get(ptr).apply(null,args)}function getDynCaller(sig,ptr){assert(sig.indexOf("j")>=0,"getDynCaller should only be called with i64 sigs");var argCache=[];return function(){argCache.length=arguments.length;for(var i=0;i0?", ":"")+argsListWired}invokerFnBody+=(returns?"var rv = ":"")+"invoker(fn"+(argsListWired.length>0?", ":"")+argsListWired+");\n";if(needsDestructorStack){invokerFnBody+="runDestructors(destructors);\n"}else{for(var i=isClassMethodFunc?1:2;i>2)+i])}return array}function __embind_register_class_class_function(rawClassType,methodName,argCount,rawArgTypesAddr,invokerSignature,rawInvoker,fn){var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);methodName=readLatin1String(methodName);rawInvoker=embind__requireFunction(invokerSignature,rawInvoker);whenDependentTypesAreResolved([],[rawClassType],function(classType){classType=classType[0];var humanName=classType.name+"."+methodName;function unboundTypesHandler(){throwUnboundTypeError("Cannot call "+humanName+" due to unbound types",rawArgTypes)}var proto=classType.registeredClass.constructor;if(undefined===proto[methodName]){unboundTypesHandler.argCount=argCount-1;proto[methodName]=unboundTypesHandler}else{ensureOverloadTable(proto,methodName,humanName);proto[methodName].overloadTable[argCount-1]=unboundTypesHandler}whenDependentTypesAreResolved([],rawArgTypes,function(argTypes){var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));var func=craftInvokerFunction(humanName,invokerArgsArray,null,rawInvoker,fn);if(undefined===proto[methodName].overloadTable){func.argCount=argCount-1;proto[methodName]=func}else{proto[methodName].overloadTable[argCount-1]=func}return[]});return[]})}function __embind_register_class_constructor(rawClassType,argCount,rawArgTypesAddr,invokerSignature,invoker,rawConstructor){assert(argCount>0);var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);invoker=embind__requireFunction(invokerSignature,invoker);var args=[rawConstructor];var destructors=[];whenDependentTypesAreResolved([],[rawClassType],function(classType){classType=classType[0];var humanName="constructor "+classType.name;if(undefined===classType.registeredClass.constructor_body){classType.registeredClass.constructor_body=[]}if(undefined!==classType.registeredClass.constructor_body[argCount-1]){throw new BindingError("Cannot register multiple constructors with identical number of parameters ("+(argCount-1)+") for class '"+classType.name+"'! Overload resolution is currently only performed using the parameter count, not actual type info!")}classType.registeredClass.constructor_body[argCount-1]=function unboundTypeHandler(){throwUnboundTypeError("Cannot construct "+classType.name+" due to unbound types",rawArgTypes)};whenDependentTypesAreResolved([],rawArgTypes,function(argTypes){classType.registeredClass.constructor_body[argCount-1]=function constructor_body(){if(arguments.length!==argCount-1){throwBindingError(humanName+" called with "+arguments.length+" arguments, expected "+(argCount-1))}destructors.length=0;args.length=argCount;for(var i=1;i4&&0===--emval_handle_array[handle].refcount){emval_handle_array[handle]=undefined;emval_free_list.push(handle)}}function count_emval_handles(){var count=0;for(var i=5;i>1])};case 2:return function(pointer){var heap=signed?HEAP32:HEAPU32;return this["fromWireType"](heap[pointer>>2])};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_enum(rawType,name,size,isSigned){var shift=getShiftFromSize(size);name=readLatin1String(name);function ctor(){}ctor.values={};registerType(rawType,{name:name,constructor:ctor,"fromWireType":function(c){return this.constructor.values[c]},"toWireType":function(destructors,c){return c.value},"argPackAdvance":8,"readValueFromPointer":enumReadValueFromPointer(name,shift,isSigned),destructorFunction:null});exposePublicSymbol(name,ctor)}function requireRegisteredType(rawType,humanName){var impl=registeredTypes[rawType];if(undefined===impl){throwBindingError(humanName+" has unknown type "+getTypeName(rawType))}return impl}function __embind_register_enum_value(rawEnumType,name,enumValue){var enumType=requireRegisteredType(rawEnumType,"enum");name=readLatin1String(name);var Enum=enumType.constructor;var Value=Object.create(enumType.constructor.prototype,{value:{value:enumValue},constructor:{value:createNamedFunction(enumType.name+"_"+name,function(){})}});Enum.values[enumValue]=Value;Enum[name]=Value}function _embind_repr(v){if(v===null){return"null"}var t=typeof v;if(t==="object"||t==="array"||t==="function"){return v.toString()}else{return""+v}}function floatReadValueFromPointer(name,shift){switch(shift){case 2:return function(pointer){return this["fromWireType"](HEAPF32[pointer>>2])};case 3:return function(pointer){return this["fromWireType"](HEAPF64[pointer>>3])};default:throw new TypeError("Unknown float type: "+name)}}function __embind_register_float(rawType,name,size){var shift=getShiftFromSize(size);name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(value){return value},"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}return value},"argPackAdvance":8,"readValueFromPointer":floatReadValueFromPointer(name,shift),destructorFunction:null})}function __embind_register_function(name,argCount,rawArgTypesAddr,signature,rawInvoker,fn){var argTypes=heap32VectorToArray(argCount,rawArgTypesAddr);name=readLatin1String(name);rawInvoker=embind__requireFunction(signature,rawInvoker);exposePublicSymbol(name,function(){throwUnboundTypeError("Cannot call "+name+" due to unbound types",argTypes)},argCount-1);whenDependentTypesAreResolved([],argTypes,function(argTypes){var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));replacePublicSymbol(name,craftInvokerFunction(name,invokerArgsArray,null,rawInvoker,fn),argCount-1);return[]})}function integerReadValueFromPointer(name,shift,signed){switch(shift){case 0:return signed?function readS8FromPointer(pointer){return HEAP8[pointer]}:function readU8FromPointer(pointer){return HEAPU8[pointer]};case 1:return signed?function readS16FromPointer(pointer){return HEAP16[pointer>>1]}:function readU16FromPointer(pointer){return HEAPU16[pointer>>1]};case 2:return signed?function readS32FromPointer(pointer){return HEAP32[pointer>>2]}:function readU32FromPointer(pointer){return HEAPU32[pointer>>2]};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_integer(primitiveType,name,size,minRange,maxRange){name=readLatin1String(name);if(maxRange===-1){maxRange=4294967295}var shift=getShiftFromSize(size);var fromWireType=function(value){return value};if(minRange===0){var bitshift=32-8*size;fromWireType=function(value){return value<>>bitshift}}var isUnsignedType=name.indexOf("unsigned")!=-1;registerType(primitiveType,{name:name,"fromWireType":fromWireType,"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}if(valuemaxRange){throw new TypeError('Passing a number "'+_embind_repr(value)+'" from JS side to C/C++ side to an argument of type "'+name+'", which is outside the valid range ['+minRange+", "+maxRange+"]!")}return isUnsignedType?value>>>0:value|0},"argPackAdvance":8,"readValueFromPointer":integerReadValueFromPointer(name,shift,minRange!==0),destructorFunction:null})}function __embind_register_memory_view(rawType,dataTypeIndex,name){var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array];var TA=typeMapping[dataTypeIndex];function decodeMemoryView(handle){handle=handle>>2;var heap=HEAPU32;var size=heap[handle];var data=heap[handle+1];return new TA(buffer,data,size)}name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":decodeMemoryView,"argPackAdvance":8,"readValueFromPointer":decodeMemoryView},{ignoreDuplicateRegistrations:true})}function __embind_register_std_string(rawType,name){name=readLatin1String(name);var stdStringIsUTF8=name==="std::string";registerType(rawType,{name:name,"fromWireType":function(value){var length=HEAPU32[value>>2];var str;if(stdStringIsUTF8){var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i;if(i==length||HEAPU8[currentBytePtr]==0){var maxRead=currentBytePtr-decodeStartPtr;var stringSegment=UTF8ToString(decodeStartPtr,maxRead);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+1}}}else{var a=new Array(length);for(var i=0;i>2]=length;if(stdStringIsUTF8&&valueIsOfTypeString){stringToUTF8(value,ptr+4,length+1)}else{if(valueIsOfTypeString){for(var i=0;i255){_free(ptr);throwBindingError("String has UTF-16 code units that do not fit in 8 bits")}HEAPU8[ptr+4+i]=charCode}}else{for(var i=0;i>2];var HEAP=getHeap();var str;var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i*charSize;if(i==length||HEAP[currentBytePtr>>shift]==0){var maxReadBytes=currentBytePtr-decodeStartPtr;var stringSegment=decodeString(decodeStartPtr,maxReadBytes);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+charSize}}_free(value);return str},"toWireType":function(destructors,value){if(!(typeof value==="string")){throwBindingError("Cannot pass non-string to C++ string type "+name)}var length=lengthBytesUTF(value);var ptr=_malloc(4+length+charSize);HEAPU32[ptr>>2]=length>>shift;encodeString(value,ptr+4,length+charSize);if(destructors!==null){destructors.push(_free,ptr)}return ptr},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:function(ptr){_free(ptr)}})}function __embind_register_value_array(rawType,name,constructorSignature,rawConstructor,destructorSignature,rawDestructor){tupleRegistrations[rawType]={name:readLatin1String(name),rawConstructor:embind__requireFunction(constructorSignature,rawConstructor),rawDestructor:embind__requireFunction(destructorSignature,rawDestructor),elements:[]}}function __embind_register_value_array_element(rawTupleType,getterReturnType,getterSignature,getter,getterContext,setterArgumentType,setterSignature,setter,setterContext){tupleRegistrations[rawTupleType].elements.push({getterReturnType:getterReturnType,getter:embind__requireFunction(getterSignature,getter),getterContext:getterContext,setterArgumentType:setterArgumentType,setter:embind__requireFunction(setterSignature,setter),setterContext:setterContext})}function __embind_register_value_object(rawType,name,constructorSignature,rawConstructor,destructorSignature,rawDestructor){structRegistrations[rawType]={name:readLatin1String(name),rawConstructor:embind__requireFunction(constructorSignature,rawConstructor),rawDestructor:embind__requireFunction(destructorSignature,rawDestructor),fields:[]}}function __embind_register_value_object_field(structType,fieldName,getterReturnType,getterSignature,getter,getterContext,setterArgumentType,setterSignature,setter,setterContext){structRegistrations[structType].fields.push({fieldName:readLatin1String(fieldName),getterReturnType:getterReturnType,getter:embind__requireFunction(getterSignature,getter),getterContext:getterContext,setterArgumentType:setterArgumentType,setter:embind__requireFunction(setterSignature,setter),setterContext:setterContext})}function __embind_register_void(rawType,name){name=readLatin1String(name);registerType(rawType,{isVoid:true,name:name,"argPackAdvance":0,"fromWireType":function(){return undefined},"toWireType":function(destructors,o){return undefined}})}function requireHandle(handle){if(!handle){throwBindingError("Cannot use deleted val. handle = "+handle)}return emval_handle_array[handle].value}function __emval_as(handle,returnType,destructorsRef){handle=requireHandle(handle);returnType=requireRegisteredType(returnType,"emval::as");var destructors=[];var rd=__emval_register(destructors);HEAP32[destructorsRef>>2]=rd;return returnType["toWireType"](destructors,handle)}function __emval_get_property(handle,key){handle=requireHandle(handle);key=requireHandle(key);return __emval_register(handle[key])}function __emval_incref(handle){if(handle>4){emval_handle_array[handle].refcount+=1}}var emval_symbols={};function getStringOrSymbol(address){var symbol=emval_symbols[address];if(symbol===undefined){return readLatin1String(address)}else{return symbol}}function __emval_new_cstring(v){return __emval_register(getStringOrSymbol(v))}function __emval_run_destructors(handle){var destructors=emval_handle_array[handle].value;runDestructors(destructors);__emval_decref(handle)}function __emval_take_value(type,argv){type=requireRegisteredType(type,"_emval_take_value");var v=type["readValueFromPointer"](argv);return __emval_register(v)}function _abort(){abort()}var _emscripten_get_now;if(ENVIRONMENT_IS_NODE){_emscripten_get_now=function(){var t=process["hrtime"]();return t[0]*1e3+t[1]/1e6}}else if(typeof dateNow!=="undefined"){_emscripten_get_now=dateNow}else _emscripten_get_now=function(){return performance.now()};var _emscripten_get_now_is_monotonic=true;function _clock_gettime(clk_id,tp){var now;if(clk_id===0){now=Date.now()}else if((clk_id===1||clk_id===4)&&_emscripten_get_now_is_monotonic){now=_emscripten_get_now()}else{setErrNo(28);return-1}HEAP32[tp>>2]=now/1e3|0;HEAP32[tp+4>>2]=now%1e3*1e3*1e3|0;return 0}function _emscripten_asm_const_int(code,sigPtr,argbuf){var args=readAsmConstArgs(sigPtr,argbuf);return ASM_CONSTS[code].apply(null,args)}function __webgl_enable_WEBGL_draw_instanced_base_vertex_base_instance(ctx){return!!(ctx.dibvbi=ctx.getExtension("WEBGL_draw_instanced_base_vertex_base_instance"))}function __webgl_enable_WEBGL_multi_draw_instanced_base_vertex_base_instance(ctx){return!!(ctx.mdibvbi=ctx.getExtension("WEBGL_multi_draw_instanced_base_vertex_base_instance"))}function __webgl_enable_WEBGL_multi_draw(ctx){return!!(ctx.multiDrawWebgl=ctx.getExtension("WEBGL_multi_draw"))}var GL={counter:1,buffers:[],mappedBuffers:{},programs:[],framebuffers:[],renderbuffers:[],textures:[],uniforms:[],shaders:[],vaos:[],contexts:[],offscreenCanvases:{},timerQueriesEXT:[],queries:[],samplers:[],transformFeedbacks:[],syncs:[],byteSizeByTypeRoot:5120,byteSizeByType:[1,1,2,2,4,4,4,2,3,4,8],programInfos:{},stringCache:{},stringiCache:{},unpackAlignment:4,recordError:function recordError(errorCode){if(!GL.lastError){GL.lastError=errorCode}},getNewId:function(table){var ret=GL.counter++;for(var i=table.length;i>1;var quadIndexes=new Uint16Array(numIndexes);var i=0,v=0;while(1){quadIndexes[i++]=v;if(i>=numIndexes)break;quadIndexes[i++]=v+1;if(i>=numIndexes)break;quadIndexes[i++]=v+2;if(i>=numIndexes)break;quadIndexes[i++]=v;if(i>=numIndexes)break;quadIndexes[i++]=v+2;if(i>=numIndexes)break;quadIndexes[i++]=v+3;if(i>=numIndexes)break;v+=4}context.GLctx.bufferData(34963,quadIndexes,35044);context.GLctx.bindBuffer(34963,null)}},getTempVertexBuffer:function getTempVertexBuffer(sizeBytes){var idx=GL.log2ceilLookup(sizeBytes);var ringbuffer=GL.currentContext.tempVertexBuffers1[idx];var nextFreeBufferIndex=GL.currentContext.tempVertexBufferCounters1[idx];GL.currentContext.tempVertexBufferCounters1[idx]=GL.currentContext.tempVertexBufferCounters1[idx]+1&GL.numTempVertexBuffersPerSize-1;var vbo=ringbuffer[nextFreeBufferIndex];if(vbo){return vbo}var prevVBO=GLctx.getParameter(34964);ringbuffer[nextFreeBufferIndex]=GLctx.createBuffer();GLctx.bindBuffer(34962,ringbuffer[nextFreeBufferIndex]);GLctx.bufferData(34962,1<>2]:-1;source+=UTF8ToString(HEAP32[string+i*4>>2],len<0?undefined:len)}return source},calcBufLength:function calcBufLength(size,type,stride,count){if(stride>0){return count*stride}var typeSize=GL.byteSizeByType[type-GL.byteSizeByTypeRoot];return size*typeSize*count},usedTempBuffers:[],preDrawHandleClientVertexAttribBindings:function preDrawHandleClientVertexAttribBindings(count){GL.resetBufferBinding=false;for(var i=0;i>2)}function _emscripten_glClearBufferiv(buffer,drawbuffer,value){GLctx["clearBufferiv"](buffer,drawbuffer,HEAP32,value>>2)}function _emscripten_glClearBufferuiv(buffer,drawbuffer,value){GLctx["clearBufferuiv"](buffer,drawbuffer,HEAPU32,value>>2)}function _emscripten_glClearColor(x0,x1,x2,x3){GLctx["clearColor"](x0,x1,x2,x3)}function _emscripten_glClearDepthf(x0){GLctx["clearDepth"](x0)}function _emscripten_glClearStencil(x0){GLctx["clearStencil"](x0)}function convertI32PairToI53(lo,hi){return(lo>>>0)+hi*4294967296}function _emscripten_glClientWaitSync(sync,flags,timeoutLo,timeoutHi){return GLctx.clientWaitSync(GL.syncs[sync],flags,convertI32PairToI53(timeoutLo,timeoutHi))}function _emscripten_glColorMask(red,green,blue,alpha){GLctx.colorMask(!!red,!!green,!!blue,!!alpha)}function _emscripten_glCompileShader(shader){GLctx.compileShader(GL.shaders[shader])}function _emscripten_glCompressedTexImage2D(target,level,internalFormat,width,height,border,imageSize,data){if(true){if(GLctx.currentPixelUnpackBufferBinding){GLctx["compressedTexImage2D"](target,level,internalFormat,width,height,border,imageSize,data)}else{GLctx["compressedTexImage2D"](target,level,internalFormat,width,height,border,HEAPU8,data,imageSize)}return}GLctx["compressedTexImage2D"](target,level,internalFormat,width,height,border,data?HEAPU8.subarray(data,data+imageSize):null)}function _emscripten_glCompressedTexImage3D(target,level,internalFormat,width,height,depth,border,imageSize,data){if(GLctx.currentPixelUnpackBufferBinding){GLctx["compressedTexImage3D"](target,level,internalFormat,width,height,depth,border,imageSize,data)}else{GLctx["compressedTexImage3D"](target,level,internalFormat,width,height,depth,border,HEAPU8,data,imageSize)}}function _emscripten_glCompressedTexSubImage2D(target,level,xoffset,yoffset,width,height,format,imageSize,data){if(true){if(GLctx.currentPixelUnpackBufferBinding){GLctx["compressedTexSubImage2D"](target,level,xoffset,yoffset,width,height,format,imageSize,data)}else{GLctx["compressedTexSubImage2D"](target,level,xoffset,yoffset,width,height,format,HEAPU8,data,imageSize)}return}GLctx["compressedTexSubImage2D"](target,level,xoffset,yoffset,width,height,format,data?HEAPU8.subarray(data,data+imageSize):null)}function _emscripten_glCompressedTexSubImage3D(target,level,xoffset,yoffset,zoffset,width,height,depth,format,imageSize,data){if(GLctx.currentPixelUnpackBufferBinding){GLctx["compressedTexSubImage3D"](target,level,xoffset,yoffset,zoffset,width,height,depth,format,imageSize,data)}else{GLctx["compressedTexSubImage3D"](target,level,xoffset,yoffset,zoffset,width,height,depth,format,HEAPU8,data,imageSize)}}function _emscripten_glCopyBufferSubData(x0,x1,x2,x3,x4){GLctx["copyBufferSubData"](x0,x1,x2,x3,x4)}function _emscripten_glCopyTexImage2D(x0,x1,x2,x3,x4,x5,x6,x7){GLctx["copyTexImage2D"](x0,x1,x2,x3,x4,x5,x6,x7)}function _emscripten_glCopyTexSubImage2D(x0,x1,x2,x3,x4,x5,x6,x7){GLctx["copyTexSubImage2D"](x0,x1,x2,x3,x4,x5,x6,x7)}function _emscripten_glCopyTexSubImage3D(x0,x1,x2,x3,x4,x5,x6,x7,x8){GLctx["copyTexSubImage3D"](x0,x1,x2,x3,x4,x5,x6,x7,x8)}function _emscripten_glCreateProgram(){var id=GL.getNewId(GL.programs);var program=GLctx.createProgram();program.name=id;GL.programs[id]=program;return id}function _emscripten_glCreateShader(shaderType){var id=GL.getNewId(GL.shaders);GL.shaders[id]=GLctx.createShader(shaderType);return id}function _emscripten_glCullFace(x0){GLctx["cullFace"](x0)}function _emscripten_glDeleteBuffers(n,buffers){for(var i=0;i>2];var buffer=GL.buffers[id];if(!buffer)continue;GLctx.deleteBuffer(buffer);buffer.name=0;GL.buffers[id]=null;if(id==GLctx.currentArrayBufferBinding)GLctx.currentArrayBufferBinding=0;if(id==GLctx.currentElementArrayBufferBinding)GLctx.currentElementArrayBufferBinding=0;if(id==GLctx.currentPixelPackBufferBinding)GLctx.currentPixelPackBufferBinding=0;if(id==GLctx.currentPixelUnpackBufferBinding)GLctx.currentPixelUnpackBufferBinding=0}}function _emscripten_glDeleteFramebuffers(n,framebuffers){for(var i=0;i>2];var framebuffer=GL.framebuffers[id];if(!framebuffer)continue;GLctx.deleteFramebuffer(framebuffer);framebuffer.name=0;GL.framebuffers[id]=null}}function _emscripten_glDeleteProgram(id){if(!id)return;var program=GL.programs[id];if(!program){GL.recordError(1281);return}GLctx.deleteProgram(program);program.name=0;GL.programs[id]=null;GL.programInfos[id]=null}function _emscripten_glDeleteQueries(n,ids){for(var i=0;i>2];var query=GL.queries[id];if(!query)continue;GLctx["deleteQuery"](query);GL.queries[id]=null}}function _emscripten_glDeleteQueriesEXT(n,ids){for(var i=0;i>2];var query=GL.timerQueriesEXT[id];if(!query)continue;GLctx.disjointTimerQueryExt["deleteQueryEXT"](query);GL.timerQueriesEXT[id]=null}}function _emscripten_glDeleteRenderbuffers(n,renderbuffers){for(var i=0;i>2];var renderbuffer=GL.renderbuffers[id];if(!renderbuffer)continue;GLctx.deleteRenderbuffer(renderbuffer);renderbuffer.name=0;GL.renderbuffers[id]=null}}function _emscripten_glDeleteSamplers(n,samplers){for(var i=0;i>2];var sampler=GL.samplers[id];if(!sampler)continue;GLctx["deleteSampler"](sampler);sampler.name=0;GL.samplers[id]=null}}function _emscripten_glDeleteShader(id){if(!id)return;var shader=GL.shaders[id];if(!shader){GL.recordError(1281);return}GLctx.deleteShader(shader);GL.shaders[id]=null}function _emscripten_glDeleteSync(id){if(!id)return;var sync=GL.syncs[id];if(!sync){GL.recordError(1281);return}GLctx.deleteSync(sync);sync.name=0;GL.syncs[id]=null}function _emscripten_glDeleteTextures(n,textures){for(var i=0;i>2];var texture=GL.textures[id];if(!texture)continue;GLctx.deleteTexture(texture);texture.name=0;GL.textures[id]=null}}function _emscripten_glDeleteTransformFeedbacks(n,ids){for(var i=0;i>2];var transformFeedback=GL.transformFeedbacks[id];if(!transformFeedback)continue;GLctx["deleteTransformFeedback"](transformFeedback);transformFeedback.name=0;GL.transformFeedbacks[id]=null}}function _emscripten_glDeleteVertexArrays(n,vaos){for(var i=0;i>2];GLctx["deleteVertexArray"](GL.vaos[id]);GL.vaos[id]=null}}function _emscripten_glDeleteVertexArraysOES(n,vaos){for(var i=0;i>2];GLctx["deleteVertexArray"](GL.vaos[id]);GL.vaos[id]=null}}function _emscripten_glDepthFunc(x0){GLctx["depthFunc"](x0)}function _emscripten_glDepthMask(flag){GLctx.depthMask(!!flag)}function _emscripten_glDepthRangef(x0,x1){GLctx["depthRange"](x0,x1)}function _emscripten_glDetachShader(program,shader){GLctx.detachShader(GL.programs[program],GL.shaders[shader])}function _emscripten_glDisable(x0){GLctx["disable"](x0)}function _emscripten_glDisableVertexAttribArray(index){var cb=GL.currentContext.clientBuffers[index];cb.enabled=false;GLctx.disableVertexAttribArray(index)}function _emscripten_glDrawArrays(mode,first,count){GL.preDrawHandleClientVertexAttribBindings(first+count);GLctx.drawArrays(mode,first,count);GL.postDrawHandleClientVertexAttribBindings()}function _emscripten_glDrawArraysInstanced(mode,first,count,primcount){GLctx["drawArraysInstanced"](mode,first,count,primcount)}function _emscripten_glDrawArraysInstancedANGLE(mode,first,count,primcount){GLctx["drawArraysInstanced"](mode,first,count,primcount)}function _emscripten_glDrawArraysInstancedARB(mode,first,count,primcount){GLctx["drawArraysInstanced"](mode,first,count,primcount)}function _emscripten_glDrawArraysInstancedEXT(mode,first,count,primcount){GLctx["drawArraysInstanced"](mode,first,count,primcount)}function _emscripten_glDrawArraysInstancedNV(mode,first,count,primcount){GLctx["drawArraysInstanced"](mode,first,count,primcount)}var tempFixedLengthArray=[];function _emscripten_glDrawBuffers(n,bufs){var bufArray=tempFixedLengthArray[n];for(var i=0;i>2]}GLctx["drawBuffers"](bufArray)}function _emscripten_glDrawBuffersEXT(n,bufs){var bufArray=tempFixedLengthArray[n];for(var i=0;i>2]}GLctx["drawBuffers"](bufArray)}function _emscripten_glDrawBuffersWEBGL(n,bufs){var bufArray=tempFixedLengthArray[n];for(var i=0;i>2]}GLctx["drawBuffers"](bufArray)}function _emscripten_glDrawElements(mode,count,type,indices){var buf;if(!GLctx.currentElementArrayBufferBinding){var size=GL.calcBufLength(1,type,0,count);buf=GL.getTempIndexBuffer(size);GLctx.bindBuffer(34963,buf);GLctx.bufferSubData(34963,0,HEAPU8.subarray(indices,indices+size));indices=0}GL.preDrawHandleClientVertexAttribBindings(count);GLctx.drawElements(mode,count,type,indices);GL.postDrawHandleClientVertexAttribBindings(count);if(!GLctx.currentElementArrayBufferBinding){GLctx.bindBuffer(34963,null)}}function _emscripten_glDrawElementsInstanced(mode,count,type,indices,primcount){GLctx["drawElementsInstanced"](mode,count,type,indices,primcount)}function _emscripten_glDrawElementsInstancedANGLE(mode,count,type,indices,primcount){GLctx["drawElementsInstanced"](mode,count,type,indices,primcount)}function _emscripten_glDrawElementsInstancedARB(mode,count,type,indices,primcount){GLctx["drawElementsInstanced"](mode,count,type,indices,primcount)}function _emscripten_glDrawElementsInstancedEXT(mode,count,type,indices,primcount){GLctx["drawElementsInstanced"](mode,count,type,indices,primcount)}function _emscripten_glDrawElementsInstancedNV(mode,count,type,indices,primcount){GLctx["drawElementsInstanced"](mode,count,type,indices,primcount)}function _glDrawElements(mode,count,type,indices){var buf;if(!GLctx.currentElementArrayBufferBinding){var size=GL.calcBufLength(1,type,0,count);buf=GL.getTempIndexBuffer(size);GLctx.bindBuffer(34963,buf);GLctx.bufferSubData(34963,0,HEAPU8.subarray(indices,indices+size));indices=0}GL.preDrawHandleClientVertexAttribBindings(count);GLctx.drawElements(mode,count,type,indices);GL.postDrawHandleClientVertexAttribBindings(count);if(!GLctx.currentElementArrayBufferBinding){GLctx.bindBuffer(34963,null)}}function _emscripten_glDrawRangeElements(mode,start,end,count,type,indices){_glDrawElements(mode,count,type,indices)}function _emscripten_glEnable(x0){GLctx["enable"](x0)}function _emscripten_glEnableVertexAttribArray(index){var cb=GL.currentContext.clientBuffers[index];cb.enabled=true;GLctx.enableVertexAttribArray(index)}function _emscripten_glEndQuery(x0){GLctx["endQuery"](x0)}function _emscripten_glEndQueryEXT(target){GLctx.disjointTimerQueryExt["endQueryEXT"](target)}function _emscripten_glEndTransformFeedback(){GLctx["endTransformFeedback"]()}function _emscripten_glFenceSync(condition,flags){var sync=GLctx.fenceSync(condition,flags);if(sync){var id=GL.getNewId(GL.syncs);sync.name=id;GL.syncs[id]=sync;return id}else{return 0}}function _emscripten_glFinish(){GLctx["finish"]()}function _emscripten_glFlush(){GLctx["flush"]()}function emscriptenWebGLGetBufferBinding(target){switch(target){case 34962:target=34964;break;case 34963:target=34965;break;case 35051:target=35053;break;case 35052:target=35055;break;case 35982:target=35983;break;case 36662:target=36662;break;case 36663:target=36663;break;case 35345:target=35368;break}var buffer=GLctx.getParameter(target);if(buffer)return buffer.name|0;else return 0}function emscriptenWebGLValidateMapBufferTarget(target){switch(target){case 34962:case 34963:case 36662:case 36663:case 35051:case 35052:case 35882:case 35982:case 35345:return true;default:return false}}function _emscripten_glFlushMappedBufferRange(target,offset,length){if(!emscriptenWebGLValidateMapBufferTarget(target)){GL.recordError(1280);err("GL_INVALID_ENUM in glFlushMappedBufferRange");return}var mapping=GL.mappedBuffers[emscriptenWebGLGetBufferBinding(target)];if(!mapping){GL.recordError(1282);err("buffer was never mapped in glFlushMappedBufferRange");return}if(!(mapping.access&16)){GL.recordError(1282);err("buffer was not mapped with GL_MAP_FLUSH_EXPLICIT_BIT in glFlushMappedBufferRange");return}if(offset<0||length<0||offset+length>mapping.length){GL.recordError(1281);err("invalid range in glFlushMappedBufferRange");return}GLctx.bufferSubData(target,mapping.offset,HEAPU8.subarray(mapping.mem+offset,mapping.mem+offset+length))}function _emscripten_glFramebufferRenderbuffer(target,attachment,renderbuffertarget,renderbuffer){GLctx.framebufferRenderbuffer(target,attachment,renderbuffertarget,GL.renderbuffers[renderbuffer])}function _emscripten_glFramebufferTexture2D(target,attachment,textarget,texture,level){GLctx.framebufferTexture2D(target,attachment,textarget,GL.textures[texture],level)}function _emscripten_glFramebufferTextureLayer(target,attachment,texture,level,layer){GLctx.framebufferTextureLayer(target,attachment,GL.textures[texture],level,layer)}function _emscripten_glFrontFace(x0){GLctx["frontFace"](x0)}function __glGenObject(n,buffers,createFunction,objectTable){for(var i=0;i>2]=id}}function _emscripten_glGenBuffers(n,buffers){__glGenObject(n,buffers,"createBuffer",GL.buffers)}function _emscripten_glGenFramebuffers(n,ids){__glGenObject(n,ids,"createFramebuffer",GL.framebuffers)}function _emscripten_glGenQueries(n,ids){__glGenObject(n,ids,"createQuery",GL.queries)}function _emscripten_glGenQueriesEXT(n,ids){for(var i=0;i>2]=0;return}var id=GL.getNewId(GL.timerQueriesEXT);query.name=id;GL.timerQueriesEXT[id]=query;HEAP32[ids+i*4>>2]=id}}function _emscripten_glGenRenderbuffers(n,renderbuffers){__glGenObject(n,renderbuffers,"createRenderbuffer",GL.renderbuffers)}function _emscripten_glGenSamplers(n,samplers){__glGenObject(n,samplers,"createSampler",GL.samplers)}function _emscripten_glGenTextures(n,textures){__glGenObject(n,textures,"createTexture",GL.textures)}function _emscripten_glGenTransformFeedbacks(n,ids){__glGenObject(n,ids,"createTransformFeedback",GL.transformFeedbacks)}function _emscripten_glGenVertexArrays(n,arrays){__glGenObject(n,arrays,"createVertexArray",GL.vaos)}function _emscripten_glGenVertexArraysOES(n,arrays){__glGenObject(n,arrays,"createVertexArray",GL.vaos)}function _emscripten_glGenerateMipmap(x0){GLctx["generateMipmap"](x0)}function __glGetActiveAttribOrUniform(funcName,program,index,bufSize,length,size,type,name){program=GL.programs[program];var info=GLctx[funcName](program,index);if(info){var numBytesWrittenExclNull=name&&stringToUTF8(info.name,name,bufSize);if(length)HEAP32[length>>2]=numBytesWrittenExclNull;if(size)HEAP32[size>>2]=info.size;if(type)HEAP32[type>>2]=info.type}}function _emscripten_glGetActiveAttrib(program,index,bufSize,length,size,type,name){__glGetActiveAttribOrUniform("getActiveAttrib",program,index,bufSize,length,size,type,name)}function _emscripten_glGetActiveUniform(program,index,bufSize,length,size,type,name){__glGetActiveAttribOrUniform("getActiveUniform",program,index,bufSize,length,size,type,name)}function _emscripten_glGetActiveUniformBlockName(program,uniformBlockIndex,bufSize,length,uniformBlockName){program=GL.programs[program];var result=GLctx["getActiveUniformBlockName"](program,uniformBlockIndex);if(!result)return;if(uniformBlockName&&bufSize>0){var numBytesWrittenExclNull=stringToUTF8(result,uniformBlockName,bufSize);if(length)HEAP32[length>>2]=numBytesWrittenExclNull}else{if(length)HEAP32[length>>2]=0}}function _emscripten_glGetActiveUniformBlockiv(program,uniformBlockIndex,pname,params){if(!params){GL.recordError(1281);return}program=GL.programs[program];switch(pname){case 35393:var name=GLctx["getActiveUniformBlockName"](program,uniformBlockIndex);HEAP32[params>>2]=name.length+1;return;default:var result=GLctx["getActiveUniformBlockParameter"](program,uniformBlockIndex,pname);if(!result)return;if(typeof result=="number"){HEAP32[params>>2]=result}else{for(var i=0;i>2]=result[i]}}}}function _emscripten_glGetActiveUniformsiv(program,uniformCount,uniformIndices,pname,params){if(!params){GL.recordError(1281);return}if(uniformCount>0&&uniformIndices==0){GL.recordError(1281);return}program=GL.programs[program];var ids=[];for(var i=0;i>2])}var result=GLctx["getActiveUniforms"](program,ids,pname);if(!result)return;var len=result.length;for(var i=0;i>2]=result[i]}}function _emscripten_glGetAttachedShaders(program,maxCount,count,shaders){var result=GLctx.getAttachedShaders(GL.programs[program]);var len=result.length;if(len>maxCount){len=maxCount}HEAP32[count>>2]=len;for(var i=0;i>2]=id}}function _emscripten_glGetAttribLocation(program,name){return GLctx.getAttribLocation(GL.programs[program],UTF8ToString(name))}function writeI53ToI64(ptr,num){HEAPU32[ptr>>2]=num;HEAPU32[ptr+4>>2]=(num-HEAPU32[ptr>>2])/4294967296}function emscriptenWebGLGet(name_,p,type){if(!p){GL.recordError(1281);return}var ret=undefined;switch(name_){case 36346:ret=1;break;case 36344:if(type!=0&&type!=1){GL.recordError(1280)}return;case 34814:case 36345:ret=0;break;case 34466:var formats=GLctx.getParameter(34467);ret=formats?formats.length:0;break;case 33309:if(GL.currentContext.version<2){GL.recordError(1282);return}var exts=GLctx.getSupportedExtensions()||[];ret=2*exts.length;break;case 33307:case 33308:if(GL.currentContext.version<2){GL.recordError(1280);return}ret=name_==33307?3:0;break}if(ret===undefined){var result=GLctx.getParameter(name_);switch(typeof result){case"number":ret=result;break;case"boolean":ret=result?1:0;break;case"string":GL.recordError(1280);return;case"object":if(result===null){switch(name_){case 34964:case 35725:case 34965:case 36006:case 36007:case 32873:case 34229:case 36662:case 36663:case 35053:case 35055:case 36010:case 35097:case 35869:case 32874:case 36389:case 35983:case 35368:case 34068:{ret=0;break}default:{GL.recordError(1280);return}}}else if(result instanceof Float32Array||result instanceof Uint32Array||result instanceof Int32Array||result instanceof Array){for(var i=0;i>2]=result[i];break;case 2:HEAPF32[p+i*4>>2]=result[i];break;case 4:HEAP8[p+i>>0]=result[i]?1:0;break}}return}else{try{ret=result.name|0}catch(e){GL.recordError(1280);err("GL_INVALID_ENUM in glGet"+type+"v: Unknown object returned from WebGL getParameter("+name_+")! (error: "+e+")");return}}break;default:GL.recordError(1280);err("GL_INVALID_ENUM in glGet"+type+"v: Native code calling glGet"+type+"v("+name_+") and it returns "+result+" of type "+typeof result+"!");return}}switch(type){case 1:writeI53ToI64(p,ret);break;case 0:HEAP32[p>>2]=ret;break;case 2:HEAPF32[p>>2]=ret;break;case 4:HEAP8[p>>0]=ret?1:0;break}}function _emscripten_glGetBooleanv(name_,p){emscriptenWebGLGet(name_,p,4)}function _emscripten_glGetBufferParameteri64v(target,value,data){if(!data){GL.recordError(1281);return}writeI53ToI64(data,GLctx.getBufferParameter(target,value))}function _emscripten_glGetBufferParameteriv(target,value,data){if(!data){GL.recordError(1281);return}HEAP32[data>>2]=GLctx.getBufferParameter(target,value)}function _emscripten_glGetBufferPointerv(target,pname,params){if(pname==35005){var ptr=0;var mappedBuffer=GL.mappedBuffers[emscriptenWebGLGetBufferBinding(target)];if(mappedBuffer){ptr=mappedBuffer.mem}HEAP32[params>>2]=ptr}else{GL.recordError(1280);err("GL_INVALID_ENUM in glGetBufferPointerv")}}function _emscripten_glGetError(){var error=GLctx.getError()||GL.lastError;GL.lastError=0;return error}function _emscripten_glGetFloatv(name_,p){emscriptenWebGLGet(name_,p,2)}function _emscripten_glGetFragDataLocation(program,name){return GLctx["getFragDataLocation"](GL.programs[program],UTF8ToString(name))}function _emscripten_glGetFramebufferAttachmentParameteriv(target,attachment,pname,params){var result=GLctx.getFramebufferAttachmentParameter(target,attachment,pname);if(result instanceof WebGLRenderbuffer||result instanceof WebGLTexture){result=result.name|0}HEAP32[params>>2]=result}function emscriptenWebGLGetIndexed(target,index,data,type){if(!data){GL.recordError(1281);return}var result=GLctx["getIndexedParameter"](target,index);var ret;switch(typeof result){case"boolean":ret=result?1:0;break;case"number":ret=result;break;case"object":if(result===null){switch(target){case 35983:case 35368:ret=0;break;default:{GL.recordError(1280);return}}}else if(result instanceof WebGLBuffer){ret=result.name|0}else{GL.recordError(1280);return}break;default:GL.recordError(1280);return}switch(type){case 1:writeI53ToI64(data,ret);break;case 0:HEAP32[data>>2]=ret;break;case 2:HEAPF32[data>>2]=ret;break;case 4:HEAP8[data>>0]=ret?1:0;break;default:throw"internal emscriptenWebGLGetIndexed() error, bad type: "+type}}function _emscripten_glGetInteger64i_v(target,index,data){emscriptenWebGLGetIndexed(target,index,data,1)}function _emscripten_glGetInteger64v(name_,p){emscriptenWebGLGet(name_,p,1)}function _emscripten_glGetIntegeri_v(target,index,data){emscriptenWebGLGetIndexed(target,index,data,0)}function _emscripten_glGetIntegerv(name_,p){emscriptenWebGLGet(name_,p,0)}function _emscripten_glGetInternalformativ(target,internalformat,pname,bufSize,params){if(bufSize<0){GL.recordError(1281);return}if(!params){GL.recordError(1281);return}var ret=GLctx["getInternalformatParameter"](target,internalformat,pname);if(ret===null)return;for(var i=0;i>2]=ret[i]}}function _emscripten_glGetProgramBinary(program,bufSize,length,binaryFormat,binary){GL.recordError(1282)}function _emscripten_glGetProgramInfoLog(program,maxLength,length,infoLog){var log=GLctx.getProgramInfoLog(GL.programs[program]);if(log===null)log="(unknown error)";var numBytesWrittenExclNull=maxLength>0&&infoLog?stringToUTF8(log,infoLog,maxLength):0;if(length)HEAP32[length>>2]=numBytesWrittenExclNull}function _emscripten_glGetProgramiv(program,pname,p){if(!p){GL.recordError(1281);return}if(program>=GL.counter){GL.recordError(1281);return}var ptable=GL.programInfos[program];if(!ptable){GL.recordError(1282);return}if(pname==35716){var log=GLctx.getProgramInfoLog(GL.programs[program]);if(log===null)log="(unknown error)";HEAP32[p>>2]=log.length+1}else if(pname==35719){HEAP32[p>>2]=ptable.maxUniformLength}else if(pname==35722){if(ptable.maxAttributeLength==-1){program=GL.programs[program];var numAttribs=GLctx.getProgramParameter(program,35721);ptable.maxAttributeLength=0;for(var i=0;i>2]=ptable.maxAttributeLength}else if(pname==35381){if(ptable.maxUniformBlockNameLength==-1){program=GL.programs[program];var numBlocks=GLctx.getProgramParameter(program,35382);ptable.maxUniformBlockNameLength=0;for(var i=0;i>2]=ptable.maxUniformBlockNameLength}else{HEAP32[p>>2]=GLctx.getProgramParameter(GL.programs[program],pname)}}function _emscripten_glGetQueryObjecti64vEXT(id,pname,params){if(!params){GL.recordError(1281);return}var query=GL.timerQueriesEXT[id];var param=GLctx.disjointTimerQueryExt["getQueryObjectEXT"](query,pname);var ret;if(typeof param=="boolean"){ret=param?1:0}else{ret=param}writeI53ToI64(params,ret)}function _emscripten_glGetQueryObjectivEXT(id,pname,params){if(!params){GL.recordError(1281);return}var query=GL.timerQueriesEXT[id];var param=GLctx.disjointTimerQueryExt["getQueryObjectEXT"](query,pname);var ret;if(typeof param=="boolean"){ret=param?1:0}else{ret=param}HEAP32[params>>2]=ret}function _emscripten_glGetQueryObjectui64vEXT(id,pname,params){if(!params){GL.recordError(1281);return}var query=GL.timerQueriesEXT[id];var param=GLctx.disjointTimerQueryExt["getQueryObjectEXT"](query,pname);var ret;if(typeof param=="boolean"){ret=param?1:0}else{ret=param}writeI53ToI64(params,ret)}function _emscripten_glGetQueryObjectuiv(id,pname,params){if(!params){GL.recordError(1281);return}var query=GL.queries[id];var param=GLctx["getQueryParameter"](query,pname);var ret;if(typeof param=="boolean"){ret=param?1:0}else{ret=param}HEAP32[params>>2]=ret}function _emscripten_glGetQueryObjectuivEXT(id,pname,params){if(!params){GL.recordError(1281);return}var query=GL.timerQueriesEXT[id];var param=GLctx.disjointTimerQueryExt["getQueryObjectEXT"](query,pname);var ret;if(typeof param=="boolean"){ret=param?1:0}else{ret=param}HEAP32[params>>2]=ret}function _emscripten_glGetQueryiv(target,pname,params){if(!params){GL.recordError(1281);return}HEAP32[params>>2]=GLctx["getQuery"](target,pname)}function _emscripten_glGetQueryivEXT(target,pname,params){if(!params){GL.recordError(1281);return}HEAP32[params>>2]=GLctx.disjointTimerQueryExt["getQueryEXT"](target,pname)}function _emscripten_glGetRenderbufferParameteriv(target,pname,params){if(!params){GL.recordError(1281);return}HEAP32[params>>2]=GLctx.getRenderbufferParameter(target,pname)}function _emscripten_glGetSamplerParameterfv(sampler,pname,params){if(!params){GL.recordError(1281);return}sampler=GL.samplers[sampler];HEAPF32[params>>2]=GLctx["getSamplerParameter"](sampler,pname)}function _emscripten_glGetSamplerParameteriv(sampler,pname,params){if(!params){GL.recordError(1281);return}sampler=GL.samplers[sampler];HEAP32[params>>2]=GLctx["getSamplerParameter"](sampler,pname)}function _emscripten_glGetShaderInfoLog(shader,maxLength,length,infoLog){var log=GLctx.getShaderInfoLog(GL.shaders[shader]);if(log===null)log="(unknown error)";var numBytesWrittenExclNull=maxLength>0&&infoLog?stringToUTF8(log,infoLog,maxLength):0;if(length)HEAP32[length>>2]=numBytesWrittenExclNull}function _emscripten_glGetShaderPrecisionFormat(shaderType,precisionType,range,precision){var result=GLctx.getShaderPrecisionFormat(shaderType,precisionType);HEAP32[range>>2]=result.rangeMin;HEAP32[range+4>>2]=result.rangeMax;HEAP32[precision>>2]=result.precision}function _emscripten_glGetShaderSource(shader,bufSize,length,source){var result=GLctx.getShaderSource(GL.shaders[shader]);if(!result)return;var numBytesWrittenExclNull=bufSize>0&&source?stringToUTF8(result,source,bufSize):0;if(length)HEAP32[length>>2]=numBytesWrittenExclNull}function _emscripten_glGetShaderiv(shader,pname,p){if(!p){GL.recordError(1281);return}if(pname==35716){var log=GLctx.getShaderInfoLog(GL.shaders[shader]);if(log===null)log="(unknown error)";var logLength=log?log.length+1:0;HEAP32[p>>2]=logLength}else if(pname==35720){var source=GLctx.getShaderSource(GL.shaders[shader]);var sourceLength=source?source.length+1:0;HEAP32[p>>2]=sourceLength}else{HEAP32[p>>2]=GLctx.getShaderParameter(GL.shaders[shader],pname)}}function stringToNewUTF8(jsString){var length=lengthBytesUTF8(jsString)+1;var cString=_malloc(length);stringToUTF8(jsString,cString,length);return cString}function _emscripten_glGetString(name_){if(GL.stringCache[name_])return GL.stringCache[name_];var ret;switch(name_){case 7939:var exts=GLctx.getSupportedExtensions()||[];exts=exts.concat(exts.map(function(e){return"GL_"+e}));ret=stringToNewUTF8(exts.join(" "));break;case 7936:case 7937:case 37445:case 37446:var s=GLctx.getParameter(name_);if(!s){GL.recordError(1280)}ret=stringToNewUTF8(s);break;case 7938:var glVersion=GLctx.getParameter(7938);if(true)glVersion="OpenGL ES 3.0 ("+glVersion+")";else{glVersion="OpenGL ES 2.0 ("+glVersion+")"}ret=stringToNewUTF8(glVersion);break;case 35724:var glslVersion=GLctx.getParameter(35724);var ver_re=/^WebGL GLSL ES ([0-9]\.[0-9][0-9]?)(?:$| .*)/;var ver_num=glslVersion.match(ver_re);if(ver_num!==null){if(ver_num[1].length==3)ver_num[1]=ver_num[1]+"0";glslVersion="OpenGL ES GLSL ES "+ver_num[1]+" ("+glslVersion+")"}ret=stringToNewUTF8(glslVersion);break;default:GL.recordError(1280);return 0}GL.stringCache[name_]=ret;return ret}function _emscripten_glGetStringi(name,index){if(GL.currentContext.version<2){GL.recordError(1282);return 0}var stringiCache=GL.stringiCache[name];if(stringiCache){if(index<0||index>=stringiCache.length){GL.recordError(1281);return 0}return stringiCache[index]}switch(name){case 7939:var exts=GLctx.getSupportedExtensions()||[];exts=exts.concat(exts.map(function(e){return"GL_"+e}));exts=exts.map(function(e){return stringToNewUTF8(e)});stringiCache=GL.stringiCache[name]=exts;if(index<0||index>=stringiCache.length){GL.recordError(1281);return 0}return stringiCache[index];default:GL.recordError(1280);return 0}}function _emscripten_glGetSynciv(sync,pname,bufSize,length,values){if(bufSize<0){GL.recordError(1281);return}if(!values){GL.recordError(1281);return}var ret=GLctx.getSyncParameter(GL.syncs[sync],pname);HEAP32[length>>2]=ret;if(ret!==null&&length)HEAP32[length>>2]=1}function _emscripten_glGetTexParameterfv(target,pname,params){if(!params){GL.recordError(1281);return}HEAPF32[params>>2]=GLctx.getTexParameter(target,pname)}function _emscripten_glGetTexParameteriv(target,pname,params){if(!params){GL.recordError(1281);return}HEAP32[params>>2]=GLctx.getTexParameter(target,pname)}function _emscripten_glGetTransformFeedbackVarying(program,index,bufSize,length,size,type,name){program=GL.programs[program];var info=GLctx["getTransformFeedbackVarying"](program,index);if(!info)return;if(name&&bufSize>0){var numBytesWrittenExclNull=stringToUTF8(info.name,name,bufSize);if(length)HEAP32[length>>2]=numBytesWrittenExclNull}else{if(length)HEAP32[length>>2]=0}if(size)HEAP32[size>>2]=info.size;if(type)HEAP32[type>>2]=info.type}function _emscripten_glGetUniformBlockIndex(program,uniformBlockName){return GLctx["getUniformBlockIndex"](GL.programs[program],UTF8ToString(uniformBlockName))}function _emscripten_glGetUniformIndices(program,uniformCount,uniformNames,uniformIndices){if(!uniformIndices){GL.recordError(1281);return}if(uniformCount>0&&(uniformNames==0||uniformIndices==0)){GL.recordError(1281);return}program=GL.programs[program];var names=[];for(var i=0;i>2]));var result=GLctx["getUniformIndices"](program,names);if(!result)return;var len=result.length;for(var i=0;i>2]=result[i]}}function jstoi_q(str){return parseInt(str)}function _emscripten_glGetUniformLocation(program,name){name=UTF8ToString(name);var arrayIndex=0;if(name[name.length-1]=="]"){var leftBrace=name.lastIndexOf("[");arrayIndex=name[leftBrace+1]!="]"?jstoi_q(name.slice(leftBrace+1)):0;name=name.slice(0,leftBrace)}var uniformInfo=GL.programInfos[program]&&GL.programInfos[program].uniforms[name];if(uniformInfo&&arrayIndex>=0&&arrayIndex>2]=data;break;case 2:HEAPF32[params>>2]=data;break}}else{for(var i=0;i>2]=data[i];break;case 2:HEAPF32[params+i*4>>2]=data[i];break}}}}function _emscripten_glGetUniformfv(program,location,params){emscriptenWebGLGetUniform(program,location,params,2)}function _emscripten_glGetUniformiv(program,location,params){emscriptenWebGLGetUniform(program,location,params,0)}function _emscripten_glGetUniformuiv(program,location,params){emscriptenWebGLGetUniform(program,location,params,0)}function emscriptenWebGLGetVertexAttrib(index,pname,params,type){if(!params){GL.recordError(1281);return}if(GL.currentContext.clientBuffers[index].enabled){err("glGetVertexAttrib*v on client-side array: not supported, bad data returned")}var data=GLctx.getVertexAttrib(index,pname);if(pname==34975){HEAP32[params>>2]=data&&data["name"]}else if(typeof data=="number"||typeof data=="boolean"){switch(type){case 0:HEAP32[params>>2]=data;break;case 2:HEAPF32[params>>2]=data;break;case 5:HEAP32[params>>2]=Math.fround(data);break}}else{for(var i=0;i>2]=data[i];break;case 2:HEAPF32[params+i*4>>2]=data[i];break;case 5:HEAP32[params+i*4>>2]=Math.fround(data[i]);break}}}}function _emscripten_glGetVertexAttribIiv(index,pname,params){emscriptenWebGLGetVertexAttrib(index,pname,params,0)}function _emscripten_glGetVertexAttribIuiv(index,pname,params){emscriptenWebGLGetVertexAttrib(index,pname,params,0)}function _emscripten_glGetVertexAttribPointerv(index,pname,pointer){if(!pointer){GL.recordError(1281);return}if(GL.currentContext.clientBuffers[index].enabled){err("glGetVertexAttribPointer on client-side array: not supported, bad data returned")}HEAP32[pointer>>2]=GLctx.getVertexAttribOffset(index,pname)}function _emscripten_glGetVertexAttribfv(index,pname,params){emscriptenWebGLGetVertexAttrib(index,pname,params,2)}function _emscripten_glGetVertexAttribiv(index,pname,params){emscriptenWebGLGetVertexAttrib(index,pname,params,5)}function _emscripten_glHint(x0,x1){GLctx["hint"](x0,x1)}function _emscripten_glInvalidateFramebuffer(target,numAttachments,attachments){var list=tempFixedLengthArray[numAttachments];for(var i=0;i>2]}GLctx["invalidateFramebuffer"](target,list)}function _emscripten_glInvalidateSubFramebuffer(target,numAttachments,attachments,x,y,width,height){var list=tempFixedLengthArray[numAttachments];for(var i=0;i>2]}GLctx["invalidateSubFramebuffer"](target,list,x,y,width,height)}function _emscripten_glIsBuffer(buffer){var b=GL.buffers[buffer];if(!b)return 0;return GLctx.isBuffer(b)}function _emscripten_glIsEnabled(x0){return GLctx["isEnabled"](x0)}function _emscripten_glIsFramebuffer(framebuffer){var fb=GL.framebuffers[framebuffer];if(!fb)return 0;return GLctx.isFramebuffer(fb)}function _emscripten_glIsProgram(program){program=GL.programs[program];if(!program)return 0;return GLctx.isProgram(program)}function _emscripten_glIsQuery(id){var query=GL.queries[id];if(!query)return 0;return GLctx["isQuery"](query)}function _emscripten_glIsQueryEXT(id){var query=GL.timerQueriesEXT[id];if(!query)return 0;return GLctx.disjointTimerQueryExt["isQueryEXT"](query)}function _emscripten_glIsRenderbuffer(renderbuffer){var rb=GL.renderbuffers[renderbuffer];if(!rb)return 0;return GLctx.isRenderbuffer(rb)}function _emscripten_glIsSampler(id){var sampler=GL.samplers[id];if(!sampler)return 0;return GLctx["isSampler"](sampler)}function _emscripten_glIsShader(shader){var s=GL.shaders[shader];if(!s)return 0;return GLctx.isShader(s)}function _emscripten_glIsSync(sync){return GLctx.isSync(GL.syncs[sync])}function _emscripten_glIsTexture(id){var texture=GL.textures[id];if(!texture)return 0;return GLctx.isTexture(texture)}function _emscripten_glIsTransformFeedback(id){return GLctx["isTransformFeedback"](GL.transformFeedbacks[id])}function _emscripten_glIsVertexArray(array){var vao=GL.vaos[array];if(!vao)return 0;return GLctx["isVertexArray"](vao)}function _emscripten_glIsVertexArrayOES(array){var vao=GL.vaos[array];if(!vao)return 0;return GLctx["isVertexArray"](vao)}function _emscripten_glLineWidth(x0){GLctx["lineWidth"](x0)}function _emscripten_glLinkProgram(program){GLctx.linkProgram(GL.programs[program]);GL.populateUniformTable(program)}function _emscripten_glMapBufferRange(target,offset,length,access){if(access!=26&&access!=10){err("glMapBufferRange is only supported when access is MAP_WRITE|INVALIDATE_BUFFER");return 0}if(!emscriptenWebGLValidateMapBufferTarget(target)){GL.recordError(1280);err("GL_INVALID_ENUM in glMapBufferRange");return 0}var mem=_malloc(length);if(!mem)return 0;GL.mappedBuffers[emscriptenWebGLGetBufferBinding(target)]={offset:offset,length:length,mem:mem,access:access};return mem}function _emscripten_glPauseTransformFeedback(){GLctx["pauseTransformFeedback"]()}function _emscripten_glPixelStorei(pname,param){if(pname==3317){GL.unpackAlignment=param}GLctx.pixelStorei(pname,param)}function _emscripten_glPolygonOffset(x0,x1){GLctx["polygonOffset"](x0,x1)}function _emscripten_glProgramBinary(program,binaryFormat,binary,length){GL.recordError(1280)}function _emscripten_glProgramParameteri(program,pname,value){GL.recordError(1280)}function _emscripten_glQueryCounterEXT(id,target){GLctx.disjointTimerQueryExt["queryCounterEXT"](GL.timerQueriesEXT[id],target)}function _emscripten_glReadBuffer(x0){GLctx["readBuffer"](x0)}function computeUnpackAlignedImageSize(width,height,sizePerPixel,alignment){function roundedToNextMultipleOf(x,y){return x+y-1&-y}var plainRowSize=width*sizePerPixel;var alignedRowSize=roundedToNextMultipleOf(plainRowSize,alignment);return height*alignedRowSize}function __colorChannelsInGlTextureFormat(format){var colorChannels={5:3,6:4,8:2,29502:3,29504:4,26917:2,26918:2,29846:3,29847:4};return colorChannels[format-6402]||1}function heapObjectForWebGLType(type){type-=5120;if(type==0)return HEAP8;if(type==1)return HEAPU8;if(type==2)return HEAP16;if(type==4)return HEAP32;if(type==6)return HEAPF32;if(type==5||type==28922||type==28520||type==30779||type==30782)return HEAPU32;return HEAPU16}function heapAccessShiftForWebGLHeap(heap){return 31-Math.clz32(heap.BYTES_PER_ELEMENT)}function emscriptenWebGLGetTexPixelData(type,format,width,height,pixels,internalFormat){var heap=heapObjectForWebGLType(type);var shift=heapAccessShiftForWebGLHeap(heap);var byteSize=1<>shift,pixels+bytes>>shift)}function _emscripten_glReadPixels(x,y,width,height,format,type,pixels){if(true){if(GLctx.currentPixelPackBufferBinding){GLctx.readPixels(x,y,width,height,format,type,pixels)}else{var heap=heapObjectForWebGLType(type);GLctx.readPixels(x,y,width,height,format,type,heap,pixels>>heapAccessShiftForWebGLHeap(heap))}return}var pixelData=emscriptenWebGLGetTexPixelData(type,format,width,height,pixels,format);if(!pixelData){GL.recordError(1280);return}GLctx.readPixels(x,y,width,height,format,type,pixelData)}function _emscripten_glReleaseShaderCompiler(){}function _emscripten_glRenderbufferStorage(x0,x1,x2,x3){GLctx["renderbufferStorage"](x0,x1,x2,x3)}function _emscripten_glRenderbufferStorageMultisample(x0,x1,x2,x3,x4){GLctx["renderbufferStorageMultisample"](x0,x1,x2,x3,x4)}function _emscripten_glResumeTransformFeedback(){GLctx["resumeTransformFeedback"]()}function _emscripten_glSampleCoverage(value,invert){GLctx.sampleCoverage(value,!!invert)}function _emscripten_glSamplerParameterf(sampler,pname,param){GLctx["samplerParameterf"](GL.samplers[sampler],pname,param)}function _emscripten_glSamplerParameterfv(sampler,pname,params){var param=HEAPF32[params>>2];GLctx["samplerParameterf"](GL.samplers[sampler],pname,param)}function _emscripten_glSamplerParameteri(sampler,pname,param){GLctx["samplerParameteri"](GL.samplers[sampler],pname,param)}function _emscripten_glSamplerParameteriv(sampler,pname,params){var param=HEAP32[params>>2];GLctx["samplerParameteri"](GL.samplers[sampler],pname,param)}function _emscripten_glScissor(x0,x1,x2,x3){GLctx["scissor"](x0,x1,x2,x3)}function _emscripten_glShaderBinary(){GL.recordError(1280)}function _emscripten_glShaderSource(shader,count,string,length){var source=GL.getSource(shader,count,string,length);GLctx.shaderSource(GL.shaders[shader],source)}function _emscripten_glStencilFunc(x0,x1,x2){GLctx["stencilFunc"](x0,x1,x2)}function _emscripten_glStencilFuncSeparate(x0,x1,x2,x3){GLctx["stencilFuncSeparate"](x0,x1,x2,x3)}function _emscripten_glStencilMask(x0){GLctx["stencilMask"](x0)}function _emscripten_glStencilMaskSeparate(x0,x1){GLctx["stencilMaskSeparate"](x0,x1)}function _emscripten_glStencilOp(x0,x1,x2){GLctx["stencilOp"](x0,x1,x2)}function _emscripten_glStencilOpSeparate(x0,x1,x2,x3){GLctx["stencilOpSeparate"](x0,x1,x2,x3)}function _emscripten_glTexImage2D(target,level,internalFormat,width,height,border,format,type,pixels){if(true){if(GLctx.currentPixelUnpackBufferBinding){GLctx.texImage2D(target,level,internalFormat,width,height,border,format,type,pixels)}else if(pixels){var heap=heapObjectForWebGLType(type);GLctx.texImage2D(target,level,internalFormat,width,height,border,format,type,heap,pixels>>heapAccessShiftForWebGLHeap(heap))}else{GLctx.texImage2D(target,level,internalFormat,width,height,border,format,type,null)}return}GLctx.texImage2D(target,level,internalFormat,width,height,border,format,type,pixels?emscriptenWebGLGetTexPixelData(type,format,width,height,pixels,internalFormat):null)}function _emscripten_glTexImage3D(target,level,internalFormat,width,height,depth,border,format,type,pixels){if(GLctx.currentPixelUnpackBufferBinding){GLctx["texImage3D"](target,level,internalFormat,width,height,depth,border,format,type,pixels)}else if(pixels){var heap=heapObjectForWebGLType(type);GLctx["texImage3D"](target,level,internalFormat,width,height,depth,border,format,type,heap,pixels>>heapAccessShiftForWebGLHeap(heap))}else{GLctx["texImage3D"](target,level,internalFormat,width,height,depth,border,format,type,null)}}function _emscripten_glTexParameterf(x0,x1,x2){GLctx["texParameterf"](x0,x1,x2)}function _emscripten_glTexParameterfv(target,pname,params){var param=HEAPF32[params>>2];GLctx.texParameterf(target,pname,param)}function _emscripten_glTexParameteri(x0,x1,x2){GLctx["texParameteri"](x0,x1,x2)}function _emscripten_glTexParameteriv(target,pname,params){var param=HEAP32[params>>2];GLctx.texParameteri(target,pname,param)}function _emscripten_glTexStorage2D(x0,x1,x2,x3,x4){GLctx["texStorage2D"](x0,x1,x2,x3,x4)}function _emscripten_glTexStorage3D(x0,x1,x2,x3,x4,x5){GLctx["texStorage3D"](x0,x1,x2,x3,x4,x5)}function _emscripten_glTexSubImage2D(target,level,xoffset,yoffset,width,height,format,type,pixels){if(true){if(GLctx.currentPixelUnpackBufferBinding){GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,pixels)}else if(pixels){var heap=heapObjectForWebGLType(type);GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,heap,pixels>>heapAccessShiftForWebGLHeap(heap))}else{GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,null)}return}var pixelData=null;if(pixels)pixelData=emscriptenWebGLGetTexPixelData(type,format,width,height,pixels,0);GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,pixelData)}function _emscripten_glTexSubImage3D(target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,pixels){if(GLctx.currentPixelUnpackBufferBinding){GLctx["texSubImage3D"](target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,pixels)}else if(pixels){var heap=heapObjectForWebGLType(type);GLctx["texSubImage3D"](target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,heap,pixels>>heapAccessShiftForWebGLHeap(heap))}else{GLctx["texSubImage3D"](target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,null)}}function _emscripten_glTransformFeedbackVaryings(program,count,varyings,bufferMode){program=GL.programs[program];var vars=[];for(var i=0;i>2]));GLctx["transformFeedbackVaryings"](program,vars,bufferMode)}function _emscripten_glUniform1f(location,v0){GLctx.uniform1f(GL.uniforms[location],v0)}function _emscripten_glUniform1fv(location,count,value){GLctx.uniform1fv(GL.uniforms[location],HEAPF32,value>>2,count)}function _emscripten_glUniform1i(location,v0){GLctx.uniform1i(GL.uniforms[location],v0)}function _emscripten_glUniform1iv(location,count,value){GLctx.uniform1iv(GL.uniforms[location],HEAP32,value>>2,count)}function _emscripten_glUniform1ui(location,v0){GLctx.uniform1ui(GL.uniforms[location],v0)}function _emscripten_glUniform1uiv(location,count,value){GLctx.uniform1uiv(GL.uniforms[location],HEAPU32,value>>2,count)}function _emscripten_glUniform2f(location,v0,v1){GLctx.uniform2f(GL.uniforms[location],v0,v1)}function _emscripten_glUniform2fv(location,count,value){GLctx.uniform2fv(GL.uniforms[location],HEAPF32,value>>2,count*2)}function _emscripten_glUniform2i(location,v0,v1){GLctx.uniform2i(GL.uniforms[location],v0,v1)}function _emscripten_glUniform2iv(location,count,value){GLctx.uniform2iv(GL.uniforms[location],HEAP32,value>>2,count*2)}function _emscripten_glUniform2ui(location,v0,v1){GLctx.uniform2ui(GL.uniforms[location],v0,v1)}function _emscripten_glUniform2uiv(location,count,value){GLctx.uniform2uiv(GL.uniforms[location],HEAPU32,value>>2,count*2)}function _emscripten_glUniform3f(location,v0,v1,v2){GLctx.uniform3f(GL.uniforms[location],v0,v1,v2)}function _emscripten_glUniform3fv(location,count,value){GLctx.uniform3fv(GL.uniforms[location],HEAPF32,value>>2,count*3)}function _emscripten_glUniform3i(location,v0,v1,v2){GLctx.uniform3i(GL.uniforms[location],v0,v1,v2)}function _emscripten_glUniform3iv(location,count,value){GLctx.uniform3iv(GL.uniforms[location],HEAP32,value>>2,count*3)}function _emscripten_glUniform3ui(location,v0,v1,v2){GLctx.uniform3ui(GL.uniforms[location],v0,v1,v2)}function _emscripten_glUniform3uiv(location,count,value){GLctx.uniform3uiv(GL.uniforms[location],HEAPU32,value>>2,count*3)}function _emscripten_glUniform4f(location,v0,v1,v2,v3){GLctx.uniform4f(GL.uniforms[location],v0,v1,v2,v3)}function _emscripten_glUniform4fv(location,count,value){GLctx.uniform4fv(GL.uniforms[location],HEAPF32,value>>2,count*4)}function _emscripten_glUniform4i(location,v0,v1,v2,v3){GLctx.uniform4i(GL.uniforms[location],v0,v1,v2,v3)}function _emscripten_glUniform4iv(location,count,value){GLctx.uniform4iv(GL.uniforms[location],HEAP32,value>>2,count*4)}function _emscripten_glUniform4ui(location,v0,v1,v2,v3){GLctx.uniform4ui(GL.uniforms[location],v0,v1,v2,v3)}function _emscripten_glUniform4uiv(location,count,value){GLctx.uniform4uiv(GL.uniforms[location],HEAPU32,value>>2,count*4)}function _emscripten_glUniformBlockBinding(program,uniformBlockIndex,uniformBlockBinding){program=GL.programs[program];GLctx["uniformBlockBinding"](program,uniformBlockIndex,uniformBlockBinding)}function _emscripten_glUniformMatrix2fv(location,count,transpose,value){GLctx.uniformMatrix2fv(GL.uniforms[location],!!transpose,HEAPF32,value>>2,count*4)}function _emscripten_glUniformMatrix2x3fv(location,count,transpose,value){GLctx.uniformMatrix2x3fv(GL.uniforms[location],!!transpose,HEAPF32,value>>2,count*6)}function _emscripten_glUniformMatrix2x4fv(location,count,transpose,value){GLctx.uniformMatrix2x4fv(GL.uniforms[location],!!transpose,HEAPF32,value>>2,count*8)}function _emscripten_glUniformMatrix3fv(location,count,transpose,value){GLctx.uniformMatrix3fv(GL.uniforms[location],!!transpose,HEAPF32,value>>2,count*9)}function _emscripten_glUniformMatrix3x2fv(location,count,transpose,value){GLctx.uniformMatrix3x2fv(GL.uniforms[location],!!transpose,HEAPF32,value>>2,count*6)}function _emscripten_glUniformMatrix3x4fv(location,count,transpose,value){GLctx.uniformMatrix3x4fv(GL.uniforms[location],!!transpose,HEAPF32,value>>2,count*12)}function _emscripten_glUniformMatrix4fv(location,count,transpose,value){GLctx.uniformMatrix4fv(GL.uniforms[location],!!transpose,HEAPF32,value>>2,count*16)}function _emscripten_glUniformMatrix4x2fv(location,count,transpose,value){GLctx.uniformMatrix4x2fv(GL.uniforms[location],!!transpose,HEAPF32,value>>2,count*8)}function _emscripten_glUniformMatrix4x3fv(location,count,transpose,value){GLctx.uniformMatrix4x3fv(GL.uniforms[location],!!transpose,HEAPF32,value>>2,count*12)}function _emscripten_glUnmapBuffer(target){if(!emscriptenWebGLValidateMapBufferTarget(target)){GL.recordError(1280);err("GL_INVALID_ENUM in glUnmapBuffer");return 0}var buffer=emscriptenWebGLGetBufferBinding(target);var mapping=GL.mappedBuffers[buffer];if(!mapping){GL.recordError(1282);err("buffer was never mapped in glUnmapBuffer");return 0}GL.mappedBuffers[buffer]=null;if(!(mapping.access&16))if(true){GLctx.bufferSubData(target,mapping.offset,HEAPU8,mapping.mem,mapping.length)}else{GLctx.bufferSubData(target,mapping.offset,HEAPU8.subarray(mapping.mem,mapping.mem+mapping.length))}_free(mapping.mem);return 1}function _emscripten_glUseProgram(program){GLctx.useProgram(GL.programs[program])}function _emscripten_glValidateProgram(program){GLctx.validateProgram(GL.programs[program])}function _emscripten_glVertexAttrib1f(x0,x1){GLctx["vertexAttrib1f"](x0,x1)}function _emscripten_glVertexAttrib1fv(index,v){GLctx.vertexAttrib1f(index,HEAPF32[v>>2])}function _emscripten_glVertexAttrib2f(x0,x1,x2){GLctx["vertexAttrib2f"](x0,x1,x2)}function _emscripten_glVertexAttrib2fv(index,v){GLctx.vertexAttrib2f(index,HEAPF32[v>>2],HEAPF32[v+4>>2])}function _emscripten_glVertexAttrib3f(x0,x1,x2,x3){GLctx["vertexAttrib3f"](x0,x1,x2,x3)}function _emscripten_glVertexAttrib3fv(index,v){GLctx.vertexAttrib3f(index,HEAPF32[v>>2],HEAPF32[v+4>>2],HEAPF32[v+8>>2])}function _emscripten_glVertexAttrib4f(x0,x1,x2,x3,x4){GLctx["vertexAttrib4f"](x0,x1,x2,x3,x4)}function _emscripten_glVertexAttrib4fv(index,v){GLctx.vertexAttrib4f(index,HEAPF32[v>>2],HEAPF32[v+4>>2],HEAPF32[v+8>>2],HEAPF32[v+12>>2])}function _emscripten_glVertexAttribDivisor(index,divisor){GLctx["vertexAttribDivisor"](index,divisor)}function _emscripten_glVertexAttribDivisorANGLE(index,divisor){GLctx["vertexAttribDivisor"](index,divisor)}function _emscripten_glVertexAttribDivisorARB(index,divisor){GLctx["vertexAttribDivisor"](index,divisor)}function _emscripten_glVertexAttribDivisorEXT(index,divisor){GLctx["vertexAttribDivisor"](index,divisor)}function _emscripten_glVertexAttribDivisorNV(index,divisor){GLctx["vertexAttribDivisor"](index,divisor)}function _emscripten_glVertexAttribI4i(x0,x1,x2,x3,x4){GLctx["vertexAttribI4i"](x0,x1,x2,x3,x4)}function _emscripten_glVertexAttribI4iv(index,v){GLctx.vertexAttribI4i(index,HEAP32[v>>2],HEAP32[v+4>>2],HEAP32[v+8>>2],HEAP32[v+12>>2])}function _emscripten_glVertexAttribI4ui(x0,x1,x2,x3,x4){GLctx["vertexAttribI4ui"](x0,x1,x2,x3,x4)}function _emscripten_glVertexAttribI4uiv(index,v){GLctx.vertexAttribI4ui(index,HEAPU32[v>>2],HEAPU32[v+4>>2],HEAPU32[v+8>>2],HEAPU32[v+12>>2])}function _emscripten_glVertexAttribIPointer(index,size,type,stride,ptr){var cb=GL.currentContext.clientBuffers[index];if(!GLctx.currentArrayBufferBinding){cb.size=size;cb.type=type;cb.normalized=false;cb.stride=stride;cb.ptr=ptr;cb.clientside=true;cb.vertexAttribPointerAdaptor=function(index,size,type,normalized,stride,ptr){this.vertexAttribIPointer(index,size,type,stride,ptr)};return}cb.clientside=false;GLctx["vertexAttribIPointer"](index,size,type,stride,ptr)}function _emscripten_glVertexAttribPointer(index,size,type,normalized,stride,ptr){var cb=GL.currentContext.clientBuffers[index];if(!GLctx.currentArrayBufferBinding){cb.size=size;cb.type=type;cb.normalized=normalized;cb.stride=stride;cb.ptr=ptr;cb.clientside=true;cb.vertexAttribPointerAdaptor=function(index,size,type,normalized,stride,ptr){this.vertexAttribPointer(index,size,type,normalized,stride,ptr)};return}cb.clientside=false;GLctx.vertexAttribPointer(index,size,type,!!normalized,stride,ptr)}function _emscripten_glViewport(x0,x1,x2,x3){GLctx["viewport"](x0,x1,x2,x3)}function _emscripten_glWaitSync(sync,flags,timeoutLo,timeoutHi){GLctx.waitSync(GL.syncs[sync],flags,convertI32PairToI53(timeoutLo,timeoutHi))}function _emscripten_memcpy_big(dest,src,num){HEAPU8.copyWithin(dest,src,src+num)}function _emscripten_get_heap_size(){return HEAPU8.length}function emscripten_realloc_buffer(size){try{wasmMemory.grow(size-buffer.byteLength+65535>>>16);updateGlobalBufferAndViews(wasmMemory.buffer);return 1}catch(e){}}function _emscripten_resize_heap(requestedSize){requestedSize=requestedSize>>>0;var oldSize=_emscripten_get_heap_size();var maxHeapSize=2147483648;if(requestedSize>maxHeapSize){return false}var minHeapSize=16777216;for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize*(1+.2/cutDown);overGrownHeapSize=Math.min(overGrownHeapSize,requestedSize+100663296);var newSize=Math.min(maxHeapSize,alignUp(Math.max(minHeapSize,requestedSize,overGrownHeapSize),65536));var replacement=emscripten_realloc_buffer(newSize);if(replacement){return true}}return false}var ENV={};function getExecutableName(){return thisProgram||"./this.program"}function getEnvStrings(){if(!getEnvStrings.strings){var lang=(typeof navigator==="object"&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8";var env={"USER":"web_user","LOGNAME":"web_user","PATH":"/","PWD":"/","HOME":"/home/web_user","LANG":lang,"_":getExecutableName()};for(var x in ENV){env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(x+"="+env[x])}getEnvStrings.strings=strings}return getEnvStrings.strings}function _environ_get(__environ,environ_buf){try{var bufSize=0;getEnvStrings().forEach(function(string,i){var ptr=environ_buf+bufSize;HEAP32[__environ+i*4>>2]=ptr;writeAsciiToMemory(string,ptr);bufSize+=string.length+1});return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _environ_sizes_get(penviron_count,penviron_buf_size){try{var strings=getEnvStrings();HEAP32[penviron_count>>2]=strings.length;var bufSize=0;strings.forEach(function(string){bufSize+=string.length+1});HEAP32[penviron_buf_size>>2]=bufSize;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_read(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=SYSCALLS.doReadv(stream,iov,iovcnt);HEAP32[pnum>>2]=num;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){try{var stream=SYSCALLS.getStreamFromFD(fd);var HIGH_OFFSET=4294967296;var offset=offset_high*HIGH_OFFSET+(offset_low>>>0);var DOUBLE_LIMIT=9007199254740992;if(offset<=-DOUBLE_LIMIT||offset>=DOUBLE_LIMIT){return-61}FS.llseek(stream,offset,whence);tempI64=[stream.position>>>0,(tempDouble=stream.position,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[newOffset>>2]=tempI64[0],HEAP32[newOffset+4>>2]=tempI64[1];if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_write(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=SYSCALLS.doWritev(stream,iov,iovcnt);HEAP32[pnum>>2]=num;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _getentropy(buffer,size){if(!_getentropy.randomDevice){_getentropy.randomDevice=getRandomDevice()}for(var i=0;i>0]=_getentropy.randomDevice()}return 0}function _glActiveTexture(x0){GLctx["activeTexture"](x0)}function _glAttachShader(program,shader){GLctx.attachShader(GL.programs[program],GL.shaders[shader])}function _glBeginQuery(target,id){GLctx["beginQuery"](target,GL.queries[id])}function _glBindBuffer(target,buffer){if(target==34962){GLctx.currentArrayBufferBinding=buffer}else if(target==34963){GLctx.currentElementArrayBufferBinding=buffer}if(target==35051){GLctx.currentPixelPackBufferBinding=buffer}else if(target==35052){GLctx.currentPixelUnpackBufferBinding=buffer}GLctx.bindBuffer(target,GL.buffers[buffer])}function _glBindBufferRange(target,index,buffer,offset,ptrsize){GLctx["bindBufferRange"](target,index,GL.buffers[buffer],offset,ptrsize)}function _glBindFramebuffer(target,framebuffer){GLctx.bindFramebuffer(target,GL.framebuffers[framebuffer])}function _glBindRenderbuffer(target,renderbuffer){GLctx.bindRenderbuffer(target,GL.renderbuffers[renderbuffer])}function _glBindSampler(unit,sampler){GLctx["bindSampler"](unit,GL.samplers[sampler])}function _glBindTexture(target,texture){GLctx.bindTexture(target,GL.textures[texture])}function _glBindVertexArray(vao){GLctx["bindVertexArray"](GL.vaos[vao]);var ibo=GLctx.getParameter(34965);GLctx.currentElementArrayBufferBinding=ibo?ibo.name|0:0}function _glBlendEquationSeparate(x0,x1){GLctx["blendEquationSeparate"](x0,x1)}function _glBlendFuncSeparate(x0,x1,x2,x3){GLctx["blendFuncSeparate"](x0,x1,x2,x3)}function _glBlitFramebuffer(x0,x1,x2,x3,x4,x5,x6,x7,x8,x9){GLctx["blitFramebuffer"](x0,x1,x2,x3,x4,x5,x6,x7,x8,x9)}function _glBufferData(target,size,data,usage){if(true){if(data){GLctx.bufferData(target,HEAPU8,usage,data,size)}else{GLctx.bufferData(target,size,usage)}}else{GLctx.bufferData(target,data?HEAPU8.subarray(data,data+size):size,usage)}}function _glBufferSubData(target,offset,size,data){if(true){GLctx.bufferSubData(target,offset,HEAPU8,data,size);return}GLctx.bufferSubData(target,offset,HEAPU8.subarray(data,data+size))}function _glClearBufferfi(x0,x1,x2,x3){GLctx["clearBufferfi"](x0,x1,x2,x3)}function _glClearBufferfv(buffer,drawbuffer,value){GLctx["clearBufferfv"](buffer,drawbuffer,HEAPF32,value>>2)}function _glClearBufferiv(buffer,drawbuffer,value){GLctx["clearBufferiv"](buffer,drawbuffer,HEAP32,value>>2)}function _glClientWaitSync(sync,flags,timeoutLo,timeoutHi){return GLctx.clientWaitSync(GL.syncs[sync],flags,convertI32PairToI53(timeoutLo,timeoutHi))}function _glColorMask(red,green,blue,alpha){GLctx.colorMask(!!red,!!green,!!blue,!!alpha)}function _glCompileShader(shader){GLctx.compileShader(GL.shaders[shader])}function _glCompressedTexSubImage2D(target,level,xoffset,yoffset,width,height,format,imageSize,data){if(true){if(GLctx.currentPixelUnpackBufferBinding){GLctx["compressedTexSubImage2D"](target,level,xoffset,yoffset,width,height,format,imageSize,data)}else{GLctx["compressedTexSubImage2D"](target,level,xoffset,yoffset,width,height,format,HEAPU8,data,imageSize)}return}GLctx["compressedTexSubImage2D"](target,level,xoffset,yoffset,width,height,format,data?HEAPU8.subarray(data,data+imageSize):null)}function _glCompressedTexSubImage3D(target,level,xoffset,yoffset,zoffset,width,height,depth,format,imageSize,data){if(GLctx.currentPixelUnpackBufferBinding){GLctx["compressedTexSubImage3D"](target,level,xoffset,yoffset,zoffset,width,height,depth,format,imageSize,data)}else{GLctx["compressedTexSubImage3D"](target,level,xoffset,yoffset,zoffset,width,height,depth,format,HEAPU8,data,imageSize)}}function _glCreateProgram(){var id=GL.getNewId(GL.programs);var program=GLctx.createProgram();program.name=id;GL.programs[id]=program;return id}function _glCreateShader(shaderType){var id=GL.getNewId(GL.shaders);GL.shaders[id]=GLctx.createShader(shaderType);return id}function _glCullFace(x0){GLctx["cullFace"](x0)}function _glDeleteBuffers(n,buffers){for(var i=0;i>2];var buffer=GL.buffers[id];if(!buffer)continue;GLctx.deleteBuffer(buffer);buffer.name=0;GL.buffers[id]=null;if(id==GLctx.currentArrayBufferBinding)GLctx.currentArrayBufferBinding=0;if(id==GLctx.currentElementArrayBufferBinding)GLctx.currentElementArrayBufferBinding=0;if(id==GLctx.currentPixelPackBufferBinding)GLctx.currentPixelPackBufferBinding=0;if(id==GLctx.currentPixelUnpackBufferBinding)GLctx.currentPixelUnpackBufferBinding=0}}function _glDeleteFramebuffers(n,framebuffers){for(var i=0;i>2];var framebuffer=GL.framebuffers[id];if(!framebuffer)continue;GLctx.deleteFramebuffer(framebuffer);framebuffer.name=0;GL.framebuffers[id]=null}}function _glDeleteProgram(id){if(!id)return;var program=GL.programs[id];if(!program){GL.recordError(1281);return}GLctx.deleteProgram(program);program.name=0;GL.programs[id]=null;GL.programInfos[id]=null}function _glDeleteQueries(n,ids){for(var i=0;i>2];var query=GL.queries[id];if(!query)continue;GLctx["deleteQuery"](query);GL.queries[id]=null}}function _glDeleteRenderbuffers(n,renderbuffers){for(var i=0;i>2];var renderbuffer=GL.renderbuffers[id];if(!renderbuffer)continue;GLctx.deleteRenderbuffer(renderbuffer);renderbuffer.name=0;GL.renderbuffers[id]=null}}function _glDeleteSamplers(n,samplers){for(var i=0;i>2];var sampler=GL.samplers[id];if(!sampler)continue;GLctx["deleteSampler"](sampler);sampler.name=0;GL.samplers[id]=null}}function _glDeleteShader(id){if(!id)return;var shader=GL.shaders[id];if(!shader){GL.recordError(1281);return}GLctx.deleteShader(shader);GL.shaders[id]=null}function _glDeleteSync(id){if(!id)return;var sync=GL.syncs[id];if(!sync){GL.recordError(1281);return}GLctx.deleteSync(sync);sync.name=0;GL.syncs[id]=null}function _glDeleteTextures(n,textures){for(var i=0;i>2];var texture=GL.textures[id];if(!texture)continue;GLctx.deleteTexture(texture);texture.name=0;GL.textures[id]=null}}function _glDeleteVertexArrays(n,vaos){for(var i=0;i>2];GLctx["deleteVertexArray"](GL.vaos[id]);GL.vaos[id]=null}}function _glDepthFunc(x0){GLctx["depthFunc"](x0)}function _glDepthMask(flag){GLctx.depthMask(!!flag)}function _glDepthRangef(x0,x1){GLctx["depthRange"](x0,x1)}function _glDetachShader(program,shader){GLctx.detachShader(GL.programs[program],GL.shaders[shader])}function _glDisable(x0){GLctx["disable"](x0)}function _glDisableVertexAttribArray(index){var cb=GL.currentContext.clientBuffers[index];cb.enabled=false;GLctx.disableVertexAttribArray(index)}function _glDrawArrays(mode,first,count){GL.preDrawHandleClientVertexAttribBindings(first+count);GLctx.drawArrays(mode,first,count);GL.postDrawHandleClientVertexAttribBindings()}function _glDrawBuffers(n,bufs){var bufArray=tempFixedLengthArray[n];for(var i=0;i>2]}GLctx["drawBuffers"](bufArray)}function _glDrawRangeElements(mode,start,end,count,type,indices){_glDrawElements(mode,count,type,indices)}function _glEnable(x0){GLctx["enable"](x0)}function _glEnableVertexAttribArray(index){var cb=GL.currentContext.clientBuffers[index];cb.enabled=true;GLctx.enableVertexAttribArray(index)}function _glEndQuery(x0){GLctx["endQuery"](x0)}function _glFenceSync(condition,flags){var sync=GLctx.fenceSync(condition,flags);if(sync){var id=GL.getNewId(GL.syncs);sync.name=id;GL.syncs[id]=sync;return id}else{return 0}}function _glFinish(){GLctx["finish"]()}function _glFlush(){GLctx["flush"]()}function _glFramebufferRenderbuffer(target,attachment,renderbuffertarget,renderbuffer){GLctx.framebufferRenderbuffer(target,attachment,renderbuffertarget,GL.renderbuffers[renderbuffer])}function _glFramebufferTexture2D(target,attachment,textarget,texture,level){GLctx.framebufferTexture2D(target,attachment,textarget,GL.textures[texture],level)}function _glFramebufferTextureLayer(target,attachment,texture,level,layer){GLctx.framebufferTextureLayer(target,attachment,GL.textures[texture],level,layer)}function _glFrontFace(x0){GLctx["frontFace"](x0)}function _glGenBuffers(n,buffers){__glGenObject(n,buffers,"createBuffer",GL.buffers)}function _glGenFramebuffers(n,ids){__glGenObject(n,ids,"createFramebuffer",GL.framebuffers)}function _glGenQueries(n,ids){__glGenObject(n,ids,"createQuery",GL.queries)}function _glGenRenderbuffers(n,renderbuffers){__glGenObject(n,renderbuffers,"createRenderbuffer",GL.renderbuffers)}function _glGenSamplers(n,samplers){__glGenObject(n,samplers,"createSampler",GL.samplers)}function _glGenTextures(n,textures){__glGenObject(n,textures,"createTexture",GL.textures)}function _glGenVertexArrays(n,arrays){__glGenObject(n,arrays,"createVertexArray",GL.vaos)}function _glGenerateMipmap(x0){GLctx["generateMipmap"](x0)}function _glGetError(){var error=GLctx.getError()||GL.lastError;GL.lastError=0;return error}function _glGetFloatv(name_,p){emscriptenWebGLGet(name_,p,2)}function _glGetIntegerv(name_,p){emscriptenWebGLGet(name_,p,0)}function _glGetProgramInfoLog(program,maxLength,length,infoLog){var log=GLctx.getProgramInfoLog(GL.programs[program]);if(log===null)log="(unknown error)";var numBytesWrittenExclNull=maxLength>0&&infoLog?stringToUTF8(log,infoLog,maxLength):0;if(length)HEAP32[length>>2]=numBytesWrittenExclNull}function _glGetProgramiv(program,pname,p){if(!p){GL.recordError(1281);return}if(program>=GL.counter){GL.recordError(1281);return}var ptable=GL.programInfos[program];if(!ptable){GL.recordError(1282);return}if(pname==35716){var log=GLctx.getProgramInfoLog(GL.programs[program]);if(log===null)log="(unknown error)";HEAP32[p>>2]=log.length+1}else if(pname==35719){HEAP32[p>>2]=ptable.maxUniformLength}else if(pname==35722){if(ptable.maxAttributeLength==-1){program=GL.programs[program];var numAttribs=GLctx.getProgramParameter(program,35721);ptable.maxAttributeLength=0;for(var i=0;i>2]=ptable.maxAttributeLength}else if(pname==35381){if(ptable.maxUniformBlockNameLength==-1){program=GL.programs[program];var numBlocks=GLctx.getProgramParameter(program,35382);ptable.maxUniformBlockNameLength=0;for(var i=0;i>2]=ptable.maxUniformBlockNameLength}else{HEAP32[p>>2]=GLctx.getProgramParameter(GL.programs[program],pname)}}function _glGetQueryObjectuiv(id,pname,params){if(!params){GL.recordError(1281);return}var query=GL.queries[id];var param=GLctx["getQueryParameter"](query,pname);var ret;if(typeof param=="boolean"){ret=param?1:0}else{ret=param}HEAP32[params>>2]=ret}function _glGetShaderInfoLog(shader,maxLength,length,infoLog){var log=GLctx.getShaderInfoLog(GL.shaders[shader]);if(log===null)log="(unknown error)";var numBytesWrittenExclNull=maxLength>0&&infoLog?stringToUTF8(log,infoLog,maxLength):0;if(length)HEAP32[length>>2]=numBytesWrittenExclNull}function _glGetShaderiv(shader,pname,p){if(!p){GL.recordError(1281);return}if(pname==35716){var log=GLctx.getShaderInfoLog(GL.shaders[shader]);if(log===null)log="(unknown error)";var logLength=log?log.length+1:0;HEAP32[p>>2]=logLength}else if(pname==35720){var source=GLctx.getShaderSource(GL.shaders[shader]);var sourceLength=source?source.length+1:0;HEAP32[p>>2]=sourceLength}else{HEAP32[p>>2]=GLctx.getShaderParameter(GL.shaders[shader],pname)}}function _glGetString(name_){if(GL.stringCache[name_])return GL.stringCache[name_];var ret;switch(name_){case 7939:var exts=GLctx.getSupportedExtensions()||[];exts=exts.concat(exts.map(function(e){return"GL_"+e}));ret=stringToNewUTF8(exts.join(" "));break;case 7936:case 7937:case 37445:case 37446:var s=GLctx.getParameter(name_);if(!s){GL.recordError(1280)}ret=stringToNewUTF8(s);break;case 7938:var glVersion=GLctx.getParameter(7938);if(true)glVersion="OpenGL ES 3.0 ("+glVersion+")";else{glVersion="OpenGL ES 2.0 ("+glVersion+")"}ret=stringToNewUTF8(glVersion);break;case 35724:var glslVersion=GLctx.getParameter(35724);var ver_re=/^WebGL GLSL ES ([0-9]\.[0-9][0-9]?)(?:$| .*)/;var ver_num=glslVersion.match(ver_re);if(ver_num!==null){if(ver_num[1].length==3)ver_num[1]=ver_num[1]+"0";glslVersion="OpenGL ES GLSL ES "+ver_num[1]+" ("+glslVersion+")"}ret=stringToNewUTF8(glslVersion);break;default:GL.recordError(1280);return 0}GL.stringCache[name_]=ret;return ret}function _glGetStringi(name,index){if(GL.currentContext.version<2){GL.recordError(1282);return 0}var stringiCache=GL.stringiCache[name];if(stringiCache){if(index<0||index>=stringiCache.length){GL.recordError(1281);return 0}return stringiCache[index]}switch(name){case 7939:var exts=GLctx.getSupportedExtensions()||[];exts=exts.concat(exts.map(function(e){return"GL_"+e}));exts=exts.map(function(e){return stringToNewUTF8(e)});stringiCache=GL.stringiCache[name]=exts;if(index<0||index>=stringiCache.length){GL.recordError(1281);return 0}return stringiCache[index];default:GL.recordError(1280);return 0}}function _glGetUniformBlockIndex(program,uniformBlockName){return GLctx["getUniformBlockIndex"](GL.programs[program],UTF8ToString(uniformBlockName))}function _glGetUniformLocation(program,name){name=UTF8ToString(name);var arrayIndex=0;if(name[name.length-1]=="]"){var leftBrace=name.lastIndexOf("[");arrayIndex=name[leftBrace+1]!="]"?jstoi_q(name.slice(leftBrace+1)):0;name=name.slice(0,leftBrace)}var uniformInfo=GL.programInfos[program]&&GL.programInfos[program].uniforms[name];if(uniformInfo&&arrayIndex>=0&&arrayIndex>2]}GLctx["invalidateFramebuffer"](target,list)}function _glIsEnabled(x0){return GLctx["isEnabled"](x0)}function _glLinkProgram(program){GLctx.linkProgram(GL.programs[program]);GL.populateUniformTable(program)}function _glMapBufferRange(target,offset,length,access){if(access!=26&&access!=10){err("glMapBufferRange is only supported when access is MAP_WRITE|INVALIDATE_BUFFER");return 0}if(!emscriptenWebGLValidateMapBufferTarget(target)){GL.recordError(1280);err("GL_INVALID_ENUM in glMapBufferRange");return 0}var mem=_malloc(length);if(!mem)return 0;GL.mappedBuffers[emscriptenWebGLGetBufferBinding(target)]={offset:offset,length:length,mem:mem,access:access};return mem}function _glPixelStorei(pname,param){if(pname==3317){GL.unpackAlignment=param}GLctx.pixelStorei(pname,param)}function _glPolygonOffset(x0,x1){GLctx["polygonOffset"](x0,x1)}function _glReadPixels(x,y,width,height,format,type,pixels){if(true){if(GLctx.currentPixelPackBufferBinding){GLctx.readPixels(x,y,width,height,format,type,pixels)}else{var heap=heapObjectForWebGLType(type);GLctx.readPixels(x,y,width,height,format,type,heap,pixels>>heapAccessShiftForWebGLHeap(heap))}return}var pixelData=emscriptenWebGLGetTexPixelData(type,format,width,height,pixels,format);if(!pixelData){GL.recordError(1280);return}GLctx.readPixels(x,y,width,height,format,type,pixelData)}function _glRenderbufferStorage(x0,x1,x2,x3){GLctx["renderbufferStorage"](x0,x1,x2,x3)}function _glRenderbufferStorageMultisample(x0,x1,x2,x3,x4){GLctx["renderbufferStorageMultisample"](x0,x1,x2,x3,x4)}function _glSamplerParameteri(sampler,pname,param){GLctx["samplerParameteri"](GL.samplers[sampler],pname,param)}function _glScissor(x0,x1,x2,x3){GLctx["scissor"](x0,x1,x2,x3)}function _glShaderSource(shader,count,string,length){var source=GL.getSource(shader,count,string,length);GLctx.shaderSource(GL.shaders[shader],source)}function _glTexParameteri(x0,x1,x2){GLctx["texParameteri"](x0,x1,x2)}function _glTexStorage2D(x0,x1,x2,x3,x4){GLctx["texStorage2D"](x0,x1,x2,x3,x4)}function _glTexStorage3D(x0,x1,x2,x3,x4,x5){GLctx["texStorage3D"](x0,x1,x2,x3,x4,x5)}function _glTexSubImage2D(target,level,xoffset,yoffset,width,height,format,type,pixels){if(true){if(GLctx.currentPixelUnpackBufferBinding){GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,pixels)}else if(pixels){var heap=heapObjectForWebGLType(type);GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,heap,pixels>>heapAccessShiftForWebGLHeap(heap))}else{GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,null)}return}var pixelData=null;if(pixels)pixelData=emscriptenWebGLGetTexPixelData(type,format,width,height,pixels,0);GLctx.texSubImage2D(target,level,xoffset,yoffset,width,height,format,type,pixelData)}function _glTexSubImage3D(target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,pixels){if(GLctx.currentPixelUnpackBufferBinding){GLctx["texSubImage3D"](target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,pixels)}else if(pixels){var heap=heapObjectForWebGLType(type);GLctx["texSubImage3D"](target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,heap,pixels>>heapAccessShiftForWebGLHeap(heap))}else{GLctx["texSubImage3D"](target,level,xoffset,yoffset,zoffset,width,height,depth,format,type,null)}}function _glUniform1i(location,v0){GLctx.uniform1i(GL.uniforms[location],v0)}function _glUniformBlockBinding(program,uniformBlockIndex,uniformBlockBinding){program=GL.programs[program];GLctx["uniformBlockBinding"](program,uniformBlockIndex,uniformBlockBinding)}function _glUnmapBuffer(target){if(!emscriptenWebGLValidateMapBufferTarget(target)){GL.recordError(1280);err("GL_INVALID_ENUM in glUnmapBuffer");return 0}var buffer=emscriptenWebGLGetBufferBinding(target);var mapping=GL.mappedBuffers[buffer];if(!mapping){GL.recordError(1282);err("buffer was never mapped in glUnmapBuffer");return 0}GL.mappedBuffers[buffer]=null;if(!(mapping.access&16))if(true){GLctx.bufferSubData(target,mapping.offset,HEAPU8,mapping.mem,mapping.length)}else{GLctx.bufferSubData(target,mapping.offset,HEAPU8.subarray(mapping.mem,mapping.mem+mapping.length))}_free(mapping.mem);return 1}function _glUseProgram(program){GLctx.useProgram(GL.programs[program])}function _glVertexAttrib4f(x0,x1,x2,x3,x4){GLctx["vertexAttrib4f"](x0,x1,x2,x3,x4)}function _glVertexAttribI4ui(x0,x1,x2,x3,x4){GLctx["vertexAttribI4ui"](x0,x1,x2,x3,x4)}function _glVertexAttribIPointer(index,size,type,stride,ptr){var cb=GL.currentContext.clientBuffers[index];if(!GLctx.currentArrayBufferBinding){cb.size=size;cb.type=type;cb.normalized=false;cb.stride=stride;cb.ptr=ptr;cb.clientside=true;cb.vertexAttribPointerAdaptor=function(index,size,type,normalized,stride,ptr){this.vertexAttribIPointer(index,size,type,stride,ptr)};return}cb.clientside=false;GLctx["vertexAttribIPointer"](index,size,type,stride,ptr)}function _glVertexAttribPointer(index,size,type,normalized,stride,ptr){var cb=GL.currentContext.clientBuffers[index];if(!GLctx.currentArrayBufferBinding){cb.size=size;cb.type=type;cb.normalized=normalized;cb.stride=stride;cb.ptr=ptr;cb.clientside=true;cb.vertexAttribPointerAdaptor=function(index,size,type,normalized,stride,ptr){this.vertexAttribPointer(index,size,type,normalized,stride,ptr)};return}cb.clientside=false;GLctx.vertexAttribPointer(index,size,type,!!normalized,stride,ptr)}function _glViewport(x0,x1,x2,x3){GLctx["viewport"](x0,x1,x2,x3)}function _glWaitSync(sync,flags,timeoutLo,timeoutHi){GLctx.waitSync(GL.syncs[sync],flags,convertI32PairToI53(timeoutLo,timeoutHi))}function _pthread_create(){return 6}function _pthread_join(){return 28}function _setTempRet0($i){setTempRet0($i|0)}function __isLeapYear(year){return year%4===0&&(year%100!==0||year%400===0)}function __arraySum(array,index){var sum=0;for(var i=0;i<=index;sum+=array[i++]){}return sum}var __MONTH_DAYS_LEAP=[31,29,31,30,31,30,31,31,30,31,30,31];var __MONTH_DAYS_REGULAR=[31,28,31,30,31,30,31,31,30,31,30,31];function __addDays(date,days){var newDate=new Date(date.getTime());while(days>0){var leap=__isLeapYear(newDate.getFullYear());var currentMonth=newDate.getMonth();var daysInCurrentMonth=(leap?__MONTH_DAYS_LEAP:__MONTH_DAYS_REGULAR)[currentMonth];if(days>daysInCurrentMonth-newDate.getDate()){days-=daysInCurrentMonth-newDate.getDate()+1;newDate.setDate(1);if(currentMonth<11){newDate.setMonth(currentMonth+1)}else{newDate.setMonth(0);newDate.setFullYear(newDate.getFullYear()+1)}}else{newDate.setDate(newDate.getDate()+days);return newDate}}return newDate}function _strftime(s,maxsize,format,tm){var tm_zone=HEAP32[tm+40>>2];var date={tm_sec:HEAP32[tm>>2],tm_min:HEAP32[tm+4>>2],tm_hour:HEAP32[tm+8>>2],tm_mday:HEAP32[tm+12>>2],tm_mon:HEAP32[tm+16>>2],tm_year:HEAP32[tm+20>>2],tm_wday:HEAP32[tm+24>>2],tm_yday:HEAP32[tm+28>>2],tm_isdst:HEAP32[tm+32>>2],tm_gmtoff:HEAP32[tm+36>>2],tm_zone:tm_zone?UTF8ToString(tm_zone):""};var pattern=UTF8ToString(format);var EXPANSION_RULES_1={"%c":"%a %b %d %H:%M:%S %Y","%D":"%m/%d/%y","%F":"%Y-%m-%d","%h":"%b","%r":"%I:%M:%S %p","%R":"%H:%M","%T":"%H:%M:%S","%x":"%m/%d/%y","%X":"%H:%M:%S","%Ec":"%c","%EC":"%C","%Ex":"%m/%d/%y","%EX":"%H:%M:%S","%Ey":"%y","%EY":"%Y","%Od":"%d","%Oe":"%e","%OH":"%H","%OI":"%I","%Om":"%m","%OM":"%M","%OS":"%S","%Ou":"%u","%OU":"%U","%OV":"%V","%Ow":"%w","%OW":"%W","%Oy":"%y"};for(var rule in EXPANSION_RULES_1){pattern=pattern.replace(new RegExp(rule,"g"),EXPANSION_RULES_1[rule])}var WEEKDAYS=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];var MONTHS=["January","February","March","April","May","June","July","August","September","October","November","December"];function leadingSomething(value,digits,character){var str=typeof value==="number"?value.toString():value||"";while(str.length0?1:0}var compare;if((compare=sgn(date1.getFullYear()-date2.getFullYear()))===0){if((compare=sgn(date1.getMonth()-date2.getMonth()))===0){compare=sgn(date1.getDate()-date2.getDate())}}return compare}function getFirstWeekStartDate(janFourth){switch(janFourth.getDay()){case 0:return new Date(janFourth.getFullYear()-1,11,29);case 1:return janFourth;case 2:return new Date(janFourth.getFullYear(),0,3);case 3:return new Date(janFourth.getFullYear(),0,2);case 4:return new Date(janFourth.getFullYear(),0,1);case 5:return new Date(janFourth.getFullYear()-1,11,31);case 6:return new Date(janFourth.getFullYear()-1,11,30)}}function getWeekBasedYear(date){var thisDate=__addDays(new Date(date.tm_year+1900,0,1),date.tm_yday);var janFourthThisYear=new Date(thisDate.getFullYear(),0,4);var janFourthNextYear=new Date(thisDate.getFullYear()+1,0,4);var firstWeekStartThisYear=getFirstWeekStartDate(janFourthThisYear);var firstWeekStartNextYear=getFirstWeekStartDate(janFourthNextYear);if(compareByDay(firstWeekStartThisYear,thisDate)<=0){if(compareByDay(firstWeekStartNextYear,thisDate)<=0){return thisDate.getFullYear()+1}else{return thisDate.getFullYear()}}else{return thisDate.getFullYear()-1}}var EXPANSION_RULES_2={"%a":function(date){return WEEKDAYS[date.tm_wday].substring(0,3)},"%A":function(date){return WEEKDAYS[date.tm_wday]},"%b":function(date){return MONTHS[date.tm_mon].substring(0,3)},"%B":function(date){return MONTHS[date.tm_mon]},"%C":function(date){var year=date.tm_year+1900;return leadingNulls(year/100|0,2)},"%d":function(date){return leadingNulls(date.tm_mday,2)},"%e":function(date){return leadingSomething(date.tm_mday,2," ")},"%g":function(date){return getWeekBasedYear(date).toString().substring(2)},"%G":function(date){return getWeekBasedYear(date)},"%H":function(date){return leadingNulls(date.tm_hour,2)},"%I":function(date){var twelveHour=date.tm_hour;if(twelveHour==0)twelveHour=12;else if(twelveHour>12)twelveHour-=12;return leadingNulls(twelveHour,2)},"%j":function(date){return leadingNulls(date.tm_mday+__arraySum(__isLeapYear(date.tm_year+1900)?__MONTH_DAYS_LEAP:__MONTH_DAYS_REGULAR,date.tm_mon-1),3)},"%m":function(date){return leadingNulls(date.tm_mon+1,2)},"%M":function(date){return leadingNulls(date.tm_min,2)},"%n":function(){return"\n"},"%p":function(date){if(date.tm_hour>=0&&date.tm_hour<12){return"AM"}else{return"PM"}},"%S":function(date){return leadingNulls(date.tm_sec,2)},"%t":function(){return"\t"},"%u":function(date){return date.tm_wday||7},"%U":function(date){var janFirst=new Date(date.tm_year+1900,0,1);var firstSunday=janFirst.getDay()===0?janFirst:__addDays(janFirst,7-janFirst.getDay());var endDate=new Date(date.tm_year+1900,date.tm_mon,date.tm_mday);if(compareByDay(firstSunday,endDate)<0){var februaryFirstUntilEndMonth=__arraySum(__isLeapYear(endDate.getFullYear())?__MONTH_DAYS_LEAP:__MONTH_DAYS_REGULAR,endDate.getMonth()-1)-31;var firstSundayUntilEndJanuary=31-firstSunday.getDate();var days=firstSundayUntilEndJanuary+februaryFirstUntilEndMonth+endDate.getDate();return leadingNulls(Math.ceil(days/7),2)}return compareByDay(firstSunday,janFirst)===0?"01":"00"},"%V":function(date){var janFourthThisYear=new Date(date.tm_year+1900,0,4);var janFourthNextYear=new Date(date.tm_year+1901,0,4);var firstWeekStartThisYear=getFirstWeekStartDate(janFourthThisYear);var firstWeekStartNextYear=getFirstWeekStartDate(janFourthNextYear);var endDate=__addDays(new Date(date.tm_year+1900,0,1),date.tm_yday);if(compareByDay(endDate,firstWeekStartThisYear)<0){return"53"}if(compareByDay(firstWeekStartNextYear,endDate)<=0){return"01"}var daysDifference;if(firstWeekStartThisYear.getFullYear()=0;off=Math.abs(off)/60;off=off/60*100+off%60;return(ahead?"+":"-")+String("0000"+off).slice(-4)},"%Z":function(date){return date.tm_zone},"%%":function(){return"%"}};for(var rule in EXPANSION_RULES_2){if(pattern.indexOf(rule)>=0){pattern=pattern.replace(new RegExp(rule,"g"),EXPANSION_RULES_2[rule](date))}}var bytes=intArrayFromString(pattern,false);if(bytes.length>maxsize){return 0}writeArrayToMemory(bytes,s);return bytes.length-1}function _strftime_l(s,maxsize,format,tm){return _strftime(s,maxsize,format,tm)}function _sysconf(name){switch(name){case 30:return 16384;case 85:var maxHeapSize=2147483648;return maxHeapSize/16384;case 132:case 133:case 12:case 137:case 138:case 15:case 235:case 16:case 17:case 18:case 19:case 20:case 149:case 13:case 10:case 236:case 153:case 9:case 21:case 22:case 159:case 154:case 14:case 77:case 78:case 139:case 82:case 68:case 67:case 164:case 11:case 29:case 47:case 48:case 95:case 52:case 51:case 46:return 200809;case 27:case 246:case 127:case 128:case 23:case 24:case 160:case 161:case 181:case 182:case 242:case 183:case 184:case 243:case 244:case 245:case 165:case 178:case 179:case 49:case 50:case 168:case 169:case 175:case 170:case 171:case 172:case 97:case 76:case 32:case 173:case 35:case 80:case 81:case 79:return-1;case 176:case 177:case 7:case 155:case 8:case 157:case 125:case 126:case 92:case 93:case 129:case 130:case 131:case 94:case 91:return 1;case 74:case 60:case 69:case 70:case 4:return 1024;case 31:case 42:case 72:return 32;case 87:case 26:case 33:return 2147483647;case 34:case 1:return 47839;case 38:case 36:return 99;case 43:case 37:return 2048;case 0:return 2097152;case 3:return 65536;case 28:return 32768;case 44:return 32767;case 75:return 16384;case 39:return 1e3;case 89:return 700;case 71:return 256;case 40:return 255;case 2:return 100;case 180:return 64;case 25:return 20;case 5:return 16;case 6:return 6;case 73:return 4;case 84:{if(typeof navigator==="object")return navigator["hardwareConcurrency"]||1;return 1}}setErrNo(28);return-1}var readAsmConstArgsArray=[];function readAsmConstArgs(sigPtr,buf){readAsmConstArgsArray.length=0;var ch;buf>>=2;while(ch=HEAPU8[sigPtr++]){var double=ch<105;if(double&&buf&1)buf++;readAsmConstArgsArray.push(double?HEAPF64[buf++>>1]:HEAP32[buf]);++buf}return readAsmConstArgsArray}var FSNode=function(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.mounted=null;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.node_ops={};this.stream_ops={};this.rdev=rdev};var readMode=292|73;var writeMode=146;Object.defineProperties(FSNode.prototype,{read:{get:function(){return(this.mode&readMode)===readMode},set:function(val){val?this.mode|=readMode:this.mode&=~readMode}},write:{get:function(){return(this.mode&writeMode)===writeMode},set:function(val){val?this.mode|=writeMode:this.mode&=~writeMode}},isFolder:{get:function(){return FS.isDir(this.mode)}},isDevice:{get:function(){return FS.isChrdev(this.mode)}}});FS.FSNode=FSNode;FS.staticInit();InternalError=Module["InternalError"]=extendError(Error,"InternalError");embind_init_charCodes();BindingError=Module["BindingError"]=extendError(Error,"BindingError");init_ClassHandle();init_RegisteredPointer();init_embind();UnboundTypeError=Module["UnboundTypeError"]=extendError(Error,"UnboundTypeError");init_emval();var GLctx;for(var i=0;i<32;++i)tempFixedLengthArray.push(new Array(i));function intArrayFromString(stringy,dontAddNull,length){var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array}var asmLibraryArg={"Ga":___sys_fcntl64,"Bg":___sys_ioctl,"Cg":___sys_open,"v":__embind_finalize_value_array,"A":__embind_finalize_value_object,"Fg":__embind_register_bool,"e":__embind_register_class,"k":__embind_register_class_class_function,"n":__embind_register_class_constructor,"a":__embind_register_class_function,"u":__embind_register_class_property,"Eg":__embind_register_emval,"i":__embind_register_enum,"b":__embind_register_enum_value,"Ia":__embind_register_float,"Ka":__embind_register_function,"D":__embind_register_integer,"y":__embind_register_memory_view,"Ja":__embind_register_std_string,"la":__embind_register_std_wstring,"x":__embind_register_value_array,"f":__embind_register_value_array_element,"B":__embind_register_value_object,"d":__embind_register_value_object_field,"Gg":__embind_register_void,"q":__emval_as,"g":__emval_decref,"r":__emval_get_property,"ja":__emval_incref,"H":__emval_new_cstring,"p":__emval_run_destructors,"s":__emval_take_value,"c":_abort,"ug":_clock_gettime,"rg":_emscripten_asm_const_int,"Zf":_emscripten_glActiveTexture,"Yf":_emscripten_glAttachShader,"$c":_emscripten_glBeginQuery,"ng":_emscripten_glBeginQueryEXT,"Dc":_emscripten_glBeginTransformFeedback,"Xf":_emscripten_glBindAttribLocation,"Wf":_emscripten_glBindBuffer,"Ac":_emscripten_glBindBufferBase,"Bc":_emscripten_glBindBufferRange,"Vf":_emscripten_glBindFramebuffer,"Uf":_emscripten_glBindRenderbuffer,"Hb":_emscripten_glBindSampler,"Tf":_emscripten_glBindTexture,"zb":_emscripten_glBindTransformFeedback,"Ic":_emscripten_glBindVertexArray,"fg":_emscripten_glBindVertexArrayOES,"Sf":_emscripten_glBlendColor,"Rf":_emscripten_glBlendEquation,"Qf":_emscripten_glBlendEquationSeparate,"Pf":_emscripten_glBlendFunc,"Of":_emscripten_glBlendFuncSeparate,"Oc":_emscripten_glBlitFramebuffer,"Nf":_emscripten_glBufferData,"Mf":_emscripten_glBufferSubData,"Lf":_emscripten_glCheckFramebufferStatus,"Kf":_emscripten_glClear,"dc":_emscripten_glClearBufferfi,"ec":_emscripten_glClearBufferfv,"gc":_emscripten_glClearBufferiv,"fc":_emscripten_glClearBufferuiv,"Jf":_emscripten_glClearColor,"If":_emscripten_glClearDepthf,"Hf":_emscripten_glClearStencil,"Rb":_emscripten_glClientWaitSync,"Gf":_emscripten_glColorMask,"Ff":_emscripten_glCompileShader,"Ef":_emscripten_glCompressedTexImage2D,"ed":_emscripten_glCompressedTexImage3D,"Df":_emscripten_glCompressedTexSubImage2D,"dd":_emscripten_glCompressedTexSubImage3D,"bc":_emscripten_glCopyBufferSubData,"Cf":_emscripten_glCopyTexImage2D,"Bf":_emscripten_glCopyTexSubImage2D,"gd":_emscripten_glCopyTexSubImage3D,"Af":_emscripten_glCreateProgram,"zf":_emscripten_glCreateShader,"yf":_emscripten_glCullFace,"xf":_emscripten_glDeleteBuffers,"wf":_emscripten_glDeleteFramebuffers,"vf":_emscripten_glDeleteProgram,"bd":_emscripten_glDeleteQueries,"pg":_emscripten_glDeleteQueriesEXT,"uf":_emscripten_glDeleteRenderbuffers,"Jb":_emscripten_glDeleteSamplers,"tf":_emscripten_glDeleteShader,"Sb":_emscripten_glDeleteSync,"sf":_emscripten_glDeleteTextures,"yb":_emscripten_glDeleteTransformFeedbacks,"Hc":_emscripten_glDeleteVertexArrays,"eg":_emscripten_glDeleteVertexArraysOES,"rf":_emscripten_glDepthFunc,"qf":_emscripten_glDepthMask,"pf":_emscripten_glDepthRangef,"of":_emscripten_glDetachShader,"nf":_emscripten_glDisable,"mf":_emscripten_glDisableVertexAttribArray,"lf":_emscripten_glDrawArrays,"Wb":_emscripten_glDrawArraysInstanced,"ag":_emscripten_glDrawArraysInstancedANGLE,"ib":_emscripten_glDrawArraysInstancedARB,"nd":_emscripten_glDrawArraysInstancedEXT,"jb":_emscripten_glDrawArraysInstancedNV,"Vc":_emscripten_glDrawBuffers,"ld":_emscripten_glDrawBuffersEXT,"bg":_emscripten_glDrawBuffersWEBGL,"kf":_emscripten_glDrawElements,"Vb":_emscripten_glDrawElementsInstanced,"$f":_emscripten_glDrawElementsInstancedANGLE,"gb":_emscripten_glDrawElementsInstancedARB,"hb":_emscripten_glDrawElementsInstancedEXT,"md":_emscripten_glDrawElementsInstancedNV,"jd":_emscripten_glDrawRangeElements,"jf":_emscripten_glEnable,"hf":_emscripten_glEnableVertexAttribArray,"_c":_emscripten_glEndQuery,"mg":_emscripten_glEndQueryEXT,"Cc":_emscripten_glEndTransformFeedback,"Ub":_emscripten_glFenceSync,"gf":_emscripten_glFinish,"ff":_emscripten_glFlush,"Jc":_emscripten_glFlushMappedBufferRange,"ef":_emscripten_glFramebufferRenderbuffer,"df":_emscripten_glFramebufferTexture2D,"Lc":_emscripten_glFramebufferTextureLayer,"cf":_emscripten_glFrontFace,"bf":_emscripten_glGenBuffers,"$e":_emscripten_glGenFramebuffers,"cd":_emscripten_glGenQueries,"qg":_emscripten_glGenQueriesEXT,"_e":_emscripten_glGenRenderbuffers,"Kb":_emscripten_glGenSamplers,"Ze":_emscripten_glGenTextures,"xb":_emscripten_glGenTransformFeedbacks,"Gc":_emscripten_glGenVertexArrays,"dg":_emscripten_glGenVertexArraysOES,"af":_emscripten_glGenerateMipmap,"Ye":_emscripten_glGetActiveAttrib,"Xe":_emscripten_glGetActiveUniform,"Yb":_emscripten_glGetActiveUniformBlockName,"Zb":_emscripten_glGetActiveUniformBlockiv,"$b":_emscripten_glGetActiveUniformsiv,"We":_emscripten_glGetAttachedShaders,"Ve":_emscripten_glGetAttribLocation,"Ue":_emscripten_glGetBooleanv,"Lb":_emscripten_glGetBufferParameteri64v,"Te":_emscripten_glGetBufferParameteriv,"Wc":_emscripten_glGetBufferPointerv,"Se":_emscripten_glGetError,"Re":_emscripten_glGetFloatv,"pc":_emscripten_glGetFragDataLocation,"Qe":_emscripten_glGetFramebufferAttachmentParameteriv,"Mb":_emscripten_glGetInteger64i_v,"Ob":_emscripten_glGetInteger64v,"Ec":_emscripten_glGetIntegeri_v,"Pe":_emscripten_glGetIntegerv,"mb":_emscripten_glGetInternalformativ,"tb":_emscripten_glGetProgramBinary,"Ne":_emscripten_glGetProgramInfoLog,"Oe":_emscripten_glGetProgramiv,"hg":_emscripten_glGetQueryObjecti64vEXT,"jg":_emscripten_glGetQueryObjectivEXT,"gg":_emscripten_glGetQueryObjectui64vEXT,"Yc":_emscripten_glGetQueryObjectuiv,"ig":_emscripten_glGetQueryObjectuivEXT,"Zc":_emscripten_glGetQueryiv,"kg":_emscripten_glGetQueryivEXT,"Me":_emscripten_glGetRenderbufferParameteriv,"Bb":_emscripten_glGetSamplerParameterfv,"Cb":_emscripten_glGetSamplerParameteriv,"Ke":_emscripten_glGetShaderInfoLog,"Je":_emscripten_glGetShaderPrecisionFormat,"Ie":_emscripten_glGetShaderSource,"Le":_emscripten_glGetShaderiv,"He":_emscripten_glGetString,"cc":_emscripten_glGetStringi,"Nb":_emscripten_glGetSynciv,"Ge":_emscripten_glGetTexParameterfv,"Fe":_emscripten_glGetTexParameteriv,"yc":_emscripten_glGetTransformFeedbackVarying,"_b":_emscripten_glGetUniformBlockIndex,"ac":_emscripten_glGetUniformIndices,"Ce":_emscripten_glGetUniformLocation,"Ee":_emscripten_glGetUniformfv,"De":_emscripten_glGetUniformiv,"qc":_emscripten_glGetUniformuiv,"wc":_emscripten_glGetVertexAttribIiv,"vc":_emscripten_glGetVertexAttribIuiv,"ze":_emscripten_glGetVertexAttribPointerv,"Be":_emscripten_glGetVertexAttribfv,"Ae":_emscripten_glGetVertexAttribiv,"ye":_emscripten_glHint,"qb":_emscripten_glInvalidateFramebuffer,"pb":_emscripten_glInvalidateSubFramebuffer,"xe":_emscripten_glIsBuffer,"we":_emscripten_glIsEnabled,"ve":_emscripten_glIsFramebuffer,"ue":_emscripten_glIsProgram,"ad":_emscripten_glIsQuery,"og":_emscripten_glIsQueryEXT,"te":_emscripten_glIsRenderbuffer,"Ib":_emscripten_glIsSampler,"se":_emscripten_glIsShader,"Tb":_emscripten_glIsSync,"re":_emscripten_glIsTexture,"wb":_emscripten_glIsTransformFeedback,"Fc":_emscripten_glIsVertexArray,"cg":_emscripten_glIsVertexArrayOES,"qe":_emscripten_glLineWidth,"pe":_emscripten_glLinkProgram,"Kc":_emscripten_glMapBufferRange,"vb":_emscripten_glPauseTransformFeedback,"oe":_emscripten_glPixelStorei,"ne":_emscripten_glPolygonOffset,"sb":_emscripten_glProgramBinary,"rb":_emscripten_glProgramParameteri,"lg":_emscripten_glQueryCounterEXT,"kd":_emscripten_glReadBuffer,"me":_emscripten_glReadPixels,"le":_emscripten_glReleaseShaderCompiler,"ke":_emscripten_glRenderbufferStorage,"Nc":_emscripten_glRenderbufferStorageMultisample,"ub":_emscripten_glResumeTransformFeedback,"je":_emscripten_glSampleCoverage,"Eb":_emscripten_glSamplerParameterf,"Db":_emscripten_glSamplerParameterfv,"Gb":_emscripten_glSamplerParameteri,"Fb":_emscripten_glSamplerParameteriv,"ie":_emscripten_glScissor,"he":_emscripten_glShaderBinary,"ge":_emscripten_glShaderSource,"fe":_emscripten_glStencilFunc,"ee":_emscripten_glStencilFuncSeparate,"de":_emscripten_glStencilMask,"ce":_emscripten_glStencilMaskSeparate,"be":_emscripten_glStencilOp,"ae":_emscripten_glStencilOpSeparate,"$d":_emscripten_glTexImage2D,"id":_emscripten_glTexImage3D,"_d":_emscripten_glTexParameterf,"Zd":_emscripten_glTexParameterfv,"Yd":_emscripten_glTexParameteri,"Xd":_emscripten_glTexParameteriv,"ob":_emscripten_glTexStorage2D,"nb":_emscripten_glTexStorage3D,"Wd":_emscripten_glTexSubImage2D,"hd":_emscripten_glTexSubImage3D,"zc":_emscripten_glTransformFeedbackVaryings,"Vd":_emscripten_glUniform1f,"Ud":_emscripten_glUniform1fv,"Td":_emscripten_glUniform1i,"Sd":_emscripten_glUniform1iv,"oc":_emscripten_glUniform1ui,"kc":_emscripten_glUniform1uiv,"Rd":_emscripten_glUniform2f,"Qd":_emscripten_glUniform2fv,"Pd":_emscripten_glUniform2i,"Od":_emscripten_glUniform2iv,"nc":_emscripten_glUniform2ui,"jc":_emscripten_glUniform2uiv,"Nd":_emscripten_glUniform3f,"Md":_emscripten_glUniform3fv,"Kd":_emscripten_glUniform3i,"Jd":_emscripten_glUniform3iv,"mc":_emscripten_glUniform3ui,"ic":_emscripten_glUniform3uiv,"Id":_emscripten_glUniform4f,"Hd":_emscripten_glUniform4fv,"Gd":_emscripten_glUniform4i,"Fd":_emscripten_glUniform4iv,"lc":_emscripten_glUniform4ui,"hc":_emscripten_glUniform4uiv,"Xb":_emscripten_glUniformBlockBinding,"Ed":_emscripten_glUniformMatrix2fv,"Uc":_emscripten_glUniformMatrix2x3fv,"Sc":_emscripten_glUniformMatrix2x4fv,"Dd":_emscripten_glUniformMatrix3fv,"Tc":_emscripten_glUniformMatrix3x2fv,"Qc":_emscripten_glUniformMatrix3x4fv,"Cd":_emscripten_glUniformMatrix4fv,"Rc":_emscripten_glUniformMatrix4x2fv,"Pc":_emscripten_glUniformMatrix4x3fv,"Xc":_emscripten_glUnmapBuffer,"Bd":_emscripten_glUseProgram,"Ad":_emscripten_glValidateProgram,"zd":_emscripten_glVertexAttrib1f,"yd":_emscripten_glVertexAttrib1fv,"xd":_emscripten_glVertexAttrib2f,"wd":_emscripten_glVertexAttrib2fv,"vd":_emscripten_glVertexAttrib3f,"ud":_emscripten_glVertexAttrib3fv,"td":_emscripten_glVertexAttrib4f,"sd":_emscripten_glVertexAttrib4fv,"Ab":_emscripten_glVertexAttribDivisor,"_f":_emscripten_glVertexAttribDivisorANGLE,"kb":_emscripten_glVertexAttribDivisorARB,"od":_emscripten_glVertexAttribDivisorEXT,"lb":_emscripten_glVertexAttribDivisorNV,"uc":_emscripten_glVertexAttribI4i,"sc":_emscripten_glVertexAttribI4iv,"tc":_emscripten_glVertexAttribI4ui,"rc":_emscripten_glVertexAttribI4uiv,"xc":_emscripten_glVertexAttribIPointer,"rd":_emscripten_glVertexAttribPointer,"pd":_emscripten_glViewport,"Qb":_emscripten_glWaitSync,"sg":_emscripten_memcpy_big,"tg":_emscripten_resize_heap,"xg":_environ_get,"yg":_environ_sizes_get,"ka":_fd_close,"Dg":_fd_read,"db":_fd_seek,"Ha":_fd_write,"vg":_getentropy,"h":_glActiveTexture,"V":_glAttachShader,"Yg":_glBeginQuery,"w":_glBindBuffer,"ua":_glBindBufferRange,"m":_glBindFramebuffer,"I":_glBindRenderbuffer,"W":_glBindSampler,"l":_glBindTexture,"G":_glBindVertexArray,"Ug":_glBlendEquationSeparate,"Tg":_glBlendFuncSeparate,"sa":_glBlitFramebuffer,"K":_glBufferData,"T":_glBufferSubData,"Kg":_glClearBufferfi,"P":_glClearBufferfv,"Jg":_glClearBufferiv,"eb":_glClientWaitSync,"Y":_glColorMask,"da":_glCompileShader,"Ma":_glCompressedTexSubImage2D,"La":_glCompressedTexSubImage3D,"Aa":_glCreateProgram,"fa":_glCreateShader,"ma":_glCullFace,"Ca":_glDeleteBuffers,"U":_glDeleteFramebuffers,"ba":_glDeleteProgram,"Ya":_glDeleteQueries,"E":_glDeleteRenderbuffers,"qa":_glDeleteSamplers,"N":_glDeleteShader,"F":_glDeleteSync,"L":_glDeleteTextures,"Pb":_glDeleteVertexArrays,"Sg":_glDepthFunc,"Rg":_glDepthMask,"Wa":_glDepthRangef,"aa":_glDetachShader,"j":_glDisable,"na":_glDisableVertexAttribArray,"Ra":_glDrawArrays,"Ta":_glDrawBuffers,"Ua":_glDrawRangeElements,"C":_glEnable,"pa":_glEnableVertexAttribArray,"Xg":_glEndQuery,"$":_glFenceSync,"Da":_glFinish,"Ea":_glFlush,"Q":_glFramebufferRenderbuffer,"R":_glFramebufferTexture2D,"Pa":_glFramebufferTextureLayer,"Vg":_glFrontFace,"O":_glGenBuffers,"S":_glGenFramebuffers,"Za":_glGenQueries,"ia":_glGenRenderbuffers,"ra":_glGenSamplers,"J":_glGenTextures,"cb":_glGenVertexArrays,"Xa":_glGenerateMipmap,"Sa":_glGetError,"qd":_glGetFloatv,"o":_glGetIntegerv,"$a":_glGetProgramInfoLog,"ya":_glGetProgramiv,"Wg":_glGetQueryObjectuiv,"_a":_glGetShaderInfoLog,"ca":_glGetShaderiv,"X":_glGetString,"Ld":_glGetStringi,"bb":_glGetUniformBlockIndex,"xa":_glGetUniformLocation,"Qa":_glGetVertexAttribiv,"fd":_glHint,"va":_glInvalidateFramebuffer,"Z":_glIsEnabled,"za":_glLinkProgram,"Ig":_glMapBufferRange,"Mc":_glPixelStorei,"Va":_glPolygonOffset,"ta":_glReadPixels,"ga":_glRenderbufferStorage,"ha":_glRenderbufferStorageMultisample,"t":_glSamplerParameteri,"Lg":_glScissor,"ea":_glShaderSource,"z":_glTexParameteri,"Qg":_glTexStorage2D,"Pg":_glTexStorage3D,"Oa":_glTexSubImage2D,"Na":_glTexSubImage3D,"wa":_glUniform1i,"ab":_glUniformBlockBinding,"Hg":_glUnmapBuffer,"M":_glUseProgram,"Mg":_glVertexAttrib4f,"Ng":_glVertexAttribI4ui,"Og":_glVertexAttribIPointer,"oa":_glVertexAttribPointer,"_":_glViewport,"fb":_glWaitSync,"Fa":_pthread_create,"Ag":_pthread_join,"Ba":_setTempRet0,"wg":_strftime_l,"zg":_sysconf};var asm=createWasm();var ___wasm_call_ctors=Module["___wasm_call_ctors"]=function(){return(___wasm_call_ctors=Module["___wasm_call_ctors"]=Module["asm"]["$g"]).apply(null,arguments)};var _free=Module["_free"]=function(){return(_free=Module["_free"]=Module["asm"]["ah"]).apply(null,arguments)};var _malloc=Module["_malloc"]=function(){return(_malloc=Module["_malloc"]=Module["asm"]["bh"]).apply(null,arguments)};var ___errno_location=Module["___errno_location"]=function(){return(___errno_location=Module["___errno_location"]=Module["asm"]["ch"]).apply(null,arguments)};var ___getTypeName=Module["___getTypeName"]=function(){return(___getTypeName=Module["___getTypeName"]=Module["asm"]["dh"]).apply(null,arguments)};var ___embind_register_native_and_builtin_types=Module["___embind_register_native_and_builtin_types"]=function(){return(___embind_register_native_and_builtin_types=Module["___embind_register_native_and_builtin_types"]=Module["asm"]["eh"]).apply(null,arguments)};var dynCall_viijii=Module["dynCall_viijii"]=function(){return(dynCall_viijii=Module["dynCall_viijii"]=Module["asm"]["fh"]).apply(null,arguments)};var dynCall_jii=Module["dynCall_jii"]=function(){return(dynCall_jii=Module["dynCall_jii"]=Module["asm"]["gh"]).apply(null,arguments)};var dynCall_iiij=Module["dynCall_iiij"]=function(){return(dynCall_iiij=Module["dynCall_iiij"]=Module["asm"]["hh"]).apply(null,arguments)};var dynCall_vij=Module["dynCall_vij"]=function(){return(dynCall_vij=Module["dynCall_vij"]=Module["asm"]["ih"]).apply(null,arguments)};var dynCall_jiji=Module["dynCall_jiji"]=function(){return(dynCall_jiji=Module["dynCall_jiji"]=Module["asm"]["jh"]).apply(null,arguments)};var dynCall_iiiiij=Module["dynCall_iiiiij"]=function(){return(dynCall_iiiiij=Module["dynCall_iiiiij"]=Module["asm"]["kh"]).apply(null,arguments)};var dynCall_iiiiijj=Module["dynCall_iiiiijj"]=function(){return(dynCall_iiiiijj=Module["dynCall_iiiiijj"]=Module["asm"]["lh"]).apply(null,arguments)};var dynCall_iiiiiijj=Module["dynCall_iiiiiijj"]=function(){return(dynCall_iiiiiijj=Module["dynCall_iiiiiijj"]=Module["asm"]["mh"]).apply(null,arguments)};var calledRun;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(args){args=args||arguments_;if(runDependencies>0){return}preRun();if(runDependencies>0)return;function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();preMain();readyPromiseResolve(Module);if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}noExitRuntime=true;run(); return Filament.ready @@ -441,6 +441,18 @@ Filament.loadClassExtensions = function() { this._setVignetteOptions(options); }; + /// BufferObject ::core class:: + + /// setBuffer ::method:: + /// engine ::argument:: [Engine] + /// buffer ::argument:: asset string, or Uint8Array, or [Buffer] + /// byteOffset ::argument:: non-negative integer + Filament.BufferObject.prototype.setBuffer = function(engine, buffer, byteOffset = 0) { + buffer = getBufferDescriptor(buffer); + this._setBuffer(engine, buffer, byteOffset); + buffer.delete(); + }; + /// VertexBuffer ::core class:: /// setBufferAt ::method:: diff --git a/docs/remote/filament.wasm b/docs/remote/filament.wasm index 4c3d53e6f6..32680cc8f0 100755 Binary files a/docs/remote/filament.wasm and b/docs/remote/filament.wasm differ diff --git a/docs/remote/index.html b/docs/remote/index.html index d9390231be..0488dafcde 100644 --- a/docs/remote/index.html +++ b/docs/remote/index.html @@ -135,7 +135,7 @@ class App { // In the future we might want to draw stuff in the central 3D viewport. const kInitialSidebarWidth = this.canvas.clientWidth * window.devicePixelRatio; - // Clear the central 3D viewport to light gray, whicht is not visible by default. + // Clear the central 3D viewport to light gray, which is not visible by default. const L = 189 / 255; const kBackgroundColor = [L, L, L, 1]; this.renderer.setClearOptions({clearColor: kBackgroundColor, clear: true}); @@ -145,7 +145,7 @@ class App { this.mouseX = -1; this.mouseY = -1; - this.mouseButton = false; + this.mouseButton = null; this.mouseButtonEvents = []; this.mouseWheelY = 0; @@ -155,10 +155,17 @@ class App { this.mouseX = e.offsetX * window.devicePixelRatio; this.mouseY = e.offsetY * window.devicePixelRatio; }); - this.canvas.addEventListener("pointerup", e => this.mouseButtonEvents.push(false)); - this.canvas.addEventListener("pointerdown", e => this.mouseButtonEvents.push(true)); + + this.canvas.addEventListener("pointerup", e => this.mouseButtonEvents.push(null)); + this.canvas.addEventListener("pointerdown", e => this.mouseButtonEvents.push(e)); this.canvas.addEventListener("mousewheel", e => this.mouseWheelY = e.deltaY / 8); + this.canvas.addEventListener("contextmenu", event => event.preventDefault()); + + document.addEventListener("keydown", e => this.simpleViewer.keyDownEvent(e.keyCode)); + document.addEventListener("keyup", e => this.simpleViewer.keyUpEvent(e.keyCode)); + document.addEventListener("keypress", e => this.simpleViewer.keyPressEvent(e.charCode)); + const resize = () => { const dpr = window.devicePixelRatio; const width = this.canvas.clientWidth * dpr; @@ -263,6 +270,8 @@ class App { } const mouseWheel = this.mouseWheelY; this.mouseWheelY = 0; + this.simpleViewer.mouseEvent(this.mouseX, this.mouseY, !!this.mouseButton, + mouseWheel, this.mouseButton && this.mouseButton.ctrlKey); // If there's no connection, let Filament clear the canvas, but do not render the UI view. if (!connection) { @@ -276,8 +285,7 @@ class App { // Draw the UI and potentially mutate the settings object. const deltaTime = 1.0 / 60.0; - this.simpleViewer.renderUserInterface(deltaTime, this.uiview, window.devicePixelRatio, - this.mouseX, this.mouseY, this.mouseButton, mouseWheel); + this.simpleViewer.renderUserInterface(deltaTime, this.uiview, window.devicePixelRatio); // Check if the user has changed any settings. const settingsJson = this.serializer.writeJson(this.simpleViewer.getSettings()).slice(); diff --git a/docs/remote/update.sh b/docs/remote/update.sh new file mode 100755 index 0000000000..f3c3cb9fad --- /dev/null +++ b/docs/remote/update.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +pushd "$(dirname "$0")" > /dev/null + +curl -OL https://nightly.link/google/filament/workflows/web-continuous/main/filament-web.zip +unzip -q filament-web.zip +tar -xvzf filament-release-web.tgz +rm filament-release-web.tgz +rm filament-web.zip +rm filament.d.ts +cp ../../web/samples/remote.html index.html + +popd + +git status +echo "" +echo "All done! Next, make a git commit that updates docs/remote." diff --git a/filament/CMakeLists.txt b/filament/CMakeLists.txt index b36532c30f..5da39dcf42 100644 --- a/filament/CMakeLists.txt +++ b/filament/CMakeLists.txt @@ -13,6 +13,7 @@ set(MATERIAL_DIR "${GENERATION_ROOT}/generated/material") set(PUBLIC_HDRS include/filament/Box.h + include/filament/BufferObject.h include/filament/Camera.h include/filament/Color.h include/filament/ColorGrading.h @@ -44,6 +45,7 @@ set(PUBLIC_HDRS set(SRCS src/Box.cpp + src/BufferObject.cpp src/Camera.cpp src/Color.cpp src/ColorGrading.cpp @@ -112,6 +114,7 @@ set(PRIVATE_HDRS src/components/RenderableManager.h src/components/TransformManager.h src/details/Allocators.h + src/details/BufferObject.h src/details/Camera.h src/details/ColorGrading.h src/details/Culler.h @@ -163,6 +166,7 @@ set(MATERIAL_SRCS src/materials/dof/dofDownsample.mat src/materials/dof/dofCombine.mat src/materials/dof/dofTiles.mat + src/materials/dof/dofTilesSwizzle.mat src/materials/dof/dofDilate.mat src/materials/dof/dofMipmap.mat src/materials/dof/dofMedian.mat @@ -190,6 +194,11 @@ list(APPEND SRCS ${RESGEN_SOURCE}) # Configuration # ================================================================================================== +# whether we're building for mobile -- this can affect some default quality settings +if (IS_MOBILE_TARGET) + add_definitions(-DFILAMENT_TARGET_MOBILE=1) +endif() + # Size of the DFG lookup table if (NOT DFG_LUT_SIZE) if (IS_MOBILE_TARGET) diff --git a/filament/backend/include/backend/DriverEnums.h b/filament/backend/include/backend/DriverEnums.h index d116ffa750..791becb8ba 100644 --- a/filament/backend/include/backend/DriverEnums.h +++ b/filament/backend/include/backend/DriverEnums.h @@ -263,6 +263,11 @@ enum class ElementType : uint8_t { HALF4, }; +//! Buffer object binding type +enum class BufferObjectBinding : uint8_t { + VERTEX, +}; + //! Face culling Mode enum class CullingMode : uint8_t { NONE, //!< No culling, front and back faces are visible @@ -513,7 +518,7 @@ enum class TextureUsage : uint8_t { }; //! Texture swizzle -enum class TextureSwizzle { +enum class TextureSwizzle : uint8_t { SUBSTITUTE_ZERO, SUBSTITUTE_ONE, CHANNEL_0, diff --git a/filament/backend/include/backend/Handle.h b/filament/backend/include/backend/Handle.h index ae46710f10..62572c728c 100644 --- a/filament/backend/include/backend/Handle.h +++ b/filament/backend/include/backend/Handle.h @@ -24,6 +24,7 @@ namespace filament { namespace backend { +struct HwBufferObject; struct HwFence; struct HwIndexBuffer; struct HwProgram; @@ -96,6 +97,7 @@ private: // Types used by the command stream // (we use this renaming because the macro-system doesn't deal well with "<" and ">") +using BufferObjectHandle = Handle; using FenceHandle = Handle; using IndexBufferHandle = Handle; using ProgramHandle = Handle; diff --git a/filament/backend/include/private/backend/DriverAPI.inc b/filament/backend/include/private/backend/DriverAPI.inc index 3ff09acde1..1cb402eee2 100644 --- a/filament/backend/include/private/backend/DriverAPI.inc +++ b/filament/backend/include/private/backend/DriverAPI.inc @@ -168,13 +168,18 @@ DECL_DRIVER_API_R_N(backend::VertexBufferHandle, createVertexBuffer, uint8_t, attributeCount, uint32_t, vertexCount, backend::AttributeArray, attributes, - backend::BufferUsage, usage) + backend::BufferUsage, usage, + bool, bufferObjectsEnabled) DECL_DRIVER_API_R_N(backend::IndexBufferHandle, createIndexBuffer, backend::ElementType, elementType, uint32_t, indexCount, backend::BufferUsage, usage) +DECL_DRIVER_API_R_N(backend::BufferObjectHandle, createBufferObject, + uint32_t, byteCount, + backend::BufferObjectBinding, bindingType) + DECL_DRIVER_API_R_N(backend::TextureHandle, createTexture, backend::SamplerType, target, uint8_t, levels, @@ -261,6 +266,7 @@ DECL_DRIVER_API_R_0(backend::TimerQueryHandle, createTimerQuery) DECL_DRIVER_API_N(destroyVertexBuffer, backend::VertexBufferHandle, vbh) DECL_DRIVER_API_N(destroyIndexBuffer, backend::IndexBufferHandle, ibh) +DECL_DRIVER_API_N(destroyBufferObject, backend::BufferObjectHandle, ibh) DECL_DRIVER_API_N(destroyRenderPrimitive, backend::RenderPrimitiveHandle, rph) DECL_DRIVER_API_N(destroyProgram, backend::ProgramHandle, ph) DECL_DRIVER_API_N(destroySamplerGroup, backend::SamplerGroupHandle, sbh) @@ -287,6 +293,7 @@ DECL_DRIVER_API_SYNCHRONOUS_N(void, updateStreams, backend::DriverApi*, driver) DECL_DRIVER_API_SYNCHRONOUS_N(void, destroyFence, backend::FenceHandle, fh) DECL_DRIVER_API_SYNCHRONOUS_N(backend::FenceStatus, wait, backend::FenceHandle, fh, uint64_t, timeout) DECL_DRIVER_API_SYNCHRONOUS_N(bool, isTextureFormatSupported, backend::TextureFormat, format) +DECL_DRIVER_API_SYNCHRONOUS_0(bool, isTextureSwizzleSupported) DECL_DRIVER_API_SYNCHRONOUS_N(bool, isTextureFormatMipmappable, backend::TextureFormat, format) DECL_DRIVER_API_SYNCHRONOUS_N(bool, isRenderTargetFormatSupported, backend::TextureFormat, format) DECL_DRIVER_API_SYNCHRONOUS_0(bool, isFrameBufferFetchSupported) @@ -310,11 +317,21 @@ DECL_DRIVER_API_N(updateVertexBuffer, backend::BufferDescriptor&&, data, uint32_t, byteOffset) +DECL_DRIVER_API_N(setVertexBufferObject, + backend::VertexBufferHandle, vbh, + size_t, index, + backend::BufferObjectHandle, bufferObject) + DECL_DRIVER_API_N(updateIndexBuffer, backend::IndexBufferHandle, ibh, backend::BufferDescriptor&&, data, uint32_t, byteOffset) +DECL_DRIVER_API_N(updateBufferObject, + backend::BufferObjectHandle, ibh, + backend::BufferDescriptor&&, data, + uint32_t, byteOffset) + DECL_DRIVER_API_N(loadUniformBuffer, backend::UniformBufferHandle, ubh, backend::BufferDescriptor&&, buffer) diff --git a/filament/backend/src/CommandStream.cpp b/filament/backend/src/CommandStream.cpp index 9b11772196..f701b733b4 100644 --- a/filament/backend/src/CommandStream.cpp +++ b/filament/backend/src/CommandStream.cpp @@ -606,6 +606,13 @@ io::ostream& operator<<(io::ostream& out, RenderPassParams const& params) { return out; } +io::ostream& operator<<(io::ostream& out, BufferObjectBinding wrap) { + switch (wrap) { + CASE(BufferObjectBinding, VERTEX) + } + return out; +} + #undef CASE #endif // !NDEBUG diff --git a/filament/backend/src/DriverBase.h b/filament/backend/src/DriverBase.h index 5cedddc76e..75017f7a35 100644 --- a/filament/backend/src/DriverBase.h +++ b/filament/backend/src/DriverBase.h @@ -55,18 +55,22 @@ struct HwVertexBuffer : public HwBase { uint32_t vertexCount{}; // 4 uint8_t bufferCount{}; // 1 uint8_t attributeCount{}; // 1 - uint8_t padding[2]{}; // 2 -> total struct is 136 bytes + bool bufferObjectsEnabled{}; // 1 + uint8_t bufferObjectsVersion{}; // 1 -> total struct is 136 bytes HwVertexBuffer() noexcept = default; HwVertexBuffer(uint8_t bufferCount, uint8_t attributeCount, uint32_t elementCount, - AttributeArray const& attributes) noexcept + AttributeArray const& attributes, bool bufferObjectsEnabled) noexcept : attributes(attributes), vertexCount(elementCount), bufferCount(bufferCount), - attributeCount(attributeCount) { + attributeCount(attributeCount), + bufferObjectsEnabled(bufferObjectsEnabled) { } }; +struct HwBufferObject : public HwBase {}; + struct HwIndexBuffer : public HwBase { uint32_t count{}; uint8_t elementSize{}; diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index 19e40c1039..84d326750e 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -160,10 +160,10 @@ void MetalDriver::finish(int) { void MetalDriver::createVertexBufferR(Handle vbh, uint8_t bufferCount, uint8_t attributeCount, uint32_t vertexCount, AttributeArray attributes, - BufferUsage usage) { + BufferUsage usage, bool bufferObjectsEnabled) { // TODO: Take BufferUsage into account when creating the buffer. construct_handle(mHandleMap, vbh, *mContext, bufferCount, - attributeCount, vertexCount, attributes); + attributeCount, vertexCount, attributes, bufferObjectsEnabled); } void MetalDriver::createIndexBufferR(Handle ibh, ElementType elementType, @@ -172,11 +172,17 @@ void MetalDriver::createIndexBufferR(Handle ibh, ElementType elem construct_handle(mHandleMap, ibh, *mContext, elementSize, indexCount); } +void MetalDriver::createBufferObjectR(Handle boh, uint32_t byteCount, + BufferObjectBinding bindingType) { + construct_handle(mHandleMap, boh, *mContext, byteCount); +} + void MetalDriver::createTextureR(Handle th, SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage) { construct_handle(mHandleMap, th, *mContext, target, levels, format, samples, - width, height, depth, usage); + width, height, depth, usage, TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_1, + TextureSwizzle::CHANNEL_2, TextureSwizzle::CHANNEL_3); } void MetalDriver::createTextureSwizzledR(Handle th, SamplerType target, uint8_t levels, @@ -184,8 +190,7 @@ void MetalDriver::createTextureSwizzledR(Handle th, SamplerType targe uint32_t depth, TextureUsage usage, TextureSwizzle r, TextureSwizzle g, TextureSwizzle b, TextureSwizzle a) { construct_handle(mHandleMap, th, *mContext, target, levels, format, samples, - width, height, depth, usage); - // TODO: implement texture swizzle + width, height, depth, usage, r, g, b, a); } void MetalDriver::importTextureR(Handle th, intptr_t i, @@ -321,6 +326,10 @@ Handle MetalDriver::createIndexBufferS() noexcept { return alloc_handle(); } +Handle MetalDriver::createBufferObjectS() noexcept { + return alloc_handle(); +} + Handle MetalDriver::createTextureS() noexcept { return alloc_handle(); } @@ -399,6 +408,12 @@ void MetalDriver::destroyIndexBuffer(Handle ibh) { } } +void MetalDriver::destroyBufferObject(Handle boh) { + if (boh) { + destruct_handle(mHandleMap, boh); + } +} + void MetalDriver::destroyRenderPrimitive(Handle rph) { if (rph) { destruct_handle(mHandleMap, rph); @@ -550,6 +565,14 @@ bool MetalDriver::isTextureFormatSupported(TextureFormat format) { TextureReshaper::canReshapeTextureFormat(format); } +bool MetalDriver::isTextureSwizzleSupported() { + if (@available(macOS 10.15, iOS 13, *)) { + return true; + } else { + return false; + } +} + bool MetalDriver::isTextureFormatMipmappable(TextureFormat format) { // Derived from the Metal 3.0 Feature Set Tables. // In order for a format to be mipmappable, it must be color-renderable and filterable. @@ -642,6 +665,22 @@ void MetalDriver::updateIndexBuffer(Handle ibh, BufferDescriptor& scheduleDestroy(std::move(data)); } +void MetalDriver::updateBufferObject(Handle boh, BufferDescriptor&& data, + uint32_t byteOffset) { + auto* bo = handle_cast(mHandleMap, boh); + bo->updateBuffer(data.buffer, data.size, byteOffset); + scheduleDestroy(std::move(data)); +} + +void MetalDriver::setVertexBufferObject(Handle vbh, size_t index, + Handle boh) { + auto* vertexBuffer = handle_cast(mHandleMap, vbh); + auto* bufferObject = handle_cast(mHandleMap, boh); + assert_invariant(index < vertexBuffer->buffers.size()); + assert_invariant(bufferObject->getBuffer()); + vertexBuffer->buffers[index] = bufferObject->getBuffer(); +} + void MetalDriver::update2DImage(Handle th, uint32_t level, uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height, PixelBufferDescriptor&& data) { ASSERT_PRECONDITION(!isInRenderPass(mContext), @@ -1211,7 +1250,8 @@ void MetalDriver::draw(backend::PipelineState ps, Handle rph) return; } const auto metalTexture = handle_const_cast(mHandleMap, sampler->t); - texturesToBind[binding] = metalTexture->texture; + texturesToBind[binding] = metalTexture->swizzledTextureView ? metalTexture->swizzledTextureView + : metalTexture->texture; if (metalTexture->externalImage.isValid()) { texturesToBind[binding] = metalTexture->externalImage.getMetalTextureForDraw(); @@ -1254,9 +1294,28 @@ void MetalDriver::draw(backend::PipelineState ps, Handle rph) withRange:samplerRange]; // Bind the vertex buffers. + + MetalBuffer* buffers[MAX_VERTEX_ATTRIBUTE_COUNT]; + size_t vertexBufferOffsets[MAX_VERTEX_ATTRIBUTE_COUNT]; + size_t bufferIndex = 0; + + auto vb = primitive->vertexBuffer; + for (uint32_t attributeIndex = 0; attributeIndex < vb->attributes.size(); attributeIndex++) { + const auto& attribute = vb->attributes[attributeIndex]; + if (attribute.buffer == Attribute::BUFFER_UNUSED) { + continue; + } + + assert_invariant(vb->buffers[attribute.buffer]); + buffers[bufferIndex] = vb->buffers[attribute.buffer].get(); + vertexBufferOffsets[bufferIndex] = attribute.offset; + bufferIndex++; + } + + const auto bufferCount = bufferIndex; MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), mContext->currentRenderPassEncoder, - VERTEX_BUFFER_START, MetalBuffer::Stage::VERTEX, primitive->buffers.data(), - primitive->offsets.data(), primitive->buffers.size()); + VERTEX_BUFFER_START, MetalBuffer::Stage::VERTEX, buffers, + vertexBufferOffsets, bufferCount); // Bind the zero buffer, used for missing vertex attributes. static const char bytes[16] = { 0 }; diff --git a/filament/backend/src/metal/MetalEnums.h b/filament/backend/src/metal/MetalEnums.h index b22a66067c..c346bb54fd 100644 --- a/filament/backend/src/metal/MetalEnums.h +++ b/filament/backend/src/metal/MetalEnums.h @@ -419,6 +419,31 @@ constexpr inline MTLCompareFunction getCompareFunction(SamplerCompareFunc compar } } +API_AVAILABLE(macos(10.15), ios(13.0)) +constexpr inline MTLTextureSwizzle getSwizzle(TextureSwizzle swizzle) { + switch (swizzle) { + case TextureSwizzle::SUBSTITUTE_ZERO: + return MTLTextureSwizzleZero; + case TextureSwizzle::SUBSTITUTE_ONE: + return MTLTextureSwizzleOne; + case TextureSwizzle::CHANNEL_0: + return MTLTextureSwizzleRed; + case TextureSwizzle::CHANNEL_1: + return MTLTextureSwizzleGreen; + case TextureSwizzle::CHANNEL_2: + return MTLTextureSwizzleBlue; + case TextureSwizzle::CHANNEL_3: + return MTLTextureSwizzleAlpha; + } +} + +API_AVAILABLE(macos(10.15), ios(13.0)) +inline MTLTextureSwizzleChannels getSwizzleChannels(TextureSwizzle r, TextureSwizzle g, TextureSwizzle b, + TextureSwizzle a) { + return MTLTextureSwizzleChannelsMake(getSwizzle(r), getSwizzle(g), getSwizzle(b), + getSwizzle(a)); +} + } // namespace backend } // namespace filament diff --git a/filament/backend/src/metal/MetalHandles.h b/filament/backend/src/metal/MetalHandles.h index 8d2587138d..cfc1be1e8e 100644 --- a/filament/backend/src/metal/MetalHandles.h +++ b/filament/backend/src/metal/MetalHandles.h @@ -112,12 +112,24 @@ private: void* frameCompletedUserData = nullptr; }; +class MetalBufferObject : public HwBufferObject { +public: + MetalBufferObject(MetalContext& context, uint32_t byteCount); + + void updateBuffer(void* data, size_t size, uint32_t byteOffset); + const std::shared_ptr& getBuffer() const { return buffer; } + +private: + uint32_t byteCount; + std::shared_ptr buffer = nullptr; + +}; + struct MetalVertexBuffer : public HwVertexBuffer { MetalVertexBuffer(MetalContext& context, uint8_t bufferCount, uint8_t attributeCount, - uint32_t vertexCount, AttributeArray const& attributes); - ~MetalVertexBuffer(); + uint32_t vertexCount, AttributeArray const& attributes, bool bufferObjectsEnabled); - std::vector buffers; + std::vector> buffers; }; struct MetalIndexBuffer : public HwIndexBuffer { @@ -142,9 +154,6 @@ struct MetalRenderPrimitive : public HwRenderPrimitive { // This struct is used to create the pipeline description to describe vertex assembly. VertexDescription vertexDescription = {}; - - std::vector buffers; - std::vector offsets; }; struct MetalProgram : public HwProgram { @@ -158,7 +167,8 @@ struct MetalProgram : public HwProgram { struct MetalTexture : public HwTexture { MetalTexture(MetalContext& context, SamplerType target, uint8_t levels, TextureFormat format, - uint8_t samples, uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage) + uint8_t samples, uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage, + TextureSwizzle r, TextureSwizzle g, TextureSwizzle b, TextureSwizzle a) noexcept; // Constructor for importing an id outside of Filament. @@ -182,6 +192,12 @@ struct MetalTexture : public HwTexture { MetalContext& context; MetalExternalImage externalImage; id texture = nil; + + // If non-nil, a swizzled texture view to use instead of "texture". + // Filament swizzling only affects texture reads, so this should not be used when the texture is + // bound as a render target attachment. + id swizzledTextureView = nil; + uint8_t bytesPerElement; // The number of bytes per pixel, or block (for compressed texture formats). uint8_t blockWidth; // The number of horizontal pixels per block (only for compressed texture formats). uint8_t blockHeight; // The number of vertical pixels per block (only for compressed texture formats). diff --git a/filament/backend/src/metal/MetalHandles.mm b/filament/backend/src/metal/MetalHandles.mm index 5a2c67961f..0a374762f7 100644 --- a/filament/backend/src/metal/MetalHandles.mm +++ b/filament/backend/src/metal/MetalHandles.mm @@ -266,11 +266,27 @@ void MetalSwapChain::scheduleFrameCompletedCallback() { }]; } +MetalBufferObject::MetalBufferObject(MetalContext& context, uint32_t byteCount) + : byteCount(byteCount), buffer(std::make_shared(context, byteCount)) {} + +void MetalBufferObject::updateBuffer(void* data, size_t size, uint32_t byteOffset) { + assert_invariant(byteOffset + size <= byteCount); + buffer->copyIntoBuffer(data, size); +} + MetalVertexBuffer::MetalVertexBuffer(MetalContext& context, uint8_t bufferCount, - uint8_t attributeCount, uint32_t vertexCount, AttributeArray const& attributes) - : HwVertexBuffer(bufferCount, attributeCount, vertexCount, attributes) { + uint8_t attributeCount, uint32_t vertexCount, AttributeArray const& attributes, + bool bufferObjectsEnabled) + : HwVertexBuffer(bufferCount, attributeCount, vertexCount, attributes, bufferObjectsEnabled) { buffers.reserve(bufferCount); + if (bufferObjectsEnabled) { + // If BufferObjects are used, we don't need to make any allocations. Simply reserve enough + // "slots" for future calls to setVertexBufferObject. + buffers.resize(bufferCount); + return; + } + for (uint8_t bufferIndex = 0; bufferIndex < bufferCount; ++bufferIndex) { // Calculate buffer size. uint32_t size = 0; @@ -281,21 +297,14 @@ MetalVertexBuffer::MetalVertexBuffer(MetalContext& context, uint8_t bufferCount, } } - MetalBuffer* buffer = nullptr; + std::shared_ptr buffer = nullptr; if (size > 0) { - buffer = new MetalBuffer(context, size); + buffer = std::make_shared(context, size); } buffers.push_back(buffer); } } -MetalVertexBuffer::~MetalVertexBuffer() { - for (auto* b : buffers) { - delete b; - } - buffers.clear(); -} - MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, uint8_t elementSize, uint32_t indexCount) : HwIndexBuffer(elementSize, indexCount), buffer(context, elementSize * indexCount, true) { } @@ -309,10 +318,6 @@ void MetalRenderPrimitive::setBuffers(MetalVertexBuffer* vertexBuffer, MetalInde const size_t attributeCount = vertexBuffer->attributes.size(); - buffers.clear(); - buffers.reserve(attributeCount); - offsets.clear(); - offsets.reserve(attributeCount); vertexDescription = {}; // Each attribute gets its own vertex buffer. @@ -339,9 +344,6 @@ void MetalRenderPrimitive::setBuffers(MetalVertexBuffer* vertexBuffer, MetalInde continue; } - buffers.push_back(vertexBuffer->buffers[attribute.buffer]); - offsets.push_back(attribute.offset); - vertexDescription.attributes[attributeIndex] = { .format = getMetalFormat(attribute.type, attribute.flags & Attribute::FLAG_NORMALIZED), @@ -416,7 +418,8 @@ static MTLPixelFormat decidePixelFormat(id device, TextureFormat form MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, uint32_t depth, - TextureUsage usage) noexcept + TextureUsage usage, TextureSwizzle r, TextureSwizzle g, TextureSwizzle b, + TextureSwizzle a) noexcept : HwTexture(target, levels, samples, width, height, depth, format, usage), context(context), externalImage(context), reshaper(format) { @@ -515,6 +518,31 @@ MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t le texture = nil; break; } + + // If swizzling is set, set up a swizzled texture view that we'll use when sampling this texture. + const bool isDefaultSwizzle = + r == TextureSwizzle::CHANNEL_0 && + g == TextureSwizzle::CHANNEL_1 && + b == TextureSwizzle::CHANNEL_2 && + a == TextureSwizzle::CHANNEL_3; + // If texture is nil, then it must be a SAMPLER_EXTERNAL texture. We'll ignore this case for now. + // TODO: implement swizzling for external textures. + if (!isDefaultSwizzle && texture) { + if (@available(macOS 10.15, iOS 13, *)) { + NSUInteger slices = texture.arrayLength; + if (texture.textureType == MTLTextureTypeCube || + texture.textureType == MTLTextureTypeCubeArray) { + slices *= 6; + } + NSUInteger mips = texture.mipmapLevelCount; + MTLTextureSwizzleChannels swizzle = getSwizzleChannels(r, g, b, a); + swizzledTextureView = [texture newTextureViewWithPixelFormat:texture.pixelFormat + textureType:texture.textureType + levels:NSMakeRange(0, mips) + slices:NSMakeRange(0, slices) + swizzle:swizzle]; + } + } } MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t levels, TextureFormat format, diff --git a/filament/backend/src/noop/NoopDriver.cpp b/filament/backend/src/noop/NoopDriver.cpp index 5b843d2f48..2b89aa19c3 100644 --- a/filament/backend/src/noop/NoopDriver.cpp +++ b/filament/backend/src/noop/NoopDriver.cpp @@ -85,6 +85,9 @@ void NoopDriver::destroyVertexBuffer(Handle vbh) { void NoopDriver::destroyIndexBuffer(Handle ibh) { } +void NoopDriver::destroyBufferObject(Handle boh) { +} + void NoopDriver::destroyTexture(Handle th) { } @@ -144,6 +147,10 @@ bool NoopDriver::isTextureFormatSupported(TextureFormat format) { return true; } +bool NoopDriver::isTextureSwizzleSupported() { + return true; +} + bool NoopDriver::isTextureFormatMipmappable(backend::TextureFormat format) { return true; } @@ -178,6 +185,15 @@ void NoopDriver::updateIndexBuffer(Handle ibh, BufferDescriptor&& scheduleDestroy(std::move(p)); } +void NoopDriver::updateBufferObject(Handle ibh, BufferDescriptor&& p, + uint32_t byteOffset) { + scheduleDestroy(std::move(p)); +} + +void NoopDriver::setVertexBufferObject(Handle vbh, size_t index, + Handle boh) { +} + void NoopDriver::update2DImage(Handle th, uint32_t level, uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height, PixelBufferDescriptor&& data) { diff --git a/filament/backend/src/opengl/OpenGLContext.h b/filament/backend/src/opengl/OpenGLContext.h index d72997526d..1b246b9913 100644 --- a/filament/backend/src/opengl/OpenGLContext.h +++ b/filament/backend/src/opengl/OpenGLContext.h @@ -22,6 +22,8 @@ #include #include +#include + #include "GLUtils.h" #include @@ -35,11 +37,23 @@ public: typedef math::details::TVec4 vec4gli; typedef math::details::TVec2 vec2glf; + // TODO: the footprint of this structure can be reduced. We need only 1 bit for index type, 16 + // bits for vertexAttribArray. We can also use fewer bits for vertexBufferVersion, although + // this will require a corollary update in OpenGLDriver::setVertexBufferObject(). struct RenderPrimitive { GLuint vao = 0; GLenum indicesType = GL_UNSIGNED_INT; GLuint elementArray = 0; utils::bitset32 vertexAttribArray; + + // The optional 32-bit handle to a GLVertexBuffer is necessary only if the referenced + // VertexBuffer supports buffer objects. If this is zero, then the VBO handles array is + // immutable. + backend::Handle vertexBufferWithObjects = {}; + + // If this version number does not match vertexBufferWithObjects->bufferObjectsVersion, + // then the VAO needs to be updated. + uint8_t vertexBufferVersion = 0; } gl; OpenGLContext() noexcept; diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index 8890bf2e91..d7af3e13e5 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -412,6 +412,10 @@ Handle OpenGLDriver::createIndexBufferS() noexcept { return initHandle(); } +Handle OpenGLDriver::createBufferObjectS() noexcept { + return initHandle(); +} + Handle OpenGLDriver::createRenderPrimitiveS() noexcept { return initHandle(); } @@ -478,12 +482,13 @@ void OpenGLDriver::createVertexBufferR( uint8_t attributeCount, uint32_t elementCount, AttributeArray attributes, - BufferUsage usage) { + BufferUsage usage, + bool bufferObjectsEnabled) { DEBUG_MARKER() auto& gl = mContext; GLVertexBuffer* vb = construct(vbh, - bufferCount, attributeCount, elementCount, attributes); + bufferCount, attributeCount, elementCount, attributes, bufferObjectsEnabled); GLsizei n = GLsizei(vb->bufferCount); @@ -524,6 +529,24 @@ void OpenGLDriver::createIndexBufferR( CHECK_GL_ERROR(utils::slog.e) } +void OpenGLDriver::createBufferObjectR( + Handle boh, + uint32_t byteCount, + BufferObjectBinding bindingType) { + DEBUG_MARKER() + + auto& gl = mContext; + GLBufferObject* bo = construct(boh); + glGenBuffers(1, &bo->gl.id); + gl.bindVertexArray(nullptr); + + assert_invariant(bindingType == BufferObjectBinding::VERTEX); + + gl.bindBuffer(GL_ARRAY_BUFFER, bo->gl.id); + glBufferData(GL_ARRAY_BUFFER, byteCount, nullptr, GL_STATIC_DRAW); + CHECK_GL_ERROR(utils::slog.e) +} + void OpenGLDriver::createRenderPrimitiveR(Handle rph, int) { DEBUG_MARKER() @@ -757,6 +780,57 @@ void OpenGLDriver::importTextureR(Handle th, intptr_t id, CHECK_GL_ERROR(utils::slog.e) } +void OpenGLDriver::updateVertexArrayObject(GLRenderPrimitive* rp, GLVertexBuffer const* vb) { + + auto& gl = mContext; + + // NOTE: this is called from draw() and must be as efficient as possible. + + // The VAO for the given render primitive must already be bound. + #ifndef NDEBUG + GLint vaoBinding; + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &vaoBinding); + assert_invariant(vaoBinding == (GLint) rp->gl.vao); + #endif + + rp->gl.vertexBufferVersion = vb->bufferObjectsVersion; + rp->maxVertexCount = vb->vertexCount; + for (size_t i = 0, n = vb->attributes.size(); i < n; i++) { + const auto& attribute = vb->attributes[i]; + if (attribute.buffer != Attribute::BUFFER_UNUSED) { + uint8_t bi = attribute.buffer; + gl.bindBuffer(GL_ARRAY_BUFFER, vb->gl.buffers[bi]); + if (UTILS_UNLIKELY(attribute.flags & Attribute::FLAG_INTEGER_TARGET)) { + glVertexAttribIPointer(GLuint(i), + getComponentCount(attribute.type), + getComponentType(attribute.type), + attribute.stride, + (void*) uintptr_t(attribute.offset)); + } else { + glVertexAttribPointer(GLuint(i), + getComponentCount(attribute.type), + getComponentType(attribute.type), + getNormalization(attribute.flags & Attribute::FLAG_NORMALIZED), + attribute.stride, + (void*) uintptr_t(attribute.offset)); + } + + gl.enableVertexAttribArray(GLuint(i)); + } else { + + // In some OpenGL implementations, we must supply a properly-typed placeholder for + // every integer input that is declared in the vertex shader. + if (UTILS_UNLIKELY(attribute.flags & Attribute::FLAG_INTEGER_TARGET)) { + glVertexAttribI4ui(GLuint(i), 0, 0, 0, 0); + } else { + glVertexAttrib4f(GLuint(i), 0, 0, 0, 0); + } + + gl.disableVertexAttribArray(GLuint(i)); + } + } +} + void OpenGLDriver::framebufferTexture(backend::TargetBufferInfo const& binfo, GLRenderTarget const* rt, GLenum attachment) noexcept { @@ -847,6 +921,17 @@ void OpenGLDriver::framebufferTexture(backend::TargetBufferInfo const& binfo, } } + // We can't use FramebufferTexture2DMultisampleEXT with GL_TEXTURE_2D_ARRAY. + if (!(target == GL_TEXTURE_2D || + target == GL_TEXTURE_CUBE_MAP_POSITIVE_X || + target == GL_TEXTURE_CUBE_MAP_NEGATIVE_X || + target == GL_TEXTURE_CUBE_MAP_POSITIVE_Y || + target == GL_TEXTURE_CUBE_MAP_NEGATIVE_Y || + target == GL_TEXTURE_CUBE_MAP_POSITIVE_Z || + target == GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)) { + attachmentTypeNotSupportedByMSRTT = true; + } + if (rt->gl.samples <= 1 || (rt->gl.samples > 1 && t->samples > 1 && gl.features.multisample_texture)) { // on GL3.2 / GLES3.1 and above multisample is handled when creating the texture. @@ -900,6 +985,7 @@ void OpenGLDriver::framebufferTexture(backend::TargetBufferInfo const& binfo, glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, t->gl.id); } + CHECK_GL_ERROR(utils::slog.e) } else #endif if (!any(t->usage & TextureUsage::SAMPLEABLE) && t->samples > 1) { @@ -1211,6 +1297,17 @@ void OpenGLDriver::destroyIndexBuffer(Handle ibh) { } } +void OpenGLDriver::destroyBufferObject(Handle boh) { + DEBUG_MARKER() + + if (boh) { + auto& gl = mContext; + GLBufferObject const* bo = handle_cast(boh); + gl.deleteBuffers(1, &bo->gl.id, GL_ARRAY_BUFFER); + destruct(boh, bo); + } +} + void OpenGLDriver::destroyRenderPrimitive(Handle rph) { DEBUG_MARKER() @@ -1479,6 +1576,10 @@ bool OpenGLDriver::isTextureFormatSupported(TextureFormat format) { return getInternalFormat(format) != 0; } +bool OpenGLDriver::isTextureSwizzleSupported() { + return true; +} + bool OpenGLDriver::isTextureFormatMipmappable(TextureFormat format) { // The OpenGL spec for GenerateMipmap stipulates that it returns INVALID_OPERATION unless // the sized internal format is both color-renderable and texture-filterable. @@ -1617,6 +1718,7 @@ void OpenGLDriver::updateVertexBuffer(Handle vbh, auto& gl = mContext; GLVertexBuffer* eb = handle_cast(vbh); + assert_invariant(!eb->bufferObjectsEnabled && "Please use setVertexBufferObject() instead."); gl.bindBuffer(GL_ARRAY_BUFFER, eb->gl.buffers[index]); glBufferSubData(GL_ARRAY_BUFFER, byteOffset, p.size, p.buffer); @@ -1626,6 +1728,29 @@ void OpenGLDriver::updateVertexBuffer(Handle vbh, CHECK_GL_ERROR(utils::slog.e) } +void OpenGLDriver::setVertexBufferObject(Handle vbh, + size_t index, Handle boh) { + DEBUG_MARKER() + + GLVertexBuffer* vb = handle_cast(vbh); + assert_invariant(vb->bufferObjectsEnabled && "Please use updateVertexBuffer() instead."); + + GLBufferObject* bo = handle_cast(boh); + + // If the specified VBO handle is different from what's already in the slot, then update the + // slot and bump the cyclical version number. Dependent VAOs use the version number to detect + // when they should be updated. + if (vb->gl.buffers[index] != bo->gl.id) { + vb->gl.buffers[index] = bo->gl.id; + static constexpr uint32_t kMaxVersion = + std::numeric_limitsbufferObjectsVersion)>::max(); + const uint32_t version = vb->bufferObjectsVersion; + vb->bufferObjectsVersion = (version + 1) % kMaxVersion; + } + + CHECK_GL_ERROR(utils::slog.e) +} + void OpenGLDriver::updateIndexBuffer( Handle ibh, BufferDescriptor&& p, uint32_t byteOffset) { DEBUG_MARKER() @@ -1643,6 +1768,22 @@ void OpenGLDriver::updateIndexBuffer( CHECK_GL_ERROR(utils::slog.e) } +void OpenGLDriver::updateBufferObject( + Handle boh, BufferDescriptor&& bd, uint32_t byteOffset) { + DEBUG_MARKER() + + auto& gl = mContext; + GLBufferObject* bo = handle_cast(boh); + + gl.bindVertexArray(nullptr); + gl.bindBuffer(GL_ARRAY_BUFFER, bo->gl.id); + glBufferSubData(GL_ARRAY_BUFFER, byteOffset, bd.size, bd.buffer); + + scheduleDestroy(std::move(bd)); + + CHECK_GL_ERROR(utils::slog.e) +} + void OpenGLDriver::loadUniformBuffer(Handle ubh, BufferDescriptor&& p) { DEBUG_MARKER() @@ -2373,41 +2514,13 @@ void OpenGLDriver::setRenderPrimitiveBuffer(Handle rph, CHECK_GL_ERROR(utils::slog.e) rp->gl.indicesType = ib->elementSize == 4 ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT; - rp->maxVertexCount = eb->vertexCount; - for (size_t i = 0, n = eb->attributes.size(); i < n; i++) { - const auto& attribute = eb->attributes[i]; - if (attribute.buffer != Attribute::BUFFER_UNUSED) { - uint8_t bi = attribute.buffer; - gl.bindBuffer(GL_ARRAY_BUFFER, eb->gl.buffers[bi]); - if (UTILS_UNLIKELY(attribute.flags & Attribute::FLAG_INTEGER_TARGET)) { - glVertexAttribIPointer(GLuint(i), - getComponentCount(attribute.type), - getComponentType(attribute.type), - attribute.stride, - (void*) uintptr_t(attribute.offset)); - } else { - glVertexAttribPointer(GLuint(i), - getComponentCount(attribute.type), - getComponentType(attribute.type), - getNormalization(attribute.flags & Attribute::FLAG_NORMALIZED), - attribute.stride, - (void*) uintptr_t(attribute.offset)); - } - gl.enableVertexAttribArray(GLuint(i)); - } else { + rp->gl.vertexBufferWithObjects = eb->bufferObjectsEnabled ? vbh : + backend::Handle {}; - // In some OpenGL implementations, we must supply a properly-typed placeholder for - // every integer input that is declared in the vertex shader. - if (UTILS_UNLIKELY(attribute.flags & Attribute::FLAG_INTEGER_TARGET)) { - glVertexAttribI4ui(GLuint(i), 0, 0, 0, 0); - } else { - glVertexAttrib4f(GLuint(i), 0, 0, 0, 0); - } + // update the VBO bindings in the VAO + updateVertexArrayObject(rp, eb); - gl.disableVertexAttribArray(GLuint(i)); - } - } // this records the index buffer into the currently bound VAO gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib->gl.buffer); @@ -3104,9 +3217,19 @@ void OpenGLDriver::draw(PipelineState state, Handle rph) { useProgram(p); - const GLRenderPrimitive* rp = handle_cast(rph); + GLRenderPrimitive* rp = handle_cast(rph); + gl.bindVertexArray(&rp->gl); + // If necessary, mutate the bindings in the VAO. + VertexBufferHandle vbwo = rp->gl.vertexBufferWithObjects; + if (UTILS_UNLIKELY(vbwo)) { + const GLVertexBuffer* vb = handle_cast(vbwo); + if (rp->gl.vertexBufferVersion != vb->bufferObjectsVersion) { + updateVertexArrayObject(rp, vb); + } + } + setRasterState(state.rasterState); gl.polygonOffset(state.polygonOffset.slope, state.polygonOffset.constant); diff --git a/filament/backend/src/opengl/OpenGLDriver.h b/filament/backend/src/opengl/OpenGLDriver.h index 1b306184f7..46e84b8127 100644 --- a/filament/backend/src/opengl/OpenGLDriver.h +++ b/filament/backend/src/opengl/OpenGLDriver.h @@ -69,6 +69,12 @@ public: backend::BufferUsage usage = {}; }; + struct GLBufferObject : public backend::HwBufferObject { + struct { + GLuint id = 0; + } gl; + }; + struct GLVertexBuffer : public backend::HwVertexBuffer { using HwVertexBuffer::HwVertexBuffer; struct { @@ -315,6 +321,8 @@ private: /* Misc... */ + void updateVertexArrayObject(GLRenderPrimitive* rp, GLVertexBuffer const* vb); + void framebufferTexture(backend::TargetBufferInfo const& binfo, GLRenderTarget const* rt, GLenum attachment) noexcept; diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index 59027c34ee..e7706bfaaf 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -446,9 +446,9 @@ void VulkanDriver::destroyRenderPrimitive(Handle rph) { void VulkanDriver::createVertexBufferR(Handle vbh, uint8_t bufferCount, uint8_t attributeCount, uint32_t elementCount, AttributeArray attributes, - BufferUsage usage) { + BufferUsage usage, bool bufferObjectsEnabled) { auto vertexBuffer = construct_handle(mHandleMap, vbh, mContext, mStagePool, - mDisposer, bufferCount, attributeCount, elementCount, attributes); + mDisposer, bufferCount, attributeCount, elementCount, attributes, bufferObjectsEnabled); mDisposer.createDisposable(vertexBuffer, [this, vbh] () { destruct_handle(mHandleMap, vbh); }); @@ -478,6 +478,22 @@ void VulkanDriver::destroyIndexBuffer(Handle ibh) { } } +void VulkanDriver::createBufferObjectR(Handle boh, + uint32_t byteCount, BufferObjectBinding bindingType) { + auto bufferObject = construct_handle(mHandleMap, boh, mContext, mStagePool, + mDisposer, byteCount); + mDisposer.createDisposable(bufferObject, [this, boh] () { + destruct_handle(mHandleMap, boh); + }); +} + +void VulkanDriver::destroyBufferObject(Handle boh) { + if (boh) { + auto bufferObject = handle_cast(mHandleMap, boh); + mDisposer.removeReference(bufferObject); + } +} + void VulkanDriver::createTextureR(Handle th, SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth, TextureUsage usage) { @@ -492,19 +508,20 @@ void VulkanDriver::createTextureSwizzledR(Handle th, SamplerType targ TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth, TextureUsage usage, TextureSwizzle r, TextureSwizzle g, TextureSwizzle b, TextureSwizzle a) { + TextureSwizzle swizzleArray[] = {r, g, b, a}; + const VkComponentMapping swizzleMap = getSwizzleMap(swizzleArray); auto vktexture = construct_handle(mHandleMap, th, mContext, target, levels, - format, samples, w, h, depth, usage, mStagePool); + format, samples, w, h, depth, usage, mStagePool, swizzleMap); mDisposer.createDisposable(vktexture, [this, th] () { destruct_handle(mHandleMap, th); }); - // TODO: implement texture swizzling } void VulkanDriver::importTextureR(Handle th, intptr_t id, SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth, TextureUsage usage) { - // not support in this backend + // not supported in this backend } void VulkanDriver::destroyTexture(Handle th) { @@ -622,6 +639,10 @@ Handle VulkanDriver::createIndexBufferS() noexcept { return alloc_handle(); } +Handle VulkanDriver::createBufferObjectS() noexcept { + return alloc_handle(); +} + Handle VulkanDriver::createTextureS() noexcept { return alloc_handle(); } @@ -795,6 +816,10 @@ bool VulkanDriver::isTextureFormatSupported(TextureFormat format) { return info.optimalTilingFeatures != 0; } +bool VulkanDriver::isTextureSwizzleSupported() { + return true; +} + bool VulkanDriver::isTextureFormatMipmappable(backend::TextureFormat format) { switch (format) { case TextureFormat::DEPTH16: @@ -847,6 +872,13 @@ void VulkanDriver::updateVertexBuffer(Handle vbh, size_t index, scheduleDestroy(std::move(p)); } +void VulkanDriver::setVertexBufferObject(Handle vbh, size_t index, + Handle boh) { + auto& vb = *handle_cast(mHandleMap, vbh); + auto& bo = *handle_cast(mHandleMap, boh); + vb.buffers[index] = bo.buffer; +} + void VulkanDriver::updateIndexBuffer(Handle ibh, BufferDescriptor&& p, uint32_t byteOffset) { auto& ib = *handle_cast(mHandleMap, ibh); @@ -854,6 +886,13 @@ void VulkanDriver::updateIndexBuffer(Handle ibh, BufferDescriptor scheduleDestroy(std::move(p)); } +void VulkanDriver::updateBufferObject(Handle boh, BufferDescriptor&& bd, + uint32_t byteOffset) { + auto& bo = *handle_cast(mHandleMap, boh); + bo.buffer->loadFromCpu(bd.buffer, byteOffset, bd.size); + scheduleDestroy(std::move(bd)); +} + void VulkanDriver::update2DImage(Handle th, uint32_t level, uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height, PixelBufferDescriptor&& data) { @@ -1611,13 +1650,46 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle r mContext.rasterState.colorTargetCount = rt->getColorTargetCount(mContext.currentRenderPass); - VulkanBinder::ProgramBundle shaderHandles = program->bundle; + // Declare fixed-size arrays that get passed to the binder and to vkCmdBindVertexBuffers. + VulkanBinder::VertexArray varray = {}; + VkBuffer buffers[backend::MAX_VERTEX_ATTRIBUTE_COUNT] = {}; + VkDeviceSize offsets[backend::MAX_VERTEX_ATTRIBUTE_COUNT] = {}; + + // For each attribute, append to each of the above lists. + const uint32_t bufferCount = prim.vertexBuffer->attributes.size(); + for (uint32_t attribIndex = 0; attribIndex < bufferCount; attribIndex++) { + Attribute attrib = prim.vertexBuffer->attributes[attribIndex]; + VkFormat vkformat = getVkFormat(attrib.type, attrib.flags & Attribute::FLAG_NORMALIZED); + + // HACK: Re-use the positions buffer as a dummy buffer for disabled attributes. Filament's + // vertex shaders declare all attributes as either vec4 or uvec4 (the latter for bone + // indices), and positions are always at least 32 bits per element. Therefore we can assign + // a dummy type of either R8G8B8A8_UINT or R8G8B8A8_SNORM, depending on whether the shader + // expects to receive floats or ints. + if (attrib.buffer == Attribute::BUFFER_UNUSED) { + const bool isInteger = attrib.flags & Attribute::FLAG_INTEGER_TARGET; + vkformat = isInteger ? VK_FORMAT_R8G8B8A8_UINT : VK_FORMAT_R8G8B8A8_SNORM; + attrib = prim.vertexBuffer->attributes[0]; + } + + buffers[attribIndex] = prim.vertexBuffer->buffers[attrib.buffer]->getGpuBuffer(); + offsets[attribIndex] = attrib.offset; + varray.attributes[attribIndex] = { + .location = attribIndex, // matches the GLSL layout specifier + .binding = attribIndex, // matches the position within vkCmdBindVertexBuffers + .format = vkformat, + }; + varray.buffers[attribIndex] = { + .binding = attribIndex, + .stride = attrib.stride, + }; + } // Push state changes to the VulkanBinder instance. This is fast and does not make VK calls. - mBinder.bindProgramBundle(shaderHandles); + mBinder.bindProgramBundle(program->bundle); mBinder.bindRasterState(mContext.rasterState); mBinder.bindPrimitiveTopology(prim.primitiveTopology); - mBinder.bindVertexArray(prim.varray); + mBinder.bindVertexArray(varray); // Query the program for the mapping from (SamplerGroupBinding,Offset) to (SamplerBinding), // where "SamplerBinding" is the integer in the GLSL, and SamplerGroupBinding is the abstract @@ -1708,8 +1780,7 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle r // Next bind the vertex buffers and index buffer. One potential performance improvement is to // avoid rebinding these if they are already bound, but since we do not (yet) support subranges // it would be rare for a client to make consecutive draw calls with the same render primitive. - vkCmdBindVertexBuffers(cmdbuffer, 0, (uint32_t) prim.buffers.size(), - prim.buffers.data(), prim.offsets.data()); + vkCmdBindVertexBuffers(cmdbuffer, 0, bufferCount, buffers, offsets); vkCmdBindIndexBuffer(cmdbuffer, prim.indexBuffer->buffer->getGpuBuffer(), 0, prim.indexBuffer->indexType); @@ -1761,6 +1832,7 @@ void VulkanDriver::debugCommand(const char* methodName) { "loadUniformBuffer", "updateVertexBuffer", "updateIndexBuffer", + "updateBufferObject", "update2DImage", "updateCubeImage", }; diff --git a/filament/backend/src/vulkan/VulkanHandles.cpp b/filament/backend/src/vulkan/VulkanHandles.cpp index 9851e50248..b1a3fc4fd9 100644 --- a/filament/backend/src/vulkan/VulkanHandles.cpp +++ b/filament/backend/src/vulkan/VulkanHandles.cpp @@ -403,18 +403,18 @@ bool VulkanRenderTarget::invalidate() { VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool, VulkanDisposer& disposer, uint8_t bufferCount, uint8_t attributeCount, - uint32_t elementCount, AttributeArray const& attributes) : - HwVertexBuffer(bufferCount, attributeCount, elementCount, attributes) { + uint32_t elementCount, AttributeArray const& attributes, bool boEnabled) : + HwVertexBuffer(bufferCount, attributeCount, elementCount, attributes, boEnabled) { buffers.reserve(bufferCount); for (uint8_t bufferIndex = 0; bufferIndex < bufferCount; ++bufferIndex) { uint32_t size = 0; for (auto const& item : attributes) { if (item.buffer == bufferIndex) { - uint32_t end = item.offset + elementCount * item.stride; + uint32_t end = item.offset + elementCount * item.stride; size = std::max(size, end); } } - buffers.emplace_back(new VulkanBuffer(context, stagePool, disposer, this, + buffers.push_back(std::make_shared(context, stagePool, disposer, this, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, size)); } } @@ -479,14 +479,14 @@ VulkanUniformBuffer::~VulkanUniformBuffer() { VulkanTexture::VulkanTexture(VulkanContext& context, SamplerType target, uint8_t levels, TextureFormat tformat, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth, - TextureUsage usage, VulkanStagePool& stagePool) : + TextureUsage usage, VulkanStagePool& stagePool, VkComponentMapping swizzle) : HwTexture(target, levels, samples, w, h, depth, tformat, usage), - mVkFormat(backend::getVkFormat(tformat)), mContext(context), mStagePool(stagePool) { - // Vulkan does not support 24-bit depth, use the official fallback format. - if (tformat == TextureFormat::DEPTH24) { - mVkFormat = mContext.finalDepthFormat; - } + // Vulkan does not support 24-bit depth, use the official fallback format. + mVkFormat(tformat == TextureFormat::DEPTH24 ? context.finalDepthFormat : + backend::getVkFormat(tformat)), + + mSwizzle(swizzle), mContext(context), mStagePool(stagePool) { // Create an appropriately-sized device-only VkImage, but do not fill it yet. VkImageCreateInfo imageInfo { @@ -752,7 +752,7 @@ VkImageView VulkanTexture::getImageView(VkImageSubresourceRange range) { .image = mTextureImage, .viewType = mViewType, .format = mVkFormat, - .components = {}, + .components = mSwizzle, .subresourceRange = range }; VkImageView imageView; @@ -868,56 +868,6 @@ void VulkanRenderPrimitive::setBuffers(VulkanVertexBuffer* vertexBuffer, VulkanIndexBuffer* indexBuffer) { this->vertexBuffer = vertexBuffer; this->indexBuffer = indexBuffer; - const size_t nattrs = vertexBuffer->attributes.size(); - - // These vectors are passed to vkCmdBindVertexBuffers at every draw call. This binds the - // VkBuffer objects, but does not describe the structure of a vertex. - buffers.clear(); - buffers.reserve(nattrs); - offsets.clear(); - offsets.reserve(nattrs); - - // The following fixed-size arrays are consumed by VulkanBinder. They describe the vertex - // structure, but do not specify the actual buffer objects to bind. - memset(varray.attributes, 0, sizeof(varray.attributes)); - memset(varray.buffers, 0, sizeof(varray.buffers)); - - // For each enabled attribute, append to each of the above lists. Note that a single VkBuffer - // handle might be appended more than once, which is perfectly fine. - uint32_t bufferIndex = 0; - for (uint32_t attribIndex = 0; attribIndex < nattrs; attribIndex++) { - Attribute attrib = vertexBuffer->attributes[attribIndex]; - VkFormat vkformat = getVkFormat(attrib.type, attrib.flags & Attribute::FLAG_NORMALIZED); - - // HACK: Re-use the positions buffer as a dummy buffer for disabled attributes. - // We know that position exists (see earlier assert) - if (attrib.buffer == Attribute::BUFFER_UNUSED) { - - // HACK: Presently, Filament's vertex shaders declare all attributes as either vec4 or - // uvec4 (the latter is for bone indices), and positions are always at least 32 bits per - // element. Therefore we can assign a dummy type of either R8G8B8A8_UINT or - // R8G8B8A8_SNORM, depending on whether the shader expects to receive floats or ints. - const bool isInteger = attrib.flags & Attribute::FLAG_INTEGER_TARGET; - vkformat = isInteger ? VK_FORMAT_R8G8B8A8_UINT : VK_FORMAT_R8G8B8A8_SNORM; - - attrib = vertexBuffer->attributes[0]; - } - - buffers.push_back(vertexBuffer->buffers[attrib.buffer]->getGpuBuffer()); - offsets.push_back(attrib.offset); - varray.attributes[bufferIndex] = { - .location = attribIndex, // matches the GLSL layout specifier - .binding = bufferIndex, // matches the position within vkCmdBindVertexBuffers - .format = vkformat, - .offset = 0 - }; - varray.buffers[bufferIndex] = { - .binding = bufferIndex, - .stride = attrib.stride, - .inputRate = VK_VERTEX_INPUT_RATE_VERTEX - }; - bufferIndex++; - } } VulkanTimerQuery::VulkanTimerQuery(VulkanContext& context) : mContext(context) { diff --git a/filament/backend/src/vulkan/VulkanHandles.h b/filament/backend/src/vulkan/VulkanHandles.h index 5de32730fb..d0a47bd31c 100644 --- a/filament/backend/src/vulkan/VulkanHandles.h +++ b/filament/backend/src/vulkan/VulkanHandles.h @@ -83,8 +83,8 @@ struct VulkanSwapChain : public HwSwapChain { struct VulkanVertexBuffer : public HwVertexBuffer { VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool, VulkanDisposer& disposer, uint8_t bufferCount, uint8_t attributeCount, uint32_t elementCount, - AttributeArray const& attributes); - std::vector> buffers; + AttributeArray const& attributes, bool bufferObjectsEnabled); + std::vector> buffers; }; struct VulkanIndexBuffer : public HwIndexBuffer { @@ -92,11 +92,19 @@ struct VulkanIndexBuffer : public HwIndexBuffer { uint8_t elementSize, uint32_t indexCount) : HwIndexBuffer(elementSize, indexCount), indexType(elementSize == 2 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32), buffer(new VulkanBuffer(context, stagePool, disposer, this, - VK_BUFFER_USAGE_INDEX_BUFFER_BIT, elementSize * indexCount)) {} + VK_BUFFER_USAGE_INDEX_BUFFER_BIT, elementSize * indexCount)) {} const VkIndexType indexType; const std::unique_ptr buffer; }; +struct VulkanBufferObject : public HwBufferObject { + VulkanBufferObject(VulkanContext& context, VulkanStagePool& stagePool, VulkanDisposer& disposer, + uint32_t byteCount) : + buffer(new VulkanBuffer(context, stagePool, disposer, this, + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, byteCount)) {} + const std::shared_ptr buffer; +}; + struct VulkanUniformBuffer : public HwUniformBuffer { VulkanUniformBuffer(VulkanContext& context, VulkanStagePool& stagePool, VulkanDisposer& disposer, uint32_t numBytes, backend::BufferUsage usage); @@ -119,7 +127,7 @@ struct VulkanSamplerGroup : public HwSamplerGroup { struct VulkanTexture : public HwTexture { VulkanTexture(VulkanContext& context, SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth, - TextureUsage usage, VulkanStagePool& stagePool); + TextureUsage usage, VulkanStagePool& stagePool, VkComponentMapping swizzle = {}); ~VulkanTexture(); void update2DImage(const PixelBufferDescriptor& data, uint32_t width, uint32_t height, int miplevel); @@ -164,7 +172,8 @@ private: uint32_t width, uint32_t height, uint32_t depth, FaceOffsets const* faceOffsets, uint32_t miplevel); - VkFormat mVkFormat; + const VkFormat mVkFormat; + const VkComponentMapping mSwizzle; VkImageViewType mViewType; VkImage mTextureImage = VK_NULL_HANDLE; VkDeviceMemory mTextureImageMemory = VK_NULL_HANDLE; @@ -182,12 +191,6 @@ struct VulkanRenderPrimitive : public HwRenderPrimitive { VulkanVertexBuffer* vertexBuffer = nullptr; VulkanIndexBuffer* indexBuffer = nullptr; VkPrimitiveTopology primitiveTopology; - // The "varray" field describes the structure of the vertex and gets passed to VulkanBinder, - // which in turn passes it to vkCreateGraphicsPipelines. The "buffers" and "offsets" vectors are - // passed to vkCmdBindVertexBuffers at draw call time. - VulkanBinder::VertexArray varray; - std::vector buffers; - std::vector offsets; }; struct VulkanFence : public HwFence { diff --git a/filament/backend/src/vulkan/VulkanUtility.cpp b/filament/backend/src/vulkan/VulkanUtility.cpp index 6981875292..173370855c 100644 --- a/filament/backend/src/vulkan/VulkanUtility.cpp +++ b/filament/backend/src/vulkan/VulkanUtility.cpp @@ -380,6 +380,33 @@ PixelDataType getComponentType(VkFormat format) { return {}; } +VkComponentMapping getSwizzleMap(TextureSwizzle swizzle[4]) { + VkComponentMapping map; + VkComponentSwizzle* dst = &map.r; + for (int i = 0; i < 4; ++i, ++dst) { + switch (swizzle[i]) { + case TextureSwizzle::SUBSTITUTE_ZERO: *dst = VK_COMPONENT_SWIZZLE_ZERO; break; + case TextureSwizzle::SUBSTITUTE_ONE: *dst = VK_COMPONENT_SWIZZLE_ONE; break; + // NOTE: In some cases, IDENTITY is equivalent to one of the other enums, in which + // case we choose IDENTITY under the premise that it could be more efficient, or + // allow for more state sharing. In practice this probably has no impact. + case TextureSwizzle::CHANNEL_0: + *dst = i == 0 ? VK_COMPONENT_SWIZZLE_IDENTITY : VK_COMPONENT_SWIZZLE_R; + break; + case TextureSwizzle::CHANNEL_1: + *dst = i == 1 ? VK_COMPONENT_SWIZZLE_IDENTITY : VK_COMPONENT_SWIZZLE_G; + break; + case TextureSwizzle::CHANNEL_2: + *dst = i == 2 ? VK_COMPONENT_SWIZZLE_IDENTITY : VK_COMPONENT_SWIZZLE_B; + break; + case TextureSwizzle::CHANNEL_3: + *dst = i == 3 ? VK_COMPONENT_SWIZZLE_IDENTITY : VK_COMPONENT_SWIZZLE_A; + break; + } + } + return map; +} + } // namespace filament } // namespace backend diff --git a/filament/backend/src/vulkan/VulkanUtility.h b/filament/backend/src/vulkan/VulkanUtility.h index c57e8a37cc..189b48533c 100644 --- a/filament/backend/src/vulkan/VulkanUtility.h +++ b/filament/backend/src/vulkan/VulkanUtility.h @@ -33,6 +33,7 @@ VkBlendFactor getBlendFactor(BlendFunction mode); VkCullModeFlags getCullMode(CullingMode mode); VkFrontFace getFrontFace(bool inverseFrontFaces); PixelDataType getComponentType(VkFormat format); +VkComponentMapping getSwizzleMap(TextureSwizzle swizzle[4]); } // namespace filament } // namespace backend diff --git a/filament/backend/test/TrianglePrimitive.cpp b/filament/backend/test/TrianglePrimitive.cpp index ad48fad0fa..594f6d5ffd 100644 --- a/filament/backend/test/TrianglePrimitive.cpp +++ b/filament/backend/test/TrianglePrimitive.cpp @@ -48,7 +48,7 @@ TrianglePrimitive::TrianglePrimitive(filament::backend::DriverApi& driverApi, enabledAttributes.set(VertexAttribute::POSITION); mVertexBuffer = mDriverApi.createVertexBuffer(1, 1, mVertexCount, attributes, - BufferUsage::STATIC); + BufferUsage::STATIC, false); BufferDescriptor vertexBufferDesc(gVertices, sizeof(filament::math::float2) * 3, nullptr); mDriverApi.updateVertexBuffer(mVertexBuffer, 0, std::move(vertexBufferDesc), 0); diff --git a/filament/include/filament/BufferObject.h b/filament/include/filament/BufferObject.h new file mode 100644 index 0000000000..1ede31b8cb --- /dev/null +++ b/filament/include/filament/BufferObject.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +//! \file + +#ifndef TNT_FILAMENT_BUFFEROBJECT_H +#define TNT_FILAMENT_BUFFEROBJECT_H + +#include + +#include + +#include + +#include + +namespace filament { + +class FBufferObject; + +class Engine; + +/** + * A generic GPU buffer containing data. + * + * Usage of this BufferObject is optional. For simple use cases it is not necessary. It is useful + * only when you need to share data between multiple VertexBuffer instances. It also allows you to + * efficiently swap-out the buffers in VertexBuffer. + * + * NOTE: For now this is only used for vertex data, but in the future we may use it for other things + * (e.g. compute). + * + * @see VertexBuffer + */ +class UTILS_PUBLIC BufferObject : public FilamentAPI { + struct BuilderDetails; + +public: + using BufferDescriptor = backend::BufferDescriptor; + using BindingType = backend::BufferObjectBinding; + + class Builder : public BuilderBase { + friend struct BuilderDetails; + public: + Builder() noexcept; + Builder(Builder const& rhs) noexcept; + Builder(Builder&& rhs) noexcept; + ~Builder() noexcept; + Builder& operator=(Builder const& rhs) noexcept; + Builder& operator=(Builder&& rhs) noexcept; + + /** + * Size of the buffer in bytes. + * @param byteCount Maximum number of bytes the BufferObject can hold. + * @return A reference to this Builder for chaining calls. + */ + Builder& size(uint32_t byteCount) noexcept; + + /** + * The binding type for this buffer object. (defaults to VERTEX) + * @param BindingType Distinguishes between SSBO, VBO, etc. For now this must be VERTEX. + * @return A reference to this Builder for chaining calls. + */ + Builder& bindingType(BindingType bindingType) noexcept; + + /** + * Creates the BufferObject and returns a pointer to it. After creation, the buffer + * object is uninitialized. Use BufferObject::setBuffer() to initialize it. + * + * @param engine Reference to the filament::Engine to associate this BufferObject with. + * + * @return pointer to the newly created object or nullptr if exceptions are disabled and + * an error occurred. + * + * @exception utils::PostConditionPanic if a runtime error occurred, such as running out of + * memory or other resources. + * @exception utils::PreConditionPanic if a parameter to a builder function was invalid. + * + * @see IndexBuffer::setBuffer + */ + BufferObject* build(Engine& engine); + private: + friend class FBufferObject; + }; + + /** + * Asynchronously copy-initializes a region of this BufferObject from the data provided. + * + * @param engine Reference to the filament::Engine associated with this BufferObject. + * @param buffer A BufferDescriptor representing the data used to initialize the BufferObject. + * @param byteOffset Offset in bytes into the BufferObject + */ + void setBuffer(Engine& engine, BufferDescriptor&& buffer, uint32_t byteOffset = 0); + + /** + * Returns the size of this BufferObject in elements. + * @return The maximum capacity of the BufferObject. + */ + size_t getByteCount() const noexcept; +}; + +} // namespace filament + +#endif // TNT_FILAMENT_BUFFEROBJECT_H diff --git a/filament/include/filament/Engine.h b/filament/include/filament/Engine.h index df494912cc..301424a96b 100644 --- a/filament/include/filament/Engine.h +++ b/filament/include/filament/Engine.h @@ -28,6 +28,7 @@ class JobSystem; namespace filament { +class BufferObject; class Camera; class ColorGrading; class DebugRegistry; @@ -390,6 +391,7 @@ public: */ Fence* createFence() noexcept; + bool destroy(const BufferObject* p); //!< Destroys a BufferObject object. bool destroy(const VertexBuffer* p); //!< Destroys an VertexBuffer object. bool destroy(const Fence* p); //!< Destroys a Fence object. bool destroy(const IndexBuffer* p); //!< Destroys an IndexBuffer object. diff --git a/filament/include/filament/IndexBuffer.h b/filament/include/filament/IndexBuffer.h index c3f5897a4b..09b479296d 100644 --- a/filament/include/filament/IndexBuffer.h +++ b/filament/include/filament/IndexBuffer.h @@ -84,7 +84,7 @@ public: /** * Creates the IndexBuffer object and returns a pointer to it. After creation, the index - * buffer is uninitialized. Use IndexBuffer::setBuffer() to initialized the IndexBuffer. + * buffer is uninitialized. Use IndexBuffer::setBuffer() to initialize the IndexBuffer. * * @param engine Reference to the filament::Engine to associate this IndexBuffer with. * diff --git a/filament/include/filament/Texture.h b/filament/include/filament/Texture.h index a88cc35f95..d3c0afde48 100644 --- a/filament/include/filament/Texture.h +++ b/filament/include/filament/Texture.h @@ -79,8 +79,12 @@ public: using Usage = backend::TextureUsage; //!< Usage affects texel layout using Swizzle = backend::TextureSwizzle; //!< Texture swizzle + /** @return whether a backend supports a particular format. */ static bool isTextureFormatSupported(Engine& engine, InternalFormat format) noexcept; + /** @return whether a backend supports texture swizzling. */ + static bool isTextureSwizzleSupported(Engine& engine) noexcept; + static size_t computeTextureDataSize(Texture::Format format, Texture::Type type, size_t stride, size_t height, size_t alignment) noexcept; @@ -177,11 +181,14 @@ public: /** * Specifies how a texture's channels map to color components * + * Texture Swizzle is only supported is isTextureSwizzleSupported() returns true. + * * @param r texture channel for red component * @param g texture channel for green component * @param b texture channel for blue component * @param a texture channel for alpha component * @return This Builder, for chaining calls. + * @see Texture::isTextureSwizzleSupported() */ Builder& swizzle(Swizzle r, Swizzle g, Swizzle b, Swizzle a) noexcept; diff --git a/filament/include/filament/VertexBuffer.h b/filament/include/filament/VertexBuffer.h index 70545b1c48..4cd677dad2 100644 --- a/filament/include/filament/VertexBuffer.h +++ b/filament/include/filament/VertexBuffer.h @@ -31,6 +31,7 @@ namespace filament { class FVertexBuffer; +class BufferObject; class Engine; /** @@ -86,6 +87,17 @@ public: */ Builder& vertexCount(uint32_t vertexCount) noexcept; + /** + * Allows buffers to be swapped out and shared using BufferObject. + * + * If buffer objects mode is enabled, clients must call setBufferObjectAt rather than + * setBufferAt. This allows sharing of data between VertexBuffer objects, but it may + * slightly increase the memory footprint of Filament's internal bookkeeping. + * + * @param enabled If true, enables buffer object mode. False by default. + */ + Builder& enableBufferObjects(bool enabled = true) noexcept; + /** * Sets up an attribute for this vertex buffer set. * @@ -154,6 +166,8 @@ public: /** * Asynchronously copy-initializes the specified buffer from the given buffer data. * + * Do not use this if you called enableBufferObjects() on the Builder. + * * @param engine Reference to the filament::Engine to associate this VertexBuffer with. * @param bufferIndex Index of the buffer to initialize. Must be between 0 * and Builder::bufferCount() - 1. @@ -166,6 +180,18 @@ public: void setBufferAt(Engine& engine, uint8_t bufferIndex, BufferDescriptor&& buffer, uint32_t byteOffset = 0); + /** + * Swaps in the given buffer object. + * + * To use this, you must first call enableBufferObjects() on the Builder. + * + * @param engine Reference to the filament::Engine to associate this VertexBuffer with. + * @param bufferIndex Index of the buffer to initialize. Must be between 0 + * and Builder::bufferCount() - 1. + * @param bufferObject The handle to the GPU data that will be used in this buffer slot. + */ + void setBufferObjectAt(Engine& engine, uint8_t bufferIndex, BufferObject const* bufferObject); + /** * Specifies the quaternion type for the "populateTangentQuaternions" utility. */ diff --git a/filament/include/filament/View.h b/filament/include/filament/View.h index 1a7706f6f3..59bee02ceb 100644 --- a/filament/include/filament/View.h +++ b/filament/include/filament/View.h @@ -196,6 +196,43 @@ public: float maxApertureDiameter = 0.01f; //!< maximum aperture diameter in meters (zero to disable rotation) bool enabled = false; //!< enable or disable depth of field effect Filter filter = Filter::MEDIAN; //!< filter to use for filling gaps in the kernel + bool nativeResolution = false; //!< perform DoF processing at native resolution + /** + * Number of of rings used by the gather kernels. The number of rings affects quality + * and performance. The actual number of sample per pixel is defined + * as (ringCount * 2 - 1)^2. Here are a few commonly used values: + * 3 rings : 25 ( 5x 5 grid) + * 4 rings : 49 ( 7x 7 grid) + * 5 rings : 81 ( 9x 9 grid) + * 17 rings : 1089 (33x33 grid) + * + * With a maximum circle-of-confusion of 32, it is never necessary to use more than 17 rings. + * + * Usually all three settings below are set to the same value, however, it is often + * acceptable to use a lower ring count for the "fast tiles", which improves performance. + * Fast tiles are regions of the screen where every pixels have a similar + * circle-of-confusion radius. + * + * A value of 0 means default, which is 5 on desktop and 3 on mobile. + * + * @{ + */ + uint8_t foregroundRingCount = 0; //!< number of kernel rings for foreground tiles + uint8_t backgroundRingCount = 0; //!< number of kernel rings for background tiles + uint8_t fastGatherRingCount = 0; //!< number of kernel rings for fast tiles + /** @}*/ + + /** + * maximum circle-of-confusion in pixels for the foreground, must be in [0, 32] range. + * A value of 0 means default, which is 32 on desktop and 24 on mobile. + */ + uint16_t maxForegroundCOC = 0; + + /** + * maximum circle-of-confusion in pixels for the background, must be in [0, 32] range. + * A value of 0 means default, which is 32 on desktop and 24 on mobile. + */ + uint16_t maxBackgroundCOC = 0; }; /** @@ -434,11 +471,11 @@ public: */ void setBlendMode(BlendMode blendMode) noexcept; - /** - * - * @return blending mode set by setBlendMode - * @see setBlendMode - */ + /** + * + * @return blending mode set by setBlendMode + * @see setBlendMode + */ BlendMode getBlendMode() const noexcept; /** diff --git a/filament/src/BufferObject.cpp b/filament/src/BufferObject.cpp new file mode 100644 index 0000000000..7a7503dc8c --- /dev/null +++ b/filament/src/BufferObject.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +#include "details/BufferObject.h" + +#include "details/Engine.h" + +#include "FilamentAPI-impl.h" + +namespace filament { + +struct BufferObject::BuilderDetails { + BindingType mBindingType = BindingType::VERTEX; + uint32_t mByteCount = 0; +}; + +using BuilderType = BufferObject; +BuilderType::Builder::Builder() noexcept = default; +BuilderType::Builder::~Builder() noexcept = default; +BuilderType::Builder::Builder(BuilderType::Builder const& rhs) noexcept = default; +BuilderType::Builder::Builder(BuilderType::Builder&& rhs) noexcept = default; +BuilderType::Builder& BuilderType::Builder::operator=(BuilderType::Builder const& rhs) noexcept = default; +BuilderType::Builder& BuilderType::Builder::operator=(BuilderType::Builder&& rhs) noexcept = default; + +BufferObject::Builder& BufferObject::Builder::size(uint32_t byteCount) noexcept { + mImpl->mByteCount = byteCount; + return *this; +} + +BufferObject::Builder& BufferObject::Builder::bindingType(BindingType bindingType) noexcept { + mImpl->mBindingType = bindingType; + return *this; +} + +BufferObject* BufferObject::Builder::build(Engine& engine) { + return upcast(engine).createBufferObject(*this); +} + +// ------------------------------------------------------------------------------------------------ + +FBufferObject::FBufferObject(FEngine& engine, const BufferObject::Builder& builder) + : mByteCount(builder->mByteCount), mBindingType(builder->mBindingType) { + FEngine::DriverApi& driver = engine.getDriverApi(); + mHandle = driver.createBufferObject(builder->mByteCount, builder->mBindingType); +} + +void FBufferObject::terminate(FEngine& engine) { + FEngine::DriverApi& driver = engine.getDriverApi(); + driver.destroyBufferObject(mHandle); +} + +void FBufferObject::setBuffer(FEngine& engine, BufferDescriptor&& buffer, uint32_t byteOffset) { + engine.getDriverApi().updateBufferObject(mHandle, std::move(buffer), byteOffset); +} + +// ------------------------------------------------------------------------------------------------ +// Trampoline calling into private implementation +// ------------------------------------------------------------------------------------------------ + +void BufferObject::setBuffer(Engine& engine, + BufferObject::BufferDescriptor&& buffer, uint32_t byteOffset) { + upcast(this)->setBuffer(upcast(engine), std::move(buffer), byteOffset); +} + +size_t BufferObject::getByteCount() const noexcept { + return upcast(this)->getByteCount(); +} + +} // namespace filament diff --git a/filament/src/Engine.cpp b/filament/src/Engine.cpp index 7b3e633989..0bd5381693 100644 --- a/filament/src/Engine.cpp +++ b/filament/src/Engine.cpp @@ -20,6 +20,7 @@ #include "ResourceAllocator.h" #include "backend/DriverEnums.h" +#include "details/BufferObject.h" #include "details/DFG.h" #include "details/VertexBuffer.h" #include "details/Fence.h" @@ -318,6 +319,7 @@ void FEngine::shutdown() { // this must be done after Skyboxes and before materials destroy(mSkyboxMaterial); + cleanupResourceList(mBufferObjects); cleanupResourceList(mIndexBuffers); cleanupResourceList(mVertexBuffers); cleanupResourceList(mTextures); @@ -546,6 +548,10 @@ inline T* FEngine::create(ResourceList& list, typename T::Builder const& buil return p; } +FBufferObject* FEngine::createBufferObject(const BufferObject::Builder& builder) noexcept { + return create(mBufferObjects, builder); +} + FVertexBuffer* FEngine::createVertexBuffer(const VertexBuffer::Builder& builder) noexcept { return create(mVertexBuffers, builder); } @@ -724,6 +730,10 @@ bool FEngine::terminateAndDestroy(const T* ptr, ResourceList& list) { // ----------------------------------------------------------------------------------------------- +bool FEngine::destroy(const FBufferObject* p) { + return terminateAndDestroy(p, mBufferObjects); +} + bool FEngine::destroy(const FVertexBuffer* p) { return terminateAndDestroy(p, mVertexBuffers); } @@ -925,6 +935,10 @@ SwapChain* Engine::createSwapChain(uint32_t width, uint32_t height, uint64_t fla return upcast(this)->createSwapChain(width, height, flags); } +bool Engine::destroy(const BufferObject* p) { + return upcast(this)->destroy(upcast(p)); +} + bool Engine::destroy(const VertexBuffer* p) { return upcast(this)->destroy(upcast(p)); } diff --git a/filament/src/PostProcessManager.cpp b/filament/src/PostProcessManager.cpp index 831c6d790c..d2d982310e 100644 --- a/filament/src/PostProcessManager.cpp +++ b/filament/src/PostProcessManager.cpp @@ -14,6 +14,14 @@ * limitations under the License. */ +#ifdef FILAMENT_TARGET_MOBILE +# define DOF_DEFAULT_RING_COUNT 3 +# define DOF_DEFAULT_MAX_COC 24 +#else +# define DOF_DEFAULT_RING_COUNT 5 +# define DOF_DEFAULT_MAX_COC 32 +#endif + #include "PostProcessManager.h" #include "details/Engine.h" @@ -212,6 +220,7 @@ static const MaterialInfo sMaterialList[] = { { "dofCoc", MATERIAL(DOFCOC) }, { "dofMipmap", MATERIAL(DOFMIPMAP) }, { "dofTiles", MATERIAL(DOFTILES) }, + { "dofTilesSwizzle", MATERIAL(DOFTILESSWIZZLE) }, { "dofDilate", MATERIAL(DOFDILATE) }, { "dof", MATERIAL(DOF) }, { "dofMedian", MATERIAL(DOFMEDIAN) }, @@ -808,10 +817,8 @@ FrameGraphId PostProcessManager::gaussianBlurPass(FrameGraph& } FrameGraphId PostProcessManager::dof(FrameGraph& fg, - FrameGraphId input, - const View::DepthOfFieldOptions& dofOptions, - bool translucent, - const CameraInfo& cameraInfo) noexcept { + FrameGraphId input, const View::DepthOfFieldOptions& dofOptions, + bool translucent, const CameraInfo& cameraInfo, float2 scale) noexcept { FEngine& engine = mEngine; Handle const& fullScreenRenderPrimitive = engine.getFullScreenRenderPrimitive(); @@ -920,7 +927,7 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, * dofResolution is used (at compile time for now) to chose between full- or quarter-resolution * for the DoF calculations. Set to [1] for full resolution or [2] for quarter-resolution. */ - const uint32_t dofResolution = 2; + const uint32_t dofResolution = dofOptions.nativeResolution ? 1u : 2u; constexpr const uint32_t maxMipLevels = 4u; // mip levels at full-resolution constexpr const uint32_t maxMipLevelsMask = (1u << maxMipLevels) - 1u; @@ -940,9 +947,8 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, struct PostProcessDofDownsample { FrameGraphId color; FrameGraphId depth; - FrameGraphId outForeground; - FrameGraphId outBackground; - FrameGraphId outCocFgBg; + FrameGraphId outColor; + FrameGraphId outCoc; }; auto& ppDoFDownsample = fg.addPass("DoF Downsample", @@ -950,21 +956,21 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, data.color = builder.sample(input); data.depth = builder.sample(depth); - data.outForeground = builder.createTexture("dof foreground output", { + data.outColor = builder.createTexture("dof downsample output", { .width = width, .height = height, .levels = mipmapCount, .format = format }); - data.outBackground = builder.createTexture("dof background output", { - .width = width, .height = height, .levels = mipmapCount, .format = format - }); - data.outCocFgBg = builder.createTexture("dof CoC output", { + data.outCoc = builder.createTexture("dof CoC output", { .width = width, .height = height, .levels = mipmapCount, - .format = TextureFormat::RG16F + .format = TextureFormat::R16F, + .swizzle = { + // the next stage expects min/max CoC in the red/green channel + .r = backend::TextureSwizzle::CHANNEL_0, + .g = backend::TextureSwizzle::CHANNEL_0 }, }); - data.outForeground = builder.write(data.outForeground, FrameGraphTexture::Usage::COLOR_ATTACHMENT); - data.outBackground = builder.write(data.outBackground, FrameGraphTexture::Usage::COLOR_ATTACHMENT); - data.outCocFgBg = builder.write(data.outCocFgBg, FrameGraphTexture::Usage::COLOR_ATTACHMENT); + data.outColor = builder.write(data.outColor, FrameGraphTexture::Usage::COLOR_ATTACHMENT); + data.outCoc = builder.write(data.outCoc, FrameGraphTexture::Usage::COLOR_ATTACHMENT); builder.declareRenderPass("DoF Target", { .attachments = { - .color = { data.outForeground, data.outBackground, data.outCocFgBg } + .color = { data.outColor, data.outCoc } } }); }, @@ -979,6 +985,9 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, mi->setParameter("color", color, { .filterMin = SamplerMinFilter::NEAREST }); mi->setParameter("depth", depth, { .filterMin = SamplerMinFilter::NEAREST }); mi->setParameter("cocParams", cocParams); + mi->setParameter("cocClamp", float2{ + -(dofOptions.maxForegroundCOC ? dofOptions.maxForegroundCOC : DOF_DEFAULT_MAX_COC), + dofOptions.maxBackgroundCOC ? dofOptions.maxBackgroundCOC : DOF_DEFAULT_MAX_COC}); mi->setParameter("uvscale", float4{ width, height, 1.0f / colorDesc.width, 1.0f / colorDesc.height }); commitAndRender(out, material, driver); @@ -990,9 +999,8 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, */ struct PostProcessDofMipmap { - FrameGraphId inOutForeground; - FrameGraphId inOutBackground; - FrameGraphId inOutCocFgBg; + FrameGraphId inOutColor; + FrameGraphId inOutCoc; uint32_t rp[maxMipLevels]; }; @@ -1000,25 +1008,22 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, auto& ppDoFMipmap = fg.addPass("DoF Mipmap", [&](FrameGraph::Builder& builder, auto& data) { - data.inOutForeground = builder.sample(ppDoFDownsample->outForeground); - data.inOutBackground = builder.sample(ppDoFDownsample->outBackground); - data.inOutCocFgBg = builder.sample(ppDoFDownsample->outCocFgBg); + data.inOutColor = builder.sample(ppDoFDownsample->outColor); + data.inOutCoc = builder.sample(ppDoFDownsample->outCoc); for (size_t i = 0; i < mipmapCount - 1u; i++) { // make sure inputs are always multiple of two (should be true by construction) // (this is so that we can compute clean mip levels) - assert_invariant((FTexture::valueForLevel(uint8_t(i), fg.getDescriptor(data.inOutForeground).width ) & 0x1u) == 0); - assert_invariant((FTexture::valueForLevel(uint8_t(i), fg.getDescriptor(data.inOutForeground).height) & 0x1u) == 0); + assert_invariant((FTexture::valueForLevel(uint8_t(i), fg.getDescriptor(data.inOutColor).width ) & 0x1u) == 0); + assert_invariant((FTexture::valueForLevel(uint8_t(i), fg.getDescriptor(data.inOutColor).height) & 0x1u) == 0); - auto inOutForeground = builder.createSubresource(data.inOutForeground, "Forground mip", { .level = uint8_t(i + 1) }); - auto inOutBackground = builder.createSubresource(data.inOutBackground, "Background mip", { .level = uint8_t(i + 1) }); - auto inOutCocFgBg = builder.createSubresource(data.inOutCocFgBg , "CocFgBg mip", { .level = uint8_t(i + 1) }); + auto inOutColor = builder.createSubresource(data.inOutColor, "Color mip", { .level = uint8_t(i + 1) }); + auto inOutCoc = builder.createSubresource(data.inOutCoc, "Coc mip", { .level = uint8_t(i + 1) }); - inOutForeground = builder.write(inOutForeground, FrameGraphTexture::Usage::COLOR_ATTACHMENT); - inOutBackground = builder.write(inOutBackground, FrameGraphTexture::Usage::COLOR_ATTACHMENT); - inOutCocFgBg = builder.write(inOutCocFgBg, FrameGraphTexture::Usage::COLOR_ATTACHMENT); + inOutColor = builder.write(inOutColor, FrameGraphTexture::Usage::COLOR_ATTACHMENT); + inOutCoc = builder.write(inOutCoc, FrameGraphTexture::Usage::COLOR_ATTACHMENT); data.rp[i] = builder.declareRenderPass("DoF Target", { .attachments = { - .color = { inOutForeground, inOutBackground, inOutCocFgBg } + .color = { inOutColor, inOutCoc } } }); } @@ -1026,15 +1031,13 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, [=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) { - auto inOutForeground = resources.getTexture(data.inOutForeground); - auto inOutBackground = resources.getTexture(data.inOutBackground); - auto inOutCocFgBg = resources.getTexture(data.inOutCocFgBg); + auto inOutColor = resources.getTexture(data.inOutColor); + auto inOutCoc = resources.getTexture(data.inOutCoc); auto const& material = getPostProcessMaterial("dofMipmap"); FMaterialInstance* const mi = material.getMaterialInstance(); - mi->setParameter("foreground", inOutForeground, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); - mi->setParameter("background", inOutBackground, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); - mi->setParameter("cocFgBg", inOutCocFgBg, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); + mi->setParameter("color", inOutColor, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); + mi->setParameter("coc", inOutCoc, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); mi->use(driver); const PipelineState pipeline(material.getPipelineState(variant)); @@ -1055,7 +1058,7 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, * - Generate min/max tiles for far/near fields (continued) */ - auto inTilesCocMaxMin = ppDoFDownsample->outCocFgBg; + auto inTilesCocMinMax = ppDoFDownsample->outCoc; // TODO: Should the tile size be in real pixels? i.e. always 16px instead of being dependant on // the DoF effect resolution? @@ -1067,38 +1070,41 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, const size_t tileReductionCount = std::log2(tileSize / dofResolution); struct PostProcessDofTiling1 { - FrameGraphId inCocMaxMin; - FrameGraphId outTilesCocMaxMin; + FrameGraphId inCocMinMax; + FrameGraphId outTilesCocMinMax; }; + const bool textureSwizzleSupported = Texture::isTextureSwizzleSupported(mEngine); for (size_t i = 0; i < tileReductionCount; i++) { auto& ppDoFTiling = fg.addPass("DoF Tiling", [&](FrameGraph::Builder& builder, auto& data) { assert_invariant(((tileBufferWidth >> i) & 1u) == 0); assert_invariant(((tileBufferHeight >> i) & 1u) == 0); - data.inCocMaxMin = builder.sample(inTilesCocMaxMin); - data.outTilesCocMaxMin = builder.createTexture("dof tiles output", { + data.inCocMinMax = builder.sample(inTilesCocMinMax); + data.outTilesCocMinMax = builder.createTexture("dof tiles output", { .width = tileBufferWidth >> (i + 1u), .height = tileBufferHeight >> (i + 1u), .format = TextureFormat::RG16F }); - data.outTilesCocMaxMin = builder.declareRenderPass(data.outTilesCocMaxMin); + data.outTilesCocMinMax = builder.declareRenderPass(data.outTilesCocMinMax); }, [=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) { - auto const& inputDesc = resources.getDescriptor(data.inCocMaxMin); - auto const& outputDesc = resources.getDescriptor(data.outTilesCocMaxMin); + auto const& inputDesc = resources.getDescriptor(data.inCocMinMax); + auto const& outputDesc = resources.getDescriptor(data.outTilesCocMinMax); auto const& out = resources.getRenderPassInfo(); - auto inCocMaxMin = resources.getTexture(data.inCocMaxMin); - auto const& material = getPostProcessMaterial("dofTiles"); + auto inCocMinMax = resources.getTexture(data.inCocMinMax); + auto const& material = (!textureSwizzleSupported && (i == 0)) ? + getPostProcessMaterial("dofTilesSwizzle") : + getPostProcessMaterial("dofTiles"); FMaterialInstance* const mi = material.getMaterialInstance(); - mi->setParameter("cocMaxMin", inCocMaxMin, { .filterMin = SamplerMinFilter::NEAREST }); + mi->setParameter("cocMinMax", inCocMinMax, { .filterMin = SamplerMinFilter::NEAREST }); mi->setParameter("uvscale", float4{ outputDesc.width, outputDesc.height, 1.0f / inputDesc.width, 1.0f / inputDesc.height }); commitAndRender(out, material, driver); }); - inTilesCocMaxMin = ppDoFTiling->outTilesCocMaxMin; + inTilesCocMinMax = ppDoFTiling->outTilesCocMinMax; } /* @@ -1109,33 +1115,33 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, auto dilate = [&](FrameGraphId input) -> FrameGraphId { struct PostProcessDofDilate { - FrameGraphId inTilesCocMaxMin; - FrameGraphId outTilesCocMaxMin; + FrameGraphId inTilesCocMinMax; + FrameGraphId outTilesCocMinMax; }; auto& ppDoFDilate = fg.addPass("DoF Dilate", [&](FrameGraph::Builder& builder, auto& data) { auto const& inputDesc = fg.getDescriptor(input); - data.inTilesCocMaxMin = builder.sample(input); - data.outTilesCocMaxMin = builder.createTexture("dof dilated tiles output", inputDesc); - data.outTilesCocMaxMin = builder.declareRenderPass(data.outTilesCocMaxMin ); + data.inTilesCocMinMax = builder.sample(input); + data.outTilesCocMinMax = builder.createTexture("dof dilated tiles output", inputDesc); + data.outTilesCocMinMax = builder.declareRenderPass(data.outTilesCocMinMax ); }, [=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) { auto const& out = resources.getRenderPassInfo(); - auto inTilesCocMaxMin = resources.getTexture(data.inTilesCocMaxMin); + auto inTilesCocMinMax = resources.getTexture(data.inTilesCocMinMax); auto const& material = getPostProcessMaterial("dofDilate"); FMaterialInstance* const mi = material.getMaterialInstance(); - mi->setParameter("tiles", inTilesCocMaxMin, { .filterMin = SamplerMinFilter::NEAREST }); + mi->setParameter("tiles", inTilesCocMinMax, { .filterMin = SamplerMinFilter::NEAREST }); commitAndRender(out, material, driver); }); - return ppDoFDilate->outTilesCocMaxMin; + return ppDoFDilate->outTilesCocMinMax; }; // Tiles of 16 full-resolution pixels requires two dilate rounds to accommodate our max Coc of 32 pixels // (note: when running at half-res, the tiles are 8 half-resolution pixels, and still need two // dilate rounds to accommodate the mac CoC pf 16 half-resolution pixels) - auto dilated = dilate(inTilesCocMaxMin); + auto dilated = dilate(inTilesCocMinMax); dilated = dilate(dilated); /* @@ -1143,66 +1149,70 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, */ struct PostProcessDof { - FrameGraphId foreground; - FrameGraphId background; - FrameGraphId cocFgBg; - FrameGraphId tilesCocMaxMin; - FrameGraphId outForeground; + FrameGraphId color; + FrameGraphId coc; + FrameGraphId tilesCocMinMax; + FrameGraphId outColor; FrameGraphId outAlpha; }; auto& ppDoF = fg.addPass("DoF", [&](FrameGraph::Builder& builder, auto& data) { - data.foreground = builder.sample(ppDoFMipmap->inOutForeground); - data.background = builder.sample(ppDoFMipmap->inOutBackground); - data.cocFgBg = builder.sample(ppDoFMipmap->inOutCocFgBg); - data.tilesCocMaxMin = builder.sample(dilated); + data.color = builder.sample(ppDoFMipmap->inOutColor); + data.coc = builder.sample(ppDoFMipmap->inOutCoc); + data.tilesCocMinMax = builder.sample(dilated); // The DoF buffer (output) doesn't need to be a multiple of 8 because it's not // mipmapped. We just need to adjust the uv properly. - data.outForeground = builder.createTexture("dof color output", { + data.outColor = builder.createTexture("dof color output", { .width = (colorDesc.width + (dofResolution / 2u)) / dofResolution, .height = (colorDesc.height + (dofResolution / 2u)) / dofResolution, - .format = fg.getDescriptor(data.foreground).format + .format = fg.getDescriptor(data.color).format }); data.outAlpha = builder.createTexture("dof alpha output", { - .width = builder.getDescriptor(data.outForeground).width, - .height = builder.getDescriptor(data.outForeground).height, + .width = builder.getDescriptor(data.outColor).width, + .height = builder.getDescriptor(data.outColor).height, .format = TextureFormat::R8 }); - data.outForeground = builder.write(data.outForeground, FrameGraphTexture::Usage::COLOR_ATTACHMENT); - data.outAlpha = builder.write(data.outAlpha, FrameGraphTexture::Usage::COLOR_ATTACHMENT); + data.outColor = builder.write(data.outColor, FrameGraphTexture::Usage::COLOR_ATTACHMENT); + data.outAlpha = builder.write(data.outAlpha, FrameGraphTexture::Usage::COLOR_ATTACHMENT); builder.declareRenderPass("DoF Target", { - .attachments = { .color = { data.outForeground, data.outAlpha }} + .attachments = { .color = { data.outColor, data.outAlpha }} }); }, [=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) { auto const& out = resources.getRenderPassInfo(); - auto foreground = resources.getTexture(data.foreground); - auto background = resources.getTexture(data.background); - auto cocFgBg = resources.getTexture(data.cocFgBg); - auto tilesCocMaxMin = resources.getTexture(data.tilesCocMaxMin); + auto color = resources.getTexture(data.color); + auto coc = resources.getTexture(data.coc); + auto tilesCocMinMax = resources.getTexture(data.tilesCocMinMax); - auto const& inputDesc = resources.getDescriptor(data.cocFgBg); - auto const& outputDesc = resources.getDescriptor(data.outForeground); - auto const& tilesDesc = resources.getDescriptor(data.tilesCocMaxMin); + auto const& inputDesc = resources.getDescriptor(data.coc); + auto const& outputDesc = resources.getDescriptor(data.outColor); + auto const& tilesDesc = resources.getDescriptor(data.tilesCocMinMax); auto const& material = getPostProcessMaterial("dof"); FMaterialInstance* const mi = material.getMaterialInstance(); // it's not safe to use bilinear filtering in the general case (causes artifacts around edges) - mi->setParameter("foreground", foreground, + mi->setParameter("color", color, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); - mi->setParameter("foregroundLinear", foreground, + mi->setParameter("colorLinear", color, { .filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST }); - mi->setParameter("background", background, + mi->setParameter("coc", coc, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); - mi->setParameter("cocFgBg", cocFgBg, - { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); - mi->setParameter("tiles", tilesCocMaxMin, + mi->setParameter("tiles", tilesCocMinMax, { .filterMin = SamplerMinFilter::NEAREST }); - mi->setParameter("cocToTexelScale", (1.0f / dofResolution) / float2{ inputDesc.width, inputDesc.height }); + + // The bokeh height is always correct regardless of the dynamic resolution scaling. + // (because the CoC is calculated w.r.t. the height), so we only need to adjust + // the width. + const float aspectRatio = scale.x / scale.y; + mi->setParameter("cocToTexelScale", float2{ + aspectRatio / (inputDesc.width * dofResolution), + 1.0 / (inputDesc.height * dofResolution) + }); + mi->setParameter("cocToPixelScale", (1.0f / dofResolution)); mi->setParameter("uvscale", float4{ outputDesc.width / float(inputDesc.width), @@ -1210,6 +1220,12 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, outputDesc.width / float(tileSize / dofResolution * tilesDesc.width), outputDesc.height / float(tileSize / dofResolution * tilesDesc.height) }); + mi->setParameter("ringCounts", float4{ + dofOptions.foregroundRingCount ? dofOptions.foregroundRingCount : DOF_DEFAULT_RING_COUNT, + dofOptions.backgroundRingCount ? dofOptions.backgroundRingCount : DOF_DEFAULT_RING_COUNT, + dofOptions.fastGatherRingCount ? dofOptions.fastGatherRingCount : DOF_DEFAULT_RING_COUNT, + 0.0 // unused for now + }); mi->setParameter("bokehAngle", bokehAngle); commitAndRender(out, material, driver); }); @@ -1220,43 +1236,43 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, struct PostProcessDofMedian { - FrameGraphId inForeground; + FrameGraphId inColor; FrameGraphId inAlpha; - FrameGraphId tilesCocMaxMin; - FrameGraphId outForeground; + FrameGraphId tilesCocMinMax; + FrameGraphId outColor; FrameGraphId outAlpha; }; auto& ppDoFMedian = fg.addPass("DoF Median", [&](FrameGraph::Builder& builder, auto& data) { - data.inForeground = builder.sample(ppDoF->outForeground); + data.inColor = builder.sample(ppDoF->outColor); data.inAlpha = builder.sample(ppDoF->outAlpha); - data.tilesCocMaxMin = builder.sample(dilated); + data.tilesCocMinMax = builder.sample(dilated); - data.outForeground = builder.createTexture("dof color output", fg.getDescriptor(data.inForeground)); - data.outAlpha = builder.createTexture("dof alpha output", fg.getDescriptor(data.inAlpha)); - data.outForeground = builder.write(data.outForeground, FrameGraphTexture::Usage::COLOR_ATTACHMENT); - data.outAlpha = builder.write(data.outAlpha, FrameGraphTexture::Usage::COLOR_ATTACHMENT); + data.outColor = builder.createTexture("dof color output", fg.getDescriptor(data.inColor)); + data.outAlpha = builder.createTexture("dof alpha output", fg.getDescriptor(data.inAlpha)); + data.outColor = builder.write(data.outColor, FrameGraphTexture::Usage::COLOR_ATTACHMENT); + data.outAlpha = builder.write(data.outAlpha, FrameGraphTexture::Usage::COLOR_ATTACHMENT); builder.declareRenderPass("DoF Target", { - .attachments = { .color = { data.outForeground, data.outAlpha }} + .attachments = { .color = { data.outColor, data.outAlpha }} }); }, [=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) { auto const& out = resources.getRenderPassInfo(); - auto inForeground = resources.getTexture(data.inForeground); + auto inColor = resources.getTexture(data.inColor); auto inAlpha = resources.getTexture(data.inAlpha); - auto tilesCocMaxMin = resources.getTexture(data.tilesCocMaxMin); + auto tilesCocMinMax = resources.getTexture(data.tilesCocMinMax); - auto const& outputDesc = resources.getDescriptor(data.outForeground); - auto const& tilesDesc = resources.getDescriptor(data.tilesCocMaxMin); + auto const& outputDesc = resources.getDescriptor(data.outColor); + auto const& tilesDesc = resources.getDescriptor(data.tilesCocMinMax); auto const& material = getPostProcessMaterial("dofMedian"); FMaterialInstance* const mi = material.getMaterialInstance(); - mi->setParameter("dof", inForeground, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); + mi->setParameter("dof", inColor, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); mi->setParameter("alpha", inAlpha, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST }); - mi->setParameter("tiles", tilesCocMaxMin, { .filterMin = SamplerMinFilter::NEAREST }); + mi->setParameter("tiles", tilesCocMinMax, { .filterMin = SamplerMinFilter::NEAREST }); mi->setParameter("uvscale", float2{ outputDesc.width / float(tileSize / dofResolution * tilesDesc.width), outputDesc.height / float(tileSize / dofResolution * tilesDesc.height) @@ -1269,10 +1285,10 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, * DoF recombine */ - auto outForeground = ppDoFMedian->outForeground; + auto outColor = ppDoFMedian->outColor; auto outAlpha = ppDoFMedian->outAlpha; if (dofOptions.filter == View::DepthOfFieldOptions::Filter::NONE) { - outForeground = ppDoF->outForeground; + outColor = ppDoF->outColor; outAlpha = ppDoF->outAlpha; } @@ -1280,16 +1296,16 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, FrameGraphId color; FrameGraphId dof; FrameGraphId alpha; - FrameGraphId tilesCocMaxMin; + FrameGraphId tilesCocMinMax; FrameGraphId output; }; auto& ppDoFCombine = fg.addPass("DoF combine", [&](FrameGraph::Builder& builder, auto& data) { data.color = builder.sample(input); - data.dof = builder.sample(outForeground); + data.dof = builder.sample(outColor); data.alpha = builder.sample(outAlpha); - data.tilesCocMaxMin = builder.sample(dilated); + data.tilesCocMinMax = builder.sample(dilated); auto const& inputDesc = fg.getDescriptor(data.color); data.output = builder.createTexture("DoF output", inputDesc); data.output = builder.declareRenderPass(data.output); @@ -1297,20 +1313,20 @@ FrameGraphId PostProcessManager::dof(FrameGraph& fg, [=](FrameGraphResources const& resources, auto const& data, DriverApi& driver) { auto const& dofDesc = resources.getDescriptor(data.dof); - auto const& tilesDesc = resources.getDescriptor(data.tilesCocMaxMin); + auto const& tilesDesc = resources.getDescriptor(data.tilesCocMinMax); auto const& out = resources.getRenderPassInfo(); auto color = resources.getTexture(data.color); auto dof = resources.getTexture(data.dof); auto alpha = resources.getTexture(data.alpha); - auto tilesCocMaxMin = resources.getTexture(data.tilesCocMaxMin); + auto tilesCocMinMax = resources.getTexture(data.tilesCocMinMax); auto const& material = getPostProcessMaterial("dofCombine"); FMaterialInstance* const mi = material.getMaterialInstance(); mi->setParameter("color", color, { .filterMin = SamplerMinFilter::NEAREST }); mi->setParameter("dof", dof, { .filterMag = SamplerMagFilter::NEAREST }); mi->setParameter("alpha", alpha, { .filterMag = SamplerMagFilter::NEAREST }); - mi->setParameter("tiles", tilesCocMaxMin, { .filterMin = SamplerMinFilter::NEAREST }); + mi->setParameter("tiles", tilesCocMinMax, { .filterMin = SamplerMinFilter::NEAREST }); mi->setParameter("uvscale", float4{ colorDesc.width / (dofDesc.width * float(dofResolution)), colorDesc.height / (dofDesc.height * float(dofResolution)), diff --git a/filament/src/PostProcessManager.h b/filament/src/PostProcessManager.h index 24aa894af1..2e109e00bd 100644 --- a/filament/src/PostProcessManager.h +++ b/filament/src/PostProcessManager.h @@ -79,11 +79,9 @@ public: size_t kernelWidth, float sigmaRatio = 6.0f) noexcept; // Depth-of-field - FrameGraphId dof(FrameGraph& fg, - FrameGraphId input, - const View::DepthOfFieldOptions& dofOptions, - bool translucent, - const CameraInfo& cameraInfo) noexcept; + FrameGraphId dof(FrameGraph& fg, FrameGraphId input, + const View::DepthOfFieldOptions& dofOptions, bool translucent, + const CameraInfo& cameraInfo, math::float2 scale) noexcept; // Color grading, tone mapping, etc. void colorGradingPrepareSubpass(backend::DriverApi& driver, const FColorGrading* colorGrading, diff --git a/filament/src/Renderer.cpp b/filament/src/Renderer.cpp index b5667dfa6e..2e46ec0260 100644 --- a/filament/src/Renderer.cpp +++ b/filament/src/Renderer.cpp @@ -445,13 +445,13 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) { }); // resolve depth -- which might be needed because of TAA or DoF. This pass will be culled - // if the depth is not use below. + // if the depth is not used below. auto& blackboard = fg.getBlackboard(); auto depth = blackboard.get("depth"); depth = ppm.resolve(fg, "Resolved Depth Buffer", depth); blackboard.put("depth", depth); - // TODO: DoF should be applied here, before TAA -- but if we do this it'll result is a lot + // TODO: DoF should be applied here, before TAA -- but if we do this it'll result in a lot of // fireflies due to the instability of the highlights. This can be fixed with a // dedicated TAA pass for the DoF, as explained in // "Life of a Bokeh" by Guillaume Abadie, SIGGRAPH 2018 @@ -466,7 +466,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) { if (hasPostProcess) { if (dofOptions.enabled) { - input = ppm.dof(fg, input, dofOptions, needsAlphaChannel, cameraInfo); + input = ppm.dof(fg, input, dofOptions, needsAlphaChannel, cameraInfo, scale); } if (colorGrading) { if (!colorGradingConfig.asSubpass) { diff --git a/filament/src/ResourceAllocator.cpp b/filament/src/ResourceAllocator.cpp index af743b58da..29deb5eeab 100644 --- a/filament/src/ResourceAllocator.cpp +++ b/filament/src/ResourceAllocator.cpp @@ -109,9 +109,10 @@ void ResourceAllocator::destroyRenderTarget(RenderTargetHandle h) noexcept { } backend::TextureHandle ResourceAllocator::createTexture(const char* name, - backend::SamplerType target, uint8_t levels, - backend::TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, - uint32_t depth, backend::TextureUsage usage) noexcept { + backend::SamplerType target, uint8_t levels, backend::TextureFormat format, uint8_t samples, + uint32_t width, uint32_t height, uint32_t depth, + std::array swizzle, + backend::TextureUsage usage) noexcept { // Some WebGL implementations complain about an incomplete framebuffer when the attachment sizes // are heterogeneous. This merits further investigation. @@ -133,7 +134,7 @@ backend::TextureHandle ResourceAllocator::createTexture(const char* name, TextureHandle handle; if (mEnabled) { auto& textureCache = mTextureCache; - const TextureKey key{ name, target, levels, format, samples, width, height, depth, usage }; + const TextureKey key{ name, target, levels, format, samples, width, height, depth, usage, swizzle }; auto it = textureCache.find(key); if (UTILS_LIKELY(it != textureCache.end())) { // we do, move the entry to the in-use list, and remove from the cache @@ -142,8 +143,17 @@ backend::TextureHandle ResourceAllocator::createTexture(const char* name, textureCache.erase(it); } else { // we don't, allocate a new texture and populate the in-use list - handle = mBackend.createTexture( - target, levels, format, samples, width, height, depth, usage); + using TS = backend::TextureSwizzle; + constexpr const auto defaultSwizzle = std::array{ + TS::CHANNEL_0, TS::CHANNEL_1, TS::CHANNEL_2, TS::CHANNEL_3}; + if (swizzle == defaultSwizzle) { + handle = mBackend.createTexture( + target, levels, format, samples, width, height, depth, usage); + } else { + handle = mBackend.createTextureSwizzled( + target, levels, format, samples, width, height, depth, usage, + swizzle[0], swizzle[1], swizzle[2], swizzle[3]); + } } mInUseTextures.emplace(handle, key); } else { diff --git a/filament/src/ResourceAllocator.h b/filament/src/ResourceAllocator.h index 2df9cd7a12..18d9eb1201 100644 --- a/filament/src/ResourceAllocator.h +++ b/filament/src/ResourceAllocator.h @@ -25,6 +25,7 @@ #include +#include #include #include @@ -47,9 +48,10 @@ public: virtual void destroyRenderTarget(backend::RenderTargetHandle h) noexcept = 0; virtual backend::TextureHandle createTexture(const char* name, backend::SamplerType target, - uint8_t levels, - backend::TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, - uint32_t depth, backend::TextureUsage usage) noexcept = 0; + uint8_t levels,backend::TextureFormat format, uint8_t samples, + uint32_t width, uint32_t height, uint32_t depth, + std::array swizzle, + backend::TextureUsage usage) noexcept = 0; virtual void destroyTexture(backend::TextureHandle h) noexcept = 0; @@ -77,9 +79,10 @@ public: void destroyRenderTarget(backend::RenderTargetHandle h) noexcept override; backend::TextureHandle createTexture(const char* name, backend::SamplerType target, - uint8_t levels, - backend::TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, - uint32_t depth, backend::TextureUsage usage) noexcept override; + uint8_t levels, backend::TextureFormat format, uint8_t samples, + uint32_t width, uint32_t height, uint32_t depth, + std::array swizzle, + backend::TextureUsage usage) noexcept override; void destroyTexture(backend::TextureHandle h) noexcept override; @@ -100,6 +103,7 @@ private: uint32_t height; uint32_t depth; backend::TextureUsage usage; + std::array swizzle; size_t getSize() const noexcept; @@ -111,7 +115,8 @@ private: width == other.width && height == other.height && depth == other.depth && - usage == other.usage; + usage == other.usage && + swizzle == other.swizzle; } friend size_t hash_value(TextureKey const& k) { @@ -124,6 +129,10 @@ private: utils::hash::combine_fast(seed, k.height); utils::hash::combine_fast(seed, k.depth); utils::hash::combine_fast(seed, k.usage); + utils::hash::combine_fast(seed, k.swizzle[0]); + utils::hash::combine_fast(seed, k.swizzle[1]); + utils::hash::combine_fast(seed, k.swizzle[2]); + utils::hash::combine_fast(seed, k.swizzle[3]); return seed; } }; diff --git a/filament/src/Texture.cpp b/filament/src/Texture.cpp index f269e0ded5..07133bec4a 100644 --- a/filament/src/Texture.cpp +++ b/filament/src/Texture.cpp @@ -443,6 +443,10 @@ bool FTexture::isTextureFormatSupported(FEngine& engine, InternalFormat format) return engine.getDriverApi().isTextureFormatSupported(format); } +bool FTexture::isTextureSwizzleSupported(FEngine& engine) noexcept { + return engine.getDriverApi().isTextureSwizzleSupported(); +} + size_t FTexture::computeTextureDataSize(Texture::Format format, Texture::Type type, size_t stride, size_t height, size_t alignment) noexcept { return PixelBufferDescriptor::computeDataSize(format, type, stride, height, alignment); @@ -1090,6 +1094,10 @@ bool Texture::isTextureFormatSupported(Engine& engine, InternalFormat format) no return FTexture::isTextureFormatSupported(upcast(engine), format); } +bool Texture::isTextureSwizzleSupported(Engine& engine) noexcept { + return FTexture::isTextureSwizzleSupported(upcast(engine)); +} + size_t Texture::computeTextureDataSize(Texture::Format format, Texture::Type type, size_t stride, size_t height, size_t alignment) noexcept { return FTexture::computeTextureDataSize(format, type, stride, height, alignment); diff --git a/filament/src/VertexBuffer.cpp b/filament/src/VertexBuffer.cpp index 80878cb65a..f11af61f86 100644 --- a/filament/src/VertexBuffer.cpp +++ b/filament/src/VertexBuffer.cpp @@ -16,6 +16,7 @@ #include "details/VertexBuffer.h" +#include "details/BufferObject.h" #include "details/Engine.h" #include "FilamentAPI-impl.h" @@ -36,6 +37,7 @@ struct VertexBuffer::BuilderDetails { AttributeBitset mDeclaredAttributes; uint32_t mVertexCount = 0; uint8_t mBufferCount = 0; + bool mBufferObjectsEnabled = false; }; using BuilderType = VertexBuffer; @@ -51,6 +53,11 @@ VertexBuffer::Builder& VertexBuffer::Builder::vertexCount(uint32_t vertexCount) return *this; } +VertexBuffer::Builder& VertexBuffer::Builder::enableBufferObjects(bool enabled) noexcept { + mImpl->mBufferObjectsEnabled = enabled; + return *this; +} + VertexBuffer::Builder& VertexBuffer::Builder::bufferCount(uint8_t bufferCount) noexcept { mImpl->mBufferCount = bufferCount; return *this; @@ -140,7 +147,8 @@ VertexBuffer* VertexBuffer::Builder::build(Engine& engine) { // ------------------------------------------------------------------------------------------------ FVertexBuffer::FVertexBuffer(FEngine& engine, const VertexBuffer::Builder& builder) - : mVertexCount(builder->mVertexCount), mBufferCount(builder->mBufferCount) { + : mVertexCount(builder->mVertexCount), mBufferCount(builder->mBufferCount), + mBufferObjectsEnabled(builder->mBufferObjectsEnabled) { std::copy(std::begin(builder->mAttributes), std::end(builder->mAttributes), mAttributes.begin()); mDeclaredAttributes = builder->mDeclaredAttributes; @@ -174,7 +182,8 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const VertexBuffer::Builder& build FEngine::DriverApi& driver = engine.getDriverApi(); mHandle = driver.createVertexBuffer( - mBufferCount, attributeCount, mVertexCount, attributeArray, backend::BufferUsage::STATIC); + mBufferCount, attributeCount, mVertexCount, attributeArray, + backend::BufferUsage::STATIC, mBufferObjectsEnabled); } void FVertexBuffer::terminate(FEngine& engine) { @@ -188,12 +197,25 @@ size_t FVertexBuffer::getVertexCount() const noexcept { void FVertexBuffer::setBufferAt(FEngine& engine, uint8_t bufferIndex, backend::BufferDescriptor&& buffer, uint32_t byteOffset) { + ASSERT_PRECONDITION(!mBufferObjectsEnabled, "Please use setBufferObjectAt()"); if (bufferIndex < mBufferCount) { engine.getDriverApi().updateVertexBuffer(mHandle, bufferIndex, std::move(buffer), byteOffset); } else { - ASSERT_PRECONDITION_NON_FATAL(bufferIndex < mBufferCount, - "bufferIndex must be < bufferCount"); + ASSERT_PRECONDITION(bufferIndex < mBufferCount, "bufferIndex must be < bufferCount"); + } +} + +void FVertexBuffer::setBufferObjectAt(FEngine& engine, uint8_t bufferIndex, + FBufferObject const * bufferObject) { + ASSERT_PRECONDITION(mBufferObjectsEnabled, "Please use setBufferAt()"); + ASSERT_PRECONDITION(bufferObject->getBindingType() == BufferObject::BindingType::VERTEX, + "Binding type must be VERTEX."); + if (bufferIndex < mBufferCount) { + auto hwBufferObject = bufferObject->getHwHandle(); + engine.getDriverApi().setVertexBufferObject(mHandle, bufferIndex, hwBufferObject); + } else { + ASSERT_PRECONDITION(bufferIndex < mBufferCount, "bufferIndex must be < bufferCount"); } } @@ -210,6 +232,11 @@ void VertexBuffer::setBufferAt(Engine& engine, uint8_t bufferIndex, upcast(this)->setBufferAt(upcast(engine), bufferIndex, std::move(buffer), byteOffset); } +void VertexBuffer::setBufferObjectAt(Engine& engine, uint8_t bufferIndex, + BufferObject const* bufferObject) { + upcast(this)->setBufferObjectAt(upcast(engine), bufferIndex, upcast(bufferObject)); +} + void VertexBuffer::populateTangentQuaternions(const QuatTangentContext& ctx) { auto* quats = geometry::SurfaceOrientation::Builder() .vertexCount(ctx.quatCount) diff --git a/filament/src/details/BufferObject.h b/filament/src/details/BufferObject.h new file mode 100644 index 0000000000..ef74b52a10 --- /dev/null +++ b/filament/src/details/BufferObject.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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. + */ + +#ifndef TNT_FILAMENT_DETAILS_BUFFEROBJECT_H +#define TNT_FILAMENT_DETAILS_BUFFEROBJECT_H + +#include "upcast.h" + +#include + +#include + +#include + +namespace filament { + +class FEngine; + +class FBufferObject : public BufferObject { +public: + FBufferObject(FEngine& engine, const Builder& builder); + + // frees driver resources, object becomes invalid + void terminate(FEngine& engine); + + backend::Handle getHwHandle() const noexcept { return mHandle; } + + size_t getByteCount() const noexcept { return mByteCount; } + + void setBuffer(FEngine& engine, BufferDescriptor&& buffer, uint32_t byteOffset = 0); + + BindingType getBindingType() const noexcept { return mBindingType; } + +private: + friend class BufferObject; + backend::Handle mHandle; + uint32_t mByteCount; + BindingType mBindingType; +}; + +FILAMENT_UPCAST(BufferObject) + +} // namespace filament + +#endif // TNT_FILAMENT_DETAILS_BUFFEROBJECT_H diff --git a/filament/src/details/Engine.h b/filament/src/details/Engine.h index 568099e290..1260c036d1 100644 --- a/filament/src/details/Engine.h +++ b/filament/src/details/Engine.h @@ -26,6 +26,7 @@ #include "components/RenderableManager.h" #include "details/Allocators.h" +#include "details/BufferObject.h" #include "details/Camera.h" #include "details/DebugRegistry.h" #include "details/Fence.h" @@ -223,6 +224,7 @@ public: template T* create(ResourceList& list, typename T::Builder const& builder) noexcept; + FBufferObject* createBufferObject(const BufferObject::Builder& builder) noexcept; FVertexBuffer* createVertexBuffer(const VertexBuffer::Builder& builder) noexcept; FIndexBuffer* createIndexBuffer(const IndexBuffer::Builder& builder) noexcept; FIndirectLight* createIndirectLight(const IndirectLight::Builder& builder) noexcept; @@ -250,6 +252,7 @@ public: void destroyCameraComponent(utils::Entity entity) noexcept; + bool destroy(const FBufferObject* p); bool destroy(const FVertexBuffer* p); bool destroy(const FFence* p); bool destroy(const FIndexBuffer* p); @@ -340,6 +343,7 @@ private: FCameraManager mCameraManager; ResourceAllocator* mResourceAllocator = nullptr; + ResourceList mBufferObjects{ "BufferObject" }; ResourceList mRenderers{ "Renderer" }; ResourceList mViews{ "View" }; ResourceList mScenes{ "Scene" }; diff --git a/filament/src/details/Texture.h b/filament/src/details/Texture.h index 1e9f6e9005..b75f475575 100644 --- a/filament/src/details/Texture.h +++ b/filament/src/details/Texture.h @@ -86,6 +86,9 @@ public: // synchronous call to the backend. returns whether a backend supports a particular format. static bool isTextureFormatSupported(FEngine& engine, InternalFormat format) noexcept; + // synchronous call to the backend. returns whether a backend supports texture swizzling. + static bool isTextureSwizzleSupported(FEngine& engine) noexcept; + // storage needed on the CPU side for texture data uploads static size_t computeTextureDataSize(Texture::Format format, Texture::Type type, size_t stride, size_t height, size_t alignment) noexcept; diff --git a/filament/src/details/VertexBuffer.h b/filament/src/details/VertexBuffer.h index 09992e2f4f..07b5c6aedb 100644 --- a/filament/src/details/VertexBuffer.h +++ b/filament/src/details/VertexBuffer.h @@ -32,6 +32,7 @@ namespace filament { +class FBufferObject; class FEngine; class FVertexBuffer : public VertexBuffer { @@ -53,6 +54,9 @@ public: void setBufferAt(FEngine& engine, uint8_t bufferIndex, backend::BufferDescriptor&& buffer, uint32_t byteOffset = 0); + void setBufferObjectAt(FEngine& engine, uint8_t bufferIndex, + FBufferObject const * bufferObject); + private: friend class VertexBuffer; @@ -65,6 +69,7 @@ private: AttributeBitset mDeclaredAttributes; uint32_t mVertexCount = 0; uint8_t mBufferCount = 0; + bool mBufferObjectsEnabled = false; }; FILAMENT_UPCAST(VertexBuffer) diff --git a/filament/src/fg2/FrameGraphTexture.cpp b/filament/src/fg2/FrameGraphTexture.cpp index 9d922b44c7..a7b03da869 100644 --- a/filament/src/fg2/FrameGraphTexture.cpp +++ b/filament/src/fg2/FrameGraphTexture.cpp @@ -24,10 +24,15 @@ namespace filament { void FrameGraphTexture::create(ResourceAllocatorInterface& resourceAllocator, const char* name, FrameGraphTexture::Descriptor const& descriptor, FrameGraphTexture::Usage usage) noexcept { + std::array swizzle = { + descriptor.swizzle.r, + descriptor.swizzle.g, + descriptor.swizzle.b, + descriptor.swizzle.a }; handle = resourceAllocator.createTexture(name, descriptor.type, descriptor.levels, descriptor.format, descriptor.samples, descriptor.width, descriptor.height, descriptor.depth, - usage); + swizzle, usage); } void FrameGraphTexture::destroy(ResourceAllocatorInterface& resourceAllocator) noexcept { diff --git a/filament/src/fg2/FrameGraphTexture.h b/filament/src/fg2/FrameGraphTexture.h index 86dab22574..a1064bbaad 100644 --- a/filament/src/fg2/FrameGraphTexture.h +++ b/filament/src/fg2/FrameGraphTexture.h @@ -49,6 +49,16 @@ struct FrameGraphTexture { uint8_t samples = 0; // 0=auto, 1=request not multisample, >1 only for NOT SAMPLEABLE backend::SamplerType type = backend::SamplerType::SAMPLER_2D; // texture target type backend::TextureFormat format = backend::TextureFormat::RGBA8; // resource internal format + struct { + using TS = backend::TextureSwizzle; + union { + backend::TextureSwizzle channels[4] = { + TS::CHANNEL_0, TS::CHANNEL_1, TS::CHANNEL_2, TS::CHANNEL_3 }; + struct { + backend::TextureSwizzle r, g, b, a; + }; + }; + } swizzle; }; /** Describes a FrameGraphTexture sub-resource */ diff --git a/filament/src/materials/dof/dof.mat b/filament/src/materials/dof/dof.mat index 642429eba5..0068554a53 100644 --- a/filament/src/materials/dof/dof.mat +++ b/filament/src/materials/dof/dof.mat @@ -3,22 +3,17 @@ material { parameters : [ { type : sampler2d, - name : foreground, + name : color, precision: medium }, { type : sampler2d, - name : foregroundLinear, + name : colorLinear, precision: medium }, { type : sampler2d, - name : background, - precision: medium - }, - { - type : sampler2d, - name : cocFgBg, + name : coc, precision: medium }, { @@ -36,14 +31,19 @@ material { name : cocToPixelScale, precision: medium }, + { + type : float, + name : bokehAngle, + }, { type : float4, name : uvscale, precision: high }, { - type : float, - name : bokehAngle, + type : float4, + name : ringCounts, + precision: medium } ], outputs : [ @@ -80,20 +80,6 @@ fragment { #define DOF_DIAPHRAGM_STRAIGHT_BLADES 1 #define DOF_DIAPHRAGM DOF_DIAPHRAGM_CIRCLE -// # of sample w.r.t. ring count and density of 8 -// 3 rings : 25 ( 5^2 grid) -// 4 rings : 49 ( 7^2 grid) -// 5 rings : 81 ( 9^2 grid) -// 17 rings : 1089 (33^2 grid) -- maximum needed for a CoC of 32 - -#if defined(TARGET_MOBILE) -#define RING_COUNT_GATHER 3 -#define RING_COUNT_FAST 3 -#else -#define RING_COUNT_GATHER 4 -#define RING_COUNT_FAST 4 -#endif - // Enabled by default. Mipmaped taps look a lot better, but do cause some // aliasing issues which force us to set all sample weights to 1.0 // This is here mostly for debugging. @@ -111,6 +97,22 @@ struct NoiseState { vec2 randomDisk; }; +// # of sample w.r.t. ring count and density of 8 +// 3 rings : 25 ( 5^2 grid) +// 4 rings : 49 ( 7^2 grid) +// 5 rings : 81 ( 9^2 grid) +// 17 rings : 1089 (33^2 grid) -- maximum needed for a CoC of 32 + +float ringCountForeground() { + return materialParams.ringCounts[0]; +} +float ringCountBackground() { + return materialParams.ringCounts[1]; +} +float ringCountFast() { + return materialParams.ringCounts[2]; +} + float sampleCount(const float ringCount) { float s = (ringCount * 2.0 - 1.0); return s * s; @@ -207,8 +209,9 @@ struct Bucket { precision mediump float; struct Sample { - vec4 s; // sample color - float coc; // sample coc + vec4 s; // sample color + float coc; // sample coc + float inLayer; // whether is sample is in the considered layer }; void initBucket(out Bucket ring) { @@ -268,7 +271,7 @@ void mergeRings(inout Bucket curr, inout Bucket prev, const float count) { void accumulate(inout Bucket curr, inout Bucket prev, const Sample tap, const float radius, const float border, const float mip, const bool first) { - float inLayer = isBackground(tap.coc); + float inLayer = tap.inLayer; float coc = abs(tap.coc); float w = inLayer * intersection(radius, coc, mip) * sampleWeight(coc, mip); @@ -303,16 +306,43 @@ void accumulate(inout Bucket curr, inout Bucket prev, const Sample tap, void accumulateBackground(inout Bucket curr, inout Bucket prev, const highp vec2 pos, const float radius, const float border, const float mip, const bool first) { Sample tap; - tap.s = textureLod(materialParams_foreground, pos, mip); - tap.coc = textureLod(materialParams_cocFgBg, pos, mip).r; + tap.s = textureLod(materialParams_color, pos, mip); + tap.coc = textureLod(materialParams_coc, pos, mip).r; + tap.inLayer = isBackground(tap.coc); accumulate(curr, prev, tap, radius, border, mip, first); } void accumulateBackgroundMirror(inout Bucket curr, inout Bucket prev, const highp vec2 center, const vec2 offset, const float radius, const float border, const float mip, const bool first) { - accumulateBackground(curr, prev, diaphragm(center, offset), radius, border, mip, first); - accumulateBackground(curr, prev, diaphragm(center, -offset), radius, border, mip, first); + // The code below is equivalent (without hole filling) to: + // accumulateBackground(curr, prev, diaphragm(center, offset), radius, border, mip, first); + // accumulateBackground(curr, prev, diaphragm(center, -offset), radius, border, mip, first); + // return; + // We use the technique used by Jimenez for the foreground applied to the background, where + // we "guess" the missing background information from neighboring pixels. In several papers it's + // not deemed useful for the background, however, it does improve the blur quality a lot very + // shallow DoF, i.e. when a moderately blurred background sits over a strongly blured one. + Sample tap0; + Sample tap1; + + highp vec2 pos0 = diaphragm(center, offset); + tap0.coc = textureLod(materialParams_coc, pos0, mip).r; + + highp vec2 pos1 = diaphragm(center, -offset); + tap1.coc = textureLod(materialParams_coc, pos1, mip).r; + + float coc = min(tap0.coc, tap1.coc); + tap0.coc = + tap1.coc = coc; + tap0.inLayer = + tap1.inLayer = isBackground(coc); + + tap0.s = textureLod(materialParams_color, pos0, mip); + accumulate(curr, prev, tap0, radius, border, mip, first); + + tap1.s = textureLod(materialParams_color, pos1, mip); + accumulate(curr, prev, tap1, radius, border, mip, first); } void accumulateBackgroundCenter(inout Bucket prev, @@ -384,8 +414,8 @@ float foregroundFadding(const float coc) { void accumulateForeground(inout Layer layer[2], const vec2 tiles, const highp vec2 pos, const float border, const float mip) { - float coc = textureLod(materialParams_cocFgBg, pos, mip).r; - vec4 s = textureLod(materialParams_foreground, pos, mip); + float coc = textureLod(materialParams_coc, pos, mip).r; + vec4 s = textureLod(materialParams_color, pos, mip); float inLayer = isForeground(coc) * foregroundFadding(coc); float i = intersection(border, abs(coc), mip) * sampleWeight(coc, mip); @@ -414,12 +444,12 @@ void accumulateForegroundMirror(inout Layer layer[2], const vec2 tiles, // the geometry. highp vec2 pos0 = diaphragm(center, offset); - float coc0 = textureLod(materialParams_cocFgBg, pos0, mip).r; - vec4 s0 = textureLod(materialParams_foreground, pos0, mip); + float coc0 = textureLod(materialParams_coc, pos0, mip).r; + vec4 s0 = textureLod(materialParams_color, pos0, mip); highp vec2 pos1 = diaphragm(center, -offset); - float coc1 = textureLod(materialParams_cocFgBg, pos1, mip).r; - vec4 s1 = textureLod(materialParams_foreground, pos1, mip); + float coc1 = textureLod(materialParams_coc, pos1, mip).r; + vec4 s1 = textureLod(materialParams_color, pos1, mip); float coc = min(coc0, coc1); float inLayer = isForeground(coc) * foregroundFadding(coc); @@ -446,7 +476,7 @@ void fastTile(inout vec4 color, inout float alpha, // The whole tile has mostly the same CoC float kernelSize = (abs(tiles.r) + abs(tiles.g)) * 0.5; - float ringCountFast = min(float(RING_COUNT_FAST), computeNeededRings(kernelSize * cocToPixelScale)); + float ringCountFast = min(ringCountFast(), computeNeededRings(kernelSize * cocToPixelScale)); float mip = getMipLevel(ringCountFast, kernelSize * cocToPixelScale); vec2 noise = vec2(0.0); @@ -457,7 +487,7 @@ void fastTile(inout vec4 color, inout float alpha, highp vec2 uvCenter = uv + noise * cocToTexelScale; - color = textureLod(materialParams_foregroundLinear, uvCenter, mip); + color = textureLod(materialParams_colorLinear, uvCenter, mip); for (float i = 1.0; i < ringCountFast; i += 1.0) { float radius; float count; @@ -467,9 +497,9 @@ void fastTile(inout vec4 color, inout float alpha, // we must use this temporary in order to accomodate mediump precision vec4 t = vec4(0.0); for (float j = 0.0; j < count; j += 2.0) { - t += textureLod(materialParams_foregroundLinear, + t += textureLod(materialParams_colorLinear, diaphragm(uvCenter, p * cocToTexelScale), mip) - + textureLod(materialParams_foregroundLinear, + + textureLod(materialParams_colorLinear, diaphragm(uvCenter, -p * cocToTexelScale), mip); p = r * p; } @@ -487,7 +517,7 @@ void foregroundTile(inout vec4 foreground, inout float fgOpacity, // Here we know that tiles.r < 0, by definition of a foreground tile. // For a foreground tile, the kernel size is the largest CoC radius of the whole tile. float kernelSize = -tiles.r; - float ringCountGather = min(float(RING_COUNT_GATHER), computeNeededRings(kernelSize * cocToPixelScale)); + float ringCountGather = min(ringCountForeground(), computeNeededRings(kernelSize * cocToPixelScale)); float mip = getMipLevel(ringCountGather, kernelSize * cocToPixelScale); vec2 noise = vec2(0.0); @@ -540,9 +570,9 @@ void backgroundTile(inout vec4 background, inout float bgOpacity, float cocToPixelScale = materialParams.cocToPixelScale; // we select a ring cound per tile, to avoid divergence between pixels - float ringCountGather = min(float(RING_COUNT_GATHER), computeNeededRings(tiles.g * cocToPixelScale)); + float ringCountGather = min(ringCountBackground(), computeNeededRings(tiles.g * cocToPixelScale)); - float centerCoc = textureLod(materialParams_cocFgBg, uv, 0.0).g; + float centerCoc = textureLod(materialParams_coc, uv, 0.0).r; float kernelSize = abs(centerCoc); float mip = getMipLevel(ringCountGather, kernelSize * cocToPixelScale); float noiseRadius = 0.0; @@ -559,16 +589,9 @@ void backgroundTile(inout vec4 background, inout float bgOpacity, initBucket(prev); accumulateRing(prev, 0.0, ringCountGather, kernelSize, noise, uvCenter, cocToTexelScale, mip, true); - - // the optimizer is not able to remove this loop when it has only one iteration -#if RING_COUNT_GATHER == 3 - accumulateRing(prev, 1.0, ringCountGather, kernelSize, noise, uvCenter, cocToTexelScale, mip, false); -#else for (float i = 1.0; i < ringCountGather - 1.0 ; i += 1.0) { accumulateRing(prev, i, ringCountGather, kernelSize, noise, uvCenter, cocToTexelScale, mip, false); } -#endif - accumulateBackgroundCenter(prev, uvCenter, ringCountGather, kernelSize, noiseRadius, mip); float kernelArea = sampleWeight(kernelSize * ((ringCountGather + 0.5) / ringCountGather), mip); @@ -647,7 +670,7 @@ void postProcess(inout PostProcessInputs postProcess) { // TODO: make this a quality setting if (fgOpacity > 0.0 && fgOpacity < 1.0 && bgOpacity == 0.0) { // this happens on very few pixels - float centerCoc = textureLod(materialParams_cocFgBg, uv, 0.0).r; + float centerCoc = textureLod(materialParams_coc, uv, 0.0).r; float correctedOpacity = max(fgOpacity, cocToAlpha(centerCoc)); float updateOpacity = correctedOpacity / fgOpacity; foreground *= updateOpacity; diff --git a/filament/src/materials/dof/dofCoc.mat b/filament/src/materials/dof/dofCoc.mat index c13766c81b..4ff3d1d3c8 100644 --- a/filament/src/materials/dof/dofCoc.mat +++ b/filament/src/materials/dof/dofCoc.mat @@ -15,6 +15,10 @@ material { type : float2, name : cocParams }, + { + type : float2, + name : cocClamp + }, { type : float4, name : uvscale, @@ -31,14 +35,9 @@ material { type : float4 }, { - name : background, + name : coc, target : color, - type : float4 - }, - { - name : cocFgBg, - target : color, - type : float2 + type : float } ], domain : postprocess, @@ -64,26 +63,30 @@ void dummy(){} void postProcess(inout PostProcessInputs postProcess) { highp vec2 uv = variable_vertex.xy; - vec4 color = textureLod(materialParams_color, uv, 0.0); + vec4 outColor = textureLod(materialParams_color, uv, 0.0); float d = textureLod(materialParams_depth, uv, 0.0).r; // Get the CoC radius. // We multiply by 0.5 to convert from diameter to radius. - float coc = getCOC(d, materialParams.cocParams) * 0.5; + float outCoc = getCOC(d, materialParams.cocParams) * 0.5; + + // Clamp to maximum allowable CoC radius. + outCoc = clamp(outCoc, materialParams.cocClamp[0], materialParams.cocClamp[1]); // If CoC is less that 0.5 full resolution pixel, we clamp to 0, this will reduce // artifacts in the "in focus" area and allow us to skip more tiles trivially. - if (abs(coc) < MAX_IN_FOCUS_COC) { - coc = 0.0; + if (abs(outCoc) < MAX_IN_FOCUS_COC) { + outCoc = 0.0; } - // Clamp to maximum allowable CoC radius. - coc = clamp(coc, -MAX_COC_RADIUS, MAX_COC_RADIUS); +#if POST_PROCESS_OPAQUE + // kill the work performed above + outColor.a = 1.0; +#endif // Output to MRTs - postProcess.color = color; - postProcess.background = color; - postProcess.cocFgBg = vec2(coc, coc); + postProcess.color = outColor; + postProcess.coc = outCoc; } } diff --git a/filament/src/materials/dof/dofCombine.mat b/filament/src/materials/dof/dofCombine.mat index b8b81daaf7..c52063d350 100644 --- a/filament/src/materials/dof/dofCombine.mat +++ b/filament/src/materials/dof/dofCombine.mat @@ -64,9 +64,9 @@ void postProcess(inout PostProcessInputs postProcess) { highp vec2 uv0 = variable_vertex.xy; highp vec2 uv1 = variable_vertex.xy * materialParams.uvscale.xy; float alpha = textureLod(materialParams_alpha, uv1, 0.0).r; - vec4 foreground = textureLod(materialParams_dof, uv1, 0.0); + vec4 dof = textureLod(materialParams_dof, uv1, 0.0); color = textureLod(materialParams_color, uv0, 0.0); - color = foreground + (1.0 - alpha) * color; + color = dof + (1.0 - alpha) * color; } #if POST_PROCESS_OPAQUE diff --git a/filament/src/materials/dof/dofDilate.mat b/filament/src/materials/dof/dofDilate.mat index 5b7339fa29..5b194760f7 100644 --- a/filament/src/materials/dof/dofDilate.mat +++ b/filament/src/materials/dof/dofDilate.mat @@ -35,20 +35,8 @@ vec2 tap(const vec2 uv, const vec2 offset) { } vec2 dilate(inout vec2 center, const vec2 tap) { - // 2.24 tiles is the longest distance between 2 pixels of adjacent tiles. - // sqrt(2^2 + 1^2) = 2.24 - const float maxDistance = max(-MAX_COC_RADIUS, -2.24 * TILE_SIZE); - // Tiles that can affect us need to transfer their CoC to us (for the foreground it's // min(ourCoc, theirCoc) because CoC are negative (the min is the larger radius). - - // TODO: However, if their CoC is very large, we can limit it by the distance to that - // tile, because we only need to reach *them*). - // The difficuty here is the this clamping depends on if we're negative or positive. - // e.g. for positive values - // center.g = min(center.g, max(tap.g, maxDistance)); - // center.r = max(center.r, max(tap.r, maxDistance)); - center.r = min(center.r, tap.r); center.g = max(center.g, tap.g); return center; diff --git a/filament/src/materials/dof/dofDownsample.mat b/filament/src/materials/dof/dofDownsample.mat index b7351ef2be..7bcea00e45 100644 --- a/filament/src/materials/dof/dofDownsample.mat +++ b/filament/src/materials/dof/dofDownsample.mat @@ -15,6 +15,10 @@ material { type : float2, name : cocParams }, + { + type : float2, + name : cocClamp + }, { type : float4, name : uvscale, @@ -31,14 +35,9 @@ material { type : float4 }, { - name : background, + name : coc, target : color, - type : float4 - }, - { - name : cocFgBg, - target : color, - type : float2 + type : float } ], domain : postprocess, @@ -73,63 +72,45 @@ void postProcess(inout PostProcessInputs postProcess) { vec4 hdr; // fireflies/flickering filtering - hdr.x = 1.0 / (1.0 + max3(s00.rgb)); - hdr.y = 1.0 / (1.0 + max3(s11.rgb)); - hdr.z = 1.0 / (1.0 + max3(s10.rgb)); - hdr.w = 1.0 / (1.0 + max3(s00.rgb)); + hdr[0] = 1.0 / (1.0 + max3(s01.rgb)); + hdr[1] = 1.0 / (1.0 + max3(s11.rgb)); + hdr[2] = 1.0 / (1.0 + max3(s10.rgb)); + hdr[3] = 1.0 / (1.0 + max3(s00.rgb)); vec4 d; #if defined(TARGET_VULKAN_ENVIRONMENT) || !defined(TARGET_MOBILE) d = textureGather(materialParams_depth, uv, 0); // 01, 11, 10, 00 #else - d.x = textureLodOffset(materialParams_depth, uv, 0.0, ivec2(0, 1)).r; - d.y = textureLodOffset(materialParams_depth, uv, 0.0, ivec2(1, 1)).r; - d.z = textureLodOffset(materialParams_depth, uv, 0.0, ivec2(1, 0)).r; - d.w = textureLodOffset(materialParams_depth, uv, 0.0, ivec2(0, 0)).r; + d[0] = textureLodOffset(materialParams_depth, uv, 0.0, ivec2(0, 1)).r; + d[1] = textureLodOffset(materialParams_depth, uv, 0.0, ivec2(1, 1)).r; + d[2] = textureLodOffset(materialParams_depth, uv, 0.0, ivec2(1, 0)).r; + d[3] = textureLodOffset(materialParams_depth, uv, 0.0, ivec2(0, 0)).r; #endif // Get the CoC radius of each four samples to downsample. // We multiply by 0.5 to convert from diameter to radius. - vec4 w = getCOC(d, materialParams.cocParams) * 0.5; + vec4 c = getCOC(d, materialParams.cocParams) * 0.5; + float outCoc = downsampleCoC(c); + vec4 w = downsampleCocWeights(c, outCoc, 1.0) * hdr; + vec4 outColor = (s01 * w[0] + s11 * w[1] + s10 * w[2] + s00 * w[3]) * rcp(dot(vec4(1.0), w)); - // We calculates the downsampled CoC to be conservative, that is if any - // of the 4 texels are foreground or background, then the whole new texel is too. - float fgCoc = min2(min(w.xy, w.zw)); - - // The forground bilateral weight is calculated as saturate(1.0 - (fgCoc - w)), which always - // yields 1.0. Note the "missing" abs(), this is because we want to let the background layer - // leak into the foreground layer, to avoid aliasing artifacts. - vec4 foreground = (s01 * hdr.x + s11 * hdr.y + s10 * hdr.z + s00 * hdr.w) * (1.0 / (hdr.x + hdr.y + hdr.z + hdr.w)); - - // the background bilateral weight is calculated as saturate(1.0 - abs(bgCoc - w)), but the - // abs() is not needed since bgCoc - w is guaranteed to be >= 0. - float bgCoc = max2(max(w.xy, w.zw)); - vec4 bw = saturate(1.0 - (bgCoc - w)) * hdr; - vec4 background = (s01 * bw.x + s11 * bw.y + s10 * bw.z + s00 * bw.w) * (1.0 / (bw.x + bw.y + bw.z + bw.w)); + // Clamp to maximum allowable CoC radius. + outCoc = clamp(outCoc, materialParams.cocClamp[0], materialParams.cocClamp[1]); // If CoC is less that 0.5 full resolution pixel, we clamp to 0, this will reduce // artifacts in the "in focus" area and allow us to skip more tiles trivially. - if (abs(fgCoc) < MAX_IN_FOCUS_COC) { - fgCoc = 0.0; + if (abs(outCoc) < MAX_IN_FOCUS_COC) { + outCoc = 0.0; } - if (abs(bgCoc) < MAX_IN_FOCUS_COC){ - bgCoc = 0.0; - } - - // Clamp to maximum allowable CoC radius. - fgCoc = clamp(fgCoc, -MAX_COC_RADIUS, MAX_COC_RADIUS); - bgCoc = clamp(bgCoc, -MAX_COC_RADIUS, MAX_COC_RADIUS); #if POST_PROCESS_OPAQUE // kill the work performed above - foreground.a = 1.0; - background.a = 1.0; + outColor.a = 1.0; #endif // Output to MRTs - postProcess.color = foreground; - postProcess.background = background; - postProcess.cocFgBg = vec2(fgCoc, bgCoc); + postProcess.color = outColor; + postProcess.coc = outCoc; } } diff --git a/filament/src/materials/dof/dofMipmap.mat b/filament/src/materials/dof/dofMipmap.mat index 249b3f4ba3..7d6ee4373f 100644 --- a/filament/src/materials/dof/dofMipmap.mat +++ b/filament/src/materials/dof/dofMipmap.mat @@ -3,17 +3,12 @@ material { parameters : [ { type : sampler2d, - name : foreground, + name : color, precision: medium }, { type : sampler2d, - name : background, - precision: medium - }, - { - type : sampler2d, - name : cocFgBg, + name : coc, precision: medium }, { @@ -35,14 +30,9 @@ material { type : float4 }, { - name : background, + name : coc, target : color, - type : float4 - }, - { - name : cocFgBg, - target : color, - type : float2 + type : float } ], domain : postprocess, @@ -63,7 +53,7 @@ fragment { void dummy(){} void postProcess(inout PostProcessInputs postProcess) { - highp vec2 pixelSize = 1.0 / vec2(textureSize(materialParams_foreground, materialParams.mip)); + highp vec2 pixelSize = 1.0 / vec2(textureSize(materialParams_color, materialParams.mip)); highp vec2 uv = variable_vertex.xy - pixelSize.xy * 0.5; // the source is guaranteed to be a multiple of two, so we now the bilinear weights are 0.25 @@ -72,60 +62,30 @@ void postProcess(inout PostProcessInputs postProcess) { float weightScale = materialParams.weightScale; float mip = float(materialParams.mip); - // fetch the 4 corresponding CoC - vec4 d0, d1; - d0.xy = textureLodOffset(materialParams_cocFgBg, uv, mip, ivec2(0, 1)).rg; - d0.zw = textureLodOffset(materialParams_cocFgBg, uv, mip, ivec2(1, 1)).rg; - d1.xy = textureLodOffset(materialParams_cocFgBg, uv, mip, ivec2(1, 0)).rg; - d1.zw = textureLodOffset(materialParams_cocFgBg, uv, mip, ivec2(0, 0)).rg; - vec4 fw = vec4(d0.xz, d1.xz); - vec4 bw = vec4(d0.yw, d1.yw); + vec4 s01 = textureLodOffset(materialParams_color, uv, mip, ivec2(0, 1)); + vec4 s11 = textureLodOffset(materialParams_color, uv, mip, ivec2(1, 1)); + vec4 s10 = textureLodOffset(materialParams_color, uv, mip, ivec2(1, 0)); + vec4 s00 = textureLodOffset(materialParams_color, uv, mip, ivec2(0, 0)); - /* - * foreground - */ + // fetch the 4 corresponding CoC (textureGather with LOD doesn't exist) + vec4 c; + c[0] = textureLodOffset(materialParams_coc, uv, mip, ivec2(0, 1)).r; + c[1] = textureLodOffset(materialParams_coc, uv, mip, ivec2(1, 1)).r; + c[2] = textureLodOffset(materialParams_coc, uv, mip, ivec2(1, 0)).r; + c[3] = textureLodOffset(materialParams_coc, uv, mip, ivec2(0, 0)).r; - vec4 fg01 = textureLodOffset(materialParams_foreground, uv, mip, ivec2(0, 1)); - vec4 fg11 = textureLodOffset(materialParams_foreground, uv, mip, ivec2(1, 1)); - vec4 fg10 = textureLodOffset(materialParams_foreground, uv, mip, ivec2(1, 0)); - vec4 fg00 = textureLodOffset(materialParams_foreground, uv, mip, ivec2(0, 0)); - - // We calculates the downsampled CoC to be conservative, that is if any - // of the 4 texels are foreground or background, then the whole new texel is too. - float fgCoc = min4(fw); - - // The forground bilateral weight is calculated as saturate(1.0 - (fgCoc - w)), which always - // yields 1.0. Note the "missing" abs(), this is because we want to let the background layer - // leak into the foreground layer, to avoid aliasing artifacts. - vec4 foreground = (fg01 + fg11 + fg10 + fg00) * 0.25; - - /* - * background - */ - - vec4 bg01 = textureLodOffset(materialParams_background, uv, mip, ivec2(0, 1)); - vec4 bg11 = textureLodOffset(materialParams_background, uv, mip, ivec2(1, 1)); - vec4 bg10 = textureLodOffset(materialParams_background, uv, mip, ivec2(1, 0)); - vec4 bg00 = textureLodOffset(materialParams_background, uv, mip, ivec2(0, 0)); - - float bgCoc = max4(bw); - - // The background bilateral weight is calculated as saturate(1.0 - abs(bgCoc - w)), - // but the abs() is not needed since bgCoc - w is guaranteed to be >= 0. - bw = saturate(1.0 - (bgCoc - bw) * weightScale); - float bgScale = 1.0 / (bw.x + bw.y + bw.z + bw.w); - vec4 background = (bg01 * bw.x + bg11 * bw.y + bg10 * bw.z + bg00 * bw.w) * bgScale; + float outCoc = downsampleCoC(c); + vec4 w = downsampleCocWeights(c, outCoc, weightScale); + vec4 outColor = (s01 * w[0] + s11 * w[1] + s10 * w[2] + s00 * w[3]) * rcp(dot(vec4(1.0), w)); #if POST_PROCESS_OPAQUE // kill the work performed above - foreground.a = 1.0; - background.a = 1.0; + outColor.a = 1.0; #endif // Output to MRTs - postProcess.color = foreground; - postProcess.background = background; - postProcess.cocFgBg = vec2(fgCoc, bgCoc); + postProcess.color = outColor; + postProcess.coc = outCoc; } } diff --git a/filament/src/materials/dof/dofTiles.mat b/filament/src/materials/dof/dofTiles.mat index 8ff850d1c3..b3c4c3f342 100644 --- a/filament/src/materials/dof/dofTiles.mat +++ b/filament/src/materials/dof/dofTiles.mat @@ -3,7 +3,7 @@ material { parameters : [ { type : sampler2d, - name : cocMaxMin, + name : cocMinMax, precision: medium }, { @@ -42,27 +42,29 @@ float min4(const vec4 f) { void postProcess(inout PostProcessInputs postProcess) { highp vec2 uv = variable_vertex.xy; - highp vec2 size = vec2(textureSize(materialParams_cocMaxMin, 0)); + highp vec2 size = vec2(textureSize(materialParams_cocMinMax, 0)); ivec2 i = ivec2(uv * size - 0.5); + // note: the first time, the source texture is swizzled -- so .r and .g are the same channel + // if swizzling is not supported, we use dofTilesSwizzle instead. #if defined(TARGET_VULKAN_ENVIRONMENT) || !defined(TARGET_MOBILE) - vec4 f = textureGather(materialParams_cocMaxMin, uv, 0); // 01, 11, 10, 00 - vec4 b = textureGather(materialParams_cocMaxMin, uv, 1); // 01, 11, 10, 00 + vec4 mi = textureGather(materialParams_cocMinMax, uv, 0); // 01, 11, 10, 00 + vec4 ma = textureGather(materialParams_cocMinMax, uv, 1); // 01, 11, 10, 00 #else - vec2 s01 = texelFetchOffset(materialParams_cocMaxMin, i, 0, ivec2(0, 1)).rg; - vec2 s11 = texelFetchOffset(materialParams_cocMaxMin, i, 0, ivec2(1, 1)).rg; - vec2 s10 = texelFetchOffset(materialParams_cocMaxMin, i, 0, ivec2(1, 0)).rg; - vec2 s00 = texelFetchOffset(materialParams_cocMaxMin, i, 0, ivec2(0, 0)).rg; - vec4 f = vec4(s01.r, s11.r, s10.r, s00.r); - vec4 b = vec4(s01.g, s11.g, s10.g, s00.g); + vec2 s01 = texelFetchOffset(materialParams_cocMinMax, i, 0, ivec2(0, 1)).rg; + vec2 s11 = texelFetchOffset(materialParams_cocMinMax, i, 0, ivec2(1, 1)).rg; + vec2 s10 = texelFetchOffset(materialParams_cocMinMax, i, 0, ivec2(1, 0)).rg; + vec2 s00 = texelFetchOffset(materialParams_cocMinMax, i, 0, ivec2(0, 0)).rg; + vec4 mi = vec4(s01.r, s11.r, s10.r, s00.r); + vec4 ma = vec4(s01.g, s11.g, s10.g, s00.g); #endif // compute tile's min CoC - postProcess.color.r = min4(min(f, b)); + postProcess.color.r = min4(mi); // compute tile's max CoC - postProcess.color.g = max4(max(f, b)); + postProcess.color.g = max4(ma); } } diff --git a/filament/src/materials/dof/dofTilesSwizzle.mat b/filament/src/materials/dof/dofTilesSwizzle.mat new file mode 100644 index 0000000000..e852b57e8c --- /dev/null +++ b/filament/src/materials/dof/dofTilesSwizzle.mat @@ -0,0 +1,66 @@ +material { + name : dofTilesSwizzle, + parameters : [ + { + type : sampler2d, + name : cocMinMax, + precision: medium + }, + { + type : float4, + name : uvscale, + precision: high + } + ], + variables : [ + vertex + ], + domain : postprocess, + depthWrite : false, + depthCulling : false +} + +vertex { + void postProcessVertex(inout PostProcessVertexInputs postProcess) { + postProcess.vertex.xy = (postProcess.normalizedUV * materialParams.uvscale.xy * 2.0 - 0.5) * materialParams.uvscale.zw; + } +} + +fragment { + +void dummy(){} + +float max4(const vec4 f) { + vec2 t = max(f.xy, f.zw); + return max(t.x, t.y); +} + +float min4(const vec4 f) { + vec2 t = min(f.xy, f.zw); + return min(t.x, t.y); +} + +void postProcess(inout PostProcessInputs postProcess) { + highp vec2 uv = variable_vertex.xy; + highp vec2 size = vec2(textureSize(materialParams_cocMinMax, 0)); + + ivec2 i = ivec2(uv * size - 0.5); + +#if defined(TARGET_VULKAN_ENVIRONMENT) || !defined(TARGET_MOBILE) + vec4 mi = textureGather(materialParams_cocMinMax, uv, 0); // 01, 11, 10, 00 +#else + vec4 mi; + mi[0] = texelFetchOffset(materialParams_cocMinMax, i, 0, ivec2(0, 1)).r; + mi[1] = texelFetchOffset(materialParams_cocMinMax, i, 0, ivec2(1, 1)).r; + mi[2] = texelFetchOffset(materialParams_cocMinMax, i, 0, ivec2(1, 0)).r; + mi[3] = texelFetchOffset(materialParams_cocMinMax, i, 0, ivec2(0, 0)).r; +#endif + + // compute tile's min CoC + postProcess.color.r = min4(mi); + + // compute tile's max CoC + postProcess.color.g = max4(mi); +} + +} diff --git a/filament/src/materials/dof/dofUtils.fs b/filament/src/materials/dof/dofUtils.fs index 56db650221..d577ce672c 100644 --- a/filament/src/materials/dof/dofUtils.fs +++ b/filament/src/materials/dof/dofUtils.fs @@ -96,3 +96,23 @@ bool isTrivialTile(const vec2 tiles) { float maxCocRadius = max(abs(tiles.r), abs(tiles.g)); return maxCocRadius < MAX_IN_FOCUS_COC; } + +float downsampleCoC(const vec4 c) { + // We need to compute a suitable CoC to represent the 4 pixels that are downsampled. + // We pick the min because this always selects the most foreground sample if there is one, + // because the foreground can leak on the background, but not the reverse. + return min4(c); +} + +vec4 downsampleCocWeights(const vec4 c, const float outCoc, const float scale) { + // The bilateral weight is normally computed as saturate(1 - |outCoc - c|) which selects + // the sample with the outCoc weight (and does a little bit of cross-fade if other samples + // are close). However, this can also cause some aliasing with dithered objects, so by + // omitting the abs() we allow samples in the background to "leak" into the foreground, which + // is not completely different from the "mirror hole filling" of the DoF pass. + // See "Life of a Bokeh" by Guillaume Abadie, SIGGRAPH 2018. + + // Note: With the implementation of downsampleCoC() above, this always resolves to 1.0; + // return saturate(1.0 - (outCoc - c) * scale); + return vec4(1.0); +} diff --git a/filament/test/filament_framegraph_test.cpp b/filament/test/filament_framegraph_test.cpp index 90b77587fc..16cd2d31c8 100644 --- a/filament/test/filament_framegraph_test.cpp +++ b/filament/test/filament_framegraph_test.cpp @@ -51,7 +51,8 @@ public: backend::TextureHandle createTexture(const char* name, backend::SamplerType target, uint8_t levels, backend::TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, - uint32_t depth, backend::TextureUsage usage) noexcept override { + uint32_t depth, std::array, + backend::TextureUsage usage) noexcept override { return backend::TextureHandle(++handle); } diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index c87c07532f..c09aa7ee5e 100644 --- a/ios/CocoaPods/Filament.podspec +++ b/ios/CocoaPods/Filament.podspec @@ -1,12 +1,12 @@ Pod::Spec.new do |spec| spec.name = "Filament" - spec.version = "1.9.19" + spec.version = "1.9.20" spec.license = { :type => "Apache 2.0", :file => "LICENSE" } spec.homepage = "https://google.github.io/filament" spec.authors = "Google LLC." spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL." spec.platform = :ios, "11.0" - spec.source = { :http => "https://github.com/google/filament/releases/download/v1.9.19/filament-v1.9.19-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.9.20/filament-v1.9.20-ios.tgz" } # Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon. spec.pod_target_xcconfig = { diff --git a/java/filament/CMakeLists.txt b/java/filament/CMakeLists.txt index b4cfed5c78..e39f25508b 100644 --- a/java/filament/CMakeLists.txt +++ b/java/filament/CMakeLists.txt @@ -40,6 +40,7 @@ set(COMMON_DIR ${ANDROID_DIR}/common) include_directories(${JNI_INCLUDE_DIRS} ${ANDROID_DIR}) set(JNI_SOURCE_FILES + ${FILAMENT_DIR}/src/main/cpp/BufferObject.cpp ${FILAMENT_DIR}/src/main/cpp/Camera.cpp ${FILAMENT_DIR}/src/main/cpp/Colors.cpp ${FILAMENT_DIR}/src/main/cpp/ColorGrading.cpp @@ -116,6 +117,7 @@ get_filename_component(FILAMENT_JAVA_DIR ${FILAMENT_JAVA_DIR} ABSOLUTE) set(JAVA_SOURCE_FILES ${FILAMENT_JAVA_DIR}/com/google/android/filament/Asserts.java ${FILAMENT_JAVA_DIR}/com/google/android/filament/Box.java + ${FILAMENT_JAVA_DIR}/com/google/android/filament/BufferObject.java ${FILAMENT_JAVA_DIR}/com/google/android/filament/Camera.java ${FILAMENT_JAVA_DIR}/com/google/android/filament/Colors.java ${FILAMENT_JAVA_DIR}/com/google/android/filament/ColorGrading.java diff --git a/libs/filagui/include/filagui/ImGuiHelper.h b/libs/filagui/include/filagui/ImGuiHelper.h index e6ffe2386e..8313974c7e 100644 --- a/libs/filagui/include/filagui/ImGuiHelper.h +++ b/libs/filagui/include/filagui/ImGuiHelper.h @@ -47,7 +47,8 @@ public: using Callback = std::function; // The constructor creates its own Scene and places it in the given View. - ImGuiHelper(filament::Engine* engine, filament::View* view, const utils::Path& fontPath); + ImGuiHelper(filament::Engine* engine, filament::View* view, const utils::Path& fontPath, + ImGuiContext* imGuiContext = nullptr); ~ImGuiHelper(); // Informs ImGui of the current display size, as well as a scaling factor when scissoring. diff --git a/libs/filagui/src/ImGuiHelper.cpp b/libs/filagui/src/ImGuiHelper.cpp index 74f750559c..91b2a4e156 100644 --- a/libs/filagui/src/ImGuiHelper.cpp +++ b/libs/filagui/src/ImGuiHelper.cpp @@ -45,9 +45,10 @@ namespace filagui { #include "generated/resources/filagui_resources.h" -ImGuiHelper::ImGuiHelper(Engine* engine, filament::View* view, const Path& fontPath) : - mEngine(engine), mView(view), mScene(engine->createScene()), - mImGuiContext(ImGui::CreateContext()) { +ImGuiHelper::ImGuiHelper(Engine* engine, filament::View* view, const Path& fontPath, + ImGuiContext *imGuiContext) + : mEngine(engine), mView(view), mScene(engine->createScene()), + mImGuiContext(imGuiContext ? imGuiContext : ImGui::CreateContext()) { ImGuiIO& io = ImGui::GetIO(); // Create a simple alpha-blended 2D blitting material. @@ -63,7 +64,7 @@ ImGuiHelper::ImGuiHelper(Engine* engine, filament::View* view, const Path& fontP createAtlasTexture(engine); // For proggy, switch to NEAREST for pixel-perfect text. - if (fontPath.isEmpty()) { + if (fontPath.isEmpty() && !imGuiContext) { TextureSampler sampler(MinFilter::NEAREST, MagFilter::NEAREST); mMaterial->setDefaultParameter("albedo", mTexture, sampler); } diff --git a/libs/gltfio/src/AssetLoader.cpp b/libs/gltfio/src/AssetLoader.cpp index c00f7c11a3..d051f41f20 100644 --- a/libs/gltfio/src/AssetLoader.cpp +++ b/libs/gltfio/src/AssetLoader.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -989,10 +988,7 @@ MaterialInstance* FAssetLoader::createMaterialInstance(const cgltf_material* inp } const float* e = inputMat->emissive_factor; - // To force emissive to bloom when emissiveFactor is at 1, we multiply by a luminance - // of 6EV. At 6EV, the luminance will be 2^(6-3) = 8 which is about the HDR range of - // our default tone mappers. - mi->setParameter("emissiveFactor", float3(e[0], e[1], e[2]) * Exposure::luminance(6.0f)); + mi->setParameter("emissiveFactor", float3(e[0], e[1], e[2])); const float* c = mrConfig.base_color_factor; mi->setParameter("baseColorFactor", float4(c[0], c[1], c[2], c[3])); diff --git a/libs/viewer/include/viewer/SimpleViewer.h b/libs/viewer/include/viewer/SimpleViewer.h index 7f5e7648f2..a8c534f675 100644 --- a/libs/viewer/include/viewer/SimpleViewer.h +++ b/libs/viewer/include/viewer/SimpleViewer.h @@ -31,6 +31,7 @@ #include +#include #include namespace filagui { @@ -122,8 +123,18 @@ public: * its callback. Note that the first call might be slower since it requires the creation of the * internal ImGuiHelper instance. */ - void renderUserInterface(float timeStepInSeconds, filament::View* guiView, float pixelRatio, - float mouseX, float mouseY, bool mouseButton, float mouseWheelY); + void renderUserInterface(float timeStepInSeconds, filament::View* guiView, float pixelRatio); + + /** + * Event-passing methods, useful only when SimpleViewer manages its own instance of ImGuiHelper. + * The key codes used in these methods are just normal ASCII/ANSI codes. + * @{ + */ + void mouseEvent(float mouseX, float mouseY, bool mouseButton, float mouseWheelY, bool control); + void keyDownEvent(int keyCode); + void keyUpEvent(int keyCode); + void keyPressEvent(int charCode); + /** @}*/ /** * Retrieves the current width of the ImGui "window" which we are using as a sidebar. @@ -233,7 +244,7 @@ private: float mCurvePlot[1024 * 3]; }; -filament::math::mat4f fitIntoUnitCube(const filament::Aabb& bounds); +filament::math::mat4f fitIntoUnitCube(const filament::Aabb& bounds, float zoffset); } // namespace viewer } // namespace filament diff --git a/libs/viewer/src/AutomationEngine.cpp b/libs/viewer/src/AutomationEngine.cpp index 359b884eb2..da117a2e51 100644 --- a/libs/viewer/src/AutomationEngine.cpp +++ b/libs/viewer/src/AutomationEngine.cpp @@ -140,7 +140,11 @@ void AutomationEngine::applySettings(const char* json, size_t jsonLength, View* MaterialInstance* const* materials, size_t materialCount, IndirectLight* ibl, utils::Entity sunlight, LightManager* lm, Scene* scene, Renderer* renderer) { JsonSerializer serializer; - serializer.readJson(json, jsonLength, mSettings); + if (!serializer.readJson(json, jsonLength, mSettings)) { + std::string jsonWithTerminator(json, json + jsonLength); + slog.e << "Badly formed JSON:\n" << jsonWithTerminator.c_str() << io::endl; + return; + } viewer::applySettings(mSettings->view, view); for (size_t i = 0; i < materialCount; i++) { viewer::applySettings(mSettings->material, materials[i]); diff --git a/libs/viewer/src/Settings.cpp b/libs/viewer/src/Settings.cpp index 75d8da1b6d..3d33d7ac17 100644 --- a/libs/viewer/src/Settings.cpp +++ b/libs/viewer/src/Settings.cpp @@ -71,6 +71,12 @@ static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, uint8_t* return i + 1; } +static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, uint16_t* val) { + CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); + *val = strtol(jsonChunk + tokens[i].start, nullptr, 10); + return i + 1; +} + static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, uint32_t* val) { CHECK_TOKTYPE(tokens[i], JSMN_PRIMITIVE); *val = strtol(jsonChunk + tokens[i].start, nullptr, 10); @@ -481,6 +487,18 @@ static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, DepthOfF i = parse(tokens, i + 1, jsonChunk, &out->enabled); } else if (0 == compare(tok, jsonChunk, "filter")) { i = parse(tokens, i + 1, jsonChunk, &out->filter); + } else if (0 == compare(tok, jsonChunk, "nativeResolution")) { + i = parse(tokens, i + 1, jsonChunk, &out->nativeResolution); + } else if (0 == compare(tok, jsonChunk, "foregroundRingCount")) { + i = parse(tokens, i + 1, jsonChunk, &out->foregroundRingCount); + } else if (0 == compare(tok, jsonChunk, "backgroundRingCount")) { + i = parse(tokens, i + 1, jsonChunk, &out->backgroundRingCount); + } else if (0 == compare(tok, jsonChunk, "fastGatherRingCount")) { + i = parse(tokens, i + 1, jsonChunk, &out->fastGatherRingCount); + } else if (0 == compare(tok, jsonChunk, "maxForegroundCOC")) { + i = parse(tokens, i + 1, jsonChunk, &out->maxForegroundCOC); + } else if (0 == compare(tok, jsonChunk, "maxBackgroundCOC")) { + i = parse(tokens, i + 1, jsonChunk, &out->maxBackgroundCOC); } else { slog.w << "Invalid dof options key: '" << STR(tok, jsonChunk) << "'" << io::endl; i = parse(tokens, i + 1); @@ -1185,7 +1203,13 @@ static std::ostream& operator<<(std::ostream& out, const DepthOfFieldOptions& in << "\"cocScale\": " << (in.cocScale) << ",\n" << "\"maxApertureDiameter\": " << (in.maxApertureDiameter) << ",\n" << "\"enabled\": " << to_string(in.enabled) << ",\n" - << "\"filter\": " << (in.filter) << "\n" + << "\"filter\": " << (in.filter) << ",\n" + << "\"nativeResolution\": " << (in.nativeResolution) << ",\n" + << "\"foregroundRingCount\": " << int(in.foregroundRingCount) << ",\n" + << "\"backgroundRingCount\": " << int(in.backgroundRingCount) << ",\n" + << "\"fastGatherRingCount\": " << int(in.fastGatherRingCount) << ",\n" + << "\"maxForegroundCOC\": " << (in.maxForegroundCOC) << ",\n" + << "\"maxBackgroundCOC\": " << (in.maxBackgroundCOC) << "\n" << "}"; } @@ -1306,7 +1330,6 @@ bool JsonSerializer::readJson(const char* jsonChunk, size_t size, Settings* out) int tokenCount = jsmn_parse(&parser, jsonChunk, size, nullptr, 0); if (tokenCount <= 0) { - slog.e << "Badly formed JSON." << io::endl; return false; } @@ -1318,7 +1341,6 @@ bool JsonSerializer::readJson(const char* jsonChunk, size_t size, Settings* out) if (tokenCount <= 0) { free(tokens); - slog.e << "Badly formed JSON." << io::endl; return false; } diff --git a/libs/viewer/src/SimpleViewer.cpp b/libs/viewer/src/SimpleViewer.cpp index 1233cec56e..b82091f19c 100644 --- a/libs/viewer/src/SimpleViewer.cpp +++ b/libs/viewer/src/SimpleViewer.cpp @@ -42,7 +42,7 @@ using namespace filament::math; namespace filament { namespace viewer { -filament::math::mat4f fitIntoUnitCube(const filament::Aabb& bounds) { +filament::math::mat4f fitIntoUnitCube(const filament::Aabb& bounds, float zoffset) { using namespace filament::math; auto minpt = bounds.min; auto maxpt = bounds.max; @@ -51,7 +51,7 @@ filament::math::mat4f fitIntoUnitCube(const filament::Aabb& bounds) { maxExtent = std::max(maxExtent, maxpt.z - minpt.z); float scaleFactor = 2.0f / maxExtent; float3 center = (minpt + maxpt) / 2.0f; - center.z += 4.0f / scaleFactor; + center.z += zoffset / scaleFactor; return mat4f::scaling(float3(scaleFactor)) * mat4f::translation(-center); } @@ -348,7 +348,7 @@ void SimpleViewer::populateScene(FilamentAsset* asset, bool scale, if (scale) { auto& tcm = mEngine->getTransformManager(); auto root = tcm.getInstance(mAsset->getRoot()); - filament::math::mat4f transform = fitIntoUnitCube(mAsset->getBoundingBox()); + filament::math::mat4f transform = fitIntoUnitCube(mAsset->getBoundingBox(), 4); tcm.setTransform(root, transform); } @@ -418,34 +418,79 @@ void SimpleViewer::applyAnimation(double currentTime) { mAnimator->updateBoneMatrices(); } -void SimpleViewer::renderUserInterface(float timeStepInSeconds, View* guiView, float pixelRatio, - float mouseX, float mouseY, bool mouseButton, float mouseWheelY) { +void SimpleViewer::renderUserInterface(float timeStepInSeconds, View* guiView, float pixelRatio) { if (mImGuiHelper == nullptr) { mImGuiHelper = new ImGuiHelper(mEngine, guiView, ""); + auto& io = ImGui::GetIO(); + + // The following table uses normal ANSI codes, which is consistent with the keyCode that + // comes from a web "keydown" event. + io.KeyMap[ImGuiKey_Tab] = 9; + io.KeyMap[ImGuiKey_LeftArrow] = 37; + io.KeyMap[ImGuiKey_RightArrow] = 39; + io.KeyMap[ImGuiKey_UpArrow] = 38; + io.KeyMap[ImGuiKey_DownArrow] = 40; + io.KeyMap[ImGuiKey_Home] = 36; + io.KeyMap[ImGuiKey_End] = 35; + io.KeyMap[ImGuiKey_Delete] = 46; + io.KeyMap[ImGuiKey_Backspace] = 8; + io.KeyMap[ImGuiKey_Enter] = 13; + io.KeyMap[ImGuiKey_Escape] = 27; + io.KeyMap[ImGuiKey_A] = 65; + io.KeyMap[ImGuiKey_C] = 67; + io.KeyMap[ImGuiKey_V] = 86; + io.KeyMap[ImGuiKey_X] = 88; + io.KeyMap[ImGuiKey_Y] = 89; + io.KeyMap[ImGuiKey_Z] = 90; + // TODO: this is not the best way to handle high DPI in ImGui, but it is fine when using the // proggy font. Users need to refresh their window when dragging between displays with // different pixel ratios. - ImGui::GetIO().FontGlobalScale = pixelRatio; + io.FontGlobalScale = pixelRatio; ImGui::GetStyle().ScaleAllSizes(pixelRatio); } const auto size = guiView->getViewport(); mImGuiHelper->setDisplaySize(size.width, size.height, 1, 1); - ImGuiIO& io = ImGui::GetIO(); - io.MousePos.x = mouseX; - io.MousePos.y = mouseY; - io.MouseWheel += mouseWheelY; - io.MouseDown[0] = mouseButton; - io.MouseDown[1] = false; - io.MouseDown[2] = false; - mImGuiHelper->render(timeStepInSeconds, [this](Engine*, View*) { this->updateUserInterface(); }); } +void SimpleViewer::mouseEvent(float mouseX, float mouseY, bool mouseButton, float mouseWheelY, + bool control) { + if (mImGuiHelper) { + ImGuiIO& io = ImGui::GetIO(); + io.MousePos.x = mouseX; + io.MousePos.y = mouseY; + io.MouseWheel += mouseWheelY; + io.MouseDown[0] = mouseButton != 0; + io.MouseDown[1] = false; + io.MouseDown[2] = false; + io.KeyCtrl = control; + } +} + +void SimpleViewer::keyDownEvent(int keyCode) { + if (mImGuiHelper && keyCode < IM_ARRAYSIZE(ImGui::GetIO().KeysDown)) { + ImGui::GetIO().KeysDown[keyCode] = true; + } +} + +void SimpleViewer::keyUpEvent(int keyCode) { + if (mImGuiHelper && keyCode < IM_ARRAYSIZE(ImGui::GetIO().KeysDown)) { + ImGui::GetIO().KeysDown[keyCode] = false; + } +} + +void SimpleViewer::keyPressEvent(int charCode) { + if (mImGuiHelper) { + ImGui::GetIO().AddInputCharacter(charCode); + } +} + void SimpleViewer::updateUserInterface() { using namespace filament; @@ -684,7 +729,11 @@ void SimpleViewer::updateUserInterface() { 16.0f, 90.0f); } - bool median = mSettings.view.dof.filter == View::DepthOfFieldOptions::Filter::MEDIAN; + bool dofMedian = mSettings.view.dof.filter == View::DepthOfFieldOptions::Filter::MEDIAN; + int dofRingCount = mSettings.view.dof.fastGatherRingCount; + int dofMaxCoC = mSettings.view.dof.maxForegroundCOC; + if (!dofRingCount) dofRingCount = 5; + if (!dofMaxCoC) dofMaxCoC = 32; ImGui::SliderFloat("Aperture", &mSettings.viewer.cameraAperture, 1.0f, 32.0f); ImGui::SliderFloat("Speed (1/s)", &mSettings.viewer.cameraSpeed, 1000.0f, 1.0f); @@ -692,10 +741,19 @@ void SimpleViewer::updateUserInterface() { ImGui::Checkbox("DoF", &mSettings.view.dof.enabled); ImGui::SliderFloat("Focus distance", &mSettings.view.dof.focusDistance, 0.0f, 30.0f); ImGui::SliderFloat("Blur scale", &mSettings.view.dof.cocScale, 0.1f, 10.0f); - ImGui::Checkbox("Median Filter", &median); - mSettings.view.dof.filter = median ? - View::DepthOfFieldOptions::Filter::MEDIAN : - View::DepthOfFieldOptions::Filter::NONE; + ImGui::SliderInt("Ring count", &dofRingCount, 1, 17); + ImGui::SliderInt("Max CoC", &dofMaxCoC, 1, 32); + ImGui::Checkbox("Native Resolution", &mSettings.view.dof.nativeResolution); + ImGui::Checkbox("Median Filter", &dofMedian); + + mSettings.view.dof.filter = dofMedian ? + View::DepthOfFieldOptions::Filter::MEDIAN : + View::DepthOfFieldOptions::Filter::NONE; + mSettings.view.dof.backgroundRingCount = dofRingCount; + mSettings.view.dof.foregroundRingCount = dofRingCount; + mSettings.view.dof.fastGatherRingCount = dofRingCount; + mSettings.view.dof.maxForegroundCOC = dofMaxCoC; + mSettings.view.dof.maxBackgroundCOC = dofMaxCoC; if (ImGui::CollapsingHeader("Vignette")) { ImGui::Checkbox("Enabled##vignetteEnabled", &mSettings.view.vignette.enabled); diff --git a/libs/viewer/tests/test_settings.cpp b/libs/viewer/tests/test_settings.cpp index 1273839a5d..6eadab3de4 100644 --- a/libs/viewer/tests/test_settings.cpp +++ b/libs/viewer/tests/test_settings.cpp @@ -162,6 +162,23 @@ TEST_F(ViewSettingsTest, JsonTestDefaults) { ASSERT_EQ(serializer.writeJson(settings2), serializer.writeJson(settings3)); } +TEST_F(ViewSettingsTest, JsonTestSerialization) { + auto canParse = [](bool parseResult, std::string json) { + if (parseResult) { + return testing::AssertionSuccess() << "Settings can be serialized."; + } else { + return testing::AssertionFailure() << "JSON has errors:\n" << json.c_str() << std::endl; + } + }; + + JsonSerializer serializer; + Settings outSettings = {}; + std::string jsonstr = serializer.writeJson(outSettings); + Settings inSettings = {}; + bool result = serializer.readJson(jsonstr.c_str(), jsonstr.size(), &inSettings); + ASSERT_TRUE(canParse(result, jsonstr)); +} + TEST_F(ViewSettingsTest, JsonTestMaterial) { JsonSerializer serializer; Settings settings = {0}; diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index 4b60fbf1ab..aed52da323 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -117,7 +117,7 @@ function(add_ktxfiles SOURCE TARGET EXTRA_ARGS) set(MONKEY_FILES ${MONKEY_FILES} ${target_path} PARENT_SCOPE) add_custom_command( OUTPUT ${target_path} - COMMAND mipgen --quiet --strip-alpha ${EXTRA_ARGS} ${source_path} ${target_path} + COMMAND mipgen --quiet ${EXTRA_ARGS} ${source_path} ${target_path} MAIN_DEPENDENCY ${source_path} DEPENDS mipgen COMMENT "Generating mip levels for ${source_path}") @@ -146,10 +146,12 @@ endfunction() add_mesh("assets/models/monkey/monkey.obj" "suzanne.filamesh") -add_ktxfiles("assets/models/monkey/albedo.png" "albedo_s3tc.ktx" "--compression=s3tc_rgb_dxt1") -add_ktxfiles("assets/models/monkey/roughness.png" "roughness.ktx" "--grayscale") -add_ktxfiles("assets/models/monkey/metallic.png" "metallic.ktx" "--grayscale") -add_ktxfiles("assets/models/monkey/ao.png" "ao.ktx" "--grayscale") +# Use a RGBA compression format for Metal and Vulkan support. +set (COMPRESSION "--compression=s3tc_rgba_dxt5") +add_ktxfiles("assets/models/monkey/albedo.png" "albedo_s3tc.ktx" "${COMPRESSION}") +add_ktxfiles("assets/models/monkey/roughness.png" "roughness.ktx" "${COMPRESSION};--grayscale") +add_ktxfiles("assets/models/monkey/metallic.png" "metallic.ktx" "${COMPRESSION};--grayscale") +add_ktxfiles("assets/models/monkey/ao.png" "ao.ktx" "${COMPRESSION};--grayscale") add_pngfile("assets/models/monkey/normal.png" "normal.png") diff --git a/samples/gltf_viewer.cpp b/samples/gltf_viewer.cpp index 6d48bae093..207751ffb0 100644 --- a/samples/gltf_viewer.cpp +++ b/samples/gltf_viewer.cpp @@ -261,7 +261,7 @@ static void createGroundPlane(Engine* engine, Scene* scene, App& app) { Aabb aabb = app.asset->getBoundingBox(); if (!app.actualSize) { - mat4f transform = fitIntoUnitCube(aabb); + mat4f transform = fitIntoUnitCube(aabb, 4); aabb = aabb.transform(transform); } @@ -341,6 +341,9 @@ static LinearColor inverseTonemapSRGB(sRGBColor x) { return (x * -0.155) / (x - 1.019); } +static float sGlobalScale = 1.0f; +static float sGlobalScaleAnamorphism = 0.0f; + int main(int argc, char** argv) { App app; @@ -567,6 +570,8 @@ int main(int argc, char** argv) { debug.getPropertyAddress("d.renderer.doFrameCapture"); *captureFrame = true; } + ImGui::SliderFloat("scale", &sGlobalScale, 0.25f, 1.0f); + ImGui::SliderFloat("anamorphism", &sGlobalScaleAnamorphism, -1.0f, 1.0f); } if (ImGui::BeginPopupModal("MessageBox", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { @@ -679,6 +684,22 @@ int main(int argc, char** argv) { } else { view->setColorGrading(nullptr); } + + view->setDynamicResolutionOptions({ + .minScale = { + lerp(sGlobalScale, 1.0f, + sGlobalScaleAnamorphism >= 0.0f ? sGlobalScaleAnamorphism : 0.0f), + lerp(sGlobalScale, 1.0f, + sGlobalScaleAnamorphism <= 0.0f ? -sGlobalScaleAnamorphism : 0.0f), + }, + .maxScale = { + lerp(sGlobalScale, 1.0f, + sGlobalScaleAnamorphism >= 0.0f ? sGlobalScaleAnamorphism : 0.0f), + lerp(sGlobalScale, 1.0f, + sGlobalScaleAnamorphism <= 0.0f ? -sGlobalScaleAnamorphism : 0.0f), + }, + .enabled = sGlobalScale != 1.0f, + }); }; auto postRender = [&app](Engine* engine, View* view, Scene* scene, Renderer* renderer) { diff --git a/samples/suzanne.cpp b/samples/suzanne.cpp index 059d8e49ff..6826597ac7 100644 --- a/samples/suzanne.cpp +++ b/samples/suzanne.cpp @@ -35,8 +35,14 @@ #include #include +#include + +#include + #include +#include + #include "generated/resources/resources.h" #include "generated/resources/monkey.h" @@ -58,6 +64,57 @@ struct App { static const char* IBL_FOLDER = "default_env"; +static void printUsage(char* name) { + std::string exec_name(utils::Path(name).getName()); + std::string usage( + "SHOWCASE renders a Suzanne model with S3TC textures.\n" + "Usage:\n" + " SHOWCASE [options]\n" + "Options:\n" + " --help, -h\n" + " Prints this message\n\n" + " --api, -a\n" + " Specify the backend API: opengl (default), vulkan, or metal\n" + ); + const std::string from("SHOWCASE"); + for (size_t pos = usage.find(from); pos != std::string::npos; pos = usage.find(from, pos)) { + usage.replace(pos, from.length(), exec_name); + } + std::cout << usage; +} + +static int handleCommandLineArguments(int argc, char* argv[], Config* config) { + static constexpr const char* OPTSTR = "ha:"; + static const struct option OPTIONS[] = { + { "help", no_argument, nullptr, 'h' }, + { "api", required_argument, nullptr, 'a' }, + { nullptr, 0, nullptr, 0 } + }; + int opt; + int option_index = 0; + while ((opt = getopt_long(argc, argv, OPTSTR, OPTIONS, &option_index)) >= 0) { + std::string arg(optarg ? optarg : ""); + switch (opt) { + default: + case 'h': + printUsage(argv[0]); + exit(0); + case 'a': + if (arg == "opengl") { + config->backend = Engine::Backend::OPENGL; + } else if (arg == "vulkan") { + config->backend = Engine::Backend::VULKAN; + } else if (arg == "metal") { + config->backend = Engine::Backend::METAL; + } else { + std::cerr << "Unrecognized backend. Must be 'opengl'|'vulkan'|'metal'.\n"; + } + break; + } + } + return optind; +} + static Texture* loadNormalMap(Engine* engine, const uint8_t* normals, size_t nbytes) { int w, h, n; unsigned char* data = stbi_load_from_memory(normals, nbytes, &w, &h, &n, 3); @@ -80,6 +137,8 @@ int main(int argc, char** argv) { config.title = "suzanne"; config.iblDirectory = FilamentApp::getRootAssetsPath() + IBL_FOLDER; + handleCommandLineArguments(argc, argv, &config); + App app; auto setup = [config, &app](Engine* engine, View* view, Scene* scene) { auto& tcm = engine->getTransformManager(); diff --git a/shaders/src/ambient_occlusion.fs b/shaders/src/ambient_occlusion.fs index 6b8f2d1a9c..59ac2b3ee5 100644 --- a/shaders/src/ambient_occlusion.fs +++ b/shaders/src/ambient_occlusion.fs @@ -58,9 +58,9 @@ float evaluateSSAO() { depths.z = unpack(vec2(dg.z, db.z)); depths.w = unpack(vec2(dg.w, db.w)); depths *= -frameUniforms.cameraFar; - mat4 m = getViewFromWorldMatrix(); - float d = dot(vec3(m[0].z, m[1].z, m[2].z), shading_position) + m[3].z; - vec4 w = (vec4(d) - depths) * frameUniforms.aoSamplingQualityAndEdgeDistance; + highp mat4 m = getViewFromWorldMatrix(); + highp float d = dot(vec3(m[0].z, m[1].z, m[2].z), shading_position) + m[3].z; + highp vec4 w = (vec4(d) - depths) * frameUniforms.aoSamplingQualityAndEdgeDistance; w = max(vec4(MEDIUMP_FLT_MIN), 1.0 - w * w) * b; return dot(ao, w) * (1.0 / (w.x + w.y + w.z + w.w)); } else { diff --git a/shaders/src/light_indirect.fs b/shaders/src/light_indirect.fs index 91a6328d9a..f95b11bd97 100644 --- a/shaders/src/light_indirect.fs +++ b/shaders/src/light_indirect.fs @@ -224,9 +224,9 @@ float prefilteredImportanceSampling(float ipdf, float iblRoughnessOneLevel) { // Prefiltering doesn't work with anisotropy const float numSamples = float(IBL_INTEGRATION_IMPORTANCE_SAMPLING_COUNT); const float invNumSamples = 1.0 / float(numSamples); - const float dim = float(textureSize(light_iblSpecular, 0).x); - const float omegaP = (4.0 * PI) / (6.0 * dim * dim); - const float invOmegaP = 1.0 / omegaP; + float dim = float(textureSize(light_iblSpecular, 0).x); + float omegaP = (4.0 * PI) / (6.0 * dim * dim); + float invOmegaP = 1.0 / omegaP; const float K = 4.0; float omegaS = invNumSamples * ipdf; float mipLevel = clamp(log2(K * omegaS * invOmegaP) * 0.5, 0.0, iblRoughnessOneLevel); @@ -304,7 +304,9 @@ void isEvaluateClearCoatIBL(const PixelParams pixel, float specularAO, inout vec p.perceptualRoughness = pixel.clearCoatPerceptualRoughness; p.f0 = vec3(0.04); p.roughness = perceptualRoughnessToRoughness(p.perceptualRoughness); +#if defined(MATERIAL_HAS_ANISOTROPY) p.anisotropy = 0.0; +#endif vec3 clearCoatLobe = isEvaluateIBL(p, clearCoatNormal, shading_view, clearCoatNoV); Fr += clearCoatLobe * (specularAO * pixel.clearCoat); diff --git a/tools/cso-lut/src/main.cpp b/tools/cso-lut/src/main.cpp index fbe21a87ed..b45a60702f 100644 --- a/tools/cso-lut/src/main.cpp +++ b/tools/cso-lut/src/main.cpp @@ -70,7 +70,7 @@ static void printUsage(const char* name) { " DDS: 8, 16 (default), 32\n\n" ); - const std::string from("ROUGHNESSPREFILTER"); + const std::string from("CSO-LUT"); for (size_t pos = usage.find(from); pos != std::string::npos; pos = usage.find(from, pos)) { usage.replace(pos, from.length(), execName); } diff --git a/tools/specular-color/src/main.cpp b/tools/specular-color/src/main.cpp index b4d0ea365b..d60121ba03 100644 --- a/tools/specular-color/src/main.cpp +++ b/tools/specular-color/src/main.cpp @@ -99,485 +99,485 @@ static int handleArguments(int argc, char* argv[]) { return optind; } -// CIE 1931 2-deg color matching functions (CMFs), from 360nm to 830nm, at 1nm intervals +// CIE 1931 10-deg color matching functions (CMFs), from 360nm to 830nm, at 1nm intervals // // Data source: // http://cvrl.ioo.ucl.ac.uk/cmfs.htm -// http://cvrl.ioo.ucl.ac.uk/database/text/cmfs/ciexyz31.htm +// http://cvrl.ioo.ucl.ac.uk/database/text/cmfs/ciexyz64.htm const size_t CIE_XYZ_START = 360; const size_t CIE_XYZ_COUNT = 471; const float3 CIE_XYZ[CIE_XYZ_COUNT] = { // NOLINT - { 0.0001299f, 0.000003917f, 0.0006061f }, - { 0.000145847f, 0.000004393581f, 0.0006808792f }, - { 0.0001638021f, 0.000004929604f, 0.0007651456f }, - { 0.0001840037f, 0.000005532136f, 0.0008600124f }, - { 0.0002066902f, 0.000006208245f, 0.0009665928f }, - { 0.0002321f, 0.000006965f, 0.001086f }, - { 0.000260728f, 0.000007813219f, 0.001220586f }, - { 0.000293075f, 0.000008767336f, 0.001372729f }, - { 0.000329388f, 0.000009839844f, 0.001543579f }, - { 0.000369914f, 0.00001104323f, 0.001734286f }, - { 0.0004149f, 0.00001239f, 0.001946f }, - { 0.0004641587f, 0.00001388641f, 0.002177777f }, - { 0.000518986f, 0.00001555728f, 0.002435809f }, - { 0.000581854f, 0.00001744296f, 0.002731953f }, - { 0.0006552347f, 0.00001958375f, 0.003078064f }, - { 0.0007416f, 0.00002202f, 0.003486f }, - { 0.0008450296f, 0.00002483965f, 0.003975227f }, - { 0.0009645268f, 0.00002804126f, 0.00454088f }, - { 0.001094949f, 0.00003153104f, 0.00515832f }, - { 0.001231154f, 0.00003521521f, 0.005802907f }, - { 0.001368f, 0.000039f, 0.006450001f }, - { 0.00150205f, 0.0000428264f, 0.007083216f }, - { 0.001642328f, 0.0000469146f, 0.007745488f }, - { 0.001802382f, 0.0000515896f, 0.008501152f }, - { 0.001995757f, 0.0000571764f, 0.009414544f }, - { 0.002236f, 0.000064f, 0.01054999f }, - { 0.002535385f, 0.00007234421f, 0.0119658f }, - { 0.002892603f, 0.00008221224f, 0.01365587f }, - { 0.003300829f, 0.00009350816f, 0.01558805f }, - { 0.003753236f, 0.0001061361f, 0.01773015f }, - { 0.004243f, 0.00012f, 0.02005001f }, - { 0.004762389f, 0.000134984f, 0.02251136f }, - { 0.005330048f, 0.000151492f, 0.02520288f }, - { 0.005978712f, 0.000170208f, 0.02827972f }, - { 0.006741117f, 0.000191816f, 0.03189704f }, - { 0.00765f, 0.000217f, 0.03621f }, - { 0.008751373f, 0.0002469067f, 0.04143771f }, - { 0.01002888f, 0.00028124f, 0.04750372f }, - { 0.0114217f, 0.00031852f, 0.05411988f }, - { 0.01286901f, 0.0003572667f, 0.06099803f }, - { 0.01431f, 0.000396f, 0.06785001f }, - { 0.01570443f, 0.0004337147f, 0.07448632f }, - { 0.01714744f, 0.000473024f, 0.08136156f }, - { 0.01878122f, 0.000517876f, 0.08915364f }, - { 0.02074801f, 0.0005722187f, 0.09854048f }, - { 0.02319f, 0.00064f, 0.1102f }, - { 0.02620736f, 0.00072456f, 0.1246133f }, - { 0.02978248f, 0.0008255f, 0.1417017f }, - { 0.03388092f, 0.00094116f, 0.1613035f }, - { 0.03846824f, 0.00106988f, 0.1832568f }, - { 0.04351f, 0.00121f, 0.2074f }, - { 0.0489956f, 0.001362091f, 0.2336921f }, - { 0.0550226f, 0.001530752f, 0.2626114f }, - { 0.0617188f, 0.001720368f, 0.2947746f }, - { 0.069212f, 0.001935323f, 0.3307985f }, - { 0.07763f, 0.00218f, 0.3713f }, - { 0.08695811f, 0.0024548f, 0.4162091f }, - { 0.09717672f, 0.002764f, 0.4654642f }, - { 0.1084063f, 0.0031178f, 0.5196948f }, - { 0.1207672f, 0.0035264f, 0.5795303f }, - { 0.13438f, 0.004f, 0.6456f }, - { 0.1493582f, 0.00454624f, 0.7184838f }, - { 0.1653957f, 0.00515932f, 0.7967133f }, - { 0.1819831f, 0.00582928f, 0.8778459f }, - { 0.198611f, 0.00654616f, 0.959439f }, - { 0.21477f, 0.0073f, 1.0390501f }, - { 0.2301868f, 0.008086507f, 1.1153673f }, - { 0.2448797f, 0.00890872f, 1.1884971f }, - { 0.2587773f, 0.00976768f, 1.2581233f }, - { 0.2718079f, 0.01066443f, 1.3239296f }, - { 0.2839f, 0.0116f, 1.3856f }, - { 0.2949438f, 0.01257317f, 1.4426352f }, - { 0.3048965f, 0.01358272f, 1.4948035f }, - { 0.3137873f, 0.01462968f, 1.5421903f }, - { 0.3216454f, 0.01571509f, 1.5848807f }, - { 0.3285f, 0.01684f, 1.62296f }, - { 0.3343513f, 0.01800736f, 1.6564048f }, - { 0.3392101f, 0.01921448f, 1.6852959f }, - { 0.3431213f, 0.02045392f, 1.7098745f }, - { 0.3461296f, 0.02171824f, 1.7303821f }, - { 0.34828f, 0.023f, 1.74706f }, - { 0.3495999f, 0.02429461f, 1.7600446f }, - { 0.3501474f, 0.02561024f, 1.7696233f }, - { 0.350013f, 0.02695857f, 1.7762637f }, - { 0.349287f, 0.02835125f, 1.7804334f }, - { 0.34806f, 0.0298f, 1.7826f }, - { 0.3463733f, 0.03131083f, 1.7829682f }, - { 0.3442624f, 0.03288368f, 1.7816998f }, - { 0.3418088f, 0.03452112f, 1.7791982f }, - { 0.3390941f, 0.03622571f, 1.7758671f }, - { 0.3362f, 0.038f, 1.77211f }, - { 0.3331977f, 0.03984667f, 1.7682589f }, - { 0.3300411f, 0.041768f, 1.764039f }, - { 0.3266357f, 0.043766f, 1.7589438f }, - { 0.3228868f, 0.04584267f, 1.7524663f }, - { 0.3187f, 0.048f, 1.7441f }, - { 0.3140251f, 0.05024368f, 1.7335595f }, - { 0.308884f, 0.05257304f, 1.7208581f }, - { 0.3032904f, 0.05498056f, 1.7059369f }, - { 0.2972579f, 0.05745872f, 1.6887372f }, - { 0.2908f, 0.06f, 1.6692f }, - { 0.2839701f, 0.06260197f, 1.6475287f }, - { 0.2767214f, 0.06527752f, 1.6234127f }, - { 0.2689178f, 0.06804208f, 1.5960223f }, - { 0.2604227f, 0.07091109f, 1.564528f }, - { 0.2511f, 0.0739f, 1.5281f }, - { 0.2408475f, 0.077016f, 1.4861114f }, - { 0.2298512f, 0.0802664f, 1.4395215f }, - { 0.2184072f, 0.0836668f, 1.3898799f }, - { 0.2068115f, 0.0872328f, 1.3387362f }, - { 0.19536f, 0.09098f, 1.28764f }, - { 0.1842136f, 0.09491755f, 1.2374223f }, - { 0.1733273f, 0.09904584f, 1.1878243f }, - { 0.1626881f, 0.1033674f, 1.1387611f }, - { 0.1522833f, 0.1078846f, 1.090148f }, - { 0.1421f, 0.1126f, 1.0419f }, - { 0.1321786f, 0.117532f, 0.9941976f }, - { 0.1225696f, 0.1226744f, 0.9473473f }, - { 0.1132752f, 0.1279928f, 0.9014531f }, - { 0.1042979f, 0.1334528f, 0.8566193f }, - { 0.09564f, 0.13902f, 0.8129501f }, - { 0.08729955f, 0.1446764f, 0.7705173f }, - { 0.07930804f, 0.1504693f, 0.7294448f }, - { 0.07171776f, 0.1564619f, 0.6899136f }, - { 0.06458099f, 0.1627177f, 0.6521049f }, - { 0.05795001f, 0.1693f, 0.6162f }, - { 0.05186211f, 0.1762431f, 0.5823286f }, - { 0.04628152f, 0.1835581f, 0.5504162f }, - { 0.04115088f, 0.1912735f, 0.5203376f }, - { 0.03641283f, 0.199418f, 0.4919673f }, - { 0.03201f, 0.20802f, 0.46518f }, - { 0.0279172f, 0.2171199f, 0.4399246f }, - { 0.0241444f, 0.2267345f, 0.4161836f }, - { 0.020687f, 0.2368571f, 0.3938822f }, - { 0.0175404f, 0.2474812f, 0.3729459f }, - { 0.0147f, 0.2586f, 0.3533f }, - { 0.01216179f, 0.2701849f, 0.3348578f }, - { 0.00991996f, 0.2822939f, 0.3175521f }, - { 0.00796724f, 0.2950505f, 0.3013375f }, - { 0.006296346f, 0.308578f, 0.2861686f }, - { 0.0049f, 0.323f, 0.272f }, - { 0.003777173f, 0.3384021f, 0.2588171f }, - { 0.00294532f, 0.3546858f, 0.2464838f }, - { 0.00242488f, 0.3716986f, 0.2347718f }, - { 0.002236293f, 0.3892875f, 0.2234533f }, - { 0.0024f, 0.4073f, 0.2123f }, - { 0.00292552f, 0.4256299f, 0.2011692f }, - { 0.00383656f, 0.4443096f, 0.1901196f }, - { 0.00517484f, 0.4633944f, 0.1792254f }, - { 0.00698208f, 0.4829395f, 0.1685608f }, - { 0.0093f, 0.503f, 0.1582f }, - { 0.01214949f, 0.5235693f, 0.1481383f }, - { 0.01553588f, 0.544512f, 0.1383758f }, - { 0.01947752f, 0.56569f, 0.1289942f }, - { 0.02399277f, 0.5869653f, 0.1200751f }, - { 0.0291f, 0.6082f, 0.1117f }, - { 0.03481485f, 0.6293456f, 0.1039048f }, - { 0.04112016f, 0.6503068f, 0.09666748f }, - { 0.04798504f, 0.6708752f, 0.08998272f }, - { 0.05537861f, 0.6908424f, 0.08384531f }, - { 0.06327f, 0.71f, 0.07824999f }, - { 0.07163501f, 0.7281852f, 0.07320899f }, - { 0.08046224f, 0.7454636f, 0.06867816f }, - { 0.08973996f, 0.7619694f, 0.06456784f }, - { 0.09945645f, 0.7778368f, 0.06078835f }, - { 0.1096f, 0.7932f, 0.05725001f }, - { 0.1201674f, 0.8081104f, 0.05390435f }, - { 0.1311145f, 0.8224962f, 0.05074664f }, - { 0.1423679f, 0.8363068f, 0.04775276f }, - { 0.1538542f, 0.8494916f, 0.04489859f }, - { 0.1655f, 0.862f, 0.04216f }, - { 0.1772571f, 0.8738108f, 0.03950728f }, - { 0.18914f, 0.8849624f, 0.03693564f }, - { 0.2011694f, 0.8954936f, 0.03445836f }, - { 0.2133658f, 0.9054432f, 0.03208872f }, - { 0.2257499f, 0.9148501f, 0.02984f }, - { 0.2383209f, 0.9237348f, 0.02771181f }, - { 0.2510668f, 0.9320924f, 0.02569444f }, - { 0.2639922f, 0.9399226f, 0.02378716f }, - { 0.2771017f, 0.9472252f, 0.02198925f }, - { 0.2904f, 0.954f, 0.0203f }, - { 0.3038912f, 0.9602561f, 0.01871805f }, - { 0.3175726f, 0.9660074f, 0.01724036f }, - { 0.3314384f, 0.9712606f, 0.01586364f }, - { 0.3454828f, 0.9760225f, 0.01458461f }, - { 0.3597f, 0.9803f, 0.0134f }, - { 0.3740839f, 0.9840924f, 0.01230723f }, - { 0.3886396f, 0.9874182f, 0.01130188f }, - { 0.4033784f, 0.9903128f, 0.01037792f }, - { 0.4183115f, 0.9928116f, 0.009529306f }, - { 0.4334499f, 0.9949501f, 0.008749999f }, - { 0.4487953f, 0.9967108f, 0.0080352f }, - { 0.464336f, 0.9980983f, 0.0073816f }, - { 0.480064f, 0.999112f, 0.0067854f }, - { 0.4959713f, 0.9997482f, 0.0062428f }, - { 0.5120501f, 1.0f, 0.005749999f }, - { 0.5282959f, 0.9998567f, 0.0053036f }, - { 0.5446916f, 0.9993046f, 0.0048998f }, - { 0.5612094f, 0.9983255f, 0.0045342f }, - { 0.5778215f, 0.9968987f, 0.0042024f }, - { 0.5945f, 0.995f, 0.0039f }, - { 0.6112209f, 0.9926005f, 0.0036232f }, - { 0.6279758f, 0.9897426f, 0.0033706f }, - { 0.6447602f, 0.9864444f, 0.0031414f }, - { 0.6615697f, 0.9827241f, 0.0029348f }, - { 0.6784f, 0.9786f, 0.002749999f }, - { 0.6952392f, 0.9740837f, 0.0025852f }, - { 0.7120586f, 0.9691712f, 0.0024386f }, - { 0.7288284f, 0.9638568f, 0.0023094f }, - { 0.7455188f, 0.9581349f, 0.0021968f }, - { 0.7621f, 0.952f, 0.0021f }, - { 0.7785432f, 0.9454504f, 0.002017733f }, - { 0.7948256f, 0.9384992f, 0.0019482f }, - { 0.8109264f, 0.9311628f, 0.0018898f }, - { 0.8268248f, 0.9234576f, 0.001840933f }, - { 0.8425f, 0.9154f, 0.0018f }, - { 0.8579325f, 0.9070064f, 0.001766267f }, - { 0.8730816f, 0.8982772f, 0.0017378f }, - { 0.8878944f, 0.8892048f, 0.0017112f }, - { 0.9023181f, 0.8797816f, 0.001683067f }, - { 0.9163f, 0.87f, 0.001650001f }, - { 0.9297995f, 0.8598613f, 0.001610133f }, - { 0.9427984f, 0.849392f, 0.0015644f }, - { 0.9552776f, 0.838622f, 0.0015136f }, - { 0.9672179f, 0.8275813f, 0.001458533f }, - { 0.9786f, 0.8163f, 0.0014f }, - { 0.9893856f, 0.8047947f, 0.001336667f }, - { 0.9995488f, 0.793082f, 0.00127f }, - { 1.0090892f, 0.781192f, 0.001205f }, - { 1.0180064f, 0.7691547f, 0.001146667f }, - { 1.0263f, 0.757f, 0.0011f }, - { 1.0339827f, 0.7447541f, 0.0010688f }, - { 1.040986f, 0.7324224f, 0.0010494f }, - { 1.047188f, 0.7200036f, 0.0010356f }, - { 1.0524667f, 0.7074965f, 0.0010212f }, - { 1.0567f, 0.6949f, 0.001f }, - { 1.0597944f, 0.6822192f, 0.00096864f }, - { 1.0617992f, 0.6694716f, 0.00092992f }, - { 1.0628068f, 0.6566744f, 0.00088688f }, - { 1.0629096f, 0.6438448f, 0.00084256f }, - { 1.0622f, 0.631f, 0.0008f }, - { 1.0607352f, 0.6181555f, 0.00076096f }, - { 1.0584436f, 0.6053144f, 0.00072368f }, - { 1.0552244f, 0.5924756f, 0.00068592f }, - { 1.0509768f, 0.5796379f, 0.00064544f }, - { 1.0456f, 0.5668f, 0.0006f }, - { 1.0390369f, 0.5539611f, 0.0005478667f }, - { 1.0313608f, 0.5411372f, 0.0004916f }, - { 1.0226662f, 0.5283528f, 0.0004354f }, - { 1.0130477f, 0.5156323f, 0.0003834667f }, - { 1.0026f, 0.503f, 0.00034f }, - { 0.9913675f, 0.4904688f, 0.0003072533f }, - { 0.9793314f, 0.4780304f, 0.00028316f }, - { 0.9664916f, 0.4656776f, 0.00026544f }, - { 0.9528479f, 0.4534032f, 0.0002518133f }, - { 0.9384f, 0.4412f, 0.00024f }, - { 0.923194f, 0.42908f, 0.0002295467f }, - { 0.907244f, 0.417036f, 0.00022064f }, - { 0.890502f, 0.405032f, 0.00021196f }, - { 0.87292f, 0.393032f, 0.0002021867f }, - { 0.8544499f, 0.381f, 0.00019f }, - { 0.835084f, 0.3689184f, 0.0001742133f }, - { 0.814946f, 0.3568272f, 0.00015564f }, - { 0.794186f, 0.3447768f, 0.00013596f }, - { 0.772954f, 0.3328176f, 0.0001168533f }, - { 0.7514f, 0.321f, 0.0001f }, - { 0.7295836f, 0.3093381f, 0.00008613333f }, - { 0.7075888f, 0.2978504f, 0.0000746f }, - { 0.6856022f, 0.2865936f, 0.000065f }, - { 0.6638104f, 0.2756245f, 0.00005693333f }, - { 0.6424f, 0.265f, 0.00004999999f }, - { 0.6215149f, 0.2547632f, 0.00004416f }, - { 0.6011138f, 0.2448896f, 0.00003948f }, - { 0.5811052f, 0.2353344f, 0.00003572f }, - { 0.5613977f, 0.2260528f, 0.00003264f }, - { 0.5419f, 0.217f, 0.00003f }, - { 0.5225995f, 0.2081616f, 0.00002765333f }, - { 0.5035464f, 0.1995488f, 0.00002556f }, - { 0.4847436f, 0.1911552f, 0.00002364f }, - { 0.4661939f, 0.1829744f, 0.00002181333f }, - { 0.4479f, 0.175f, 0.00002f }, - { 0.4298613f, 0.1672235f, 0.00001813333f }, - { 0.412098f, 0.1596464f, 0.0000162f }, - { 0.394644f, 0.1522776f, 0.0000142f }, - { 0.3775333f, 0.1451259f, 0.00001213333f }, - { 0.3608f, 0.1382f, 0.00001f }, - { 0.3444563f, 0.1315003f, 0.000007733333f }, - { 0.3285168f, 0.1250248f, 0.0000054f }, - { 0.3130192f, 0.1187792f, 0.0000032f }, - { 0.2980011f, 0.1127691f, 0.000001333333f }, - { 0.2835f, 0.107f, 0.0f }, - { 0.2695448f, 0.1014762f, 0.0f }, - { 0.2561184f, 0.09618864f, 0.0f }, - { 0.2431896f, 0.09112296f, 0.0f }, - { 0.2307272f, 0.08626485f, 0.0f }, - { 0.2187f, 0.0816f, 0.0f }, - { 0.2070971f, 0.07712064f, 0.0f }, - { 0.1959232f, 0.07282552f, 0.0f }, - { 0.1851708f, 0.06871008f, 0.0f }, - { 0.1748323f, 0.06476976f, 0.0f }, - { 0.1649f, 0.061f, 0.0f }, - { 0.1553667f, 0.05739621f, 0.0f }, - { 0.14623f, 0.05395504f, 0.0f }, - { 0.13749f, 0.05067376f, 0.0f }, - { 0.1291467f, 0.04754965f, 0.0f }, - { 0.1212f, 0.04458f, 0.0f }, - { 0.1136397f, 0.04175872f, 0.0f }, - { 0.106465f, 0.03908496f, 0.0f }, - { 0.09969044f, 0.03656384f, 0.0f }, - { 0.09333061f, 0.03420048f, 0.0f }, - { 0.0874f, 0.032f, 0.0f }, - { 0.08190096f, 0.02996261f, 0.0f }, - { 0.07680428f, 0.02807664f, 0.0f }, - { 0.07207712f, 0.02632936f, 0.0f }, - { 0.06768664f, 0.02470805f, 0.0f }, - { 0.0636f, 0.0232f, 0.0f }, - { 0.05980685f, 0.02180077f, 0.0f }, - { 0.05628216f, 0.02050112f, 0.0f }, - { 0.05297104f, 0.01928108f, 0.0f }, - { 0.04981861f, 0.01812069f, 0.0f }, - { 0.04677f, 0.017f, 0.0f }, - { 0.04378405f, 0.01590379f, 0.0f }, - { 0.04087536f, 0.01483718f, 0.0f }, - { 0.03807264f, 0.01381068f, 0.0f }, - { 0.03540461f, 0.01283478f, 0.0f }, - { 0.0329f, 0.01192f, 0.0f }, - { 0.03056419f, 0.01106831f, 0.0f }, - { 0.02838056f, 0.01027339f, 0.0f }, - { 0.02634484f, 0.009533311f, 0.0f }, - { 0.02445275f, 0.008846157f, 0.0f }, - { 0.0227f, 0.00821f, 0.0f }, - { 0.02108429f, 0.007623781f, 0.0f }, - { 0.01959988f, 0.007085424f, 0.0f }, - { 0.01823732f, 0.006591476f, 0.0f }, - { 0.01698717f, 0.006138485f, 0.0f }, - { 0.01584f, 0.005723f, 0.0f }, - { 0.01479064f, 0.005343059f, 0.0f }, - { 0.01383132f, 0.004995796f, 0.0f }, - { 0.01294868f, 0.004676404f, 0.0f }, - { 0.0121292f, 0.004380075f, 0.0f }, - { 0.01135916f, 0.004102f, 0.0f }, - { 0.01062935f, 0.003838453f, 0.0f }, - { 0.009938846f, 0.003589099f, 0.0f }, - { 0.009288422f, 0.003354219f, 0.0f }, - { 0.008678854f, 0.003134093f, 0.0f }, - { 0.008110916f, 0.002929f, 0.0f }, - { 0.007582388f, 0.002738139f, 0.0f }, - { 0.007088746f, 0.002559876f, 0.0f }, - { 0.006627313f, 0.002393244f, 0.0f }, - { 0.006195408f, 0.002237275f, 0.0f }, - { 0.005790346f, 0.002091f, 0.0f }, - { 0.005409826f, 0.001953587f, 0.0f }, - { 0.005052583f, 0.00182458f, 0.0f }, - { 0.004717512f, 0.00170358f, 0.0f }, - { 0.004403507f, 0.001590187f, 0.0f }, - { 0.004109457f, 0.001484f, 0.0f }, - { 0.003833913f, 0.001384496f, 0.0f }, - { 0.003575748f, 0.001291268f, 0.0f }, - { 0.003334342f, 0.001204092f, 0.0f }, - { 0.003109075f, 0.001122744f, 0.0f }, - { 0.002899327f, 0.001047f, 0.0f }, - { 0.002704348f, 0.0009765896f, 0.0f }, - { 0.00252302f, 0.0009111088f, 0.0f }, - { 0.002354168f, 0.0008501332f, 0.0f }, - { 0.002196616f, 0.0007932384f, 0.0f }, - { 0.00204919f, 0.00074f, 0.0f }, - { 0.00191096f, 0.0006900827f, 0.0f }, - { 0.001781438f, 0.00064331f, 0.0f }, - { 0.00166011f, 0.000599496f, 0.0f }, - { 0.001546459f, 0.0005584547f, 0.0f }, - { 0.001439971f, 0.00052f, 0.0f }, - { 0.001340042f, 0.0004839136f, 0.0f }, - { 0.001246275f, 0.0004500528f, 0.0f }, - { 0.001158471f, 0.0004183452f, 0.0f }, - { 0.00107643f, 0.0003887184f, 0.0f }, - { 0.0009999493f, 0.0003611f, 0.0f }, - { 0.0009287358f, 0.0003353835f, 0.0f }, - { 0.0008624332f, 0.0003114404f, 0.0f }, - { 0.0008007503f, 0.0002891656f, 0.0f }, - { 0.000743396f, 0.0002684539f, 0.0f }, - { 0.0006900786f, 0.0002492f, 0.0f }, - { 0.0006405156f, 0.0002313019f, 0.0f }, - { 0.0005945021f, 0.0002146856f, 0.0f }, - { 0.0005518646f, 0.0001992884f, 0.0f }, - { 0.000512429f, 0.0001850475f, 0.0f }, - { 0.0004760213f, 0.0001719f, 0.0f }, - { 0.0004424536f, 0.0001597781f, 0.0f }, - { 0.0004115117f, 0.0001486044f, 0.0f }, - { 0.0003829814f, 0.0001383016f, 0.0f }, - { 0.0003566491f, 0.0001287925f, 0.0f }, - { 0.0003323011f, 0.00012f, 0.0f }, - { 0.0003097586f, 0.0001118595f, 0.0f }, - { 0.0002888871f, 0.0001043224f, 0.0f }, - { 0.0002695394f, 0.0000973356f, 0.0f }, - { 0.0002515682f, 0.00009084587f, 0.0f }, - { 0.0002348261f, 0.0000848f, 0.0f }, - { 0.000219171f, 0.00007914667f, 0.0f }, - { 0.0002045258f, 0.000073858f, 0.0f }, - { 0.0001908405f, 0.000068916f, 0.0f }, - { 0.0001780654f, 0.00006430267f, 0.0f }, - { 0.0001661505f, 0.00006f, 0.0f }, - { 0.0001550236f, 0.00005598187f, 0.0f }, - { 0.0001446219f, 0.0000522256f, 0.0f }, - { 0.0001349098f, 0.0000487184f, 0.0f }, - { 0.000125852f, 0.00004544747f, 0.0f }, - { 0.000117413f, 0.0000424f, 0.0f }, - { 0.0001095515f, 0.00003956104f, 0.0f }, - { 0.0001022245f, 0.00003691512f, 0.0f }, - { 0.00009539445f, 0.00003444868f, 0.0f }, - { 0.0000890239f, 0.00003214816f, 0.0f }, - { 0.00008307527f, 0.00003f, 0.0f }, - { 0.00007751269f, 0.00002799125f, 0.0f }, - { 0.00007231304f, 0.00002611356f, 0.0f }, - { 0.00006745778f, 0.00002436024f, 0.0f }, - { 0.00006292844f, 0.00002272461f, 0.0f }, - { 0.00005870652f, 0.0000212f, 0.0f }, - { 0.00005477028f, 0.00001977855f, 0.0f }, - { 0.00005109918f, 0.00001845285f, 0.0f }, - { 0.00004767654f, 0.00001721687f, 0.0f }, - { 0.00004448567f, 0.00001606459f, 0.0f }, - { 0.00004150994f, 0.00001499f, 0.0f }, - { 0.00003873324f, 0.00001398728f, 0.0f }, - { 0.00003614203f, 0.00001305155f, 0.0f }, - { 0.00003372352f, 0.00001217818f, 0.0f }, - { 0.00003146487f, 0.00001136254f, 0.0f }, - { 0.00002935326f, 0.0000106f, 0.0f }, - { 0.00002737573f, 0.000009885877f, 0.0f }, - { 0.00002552433f, 0.000009217304f, 0.0f }, - { 0.00002379376f, 0.000008592362f, 0.0f }, - { 0.0000221787f, 0.000008009133f, 0.0f }, - { 0.00002067383f, 0.0000074657f, 0.0f }, - { 0.00001927226f, 0.000006959567f, 0.0f }, - { 0.0000179664f, 0.000006487995f, 0.0f }, - { 0.00001674991f, 0.000006048699f, 0.0f }, - { 0.00001561648f, 0.000005639396f, 0.0f }, - { 0.00001455977f, 0.0000052578f, 0.0f }, - { 0.00001357387f, 0.000004901771f, 0.0f }, - { 0.00001265436f, 0.00000456972f, 0.0f }, - { 0.00001179723f, 0.000004260194f, 0.0f }, - { 0.00001099844f, 0.000003971739f, 0.0f }, - { 0.00001025398f, 0.0000037029f, 0.0f }, - { 0.000009559646f, 0.000003452163f, 0.0f }, - { 0.000008912044f, 0.000003218302f, 0.0f }, - { 0.000008308358f, 0.0000030003f, 0.0f }, - { 0.000007745769f, 0.000002797139f, 0.0f }, - { 0.000007221456f, 0.0000026078f, 0.0f }, - { 0.000006732475f, 0.00000243122f, 0.0f }, - { 0.000006276423f, 0.000002266531f, 0.0f }, - { 0.000005851304f, 0.000002113013f, 0.0f }, - { 0.000005455118f, 0.000001969943f, 0.0f }, - { 0.000005085868f, 0.0000018366f, 0.0f }, - { 0.000004741466f, 0.00000171223f, 0.0f }, - { 0.000004420236f, 0.000001596228f, 0.0f }, - { 0.000004120783f, 0.00000148809f, 0.0f }, - { 0.000003841716f, 0.000001387314f, 0.0f }, - { 0.000003581652f, 0.0000012934f, 0.0f }, - { 0.000003339127f, 0.00000120582f, 0.0f }, - { 0.000003112949f, 0.000001124143f, 0.0f }, - { 0.000002902121f, 0.000001048009f, 0.0f }, - { 0.000002705645f, 0.000000977058f, 0.0f }, - { 0.000002522525f, 0.00000091093f, 0.0f }, - { 0.000002351726f, 0.000000849251f, 0.0f }, - { 0.000002192415f, 0.000000791721f, 0.0f }, - { 0.000002043902f, 0.00000073809f, 0.0f }, - { 0.000001905497f, 0.00000068811f, 0.0f }, - { 0.000001776509f, 0.00000064153f, 0.0f }, - { 0.000001656215f, 0.00000059809f, 0.0f }, - { 0.000001544022f, 0.000000557575f, 0.0f }, - { 0.00000143944f, 0.000000519808f, 0.0f }, - { 0.000001341977f, 0.000000484612f, 0.0f }, - { 0.000001251141f, 0.00000045181f, 0.0f } + { 0.000000122200f, 0.000000013398f, 0.000000535027f }, + { 0.000000185138f, 0.000000020294f, 0.000000810720f }, + { 0.000000278830f, 0.000000030560f, 0.000001221200f }, + { 0.000000417470f, 0.000000045740f, 0.000001828700f }, + { 0.000000621330f, 0.000000068050f, 0.000002722200f }, + { 0.000000919270f, 0.000000100650f, 0.000004028300f }, + { 0.000001351980f, 0.000000147980f, 0.000005925700f }, + { 0.000001976540f, 0.000000216270f, 0.000008665100f }, + { 0.000002872500f, 0.000000314200f, 0.000012596000f }, + { 0.000004149500f, 0.000000453700f, 0.000018201000f }, + { 0.000005958600f, 0.000000651100f, 0.000026143700f }, + { 0.000008505600f, 0.000000928800f, 0.000037330000f }, + { 0.000012068600f, 0.000001317500f, 0.000052987000f }, + { 0.000017022600f, 0.000001857200f, 0.000074764000f }, + { 0.000023868000f, 0.000002602000f, 0.000104870000f }, + { 0.000033266000f, 0.000003625000f, 0.000146220000f }, + { 0.000046087000f, 0.000005019000f, 0.000202660000f }, + { 0.000063472000f, 0.000006907000f, 0.000279230000f }, + { 0.000086892000f, 0.000009449000f, 0.000382450000f }, + { 0.000118246000f, 0.000012848000f, 0.000520720000f }, + { 0.000159952000f, 0.000017364000f, 0.000704776000f }, + { 0.000215080000f, 0.000023327000f, 0.000948230000f }, + { 0.000287490000f, 0.000031150000f, 0.001268200000f }, + { 0.000381990000f, 0.000041350000f, 0.001686100000f }, + { 0.000504550000f, 0.000054560000f, 0.002228500000f }, + { 0.000662440000f, 0.000071560000f, 0.002927800000f }, + { 0.000864500000f, 0.000093300000f, 0.003823700000f }, + { 0.001121500000f, 0.000120870000f, 0.004964200000f }, + { 0.001446160000f, 0.000155640000f, 0.006406700000f }, + { 0.001853590000f, 0.000199200000f, 0.008219300000f }, + { 0.002361600000f, 0.000253400000f, 0.010482200000f }, + { 0.002990600000f, 0.000320200000f, 0.013289000000f }, + { 0.003764500000f, 0.000402400000f, 0.016747000000f }, + { 0.004710200000f, 0.000502300000f, 0.020980000000f }, + { 0.005858100000f, 0.000623200000f, 0.026127000000f }, + { 0.007242300000f, 0.000768500000f, 0.032344000000f }, + { 0.008899600000f, 0.000941700000f, 0.039802000000f }, + { 0.010870900000f, 0.001147800000f, 0.048691000000f }, + { 0.013198900000f, 0.001390300000f, 0.059210000000f }, + { 0.015929200000f, 0.001674000000f, 0.071576000000f }, + { 0.019109700000f, 0.002004400000f, 0.086010900000f }, + { 0.022788000000f, 0.002386000000f, 0.102740000000f }, + { 0.027011000000f, 0.002822000000f, 0.122000000000f }, + { 0.031829000000f, 0.003319000000f, 0.144020000000f }, + { 0.037278000000f, 0.003880000000f, 0.168990000000f }, + { 0.043400000000f, 0.004509000000f, 0.197120000000f }, + { 0.050223000000f, 0.005209000000f, 0.228570000000f }, + { 0.057764000000f, 0.005985000000f, 0.263470000000f }, + { 0.066038000000f, 0.006833000000f, 0.301900000000f }, + { 0.075033000000f, 0.007757000000f, 0.343870000000f }, + { 0.084736000000f, 0.008756000000f, 0.389366000000f }, + { 0.095041000000f, 0.009816000000f, 0.437970000000f }, + { 0.105836000000f, 0.010918000000f, 0.489220000000f }, + { 0.117066000000f, 0.012058000000f, 0.542900000000f }, + { 0.128682000000f, 0.013237000000f, 0.598810000000f }, + { 0.140638000000f, 0.014456000000f, 0.656760000000f }, + { 0.152893000000f, 0.015717000000f, 0.716580000000f }, + { 0.165416000000f, 0.017025000000f, 0.778120000000f }, + { 0.178191000000f, 0.018399000000f, 0.841310000000f }, + { 0.191214000000f, 0.019848000000f, 0.906110000000f }, + { 0.204492000000f, 0.021391000000f, 0.972542000000f }, + { 0.217650000000f, 0.022992000000f, 1.038900000000f }, + { 0.230267000000f, 0.024598000000f, 1.103100000000f }, + { 0.242311000000f, 0.026213000000f, 1.165100000000f }, + { 0.253793000000f, 0.027841000000f, 1.224900000000f }, + { 0.264737000000f, 0.029497000000f, 1.282500000000f }, + { 0.275195000000f, 0.031195000000f, 1.338200000000f }, + { 0.285301000000f, 0.032927000000f, 1.392600000000f }, + { 0.295143000000f, 0.034738000000f, 1.446100000000f }, + { 0.304869000000f, 0.036654000000f, 1.499400000000f }, + { 0.314679000000f, 0.038676000000f, 1.553480000000f }, + { 0.324355000000f, 0.040792000000f, 1.607200000000f }, + { 0.333570000000f, 0.042946000000f, 1.658900000000f }, + { 0.342243000000f, 0.045114000000f, 1.708200000000f }, + { 0.350312000000f, 0.047333000000f, 1.754800000000f }, + { 0.357719000000f, 0.049602000000f, 1.798500000000f }, + { 0.364482000000f, 0.051934000000f, 1.839200000000f }, + { 0.370493000000f, 0.054337000000f, 1.876600000000f }, + { 0.375727000000f, 0.056822000000f, 1.910500000000f }, + { 0.380158000000f, 0.059399000000f, 1.940800000000f }, + { 0.383734000000f, 0.062077000000f, 1.967280000000f }, + { 0.386327000000f, 0.064737000000f, 1.989100000000f }, + { 0.387858000000f, 0.067285000000f, 2.005700000000f }, + { 0.388396000000f, 0.069764000000f, 2.017400000000f }, + { 0.387978000000f, 0.072218000000f, 2.024400000000f }, + { 0.386726000000f, 0.074704000000f, 2.027300000000f }, + { 0.384696000000f, 0.077272000000f, 2.026400000000f }, + { 0.382006000000f, 0.079979000000f, 2.022300000000f }, + { 0.378709000000f, 0.082874000000f, 2.015300000000f }, + { 0.374915000000f, 0.086000000000f, 2.006000000000f }, + { 0.370702000000f, 0.089456000000f, 1.994800000000f }, + { 0.366089000000f, 0.092947000000f, 1.981400000000f }, + { 0.361045000000f, 0.096275000000f, 1.965300000000f }, + { 0.355518000000f, 0.099535000000f, 1.946400000000f }, + { 0.349486000000f, 0.102829000000f, 1.924800000000f }, + { 0.342957000000f, 0.106256000000f, 1.900700000000f }, + { 0.335893000000f, 0.109901000000f, 1.874100000000f }, + { 0.328284000000f, 0.113835000000f, 1.845100000000f }, + { 0.320150000000f, 0.118167000000f, 1.813900000000f }, + { 0.311475000000f, 0.122932000000f, 1.780600000000f }, + { 0.302273000000f, 0.128201000000f, 1.745370000000f }, + { 0.292858000000f, 0.133457000000f, 1.709100000000f }, + { 0.283502000000f, 0.138323000000f, 1.672300000000f }, + { 0.274044000000f, 0.143042000000f, 1.634700000000f }, + { 0.264263000000f, 0.147787000000f, 1.595600000000f }, + { 0.254085000000f, 0.152761000000f, 1.554900000000f }, + { 0.243392000000f, 0.158102000000f, 1.512200000000f }, + { 0.232187000000f, 0.163941000000f, 1.467300000000f }, + { 0.220488000000f, 0.170362000000f, 1.419900000000f }, + { 0.208198000000f, 0.177425000000f, 1.370000000000f }, + { 0.195618000000f, 0.185190000000f, 1.317560000000f }, + { 0.183034000000f, 0.193025000000f, 1.262400000000f }, + { 0.170222000000f, 0.200313000000f, 1.205000000000f }, + { 0.157348000000f, 0.207156000000f, 1.146600000000f }, + { 0.144650000000f, 0.213644000000f, 1.088000000000f }, + { 0.132349000000f, 0.219940000000f, 1.030200000000f }, + { 0.120584000000f, 0.226170000000f, 0.973830000000f }, + { 0.109456000000f, 0.232467000000f, 0.919430000000f }, + { 0.099042000000f, 0.239025000000f, 0.867460000000f }, + { 0.089388000000f, 0.245997000000f, 0.818280000000f }, + { 0.080507000000f, 0.253589000000f, 0.772125000000f }, + { 0.072034000000f, 0.261876000000f, 0.728290000000f }, + { 0.063710000000f, 0.270643000000f, 0.686040000000f }, + { 0.055694000000f, 0.279645000000f, 0.645530000000f }, + { 0.048117000000f, 0.288694000000f, 0.606850000000f }, + { 0.041072000000f, 0.297665000000f, 0.570060000000f }, + { 0.034642000000f, 0.306469000000f, 0.535220000000f }, + { 0.028896000000f, 0.315035000000f, 0.502340000000f }, + { 0.023876000000f, 0.323335000000f, 0.471400000000f }, + { 0.019628000000f, 0.331366000000f, 0.442390000000f }, + { 0.016172000000f, 0.339133000000f, 0.415254000000f }, + { 0.013300000000f, 0.347860000000f, 0.390024000000f }, + { 0.010759000000f, 0.358326000000f, 0.366399000000f }, + { 0.008542000000f, 0.370001000000f, 0.344015000000f }, + { 0.006661000000f, 0.382464000000f, 0.322689000000f }, + { 0.005132000000f, 0.395379000000f, 0.302356000000f }, + { 0.003982000000f, 0.408482000000f, 0.283036000000f }, + { 0.003239000000f, 0.421588000000f, 0.264816000000f }, + { 0.002934000000f, 0.434619000000f, 0.247848000000f }, + { 0.003114000000f, 0.447601000000f, 0.232318000000f }, + { 0.003816000000f, 0.460777000000f, 0.218502000000f }, + { 0.005095000000f, 0.474340000000f, 0.205851000000f }, + { 0.006936000000f, 0.488200000000f, 0.193596000000f }, + { 0.009299000000f, 0.502340000000f, 0.181736000000f }, + { 0.012147000000f, 0.516740000000f, 0.170281000000f }, + { 0.015444000000f, 0.531360000000f, 0.159249000000f }, + { 0.019156000000f, 0.546190000000f, 0.148673000000f }, + { 0.023250000000f, 0.561180000000f, 0.138609000000f }, + { 0.027690000000f, 0.576290000000f, 0.129096000000f }, + { 0.032444000000f, 0.591500000000f, 0.120215000000f }, + { 0.037465000000f, 0.606741000000f, 0.112044000000f }, + { 0.042956000000f, 0.622150000000f, 0.104710000000f }, + { 0.049114000000f, 0.637830000000f, 0.098196000000f }, + { 0.055920000000f, 0.653710000000f, 0.092361000000f }, + { 0.063349000000f, 0.669680000000f, 0.087088000000f }, + { 0.071358000000f, 0.685660000000f, 0.082248000000f }, + { 0.079901000000f, 0.701550000000f, 0.077744000000f }, + { 0.088909000000f, 0.717230000000f, 0.073456000000f }, + { 0.098293000000f, 0.732570000000f, 0.069268000000f }, + { 0.107949000000f, 0.747460000000f, 0.065060000000f }, + { 0.117749000000f, 0.761757000000f, 0.060709000000f }, + { 0.127839000000f, 0.775340000000f, 0.056457000000f }, + { 0.138450000000f, 0.788220000000f, 0.052609000000f }, + { 0.149516000000f, 0.800460000000f, 0.049122000000f }, + { 0.161041000000f, 0.812140000000f, 0.045954000000f }, + { 0.172953000000f, 0.823330000000f, 0.043050000000f }, + { 0.185209000000f, 0.834120000000f, 0.040368000000f }, + { 0.197755000000f, 0.844600000000f, 0.037839000000f }, + { 0.210538000000f, 0.854870000000f, 0.035384000000f }, + { 0.223460000000f, 0.865040000000f, 0.032949000000f }, + { 0.236491000000f, 0.875211000000f, 0.030451000000f }, + { 0.249633000000f, 0.885370000000f, 0.028029000000f }, + { 0.262972000000f, 0.895370000000f, 0.025862000000f }, + { 0.276515000000f, 0.905150000000f, 0.023920000000f }, + { 0.290269000000f, 0.914650000000f, 0.022174000000f }, + { 0.304213000000f, 0.923810000000f, 0.020584000000f }, + { 0.318361000000f, 0.932550000000f, 0.019127000000f }, + { 0.332705000000f, 0.940810000000f, 0.017740000000f }, + { 0.347232000000f, 0.948520000000f, 0.016403000000f }, + { 0.361926000000f, 0.955600000000f, 0.015064000000f }, + { 0.376772000000f, 0.961988000000f, 0.013676000000f }, + { 0.391683000000f, 0.967540000000f, 0.012308000000f }, + { 0.406594000000f, 0.972230000000f, 0.011056000000f }, + { 0.421539000000f, 0.976170000000f, 0.009915000000f }, + { 0.436517000000f, 0.979460000000f, 0.008872000000f }, + { 0.451584000000f, 0.982200000000f, 0.007918000000f }, + { 0.466782000000f, 0.984520000000f, 0.007030000000f }, + { 0.482147000000f, 0.986520000000f, 0.006223000000f }, + { 0.497738000000f, 0.988320000000f, 0.005453000000f }, + { 0.513606000000f, 0.990020000000f, 0.004714000000f }, + { 0.529826000000f, 0.991761000000f, 0.003988000000f }, + { 0.546440000000f, 0.993530000000f, 0.003289000000f }, + { 0.563426000000f, 0.995230000000f, 0.002646000000f }, + { 0.580726000000f, 0.996770000000f, 0.002063000000f }, + { 0.598290000000f, 0.998090000000f, 0.001533000000f }, + { 0.616053000000f, 0.999110000000f, 0.001091000000f }, + { 0.633948000000f, 0.999770000000f, 0.000711000000f }, + { 0.651901000000f, 1.000000000000f, 0.000407000000f }, + { 0.669824000000f, 0.999710000000f, 0.000184000000f }, + { 0.687632000000f, 0.998850000000f, 0.000047000000f }, + { 0.705224000000f, 0.997340000000f, 0.000000000000f }, + { 0.722773000000f, 0.995260000000f, 0.000000000000f }, + { 0.740483000000f, 0.992740000000f, 0.000000000000f }, + { 0.758273000000f, 0.989750000000f, 0.000000000000f }, + { 0.776083000000f, 0.986300000000f, 0.000000000000f }, + { 0.793832000000f, 0.982380000000f, 0.000000000000f }, + { 0.811436000000f, 0.977980000000f, 0.000000000000f }, + { 0.828822000000f, 0.973110000000f, 0.000000000000f }, + { 0.845879000000f, 0.967740000000f, 0.000000000000f }, + { 0.862525000000f, 0.961890000000f, 0.000000000000f }, + { 0.878655000000f, 0.955552000000f, 0.000000000000f }, + { 0.894208000000f, 0.948601000000f, 0.000000000000f }, + { 0.909206000000f, 0.940981000000f, 0.000000000000f }, + { 0.923672000000f, 0.932798000000f, 0.000000000000f }, + { 0.937638000000f, 0.924158000000f, 0.000000000000f }, + { 0.951162000000f, 0.915175000000f, 0.000000000000f }, + { 0.964283000000f, 0.905954000000f, 0.000000000000f }, + { 0.977068000000f, 0.896608000000f, 0.000000000000f }, + { 0.989590000000f, 0.887249000000f, 0.000000000000f }, + { 1.001910000000f, 0.877986000000f, 0.000000000000f }, + { 1.014160000000f, 0.868934000000f, 0.000000000000f }, + { 1.026500000000f, 0.860164000000f, 0.000000000000f }, + { 1.038800000000f, 0.851519000000f, 0.000000000000f }, + { 1.051000000000f, 0.842963000000f, 0.000000000000f }, + { 1.062900000000f, 0.834393000000f, 0.000000000000f }, + { 1.074300000000f, 0.825623000000f, 0.000000000000f }, + { 1.085200000000f, 0.816764000000f, 0.000000000000f }, + { 1.095200000000f, 0.807544000000f, 0.000000000000f }, + { 1.104200000000f, 0.797947000000f, 0.000000000000f }, + { 1.112000000000f, 0.787893000000f, 0.000000000000f }, + { 1.118520000000f, 0.777405000000f, 0.000000000000f }, + { 1.123800000000f, 0.766490000000f, 0.000000000000f }, + { 1.128000000000f, 0.755309000000f, 0.000000000000f }, + { 1.131100000000f, 0.743845000000f, 0.000000000000f }, + { 1.133200000000f, 0.732190000000f, 0.000000000000f }, + { 1.134300000000f, 0.720353000000f, 0.000000000000f }, + { 1.134300000000f, 0.708281000000f, 0.000000000000f }, + { 1.133300000000f, 0.696055000000f, 0.000000000000f }, + { 1.131200000000f, 0.683621000000f, 0.000000000000f }, + { 1.128100000000f, 0.671048000000f, 0.000000000000f }, + { 1.123990000000f, 0.658341000000f, 0.000000000000f }, + { 1.118900000000f, 0.645545000000f, 0.000000000000f }, + { 1.112900000000f, 0.632718000000f, 0.000000000000f }, + { 1.105900000000f, 0.619815000000f, 0.000000000000f }, + { 1.098000000000f, 0.606887000000f, 0.000000000000f }, + { 1.089100000000f, 0.593878000000f, 0.000000000000f }, + { 1.079200000000f, 0.580781000000f, 0.000000000000f }, + { 1.068400000000f, 0.567653000000f, 0.000000000000f }, + { 1.056700000000f, 0.554490000000f, 0.000000000000f }, + { 1.044000000000f, 0.541228000000f, 0.000000000000f }, + { 1.030480000000f, 0.527963000000f, 0.000000000000f }, + { 1.016000000000f, 0.514634000000f, 0.000000000000f }, + { 1.000800000000f, 0.501363000000f, 0.000000000000f }, + { 0.984790000000f, 0.488124000000f, 0.000000000000f }, + { 0.968080000000f, 0.474935000000f, 0.000000000000f }, + { 0.950740000000f, 0.461834000000f, 0.000000000000f }, + { 0.932800000000f, 0.448823000000f, 0.000000000000f }, + { 0.914340000000f, 0.435917000000f, 0.000000000000f }, + { 0.895390000000f, 0.423153000000f, 0.000000000000f }, + { 0.876030000000f, 0.410526000000f, 0.000000000000f }, + { 0.856297000000f, 0.398057000000f, 0.000000000000f }, + { 0.836350000000f, 0.385835000000f, 0.000000000000f }, + { 0.816290000000f, 0.373951000000f, 0.000000000000f }, + { 0.796050000000f, 0.362311000000f, 0.000000000000f }, + { 0.775610000000f, 0.350863000000f, 0.000000000000f }, + { 0.754930000000f, 0.339554000000f, 0.000000000000f }, + { 0.733990000000f, 0.328309000000f, 0.000000000000f }, + { 0.712780000000f, 0.317118000000f, 0.000000000000f }, + { 0.691290000000f, 0.305936000000f, 0.000000000000f }, + { 0.669520000000f, 0.294737000000f, 0.000000000000f }, + { 0.647467000000f, 0.283493000000f, 0.000000000000f }, + { 0.625110000000f, 0.272222000000f, 0.000000000000f }, + { 0.602520000000f, 0.260990000000f, 0.000000000000f }, + { 0.579890000000f, 0.249877000000f, 0.000000000000f }, + { 0.557370000000f, 0.238946000000f, 0.000000000000f }, + { 0.535110000000f, 0.228254000000f, 0.000000000000f }, + { 0.513240000000f, 0.217853000000f, 0.000000000000f }, + { 0.491860000000f, 0.207780000000f, 0.000000000000f }, + { 0.471080000000f, 0.198072000000f, 0.000000000000f }, + { 0.450960000000f, 0.188748000000f, 0.000000000000f }, + { 0.431567000000f, 0.179828000000f, 0.000000000000f }, + { 0.412870000000f, 0.171285000000f, 0.000000000000f }, + { 0.394750000000f, 0.163059000000f, 0.000000000000f }, + { 0.377210000000f, 0.155151000000f, 0.000000000000f }, + { 0.360190000000f, 0.147535000000f, 0.000000000000f }, + { 0.343690000000f, 0.140211000000f, 0.000000000000f }, + { 0.327690000000f, 0.133170000000f, 0.000000000000f }, + { 0.312170000000f, 0.126400000000f, 0.000000000000f }, + { 0.297110000000f, 0.119892000000f, 0.000000000000f }, + { 0.282500000000f, 0.113640000000f, 0.000000000000f }, + { 0.268329000000f, 0.107633000000f, 0.000000000000f }, + { 0.254590000000f, 0.101870000000f, 0.000000000000f }, + { 0.241300000000f, 0.096347000000f, 0.000000000000f }, + { 0.228480000000f, 0.091063000000f, 0.000000000000f }, + { 0.216140000000f, 0.086010000000f, 0.000000000000f }, + { 0.204300000000f, 0.081187000000f, 0.000000000000f }, + { 0.192950000000f, 0.076583000000f, 0.000000000000f }, + { 0.182110000000f, 0.072198000000f, 0.000000000000f }, + { 0.171770000000f, 0.068024000000f, 0.000000000000f }, + { 0.161920000000f, 0.064052000000f, 0.000000000000f }, + { 0.152568000000f, 0.060281000000f, 0.000000000000f }, + { 0.143670000000f, 0.056697000000f, 0.000000000000f }, + { 0.135200000000f, 0.053292000000f, 0.000000000000f }, + { 0.127130000000f, 0.050059000000f, 0.000000000000f }, + { 0.119480000000f, 0.046998000000f, 0.000000000000f }, + { 0.112210000000f, 0.044096000000f, 0.000000000000f }, + { 0.105310000000f, 0.041345000000f, 0.000000000000f }, + { 0.098786000000f, 0.038750700000f, 0.000000000000f }, + { 0.092610000000f, 0.036297800000f, 0.000000000000f }, + { 0.086773000000f, 0.033983200000f, 0.000000000000f }, + { 0.081260600000f, 0.031800400000f, 0.000000000000f }, + { 0.076048000000f, 0.029739500000f, 0.000000000000f }, + { 0.071114000000f, 0.027791800000f, 0.000000000000f }, + { 0.066454000000f, 0.025955100000f, 0.000000000000f }, + { 0.062062000000f, 0.024226300000f, 0.000000000000f }, + { 0.057930000000f, 0.022601700000f, 0.000000000000f }, + { 0.054050000000f, 0.021077900000f, 0.000000000000f }, + { 0.050412000000f, 0.019650500000f, 0.000000000000f }, + { 0.047006000000f, 0.018315300000f, 0.000000000000f }, + { 0.043823000000f, 0.017068600000f, 0.000000000000f }, + { 0.040850800000f, 0.015905100000f, 0.000000000000f }, + { 0.038072000000f, 0.014818300000f, 0.000000000000f }, + { 0.035468000000f, 0.013800800000f, 0.000000000000f }, + { 0.033031000000f, 0.012849500000f, 0.000000000000f }, + { 0.030753000000f, 0.011960700000f, 0.000000000000f }, + { 0.028623000000f, 0.011130300000f, 0.000000000000f }, + { 0.026635000000f, 0.010355500000f, 0.000000000000f }, + { 0.024781000000f, 0.009633200000f, 0.000000000000f }, + { 0.023052000000f, 0.008959900000f, 0.000000000000f }, + { 0.021441000000f, 0.008332400000f, 0.000000000000f }, + { 0.019941300000f, 0.007748800000f, 0.000000000000f }, + { 0.018544000000f, 0.007204600000f, 0.000000000000f }, + { 0.017241000000f, 0.006697500000f, 0.000000000000f }, + { 0.016027000000f, 0.006225100000f, 0.000000000000f }, + { 0.014896000000f, 0.005785000000f, 0.000000000000f }, + { 0.013842000000f, 0.005375100000f, 0.000000000000f }, + { 0.012862000000f, 0.004994100000f, 0.000000000000f }, + { 0.011949000000f, 0.004639200000f, 0.000000000000f }, + { 0.011100000000f, 0.004309300000f, 0.000000000000f }, + { 0.010311000000f, 0.004002800000f, 0.000000000000f }, + { 0.009576880000f, 0.003717740000f, 0.000000000000f }, + { 0.008894000000f, 0.003452620000f, 0.000000000000f }, + { 0.008258100000f, 0.003205830000f, 0.000000000000f }, + { 0.007666400000f, 0.002976230000f, 0.000000000000f }, + { 0.007116300000f, 0.002762810000f, 0.000000000000f }, + { 0.006605200000f, 0.002564560000f, 0.000000000000f }, + { 0.006130600000f, 0.002380480000f, 0.000000000000f }, + { 0.005690300000f, 0.002209710000f, 0.000000000000f }, + { 0.005281900000f, 0.002051320000f, 0.000000000000f }, + { 0.004903300000f, 0.001904490000f, 0.000000000000f }, + { 0.004552630000f, 0.001768470000f, 0.000000000000f }, + { 0.004227500000f, 0.001642360000f, 0.000000000000f }, + { 0.003925800000f, 0.001525350000f, 0.000000000000f }, + { 0.003645700000f, 0.001416720000f, 0.000000000000f }, + { 0.003385900000f, 0.001315950000f, 0.000000000000f }, + { 0.003144700000f, 0.001222390000f, 0.000000000000f }, + { 0.002920800000f, 0.001135550000f, 0.000000000000f }, + { 0.002713000000f, 0.001054940000f, 0.000000000000f }, + { 0.002520200000f, 0.000980140000f, 0.000000000000f }, + { 0.002341100000f, 0.000910660000f, 0.000000000000f }, + { 0.002174960000f, 0.000846190000f, 0.000000000000f }, + { 0.002020600000f, 0.000786290000f, 0.000000000000f }, + { 0.001877300000f, 0.000730680000f, 0.000000000000f }, + { 0.001744100000f, 0.000678990000f, 0.000000000000f }, + { 0.001620500000f, 0.000631010000f, 0.000000000000f }, + { 0.001505700000f, 0.000586440000f, 0.000000000000f }, + { 0.001399200000f, 0.000545110000f, 0.000000000000f }, + { 0.001300400000f, 0.000506720000f, 0.000000000000f }, + { 0.001208700000f, 0.000471110000f, 0.000000000000f }, + { 0.001123600000f, 0.000438050000f, 0.000000000000f }, + { 0.001044760000f, 0.000407410000f, 0.000000000000f }, + { 0.000971560000f, 0.000378962000f, 0.000000000000f }, + { 0.000903600000f, 0.000352543000f, 0.000000000000f }, + { 0.000840480000f, 0.000328001000f, 0.000000000000f }, + { 0.000781870000f, 0.000305208000f, 0.000000000000f }, + { 0.000727450000f, 0.000284041000f, 0.000000000000f }, + { 0.000676900000f, 0.000264375000f, 0.000000000000f }, + { 0.000629960000f, 0.000246109000f, 0.000000000000f }, + { 0.000586370000f, 0.000229143000f, 0.000000000000f }, + { 0.000545870000f, 0.000213376000f, 0.000000000000f }, + { 0.000508258000f, 0.000198730000f, 0.000000000000f }, + { 0.000473300000f, 0.000185115000f, 0.000000000000f }, + { 0.000440800000f, 0.000172454000f, 0.000000000000f }, + { 0.000410580000f, 0.000160678000f, 0.000000000000f }, + { 0.000382490000f, 0.000149730000f, 0.000000000000f }, + { 0.000356380000f, 0.000139550000f, 0.000000000000f }, + { 0.000332110000f, 0.000130086000f, 0.000000000000f }, + { 0.000309550000f, 0.000121290000f, 0.000000000000f }, + { 0.000288580000f, 0.000113106000f, 0.000000000000f }, + { 0.000269090000f, 0.000105501000f, 0.000000000000f }, + { 0.000250969000f, 0.000098428000f, 0.000000000000f }, + { 0.000234130000f, 0.000091853000f, 0.000000000000f }, + { 0.000218470000f, 0.000085738000f, 0.000000000000f }, + { 0.000203910000f, 0.000080048000f, 0.000000000000f }, + { 0.000190350000f, 0.000074751000f, 0.000000000000f }, + { 0.000177730000f, 0.000069819000f, 0.000000000000f }, + { 0.000165970000f, 0.000065222000f, 0.000000000000f }, + { 0.000155020000f, 0.000060939000f, 0.000000000000f }, + { 0.000144800000f, 0.000056942000f, 0.000000000000f }, + { 0.000135280000f, 0.000053217000f, 0.000000000000f }, + { 0.000126390000f, 0.000049737000f, 0.000000000000f }, + { 0.000118100000f, 0.000046491000f, 0.000000000000f }, + { 0.000110370000f, 0.000043464000f, 0.000000000000f }, + { 0.000103150000f, 0.000040635000f, 0.000000000000f }, + { 0.000096427000f, 0.000038000000f, 0.000000000000f }, + { 0.000090151000f, 0.000035540500f, 0.000000000000f }, + { 0.000084294000f, 0.000033244800f, 0.000000000000f }, + { 0.000078830000f, 0.000031100600f, 0.000000000000f }, + { 0.000073729000f, 0.000029099000f, 0.000000000000f }, + { 0.000068969000f, 0.000027230700f, 0.000000000000f }, + { 0.000064525800f, 0.000025486000f, 0.000000000000f }, + { 0.000060376000f, 0.000023856100f, 0.000000000000f }, + { 0.000056500000f, 0.000022333200f, 0.000000000000f }, + { 0.000052880000f, 0.000020910400f, 0.000000000000f }, + { 0.000049498000f, 0.000019580800f, 0.000000000000f }, + { 0.000046339000f, 0.000018338400f, 0.000000000000f }, + { 0.000043389000f, 0.000017177700f, 0.000000000000f }, + { 0.000040634000f, 0.000016093400f, 0.000000000000f }, + { 0.000038060000f, 0.000015080000f, 0.000000000000f }, + { 0.000035657000f, 0.000014133600f, 0.000000000000f }, + { 0.000033411700f, 0.000013249000f, 0.000000000000f }, + { 0.000031315000f, 0.000012422600f, 0.000000000000f }, + { 0.000029355000f, 0.000011649900f, 0.000000000000f }, + { 0.000027524000f, 0.000010927700f, 0.000000000000f }, + { 0.000025811000f, 0.000010251900f, 0.000000000000f }, + { 0.000024209000f, 0.000009619600f, 0.000000000000f }, + { 0.000022711000f, 0.000009028100f, 0.000000000000f }, + { 0.000021308000f, 0.000008474000f, 0.000000000000f }, + { 0.000019994000f, 0.000007954800f, 0.000000000000f }, + { 0.000018764000f, 0.000007468600f, 0.000000000000f }, + { 0.000017611500f, 0.000007012800f, 0.000000000000f }, + { 0.000016532000f, 0.000006585800f, 0.000000000000f }, + { 0.000015521000f, 0.000006185700f, 0.000000000000f }, + { 0.000014574000f, 0.000005810700f, 0.000000000000f }, + { 0.000013686000f, 0.000005459000f, 0.000000000000f }, + { 0.000012855000f, 0.000005129800f, 0.000000000000f }, + { 0.000012075000f, 0.000004820600f, 0.000000000000f }, + { 0.000011345000f, 0.000004531200f, 0.000000000000f }, + { 0.000010659000f, 0.000004259100f, 0.000000000000f }, + { 0.000010017000f, 0.000004004200f, 0.000000000000f }, + { 0.000009413630f, 0.000003764730f, 0.000000000000f }, + { 0.000008847900f, 0.000003539950f, 0.000000000000f }, + { 0.000008317100f, 0.000003329140f, 0.000000000000f }, + { 0.000007819000f, 0.000003131150f, 0.000000000000f }, + { 0.000007351600f, 0.000002945290f, 0.000000000000f }, + { 0.000006913000f, 0.000002770810f, 0.000000000000f }, + { 0.000006501500f, 0.000002607050f, 0.000000000000f }, + { 0.000006115300f, 0.000002453290f, 0.000000000000f }, + { 0.000005752900f, 0.000002308940f, 0.000000000000f }, + { 0.000005412700f, 0.000002173380f, 0.000000000000f }, + { 0.000005093470f, 0.000002046130f, 0.000000000000f }, + { 0.000004793800f, 0.000001926620f, 0.000000000000f }, + { 0.000004512500f, 0.000001814400f, 0.000000000000f }, + { 0.000004248300f, 0.000001708950f, 0.000000000000f }, + { 0.000004000200f, 0.000001609880f, 0.000000000000f }, + { 0.000003767100f, 0.000001516770f, 0.000000000000f }, + { 0.000003548000f, 0.000001429210f, 0.000000000000f }, + { 0.000003342100f, 0.000001346860f, 0.000000000000f }, + { 0.000003148500f, 0.000001269450f, 0.000000000000f }, + { 0.000002966500f, 0.000001196620f, 0.000000000000f }, + { 0.000002795310f, 0.000001128090f, 0.000000000000f }, + { 0.000002634500f, 0.000001063680f, 0.000000000000f }, + { 0.000002483400f, 0.000001003130f, 0.000000000000f }, + { 0.000002341400f, 0.000000946220f, 0.000000000000f }, + { 0.000002207800f, 0.000000892630f, 0.000000000000f }, + { 0.000002082000f, 0.000000842160f, 0.000000000000f }, + { 0.000001963600f, 0.000000794640f, 0.000000000000f }, + { 0.000001851900f, 0.000000749780f, 0.000000000000f }, + { 0.000001746500f, 0.000000707440f, 0.000000000000f }, + { 0.000001647100f, 0.000000667480f, 0.000000000000f }, + { 0.000001553140f, 0.000000629700f, 0.000000000000f } }; // CIE Standard Illuminant D65 relative spectral power distribution, diff --git a/web/docs/Pipfile.lock b/web/docs/Pipfile.lock deleted file mode 100644 index 867304fb60..0000000000 --- a/web/docs/Pipfile.lock +++ /dev/null @@ -1,58 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "64fb040f71095c2ee61210e3b1cab5dfde8866fcc1121829f2106ea38abbd125" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.8" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "editorconfig": { - "hashes": [ - "sha256:1b0ef345f9c3a673e492cfe608ed644b236139f7fceab5c6f513a71bcaf8a56c", - "sha256:60d6f10b87d2572ac1581cb8c9f018163e2b13a9e49588f9fb6dc8c715a1744c" - ], - "version": "==0.12.2" - }, - "jsbeautifier": { - "hashes": [ - "sha256:4f670c6f4f8d4caca9a348271010742f494becb96fe0a9be6ffac8b3d46350ca" - ], - "index": "pypi", - "version": "==1.11.0" - }, - "mistletoe": { - "hashes": [ - "sha256:24d0f18cc5f0381c2cfb8a24ef3de83eb9f7929cb7d0e71ecb164b671d86e6a3", - "sha256:3e2d31b2fa6231ea2ee46981274ebac8d5e5736e3aad2d1cd449e2c053b7023b" - ], - "index": "pypi", - "version": "==0.7.2" - }, - "pygments": { - "hashes": [ - "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44", - "sha256:ff7a40b4860b727ab48fad6360eb351cc1b33cbf9b15a0f689ca5353e9463324" - ], - "index": "pypi", - "version": "==2.6.1" - }, - "six": { - "hashes": [ - "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a", - "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c" - ], - "version": "==1.14.0" - } - }, - "develop": {} -} diff --git a/web/filament-js/extensions.js b/web/filament-js/extensions.js index ef05f7e89e..baaf47c7fc 100644 --- a/web/filament-js/extensions.js +++ b/web/filament-js/extensions.js @@ -73,16 +73,35 @@ Filament.loadClassExtensions = function() { // Create the WebGL 2.0 context. const ctx = canvas.getContext("webgl2", options); - Filament.glOptions = options; - Filament.glContext = ctx; // Enable all desired extensions by calling getExtension on each one. ctx.getExtension('WEBGL_compressed_texture_s3tc'); ctx.getExtension('WEBGL_compressed_texture_astc'); ctx.getExtension('WEBGL_compressed_texture_etc'); + // These transient globals are used temporarily during Engine construction. + window.filament_glOptions = options; + window.filament_glContext = ctx; + // Register the GL context with emscripten and create the Engine. - return Filament.Engine._create(); + const engine = Filament.Engine._create(); + + // Annotate the engine with the GL context to support multiple canvases. + engine.context = window.filament_glContext; + engine.handle = window.filament_contextHandle; + + // Ensure that we do not pollute the global namespace. + delete window.filament_glOptions; + delete window.filament_glContext; + delete window.filament_contextHandle; + + return engine; + }; + + Filament.Engine.prototype.execute = function() { + window.filament_contextHandle = this.handle; + this._execute(); + delete window.filament_contextHandle; }; /// createMaterial ::method:: @@ -306,6 +325,18 @@ Filament.loadClassExtensions = function() { this._setVignetteOptions(options); }; + /// BufferObject ::core class:: + + /// setBuffer ::method:: + /// engine ::argument:: [Engine] + /// buffer ::argument:: asset string, or Uint8Array, or [Buffer] + /// byteOffset ::argument:: non-negative integer + Filament.BufferObject.prototype.setBuffer = function(engine, buffer, byteOffset = 0) { + buffer = getBufferDescriptor(buffer); + this._setBuffer(engine, buffer, byteOffset); + buffer.delete(); + }; + /// VertexBuffer ::core class:: /// setBufferAt ::method:: diff --git a/web/filament-js/filament.d.ts b/web/filament-js/filament.d.ts index 8642bd6ad8..ee4304b1e0 100644 --- a/web/filament-js/filament.d.ts +++ b/web/filament-js/filament.d.ts @@ -143,6 +143,9 @@ export interface View$VignetteOptions { enabled?: boolean; } +export function fitIntoUnitCube(box: Aabb): mat4; +export function multiplyMatrices(a: mat4, b: mat4): mat4; + // Clients should use the [PixelBuffer/CompressedPixelBuffer] helper function to contruct PixelBufferDescriptor objects. export class driver$PixelBufferDescriptor { constructor(byteLength: number, format: PixelDataFormat, datatype: PixelDataType); @@ -225,6 +228,7 @@ export class VertexBuffer$Builder { public bufferCount(count: number): VertexBuffer$Builder; public attribute(attrib: VertexAttribute, bufindex: number, atype: VertexBuffer$AttributeType, offset: number, stride: number): VertexBuffer$Builder; + public enableBufferObjects(enabled: boolean): VertexBuffer$Builder; public normalized(attrib: VertexAttribute): VertexBuffer$Builder; public normalizedIf(attrib: VertexAttribute, normalized: boolean): VertexBuffer$Builder; public build(engine: Engine): VertexBuffer; @@ -236,6 +240,12 @@ export class IndexBuffer$Builder { public build(engine: Engine): IndexBuffer; } +export class BufferObject$Builder { + public size(byteCount: number): BufferObject$Builder; + public bindingType(type: BufferObject$BindingType): BufferObject$Builder; + public build(engine: Engine): BufferObject; +} + export class RenderableManager$Builder { public geometry(slot: number, ptype: RenderableManager$PrimitiveType, vb: VertexBuffer, ib: IndexBuffer): RenderableManager$Builder; @@ -364,6 +374,12 @@ export class VertexBuffer { public static Builder(): VertexBuffer$Builder; public setBufferAt(engine: Engine, bufindex: number, f32array: BufferReference, byteOffset?: number): void; + public setBufferObjectAt(engine: Engine, bufindex: number, bo: BufferObject): void; +} + +export class BufferObject { + public static Builder(): BufferObject$Builder; + public setBuffer(engine: Engine, data: BufferReference, byteOffset?: number): void; } export class IndexBuffer { @@ -719,6 +735,10 @@ export enum IndexBuffer$IndexType { UINT, } +export enum BufferObject$BindingType { + VERTEX_BINDING, +} + export enum LightManager$Type { SUN, DIRECTIONAL, @@ -955,6 +975,10 @@ export enum VertexAttribute { MORPH_TANGENTS_3 = CUSTOM7, } +export enum BufferObject$BindingType { + VERTEX, +} + export enum VertexBuffer$AttributeType { BYTE, BYTE2, diff --git a/web/filament-js/jsbindings.cpp b/web/filament-js/jsbindings.cpp index f44c9374e4..3e02d3e47b 100644 --- a/web/filament-js/jsbindings.cpp +++ b/web/filament-js/jsbindings.cpp @@ -34,6 +34,7 @@ #include +#include #include #include #include @@ -117,6 +118,7 @@ namespace emscripten { namespace internal { BIND(Animator) BIND(AssetLoader) + BIND(BufferObject) BIND(Camera) BIND(ColorGrading) BIND(Engine) @@ -204,6 +206,7 @@ using SkyBuilder = Skybox::Builder; using SurfaceBuilder = SurfaceOrientation::Builder; using TexBuilder = Texture::Builder; using VertexBuilder = VertexBuffer::Builder; +using BufferBuilder = BufferObject::Builder; // We avoid directly exposing backend::BufferDescriptor because embind does not support move // semantics and void* doesn't make sense to JavaScript anyway. This little wrapper class is exposed @@ -368,7 +371,13 @@ value_object("View$DepthOfFieldOptions") .field("cocScale", &filament::View::DepthOfFieldOptions::cocScale) .field("maxApertureDiameter", &filament::View::DepthOfFieldOptions::maxApertureDiameter) .field("enabled", &filament::View::DepthOfFieldOptions::enabled) - .field("filter", &filament::View::DepthOfFieldOptions::filter); + .field("filter", &filament::View::DepthOfFieldOptions::filter) + .field("nativeResolution", &filament::View::DepthOfFieldOptions::nativeResolution) + .field("foregroundRingCount", &filament::View::DepthOfFieldOptions::foregroundRingCount) + .field("backgroundRingCount", &filament::View::DepthOfFieldOptions::backgroundRingCount) + .field("fastGatherRingCount", &filament::View::DepthOfFieldOptions::fastGatherRingCount) + .field("maxForegroundCOC", &filament::View::DepthOfFieldOptions::maxForegroundCOC) + .field("maxBackgroundCOC", &filament::View::DepthOfFieldOptions::maxBackgroundCOC); value_object("View$BloomOptions") .field("dirtStrength", &filament::View::BloomOptions::dirtStrength) @@ -463,16 +472,27 @@ register_vector("MaterialInstanceVector"); class_("Engine") .class_function("_create", (Engine* (*)()) [] { EM_ASM_INT({ - const handle = GL.registerContext(Filament.glContext, Filament.glOptions); + const options = window.filament_glOptions; + const context = window.filament_glContext; + const handle = GL.registerContext(context, options); + window.filament_contextHandle = handle; GL.makeContextCurrent(handle); }); return Engine::create(); }, allow_raw_pointers()) + + .function("_execute", EMBIND_LAMBDA(void, (Engine* engine), { + EM_ASM_INT({ + const handle = window.filament_contextHandle; + GL.makeContextCurrent(handle); + }); + engine->execute(); + }), allow_raw_pointers()) + /// destroy ::static method:: Destroys an engine instance and cleans up resources. /// engine ::argument:: the instance to destroy .class_function("destroy", (void (*)(Engine*)) [] (Engine* engine) { Engine::destroy(&engine); }, allow_raw_pointers()) - .function("execute", &Engine::execute) /// getTransformManager ::method:: /// ::retval:: an instance of [TransformManager] @@ -1156,6 +1176,25 @@ class_("LightManager") class_("LightManager$Instance"); /// delete ::method:: Frees an instance obtained via `getInstance` +class_("BufferObject$Builder") + .function("_build", EMBIND_LAMBDA(BufferObject*, (BufferBuilder* builder, Engine* engine), { + return builder->build(*engine); + }), allow_raw_pointers()) + .BUILDER_FUNCTION("bindingType", BufferBuilder, (BufferBuilder* builder, + BufferObject::BindingType bt), { + return &builder->bindingType(bt); }) + .BUILDER_FUNCTION("size", BufferBuilder, (BufferBuilder* builder, int byteCount), { + return &builder->size(byteCount); }); + +/// BufferObject ::core class:: Represents a single GPU buffer. +class_("BufferObject") + .class_function("Builder", (BufferBuilder (*)()) [] { return BufferBuilder(); }) + .function("getByteCount", &BufferObject::getByteCount) + .function("_setBuffer", EMBIND_LAMBDA(void, (BufferObject* self, + Engine* engine, BufferDescriptor bd, uint32_t byteOffset), { + self->setBuffer(*engine, std::move(*bd.bd), byteOffset); + }), allow_raw_pointers()); + class_("VertexBuffer$Builder") .function("_build", EMBIND_LAMBDA(VertexBuffer*, (VertexBuilder* builder, Engine* engine), { return builder->build(*engine); @@ -1169,6 +1208,8 @@ class_("VertexBuffer$Builder") return &builder->attribute(attr, bufferIndex, attrType, byteOffset, byteStride); }) .BUILDER_FUNCTION("vertexCount", VertexBuilder, (VertexBuilder* builder, int count), { return &builder->vertexCount(count); }) + .BUILDER_FUNCTION("enableBufferObjects", VertexBuilder, (VertexBuilder* builder, bool enable), { + return &builder->enableBufferObjects(enable); }) .BUILDER_FUNCTION("normalized", VertexBuilder, (VertexBuilder* builder, VertexAttribute attrib), { return &builder->normalized(attrib); }) @@ -1181,6 +1222,7 @@ class_("VertexBuffer$Builder") /// VertexBuffer ::core class:: Bundle of buffers and associated vertex attributes. class_("VertexBuffer") .class_function("Builder", (VertexBuilder (*)()) [] { return VertexBuilder(); }) + .function("setBufferObjectAt", &VertexBuffer::setBufferObjectAt, allow_raw_pointers()) .function("_setBufferAt", EMBIND_LAMBDA(void, (VertexBuffer* self, Engine* engine, uint8_t bufferIndex, BufferDescriptor vbd, uint32_t byteOffset), { self->setBufferAt(*engine, bufferIndex, std::move(*vbd.bd), byteOffset); @@ -1855,7 +1897,19 @@ class_("JsonSerializer") class_("SimpleViewer") .constructor() .function("renderUserInterface", &SimpleViewer::renderUserInterface, allow_raw_pointers()) - .function("getSettings", &SimpleViewer::getSettings); + .function("getSettings", &SimpleViewer::getSettings) + .function("mouseEvent", &SimpleViewer::mouseEvent) + .function("keyDownEvent", &SimpleViewer::keyDownEvent) + .function("keyUpEvent", &SimpleViewer::keyUpEvent) + .function("keyPressEvent", &SimpleViewer::keyPressEvent); + +function("fitIntoUnitCube", EMBIND_LAMBDA(flatmat4, (Aabb box, float zoffset), { + return flatmat4 { fitIntoUnitCube(box, zoffset) }; +})); + +function("multiplyMatrices", EMBIND_LAMBDA(flatmat4, (flatmat4 a, flatmat4 b), { + return flatmat4 { a.m * b.m }; +})); } // EMSCRIPTEN_BINDINGS diff --git a/web/filament-js/jsenums.cpp b/web/filament-js/jsenums.cpp index 9e63c23213..226b60b42c 100644 --- a/web/filament-js/jsenums.cpp +++ b/web/filament-js/jsenums.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include #include #include #include @@ -69,6 +70,9 @@ enum_("VertexAttribute") .value("MORPH_TANGENTS_2", MORPH_TANGENTS_2) .value("MORPH_TANGENTS_3", MORPH_TANGENTS_3); +enum_("BufferObject$BindingType") + .value("VERTEX_BINDING", BufferObject::BindingType::VERTEX); + enum_("VertexBuffer$AttributeType") .value("BYTE", VertexBuffer::AttributeType::BYTE) .value("BYTE2", VertexBuffer::AttributeType::BYTE2) diff --git a/web/filament-js/package.json b/web/filament-js/package.json index 4ab5468503..265de588c8 100644 --- a/web/filament-js/package.json +++ b/web/filament-js/package.json @@ -1,6 +1,6 @@ { "name": "filament", - "version": "1.9.19", + "version": "1.9.20", "description": "Real-time physically based rendering engine", "main": "filament.js", "module": "filament.js", diff --git a/web/samples/remote.html b/web/samples/remote.html index d9390231be..0488dafcde 100644 --- a/web/samples/remote.html +++ b/web/samples/remote.html @@ -135,7 +135,7 @@ class App { // In the future we might want to draw stuff in the central 3D viewport. const kInitialSidebarWidth = this.canvas.clientWidth * window.devicePixelRatio; - // Clear the central 3D viewport to light gray, whicht is not visible by default. + // Clear the central 3D viewport to light gray, which is not visible by default. const L = 189 / 255; const kBackgroundColor = [L, L, L, 1]; this.renderer.setClearOptions({clearColor: kBackgroundColor, clear: true}); @@ -145,7 +145,7 @@ class App { this.mouseX = -1; this.mouseY = -1; - this.mouseButton = false; + this.mouseButton = null; this.mouseButtonEvents = []; this.mouseWheelY = 0; @@ -155,10 +155,17 @@ class App { this.mouseX = e.offsetX * window.devicePixelRatio; this.mouseY = e.offsetY * window.devicePixelRatio; }); - this.canvas.addEventListener("pointerup", e => this.mouseButtonEvents.push(false)); - this.canvas.addEventListener("pointerdown", e => this.mouseButtonEvents.push(true)); + + this.canvas.addEventListener("pointerup", e => this.mouseButtonEvents.push(null)); + this.canvas.addEventListener("pointerdown", e => this.mouseButtonEvents.push(e)); this.canvas.addEventListener("mousewheel", e => this.mouseWheelY = e.deltaY / 8); + this.canvas.addEventListener("contextmenu", event => event.preventDefault()); + + document.addEventListener("keydown", e => this.simpleViewer.keyDownEvent(e.keyCode)); + document.addEventListener("keyup", e => this.simpleViewer.keyUpEvent(e.keyCode)); + document.addEventListener("keypress", e => this.simpleViewer.keyPressEvent(e.charCode)); + const resize = () => { const dpr = window.devicePixelRatio; const width = this.canvas.clientWidth * dpr; @@ -263,6 +270,8 @@ class App { } const mouseWheel = this.mouseWheelY; this.mouseWheelY = 0; + this.simpleViewer.mouseEvent(this.mouseX, this.mouseY, !!this.mouseButton, + mouseWheel, this.mouseButton && this.mouseButton.ctrlKey); // If there's no connection, let Filament clear the canvas, but do not render the UI view. if (!connection) { @@ -276,8 +285,7 @@ class App { // Draw the UI and potentially mutate the settings object. const deltaTime = 1.0 / 60.0; - this.simpleViewer.renderUserInterface(deltaTime, this.uiview, window.devicePixelRatio, - this.mouseX, this.mouseY, this.mouseButton, mouseWheel); + this.simpleViewer.renderUserInterface(deltaTime, this.uiview, window.devicePixelRatio); // Check if the user has changed any settings. const settingsJson = this.serializer.writeJson(this.simpleViewer.getSettings()).slice();