Exposed SkMeshSpecification
Exposed SkMeshSpecification through MeshSpecification, exposing Make
methods as well as constructing a Attribute and Varying data classes.
Bug: b/254354404
Test: build hwui
Change-Id: I1040bde091575ac4bb786f7600d016b7c9168831
diff --git a/graphics/java/android/graphics/MeshSpecification.java b/graphics/java/android/graphics/MeshSpecification.java
new file mode 100644
index 0000000..b27c5e0
--- /dev/null
+++ b/graphics/java/android/graphics/MeshSpecification.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2022 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 android.graphics;
+
+import android.annotation.IntDef;
+
+import libcore.util.NativeAllocationRegistry;
+
+/**
+ * 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 alpha type.
+ *
+ * Note that there are several limitations on various mesh specifications:
+ * 1. The max amount of attributes allowed is 8.
+ * 2. The offset alignment length is 4 bytes.
+ * 2. The max stride length is 1024.
+ * 3. The max amount of varyings is 6.
+ *
+ * These should be kept in mind when generating a mesh specification, as exceeding them will
+ * lead to errors.
+ *
+ * @hide
+ */
+public class MeshSpecification {
+ private long mNativeMeshSpec;
+
+ /**
+ * Constants for {@link #make(Attribute[], int, Varying[], String, String, ColorSpace, int)}
+ * to determine alpha type
+ */
+ @IntDef({UNKNOWN, OPAQUE, PREMUL, UNPREMULT})
+ public @interface AlphaType {
+ }
+
+ public static final int UNKNOWN = 0;
+ public static final int OPAQUE = 1;
+ public static final int PREMUL = 2;
+ public static final int UNPREMULT = 3;
+
+ /**
+ * Constants for {@link Attribute} and {@link Varying} for determining the data type.
+ */
+ @IntDef({FLOAT, FLOAT2, FLOAT3, FLOAT4, UBYTE4})
+ public @interface Type {
+ }
+
+ public static final int FLOAT = 0;
+ public static final int FLOAT2 = 1;
+ public static final int FLOAT3 = 2;
+ public static final int FLOAT4 = 3;
+ public static final int UBYTE4 = 4;
+
+ /**
+ * Data class to represent a single attribute in a shader. Note that type parameter must be
+ * one of {@link #FLOAT}, {@link #FLOAT2}, {@link #FLOAT3}, {@link #FLOAT4}, or {@link #UBYTE4}.
+ */
+ public static class Attribute {
+ @Type
+ private int mType;
+ private int mOffset;
+ private String mName;
+
+ public Attribute(@Type int type, int offset, String name) {
+ mType = type;
+ mOffset = offset;
+ mName = name;
+ }
+ }
+
+ /**
+ * Data class to represent a single varying variable. Note that type parameter must be
+ * one of {@link #FLOAT}, {@link #FLOAT2}, {@link #FLOAT3}, {@link #FLOAT4}, or {@link #UBYTE4}.
+ */
+ public static class Varying {
+ @Type
+ private int mType;
+ private String mName;
+
+ public Varying(@Type int type, String name) {
+ mType = type;
+ mName = name;
+ }
+ }
+
+ private static class MeshSpecificationHolder {
+ public static final NativeAllocationRegistry MESH_SPECIFICATION_REGISTRY =
+ NativeAllocationRegistry.createMalloced(
+ MeshSpecification.class.getClassLoader(), nativeGetFinalizer());
+ }
+
+ /**
+ * Creates a {@link MeshSpecification} object.
+ *
+ * @param attributes list of attributes represented by {@link Attribute}. Can hold a max of
+ * 8.
+ * @param vertexStride length of vertex stride. Max of 1024 is accepted.
+ * @param varyings List of varyings represented by {@link Varying}. Can hold a max of 6.
+ * @param vertexShader vertex shader to be supplied to the mesh.
+ * @param fragmentShader fragment shader to be suppied 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);
+ if (nativeMeshSpec == 0) {
+ throw new IllegalArgumentException("MeshSpecification construction failed");
+ }
+ return new MeshSpecification(nativeMeshSpec);
+ }
+
+ /**
+ * Creates a {@link MeshSpecification} object.
+ *
+ * @param attributes list of attributes represented by {@link Attribute}. Can hold a max of
+ * 8.
+ * @param vertexStride length of vertex stride. Max of 1024 is accepted.
+ * @param varyings List of varyings represented by {@link Varying}. Can hold a max of
+ * 6.
+ * @param vertexShader vertex shader to be supplied to the mesh.
+ * @param fragmentShader fragment shader to be supplied to the mesh.
+ * @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,
+ fragmentShader, colorSpace.getNativeInstance());
+ if (nativeMeshSpec == 0) {
+ throw new IllegalArgumentException("MeshSpecification construction failed");
+ }
+ return new MeshSpecification(nativeMeshSpec);
+ }
+
+ /**
+ * Creates a {@link MeshSpecification} object.
+ *
+ * @param attributes list of attributes represented by {@link Attribute}. Can hold a max of
+ * 8.
+ * @param vertexStride length of vertex stride. Max of 1024 is accepted.
+ * @param varyings List of varyings represented by {@link Varying}. Can hold a max of 6.
+ * @param vertexShader vertex shader code to be supplied to the mesh.
+ * @param fragmentShader fragment shader code to be suppied 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.
+ * @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,
+ @AlphaType int alphaType) {
+ long nativeMeshSpec = nativeMakeWithAlpha(attributes, vertexStride, varyings, vertexShader,
+ fragmentShader, colorSpace.getNativeInstance(), alphaType);
+ if (nativeMeshSpec == 0) {
+ throw new IllegalArgumentException("MeshSpecification construction failed");
+ }
+ return new MeshSpecification(nativeMeshSpec);
+ }
+
+ private MeshSpecification(long meshSpec) {
+ mNativeMeshSpec = meshSpec;
+ MeshSpecificationHolder.MESH_SPECIFICATION_REGISTRY.registerNativeAllocation(
+ this, meshSpec);
+ }
+
+ private static native long nativeGetFinalizer();
+
+ private static native long nativeMake(Attribute[] attributes, int vertexStride,
+ Varying[] varyings, String vertexShader, String fragmentShader);
+
+ private static native long nativeMakeWithCS(Attribute[] attributes, int vertexStride,
+ Varying[] varyings, String vertexShader, String fragmentShader, long colorSpace);
+
+ private static native long nativeMakeWithAlpha(Attribute[] attributes, int vertexStride,
+ Varying[] varyings, String vertexShader, String fragmentShader, long colorSpace,
+ int alphaType);
+}
diff --git a/libs/hwui/jni/MeshSpecification.cpp b/libs/hwui/jni/MeshSpecification.cpp
new file mode 100644
index 0000000..22fa4d3
--- /dev/null
+++ b/libs/hwui/jni/MeshSpecification.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2022 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 <SkMesh.h>
+
+#include "GraphicsJNI.h"
+#include "graphics_jni_helpers.h"
+
+namespace android {
+
+using Attribute = SkMeshSpecification::Attribute;
+using Varying = SkMeshSpecification::Varying;
+
+static struct {
+ jclass clazz{};
+ jfieldID type{};
+ jfieldID offset{};
+ jfieldID name{};
+} gAttributeInfo;
+
+static struct {
+ jclass clazz{};
+ jfieldID type{};
+ jfieldID name{};
+} gVaryingInfo;
+
+std::vector<Attribute> extractAttributes(JNIEnv* env, jobjectArray attributes) {
+ int size = env->GetArrayLength(attributes);
+ std::vector<Attribute> attVector;
+ attVector.reserve(size);
+ for (int i = 0; i < size; i++) {
+ jobject attribute = env->GetObjectArrayElement(attributes, i);
+ auto name = (jstring)env->GetObjectField(attribute, gAttributeInfo.name);
+ auto attName = ScopedUtfChars(env, name);
+ Attribute temp{Attribute::Type(env->GetIntField(attribute, gAttributeInfo.type)),
+ static_cast<size_t>(env->GetIntField(attribute, gAttributeInfo.offset)),
+ SkString(attName.c_str())};
+ attVector.push_back(std::move(temp));
+ }
+
+ return attVector;
+}
+
+std::vector<Varying> extractVaryings(JNIEnv* env, jobjectArray varyings) {
+ int size = env->GetArrayLength(varyings);
+ std::vector<Varying> varyVector;
+ varyVector.reserve(size);
+ for (int i = 0; i < size; i++) {
+ jobject varying = env->GetObjectArrayElement(varyings, i);
+ auto name = (jstring)env->GetObjectField(varying, gVaryingInfo.name);
+ auto varyName = ScopedUtfChars(env, name);
+ Varying temp{Varying::Type(env->GetIntField(varying, gVaryingInfo.type)),
+ SkString(varyName.c_str())};
+ varyVector.push_back(std::move(temp));
+ }
+
+ return varyVector;
+}
+
+static jlong Make(JNIEnv* env, jobject thiz, jobjectArray attributeArray, jint vertexStride,
+ jobjectArray varyingArray, jstring vertexShader, jstring fragmentShader) {
+ auto attributes = extractAttributes(env, attributeArray);
+ auto varyings = extractVaryings(env, varyingArray);
+ auto skVertexShader = ScopedUtfChars(env, vertexShader);
+ auto skFragmentShader = ScopedUtfChars(env, fragmentShader);
+ auto meshSpec = SkMeshSpecification::Make(attributes, vertexStride, varyings,
+ SkString(skVertexShader.c_str()),
+ SkString(skFragmentShader.c_str()))
+ .specification;
+ return reinterpret_cast<jlong>(meshSpec.release());
+}
+
+static jlong MakeWithCS(JNIEnv* env, jobject thiz, jobjectArray attributeArray, jint vertexStride,
+ jobjectArray varyingArray, jstring vertexShader, jstring fragmentShader,
+ jlong colorSpace) {
+ auto attributes = extractAttributes(env, attributeArray);
+ auto varyings = extractVaryings(env, varyingArray);
+ auto skVertexShader = ScopedUtfChars(env, vertexShader);
+ auto skFragmentShader = ScopedUtfChars(env, fragmentShader);
+ auto meshSpec = SkMeshSpecification::Make(attributes, vertexStride, varyings,
+ SkString(skVertexShader.c_str()),
+ SkString(skFragmentShader.c_str()),
+ GraphicsJNI::getNativeColorSpace(colorSpace))
+ .specification;
+
+ return reinterpret_cast<jlong>(meshSpec.release());
+}
+
+static jlong MakeWithAlpha(JNIEnv* env, jobject thiz, jobjectArray attributeArray,
+ jint vertexStride, jobjectArray varyingArray, jstring vertexShader,
+ jstring fragmentShader, jlong colorSpace, jint alphaType) {
+ auto attributes = extractAttributes(env, attributeArray);
+ auto varyings = extractVaryings(env, varyingArray);
+ auto skVertexShader = ScopedUtfChars(env, vertexShader);
+ auto skFragmentShader = ScopedUtfChars(env, fragmentShader);
+ auto meshSpec = SkMeshSpecification::Make(
+ attributes, vertexStride, varyings, SkString(skVertexShader.c_str()),
+ SkString(skFragmentShader.c_str()),
+ GraphicsJNI::getNativeColorSpace(colorSpace), SkAlphaType(alphaType))
+ .specification;
+ return reinterpret_cast<jlong>(meshSpec.release());
+}
+
+static void MeshSpecification_safeUnref(SkMeshSpecification* meshSpec) {
+ SkSafeUnref(meshSpec);
+}
+
+static jlong getMeshSpecificationFinalizer() {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(&MeshSpecification_safeUnref));
+}
+
+static const JNINativeMethod gMeshSpecificationMethods[] = {
+ {"nativeGetFinalizer", "()J", (void*)getMeshSpecificationFinalizer},
+ {"nativeMake",
+ "([Landroid/graphics/MeshSpecification$Attribute;I[Landroid/graphics/"
+ "MeshSpecification$Varying;"
+ "Ljava/lang/String;Ljava/lang/String;)J",
+ (void*)Make},
+ {"nativeMakeWithCS",
+ "([Landroid/graphics/MeshSpecification$Attribute;I"
+ "[Landroid/graphics/MeshSpecification$Varying;Ljava/lang/String;Ljava/lang/String;J)J",
+ (void*)MakeWithCS},
+ {"nativeMakeWithAlpha",
+ "([Landroid/graphics/MeshSpecification$Attribute;I"
+ "[Landroid/graphics/MeshSpecification$Varying;Ljava/lang/String;Ljava/lang/String;JI)J",
+ (void*)MakeWithAlpha}};
+
+int register_android_graphics_MeshSpecification(JNIEnv* env) {
+ android::RegisterMethodsOrDie(env, "android/graphics/MeshSpecification",
+ gMeshSpecificationMethods, NELEM(gMeshSpecificationMethods));
+
+ gAttributeInfo.clazz = env->FindClass("android/graphics/MeshSpecification$Attribute");
+ gAttributeInfo.type = env->GetFieldID(gAttributeInfo.clazz, "mType", "I");
+ gAttributeInfo.offset = env->GetFieldID(gAttributeInfo.clazz, "mOffset", "I");
+ gAttributeInfo.name = env->GetFieldID(gAttributeInfo.clazz, "mName", "Ljava/lang/String;");
+
+ gVaryingInfo.clazz = env->FindClass("android/graphics/MeshSpecification$Varying");
+ gVaryingInfo.type = env->GetFieldID(gVaryingInfo.clazz, "mType", "I");
+ gVaryingInfo.name = env->GetFieldID(gVaryingInfo.clazz, "mName", "Ljava/lang/String;");
+ return 0;
+}
+
+} // namespace android
\ No newline at end of file