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

Commit 79e5f58f authored by Angel Aguayo's avatar Angel Aguayo
Browse files

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
parent fe5097cf
Loading
Loading
Loading
Loading
+194 −0
Original line number Original line Diff line number Diff line
/*
 * 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);
}
+156 −0
Original line number Original line Diff line number Diff line
/*
 * 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