Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c321395b authored by Angel Aguayo's avatar Angel Aguayo
Browse files

Prepare for publicly exposing drawMesh API

Fixes API issues to keep it consistent
with the framework.

Bug: b/253321460, b/261768939
Test: HwAccelerationTest MeshActivity
Change-Id: I436d06bc8189fcea97e0c0842e68872afe05e022
parent 4a39f278
Loading
Loading
Loading
Loading
+10 −1
Original line number Diff line number Diff line
@@ -668,12 +668,21 @@ public abstract class BaseCanvas {
    }

    /**
     * Draws a mesh object to the screen.
     *
     * @param mesh {@link Mesh} object that will be drawn to the screen
     * @param blendMode {@link BlendMode} used to blend mesh primitives with the Paint color/shader
     * @param paint {@link Paint} used to provide a color/shader/blend mode.
     *
     * @hide
     */
    public void drawMesh(Mesh mesh, BlendMode blendMode, Paint paint) {
    public void drawMesh(@NonNull Mesh mesh, BlendMode blendMode, @NonNull Paint paint) {
        if (!isHardwareAccelerated() && onHwFeatureInSwMode()) {
            throw new RuntimeException("software rendering doesn't support meshes");
        }
        if (blendMode == null) {
            blendMode = BlendMode.MODULATE;
        }
        nDrawMesh(this.mNativeCanvasWrapper, mesh.getNativeWrapperInstance(),
                blendMode.getXfermode().porterDuffMode, paint.getNativeInstance());
    }
+4 −1
Original line number Diff line number Diff line
@@ -607,7 +607,10 @@ public class BaseRecordingCanvas extends Canvas {
    }

    @Override
    public final void drawMesh(Mesh mesh, BlendMode blendMode, Paint paint) {
    public final void drawMesh(@NonNull Mesh mesh, BlendMode blendMode, @NonNull Paint paint) {
        if (blendMode == null) {
            blendMode = BlendMode.MODULATE;
        }
        nDrawMesh(mNativeCanvasWrapper, mesh.getNativeWrapperInstance(),
                blendMode.getXfermode().porterDuffMode, paint.getNativeInstance());
    }
+52 −25
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package android.graphics;

import android.annotation.IntDef;
import android.annotation.NonNull;

import libcore.util.NativeAllocationRegistry;

import java.nio.Buffer;
@@ -25,8 +28,8 @@ import java.nio.ShortBuffer;
 * Class representing a mesh object.
 *
 * This class generates Mesh objects via the
 * {@link #make(MeshSpecification, Mode, Buffer, int, Rect)} and
 * {@link #makeIndexed(MeshSpecification, Mode, Buffer, int, ShortBuffer, Rect)} methods,
 * {@link #make(MeshSpecification, int, Buffer, int, Rect)} and
 * {@link #makeIndexed(MeshSpecification, int, Buffer, int, ShortBuffer, Rect)} methods,
 * where a {@link MeshSpecification} is required along with various attributes for
 * detailing the mesh object, including a mode, vertex buffer, optional index buffer, and bounds
 * for the mesh. Once generated, a mesh object can be drawn through
@@ -39,9 +42,20 @@ public class Mesh {
    private boolean mIsIndexed;

    /**
     * Enum to determine how the mesh is represented.
     * Determines how the mesh is represented and will be drawn.
     */
    @IntDef({TRIANGLES, TRIANGLE_STRIP})
    private @interface Mode {}

    /**
     * The mesh will be drawn with triangles without utilizing shared vertices.
     */
    public static final int TRIANGLES = 0;

    /**
     * The mesh will be drawn with triangles utilizing shared vertices.
     */
    public enum Mode {Triangles, TriangleStrip}
    public static final int TRIANGLE_STRIP = 1;

    private static class MeshHolder {
        public static final NativeAllocationRegistry MESH_SPECIFICATION_REGISTRY =
@@ -53,7 +67,8 @@ public class Mesh {
     * Generates a {@link Mesh} object.
     *
     * @param meshSpec     {@link MeshSpecification} used when generating the mesh.
     * @param mode         {@link Mode} enum
     * @param mode         Determines what mode to draw the mesh in. Must be one of
     *                     {@link Mesh#TRIANGLES} or {@link Mesh#TRIANGLE_STRIP}
     * @param vertexBuffer vertex buffer representing through {@link Buffer}. This provides the data
     *                     for all attributes provided within the meshSpec for every vertex. That
     *                     is, a vertex buffer should be (attributes size * number of vertices) in
@@ -63,9 +78,13 @@ public class Mesh {
     * @param bounds       bounds of the mesh object.
     * @return a new Mesh object.
     */
    public static Mesh make(MeshSpecification meshSpec, Mode mode, Buffer vertexBuffer,
            int vertexCount, Rect bounds) {
        long nativeMesh = nativeMake(meshSpec.mNativeMeshSpec, mode.ordinal(), vertexBuffer,
    @NonNull
    public static Mesh make(@NonNull MeshSpecification meshSpec, @Mode int mode,
            @NonNull Buffer vertexBuffer, int vertexCount, @NonNull Rect bounds) {
        if (mode != TRIANGLES && mode != TRIANGLE_STRIP) {
            throw new IllegalArgumentException("Invalid value passed in for mode parameter");
        }
        long nativeMesh = nativeMake(meshSpec.mNativeMeshSpec, mode, vertexBuffer,
                vertexBuffer.isDirect(), vertexCount, vertexBuffer.position(), bounds.left,
                bounds.top, bounds.right, bounds.bottom);
        if (nativeMesh == 0) {
@@ -78,7 +97,8 @@ public class Mesh {
     * Generates a {@link Mesh} object.
     *
     * @param meshSpec     {@link MeshSpecification} used when generating the mesh.
     * @param mode         {@link Mode} enum
     * @param mode         Determines what mode to draw the mesh in. Must be one of
     *                     {@link Mesh#TRIANGLES} or {@link Mesh#TRIANGLE_STRIP}
     * @param vertexBuffer vertex buffer representing through {@link Buffer}. This provides the data
     *                     for all attributes provided within the meshSpec for every vertex. That
     *                     is, a vertex buffer should be (attributes size * number of vertices) in
@@ -92,9 +112,14 @@ public class Mesh {
     * @param bounds       bounds of the mesh object.
     * @return a new Mesh object.
     */
    public static Mesh makeIndexed(MeshSpecification meshSpec, Mode mode, Buffer vertexBuffer,
            int vertexCount, ShortBuffer indexBuffer, Rect bounds) {
        long nativeMesh = nativeMakeIndexed(meshSpec.mNativeMeshSpec, mode.ordinal(), vertexBuffer,
    @NonNull
    public static Mesh makeIndexed(@NonNull MeshSpecification meshSpec, @Mode int mode,
            @NonNull Buffer vertexBuffer, int vertexCount, @NonNull ShortBuffer indexBuffer,
            @NonNull Rect bounds) {
        if (mode != TRIANGLES && mode != TRIANGLE_STRIP) {
            throw new IllegalArgumentException("Invalid value passed in for mode parameter");
        }
        long nativeMesh = nativeMakeIndexed(meshSpec.mNativeMeshSpec, mode, vertexBuffer,
                vertexBuffer.isDirect(), vertexCount, vertexBuffer.position(), indexBuffer,
                indexBuffer.isDirect(), indexBuffer.capacity(), indexBuffer.position(), bounds.left,
                bounds.top, bounds.right, bounds.bottom);
@@ -114,7 +139,7 @@ public class Mesh {
     * @param color       the provided sRGB color will be converted into the shader program's output
     *                    colorspace and be available as a vec4 uniform in the program.
     */
    public void setColorUniform(String uniformName, int color) {
    public void setColorUniform(@NonNull String uniformName, int color) {
        setUniform(uniformName, Color.valueOf(color).getComponents(), true);
    }

@@ -128,7 +153,7 @@ public class Mesh {
     * @param color       the provided sRGB color will be converted into the shader program's output
     *                    colorspace and be available as a vec4 uniform in the program.
     */
    public void setColorUniform(String uniformName, long color) {
    public void setColorUniform(@NonNull String uniformName, long color) {
        Color exSRGB = Color.valueOf(color).convert(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB));
        setUniform(uniformName, exSRGB.getComponents(), true);
    }
@@ -143,7 +168,7 @@ public class Mesh {
     * @param color       the provided sRGB color will be converted into the shader program's output
     *                    colorspace and will be made available as a vec4 uniform in the program.
     */
    public void setColorUniform(String uniformName, Color color) {
    public void setColorUniform(@NonNull String uniformName, @NonNull Color color) {
        if (color == null) {
            throw new NullPointerException("The color parameter must not be null");
        }
@@ -160,7 +185,7 @@ public class Mesh {
     * @param uniformName name matching the float uniform declared in the shader program.
     * @param value       float value corresponding to the float uniform with the given name.
     */
    public void setFloatUniform(String uniformName, float value) {
    public void setFloatUniform(@NonNull String uniformName, float value) {
        setFloatUniform(uniformName, value, 0.0f, 0.0f, 0.0f, 1);
    }

@@ -173,7 +198,7 @@ public class Mesh {
     * @param value1      first float value corresponding to the float uniform with the given name.
     * @param value2      second float value corresponding to the float uniform with the given name.
     */
    public void setFloatUniform(String uniformName, float value1, float value2) {
    public void setFloatUniform(@NonNull String uniformName, float value1, float value2) {
        setFloatUniform(uniformName, value1, value2, 0.0f, 0.0f, 2);
    }

@@ -188,7 +213,8 @@ public class Mesh {
     * @param value3      third float value corresponding to the float unifiform with the given
     *                    name.
     */
    public void setFloatUniform(String uniformName, float value1, float value2, float value3) {
    public void setFloatUniform(
            @NonNull String uniformName, float value1, float value2, float value3) {
        setFloatUniform(uniformName, value1, value2, value3, 0.0f, 3);
    }

@@ -204,7 +230,7 @@ public class Mesh {
     * @param value4      fourth float value corresponding to the float uniform with the given name.
     */
    public void setFloatUniform(
            String uniformName, float value1, float value2, float value3, float value4) {
            @NonNull String uniformName, float value1, float value2, float value3, float value4) {
        setFloatUniform(uniformName, value1, value2, value3, value4, 4);
    }

@@ -217,7 +243,7 @@ public class Mesh {
     * @param uniformName name matching the float uniform declared in the shader program.
     * @param values      float value corresponding to the vec4 float uniform with the given name.
     */
    public void setFloatUniform(String uniformName, float[] values) {
    public void setFloatUniform(@NonNull String uniformName, @NonNull float[] values) {
        setUniform(uniformName, values, false);
    }

@@ -249,7 +275,7 @@ public class Mesh {
     * @param uniformName name matching the int uniform delcared in the shader program.
     * @param value       value corresponding to the int uniform with the given name.
     */
    public void setIntUniform(String uniformName, int value) {
    public void setIntUniform(@NonNull String uniformName, int value) {
        setIntUniform(uniformName, value, 0, 0, 0, 1);
    }

@@ -262,7 +288,7 @@ public class Mesh {
     * @param value1      first value corresponding to the int uniform with the given name.
     * @param value2      second value corresponding to the int uniform with the given name.
     */
    public void setIntUniform(String uniformName, int value1, int value2) {
    public void setIntUniform(@NonNull String uniformName, int value1, int value2) {
        setIntUniform(uniformName, value1, value2, 0, 0, 2);
    }

@@ -276,7 +302,7 @@ public class Mesh {
     * @param value2      second value corresponding to the int uniform with the given name.
     * @param value3      third value corresponding to the int uniform with the given name.
     */
    public void setIntUniform(String uniformName, int value1, int value2, int value3) {
    public void setIntUniform(@NonNull String uniformName, int value1, int value2, int value3) {
        setIntUniform(uniformName, value1, value2, value3, 0, 3);
    }

@@ -291,7 +317,8 @@ public class Mesh {
     * @param value3      third value corresponding to the int uniform with the given name.
     * @param value4      fourth value corresponding to the int uniform with the given name.
     */
    public void setIntUniform(String uniformName, int value1, int value2, int value3, int value4) {
    public void setIntUniform(
            @NonNull String uniformName, int value1, int value2, int value3, int value4) {
        setIntUniform(uniformName, value1, value2, value3, value4, 4);
    }

@@ -304,7 +331,7 @@ public class Mesh {
     * @param uniformName name matching the int uniform delcared in the shader program.
     * @param values      int values corresponding to the vec4 int uniform with the given name.
     */
    public void setIntUniform(String uniformName, int[] values) {
    public void setIntUniform(@NonNull String uniformName, @NonNull int[] values) {
        if (uniformName == null) {
            throw new NullPointerException("The uniformName parameter must not be null");
        }
+35 −20
Original line number Diff line number Diff line
@@ -17,15 +17,18 @@
package android.graphics;

import android.annotation.IntDef;
import android.annotation.NonNull;

import libcore.util.NativeAllocationRegistry;

import java.util.List;

/**
 * Class responsible for holding specifications for {@link Mesh} creations. This class
 * generates a {@link MeshSpecification} via the Make method, where multiple parameters to set up
 * the mesh are supplied, including attributes, vertex stride, varyings, and
 * vertex/fragment shaders. There are also additional methods to provide an optional
 * {@link ColorSpace} as well as an {@link AlphaType}.
 * {@link ColorSpace} as well as an alpha type.
 *
 * Note that there are several limitations on various mesh specifications:
 * 1. The max amount of attributes allowed is 8.
@@ -42,12 +45,11 @@ public class MeshSpecification {
    long mNativeMeshSpec;

    /**
     * Constants for {@link #make(Attribute[], int, Varying[], String, String, ColorSpace, int)}
     * Constants for {@link #make(List, int, List, String, String)}
     * to determine alpha type. Describes how to interpret the alpha component of a pixel.
     */
    @IntDef({UNKNOWN, OPAQUE, PREMUL, UNPREMULT})
    public @interface AlphaType {
    }
    private @interface AlphaType {}

    /**
     * uninitialized.
@@ -73,8 +75,7 @@ public class MeshSpecification {
     * Constants for {@link Attribute} and {@link Varying} for determining the data type.
     */
    @IntDef({FLOAT, FLOAT2, FLOAT3, FLOAT4, UBYTE4})
    public @interface Type {
    }
    private @interface Type {}

    /**
     * Represents one float. Its equivalent shader type is float.
@@ -118,7 +119,7 @@ public class MeshSpecification {
        private int mOffset;
        private String mName;

        public Attribute(@Type int type, int offset, String name) {
        public Attribute(@Type int type, int offset, @NonNull String name) {
            mType = type;
            mOffset = offset;
            mName = name;
@@ -134,7 +135,7 @@ public class MeshSpecification {
        private int mType;
        private String mName;

        public Varying(@Type int type, String name) {
        public Varying(@Type int type, @NonNull String name) {
            mType = type;
            mName = name;
        }
@@ -162,10 +163,13 @@ public class MeshSpecification {
     * @param fragmentShader fragment shader to be supplied to the mesh.
     * @return {@link MeshSpecification} object for use when creating {@link Mesh}
     */
    public static MeshSpecification make(Attribute[] attributes, int vertexStride,
            Varying[] varyings, String vertexShader, String fragmentShader) {
        long nativeMeshSpec =
                nativeMake(attributes, vertexStride, varyings, vertexShader, fragmentShader);
    @NonNull
    public static MeshSpecification make(@NonNull List<Attribute> attributes, int vertexStride,
            @NonNull List<Varying> varyings, @NonNull String vertexShader,
            @NonNull String fragmentShader) {
        long nativeMeshSpec = nativeMake(attributes.toArray(new Attribute[attributes.size()]),
                vertexStride, varyings.toArray(new Varying[varyings.size()]), vertexShader,
                fragmentShader);
        if (nativeMeshSpec == 0) {
            throw new IllegalArgumentException("MeshSpecification construction failed");
        }
@@ -189,9 +193,12 @@ public class MeshSpecification {
     * @param colorSpace     {@link ColorSpace} to tell what color space to work in.
     * @return {@link MeshSpecification} object for use when creating {@link Mesh}
     */
    public static MeshSpecification make(Attribute[] attributes, int vertexStride,
            Varying[] varyings, String vertexShader, String fragmentShader, ColorSpace colorSpace) {
        long nativeMeshSpec = nativeMakeWithCS(attributes, vertexStride, varyings, vertexShader,
    @NonNull
    public static MeshSpecification make(@NonNull List<Attribute> attributes, int vertexStride,
            @NonNull List<Varying> varyings, @NonNull String vertexShader,
            @NonNull String fragmentShader, @NonNull ColorSpace colorSpace) {
        long nativeMeshSpec = nativeMakeWithCS(attributes.toArray(new Attribute[attributes.size()]),
                vertexStride, varyings.toArray(new Varying[varyings.size()]), vertexShader,
                fragmentShader, colorSpace.getNativeInstance());
        if (nativeMeshSpec == 0) {
            throw new IllegalArgumentException("MeshSpecification construction failed");
@@ -215,13 +222,21 @@ public class MeshSpecification {
     * @param fragmentShader fragment shader to be supplied to the mesh.
     * @param colorSpace     {@link ColorSpace} to tell what color space to work in.
     * @param alphaType      Describes how to interpret the alpha component for a pixel. Must be
     *                       one of {@link AlphaType} values.
     *                       one of
     *                       {@link MeshSpecification#UNKNOWN},
     *                       {@link MeshSpecification#OPAQUE},
     *                       {@link MeshSpecification#PREMUL}, or
     *                       {@link MeshSpecification#UNPREMULT}
     * @return {@link MeshSpecification} object for use when creating {@link Mesh}
     */
    public static MeshSpecification make(Attribute[] attributes, int vertexStride,
            Varying[] varyings, String vertexShader, String fragmentShader, ColorSpace colorSpace,
    @NonNull
    public static MeshSpecification make(@NonNull List<Attribute> attributes, int vertexStride,
            @NonNull List<Varying> varyings, @NonNull String vertexShader,
            @NonNull String fragmentShader, @NonNull ColorSpace colorSpace,
            @AlphaType int alphaType) {
        long nativeMeshSpec = nativeMakeWithAlpha(attributes, vertexStride, varyings, vertexShader,
        long nativeMeshSpec =
                nativeMakeWithAlpha(attributes.toArray(new Attribute[attributes.size()]),
                        vertexStride, varyings.toArray(new Varying[varyings.size()]), vertexShader,
                        fragmentShader, colorSpace.getNativeInstance(), alphaType);
        if (nativeMeshSpec == 0) {
            throw new IllegalArgumentException("MeshSpecification construction failed");
+35 −22
Original line number Diff line number Diff line
@@ -44,10 +44,16 @@ static jlong make(JNIEnv* env, jobject, jlong meshSpec, jint mode, jobject verte
    sk_sp<SkMesh::VertexBuffer> skVertexBuffer =
            genVertexBuffer(env, vertexBuffer, vertexCount * skMeshSpec->stride(), isDirect);
    auto skRect = SkRect::MakeLTRB(left, top, right, bottom);
    auto mesh = SkMesh::Make(skMeshSpec, SkMesh::Mode(mode), skVertexBuffer, vertexCount,
                             vertexOffset, nullptr, skRect)
                        .mesh;
    auto meshPtr = std::make_unique<MeshWrapper>(MeshWrapper{mesh, MeshUniformBuilder(skMeshSpec)});
    auto meshResult = SkMesh::Make(
            skMeshSpec, SkMesh::Mode(mode), skVertexBuffer, vertexCount, vertexOffset,
            SkData::MakeWithCopy(skMeshSpec->uniforms().data(), skMeshSpec->uniformSize()), skRect);

    if (!meshResult.error.isEmpty()) {
        jniThrowException(env, "java/lang/IllegalArgumentException", meshResult.error.c_str());
    }

    auto meshPtr = std::make_unique<MeshWrapper>(
            MeshWrapper{meshResult.mesh, MeshUniformBuilder(skMeshSpec)});
    return reinterpret_cast<jlong>(meshPtr.release());
}

@@ -61,11 +67,17 @@ static jlong makeIndexed(JNIEnv* env, jobject, jlong meshSpec, jint mode, jobjec
    sk_sp<SkMesh::IndexBuffer> skIndexBuffer =
            genIndexBuffer(env, indexBuffer, indexCount * gIndexByteSize, isIndexDirect);
    auto skRect = SkRect::MakeLTRB(left, top, right, bottom);
    auto mesh = SkMesh::MakeIndexed(skMeshSpec, SkMesh::Mode(mode), skVertexBuffer, vertexCount,
                                    vertexOffset, skIndexBuffer, indexCount, indexOffset, nullptr,
                                    skRect)
                        .mesh;
    auto meshPtr = std::make_unique<MeshWrapper>(MeshWrapper{mesh, MeshUniformBuilder(skMeshSpec)});

    auto meshResult = SkMesh::MakeIndexed(
            skMeshSpec, SkMesh::Mode(mode), skVertexBuffer, vertexCount, vertexOffset,
            skIndexBuffer, indexCount, indexOffset,
            SkData::MakeWithCopy(skMeshSpec->uniforms().data(), skMeshSpec->uniformSize()), skRect);

    if (!meshResult.error.isEmpty()) {
        jniThrowException(env, "java/lang/IllegalArgumentException", meshResult.error.c_str());
    }
    auto meshPtr = std::make_unique<MeshWrapper>(
            MeshWrapper{meshResult.mesh, MeshUniformBuilder(skMeshSpec)});
    return reinterpret_cast<jlong>(meshPtr.release());
}

@@ -139,22 +151,22 @@ static void nativeUpdateFloatUniforms(JNIEnv* env, MeshUniformBuilder* builder,
    }
}

static void updateFloatUniforms(JNIEnv* env, jobject, jlong uniBuilder, jstring uniformName,
static void updateFloatUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring uniformName,
                                jfloat value1, jfloat value2, jfloat value3, jfloat value4,
                                jint count) {
    auto* builder = reinterpret_cast<MeshUniformBuilder*>(uniBuilder);
    auto* wrapper = reinterpret_cast<MeshWrapper*>(meshWrapper);
    ScopedUtfChars name(env, uniformName);
    const float values[4] = {value1, value2, value3, value4};
    nativeUpdateFloatUniforms(env, builder, name.c_str(), values, count, false);
    nativeUpdateFloatUniforms(env, &wrapper->builder, name.c_str(), values, count, false);
}

static void updateFloatArrayUniforms(JNIEnv* env, jobject, jlong uniBuilder, jstring jUniformName,
static void updateFloatArrayUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring jUniformName,
                                     jfloatArray jvalues, jboolean isColor) {
    auto builder = reinterpret_cast<MeshUniformBuilder*>(uniBuilder);
    auto wrapper = reinterpret_cast<MeshWrapper*>(meshWrapper);
    ScopedUtfChars name(env, jUniformName);
    AutoJavaFloatArray autoValues(env, jvalues, 0, kRO_JNIAccess);
    nativeUpdateFloatUniforms(env, builder, name.c_str(), autoValues.ptr(), autoValues.length(),
                              isColor);
    nativeUpdateFloatUniforms(env, &wrapper->builder, name.c_str(), autoValues.ptr(),
                              autoValues.length(), isColor);
}

static void nativeUpdateIntUniforms(JNIEnv* env, MeshUniformBuilder* builder,
@@ -171,20 +183,21 @@ static void nativeUpdateIntUniforms(JNIEnv* env, MeshUniformBuilder* builder,
    }
}

static void updateIntUniforms(JNIEnv* env, jobject, jlong uniBuilder, jstring uniformName,
static void updateIntUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring uniformName,
                              jint value1, jint value2, jint value3, jint value4, jint count) {
    auto builder = reinterpret_cast<MeshUniformBuilder*>(uniBuilder);
    auto wrapper = reinterpret_cast<MeshWrapper*>(meshWrapper);
    ScopedUtfChars name(env, uniformName);
    const int values[4] = {value1, value2, value3, value4};
    nativeUpdateIntUniforms(env, builder, name.c_str(), values, count);
    nativeUpdateIntUniforms(env, &wrapper->builder, name.c_str(), values, count);
}

static void updateIntArrayUniforms(JNIEnv* env, jobject, jlong uniBuilder, jstring uniformName,
static void updateIntArrayUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring uniformName,
                                   jintArray values) {
    auto builder = reinterpret_cast<MeshUniformBuilder*>(uniBuilder);
    auto wrapper = reinterpret_cast<MeshWrapper*>(meshWrapper);
    ScopedUtfChars name(env, uniformName);
    AutoJavaIntArray autoValues(env, values, 0);
    nativeUpdateIntUniforms(env, builder, name.c_str(), autoValues.ptr(), autoValues.length());
    nativeUpdateIntUniforms(env, &wrapper->builder, name.c_str(), autoValues.ptr(),
                            autoValues.length());
}

static void MeshWrapper_destroy(MeshWrapper* wrapper) {
Loading