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

Commit fb11cf82 authored by Angel Aguayo's avatar Angel Aguayo Committed by Android (Google) Code Review
Browse files

Merge "Exposed SkMesh"

parents 966144cc 4804746c
Loading
Loading
Loading
Loading
+317 −0
Original line number 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 libcore.util.NativeAllocationRegistry;

import java.nio.Buffer;
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,
 * 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.
 *
 * @hide
 */
public class Mesh {
    private long mNativeMeshWrapper;
    private boolean mIsIndexed;

    /**
     * Enum to determine how the mesh is represented.
     */
    public enum Mode {Triangles, TriangleStrip}

    private static class MeshHolder {
        public static final NativeAllocationRegistry MESH_SPECIFICATION_REGISTRY =
                NativeAllocationRegistry.createMalloced(
                        MeshSpecification.class.getClassLoader(), nativeGetFinalizer());
    }

    /**
     * Generates a {@link Mesh} object.
     *
     * @param meshSpec     {@link MeshSpecification} used when generating the mesh.
     * @param mode         {@link Mode} enum
     * @param vertexBuffer vertex buffer representing through {@link Buffer}.
     * @param vertexCount  the number of vertices represented in the vertexBuffer.
     * @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,
                vertexBuffer.isDirect(), vertexCount, vertexBuffer.position(), bounds.left,
                bounds.top, bounds.right, bounds.bottom);
        if (nativeMesh == 0) {
            throw new IllegalArgumentException("Mesh construction failed.");
        }
        return new Mesh(nativeMesh, false);
    }

    /**
     * Generates an indexed {@link Mesh} object.
     *
     * @param meshSpec     {@link MeshSpecification} used when generating the mesh.
     * @param mode         {@link Mode} enum
     * @param vertexBuffer vertex buffer representing through {@link Buffer}.
     * @param vertexCount  the number of vertices represented in the vertexBuffer.
     * @param indexBuffer  index buffer representing through {@link ShortBuffer}.
     * @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,
                vertexBuffer.isDirect(), vertexCount, vertexBuffer.position(), indexBuffer,
                indexBuffer.isDirect(), indexBuffer.capacity(), indexBuffer.position(), bounds.left,
                bounds.top, bounds.right, bounds.bottom);
        if (nativeMesh == 0) {
            throw new IllegalArgumentException("Mesh construction failed.");
        }
        return new Mesh(nativeMesh, true);
    }

    /**
     * Sets the uniform color value corresponding to the shader assigned to the mesh.
     *
     * @param uniformName name matching the color uniform declared in the shader program.
     * @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) {
        setUniform(uniformName, Color.valueOf(color).getComponents(), true);
    }

    /**
     * Sets the uniform color value corresponding to the shader assigned to the mesh.
     *
     * @param uniformName name matching the color uniform declared in the shader program.
     * @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) {
        Color exSRGB = Color.valueOf(color).convert(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB));
        setUniform(uniformName, exSRGB.getComponents(), true);
    }

    /**
     * Sets the uniform color value corresponding to the shader assigned to the mesh.
     *
     * @param uniformName name matching the color uniform declared in the shader program.
     * @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) {
        if (color == null) {
            throw new NullPointerException("The color parameter must not be null");
        }

        Color exSRGB = color.convert(ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB));
        setUniform(uniformName, exSRGB.getComponents(), true);
    }

    /**
     * Sets the uniform color value corresponding to the shader assigned to the 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) {
        setFloatUniform(uniformName, value, 0.0f, 0.0f, 0.0f, 1);
    }

    /**
     * Sets the uniform color value corresponding to the shader assigned to the mesh.
     *
     * @param uniformName name matching the float uniform declared in the shader program.
     * @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) {
        setFloatUniform(uniformName, value1, value2, 0.0f, 0.0f, 2);
    }

    /**
     * Sets the uniform color value corresponding to the shader assigned to the mesh.
     *
     * @param uniformName name matching the float uniform declared in the shader program.
     * @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.
     * @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) {
        setFloatUniform(uniformName, value1, value2, value3, 0.0f, 3);
    }

    /**
     * Sets the uniform color value corresponding to the shader assigned to the mesh.
     *
     * @param uniformName name matching the float uniform declared in the shader program.
     * @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.
     * @param value3      third float value corresponding to the float uniform with the given name.
     * @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) {
        setFloatUniform(uniformName, value1, value2, value3, value4, 4);
    }

    /**
     * Sets the uniform color value corresponding to the shader assigned to the 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) {
        setUniform(uniformName, values, false);
    }

    private void setFloatUniform(
            String uniformName, float value1, float value2, float value3, float value4, int count) {
        if (uniformName == null) {
            throw new NullPointerException("The uniformName parameter must not be null");
        }
        nativeUpdateUniforms(
                mNativeMeshWrapper, uniformName, value1, value2, value3, value4, count);
        nativeUpdateMesh(mNativeMeshWrapper);
    }

    private void setUniform(String uniformName, float[] values, boolean isColor) {
        if (uniformName == null) {
            throw new NullPointerException("The uniformName parameter must not be null");
        }
        if (values == null) {
            throw new NullPointerException("The uniform values parameter must not be null");
        }

        nativeUpdateUniforms(mNativeMeshWrapper, uniformName, values, isColor);
        nativeUpdateMesh(mNativeMeshWrapper);
    }

    /**
     * Sets the uniform color value corresponding to the shader assigned to the 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) {
        setIntUniform(uniformName, value, 0, 0, 0, 1);
    }

    /**
     * Sets the uniform color value corresponding to the shader assigned to the mesh.
     *
     * @param uniformName name matching the int uniform delcared in the shader program.
     * @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) {
        setIntUniform(uniformName, value1, value2, 0, 0, 2);
    }

    /**
     * Sets the uniform color value corresponding to the shader assigned to the mesh.
     *
     * @param uniformName name matching the int uniform delcared in the shader program.
     * @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.
     * @param value3      third value corresponding to the int uniform with the given name.
     */
    public void setIntUniform(String uniformName, int value1, int value2, int value3) {
        setIntUniform(uniformName, value1, value2, value3, 0, 3);
    }

    /**
     * Sets the uniform color value corresponding to the shader assigned to the mesh.
     *
     * @param uniformName name matching the int uniform delcared in the shader program.
     * @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.
     * @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) {
        setIntUniform(uniformName, value1, value2, value3, value4, 4);
    }

    /**
     * Sets the uniform color value corresponding to the shader assigned to the 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) {
        if (uniformName == null) {
            throw new NullPointerException("The uniformName parameter must not be null");
        }
        if (values == null) {
            throw new NullPointerException("The uniform values parameter must not be null");
        }
        nativeUpdateUniforms(mNativeMeshWrapper, uniformName, values);
        nativeUpdateMesh(mNativeMeshWrapper);
    }

    private void setIntUniform(
            String uniformName, int value1, int value2, int value3, int value4, int count) {
        if (uniformName == null) {
            throw new NullPointerException("The uniformName parameter must not be null");
        }

        nativeUpdateUniforms(
                mNativeMeshWrapper, uniformName, value1, value2, value3, value4, count);
        nativeUpdateMesh(mNativeMeshWrapper);
    }

    private Mesh(long nativeMeshWrapper, boolean isIndexed) {
        mNativeMeshWrapper = nativeMeshWrapper;
        this.mIsIndexed = isIndexed;
        MeshHolder.MESH_SPECIFICATION_REGISTRY.registerNativeAllocation(this, mNativeMeshWrapper);
    }

    private static native long nativeGetFinalizer();

    private static native long nativeMake(long meshSpec, int mode, Buffer vertexBuffer,
            boolean isDirect, int vertexCount, int vertexOffset, int left, int top, int right,
            int bottom);

    private static native long nativeMakeIndexed(long meshSpec, int mode, Buffer vertexBuffer,
            boolean isVertexDirect, int vertexCount, int vertexOffset, ShortBuffer indexBuffer,
            boolean isIndexDirect, int indexCount, int indexOffset, int left, int top, int right,
            int bottom);

    private static native void nativeUpdateUniforms(long builder, String uniformName, float value1,
            float value2, float value3, float value4, int count);

    private static native void nativeUpdateUniforms(
            long builder, String uniformName, float[] values, boolean isColor);

    private static native void nativeUpdateUniforms(long builder, String uniformName, int value1,
            int value2, int value3, int value4, int count);

    private static native void nativeUpdateUniforms(long builder, String uniformName, int[] values);

    private static native void nativeUpdateMesh(long nativeMeshWrapper);
}
+1 −1
Original line number Diff line number Diff line
@@ -39,7 +39,7 @@ import libcore.util.NativeAllocationRegistry;
 * @hide
 */
public class MeshSpecification {
    private long mNativeMeshSpec;
    long mNativeMeshSpec;

    /**
     * Constants for {@link #make(Attribute[], int, Varying[], String, String, ColorSpace, int)}

libs/hwui/jni/Mesh.cpp

0 → 100644
+209 −0

File added.

Preview size limit exceeded, changes collapsed.

libs/hwui/jni/Mesh.h

0 → 100644
+261 −0
Original line number 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.
 */

#ifndef FRAMEWORKS_BASE_LIBS_HWUI_JNI_MESH_H_
#define FRAMEWORKS_BASE_LIBS_HWUI_JNI_MESH_H_

#include <SkMesh.h>
#include <jni.h>

#include <utility>

#include "graphics_jni_helpers.h"

#define gIndexByteSize 2

// A smart pointer that provides read only access to Java.nio.Buffer. This handles both
// direct and indrect buffers, allowing access to the underlying data in both
// situations. If passed a null buffer, we will throw NullPointerException,
// and c_data will return nullptr.
//
// This class draws from com_google_android_gles_jni_GLImpl.cpp for Buffer to void *
// conversion.
class ScopedJavaNioBuffer {
public:
    ScopedJavaNioBuffer(JNIEnv* env, jobject buffer, jint size, jboolean isDirect)
            : mEnv(env), mBuffer(buffer) {
        if (buffer == nullptr) {
            mDataBase = nullptr;
            mData = nullptr;
            jniThrowNullPointerException(env);
        } else {
            mArray = (jarray) nullptr;
            if (isDirect) {
                mData = getDirectBufferPointer(mEnv, mBuffer);
            } else {
                mData = setIndirectData(size);
            }
        }
    }

    ScopedJavaNioBuffer(ScopedJavaNioBuffer&& rhs) noexcept { *this = std::move(rhs); }

    ~ScopedJavaNioBuffer() { reset(); }

    void reset() {
        if (mDataBase) {
            releasePointer(mEnv, mArray, mDataBase, JNI_FALSE);
            mDataBase = nullptr;
        }
    }

    ScopedJavaNioBuffer& operator=(ScopedJavaNioBuffer&& rhs) noexcept {
        if (this != &rhs) {
            reset();

            mEnv = rhs.mEnv;
            mBuffer = rhs.mBuffer;
            mDataBase = rhs.mDataBase;
            mData = rhs.mData;
            mArray = rhs.mArray;
            rhs.mEnv = nullptr;
            rhs.mData = nullptr;
            rhs.mBuffer = nullptr;
            rhs.mArray = nullptr;
            rhs.mDataBase = nullptr;
        }
        return *this;
    }

    const void* data() const { return mData; }

private:
    /**
     * This code is taken and modified from com_google_android_gles_jni_GLImpl.cpp to extract data
     * from a java.nio.Buffer.
     */
    void* getDirectBufferPointer(JNIEnv* env, jobject buffer) {
        if (buffer == nullptr) {
            return nullptr;
        }

        jint position;
        jint limit;
        jint elementSizeShift;
        jlong pointer;
        pointer = jniGetNioBufferFields(env, buffer, &position, &limit, &elementSizeShift);
        if (pointer == 0) {
            jniThrowException(mEnv, "java/lang/IllegalArgumentException",
                              "Must use a native order direct Buffer");
            return nullptr;
        }
        pointer += position << elementSizeShift;
        return reinterpret_cast<void*>(pointer);
    }

    static void releasePointer(JNIEnv* env, jarray array, void* data, jboolean commit) {
        env->ReleasePrimitiveArrayCritical(array, data, commit ? 0 : JNI_ABORT);
    }

    static void* getPointer(JNIEnv* env, jobject buffer, jarray* array, jint* remaining,
                            jint* offset) {
        jint position;
        jint limit;
        jint elementSizeShift;

        jlong pointer;
        pointer = jniGetNioBufferFields(env, buffer, &position, &limit, &elementSizeShift);
        *remaining = (limit - position) << elementSizeShift;
        if (pointer != 0L) {
            *array = nullptr;
            pointer += position << elementSizeShift;
            return reinterpret_cast<void*>(pointer);
        }

        *array = jniGetNioBufferBaseArray(env, buffer);
        *offset = jniGetNioBufferBaseArrayOffset(env, buffer);
        return nullptr;
    }

    /**
     * This is a copy of
     * static void android_glBufferData__IILjava_nio_Buffer_2I
     * from com_google_android_gles_jni_GLImpl.cpp
     */
    void* setIndirectData(jint size) {
        jint exception;
        const char* exceptionType;
        const char* exceptionMessage;
        jint bufferOffset = (jint)0;
        jint remaining;
        void* tempData;

        if (mBuffer) {
            tempData =
                    (void*)getPointer(mEnv, mBuffer, (jarray*)&mArray, &remaining, &bufferOffset);
            if (remaining < size) {
                exception = 1;
                exceptionType = "java/lang/IllegalArgumentException";
                exceptionMessage = "remaining() < size < needed";
                goto exit;
            }
        }
        if (mBuffer && tempData == nullptr) {
            mDataBase = (char*)mEnv->GetPrimitiveArrayCritical(mArray, (jboolean*)0);
            tempData = (void*)(mDataBase + bufferOffset);
        }
        return tempData;
    exit:
        if (mArray) {
            releasePointer(mEnv, mArray, (void*)(mDataBase), JNI_FALSE);
        }
        if (exception) {
            jniThrowException(mEnv, exceptionType, exceptionMessage);
        }
        return nullptr;
    }

    JNIEnv* mEnv;

    // Java Buffer data
    void* mData;
    jobject mBuffer;

    // Indirect Buffer Data
    jarray mArray;
    char* mDataBase;
};

class MeshUniformBuilder {
public:
    struct MeshUniform {
        template <typename T>
        std::enable_if_t<std::is_trivially_copyable<T>::value, MeshUniform> operator=(
                const T& val) {
            if (!fVar) {
                SkDEBUGFAIL("Assigning to missing variable");
            } else if (sizeof(val) != fVar->sizeInBytes()) {
                SkDEBUGFAIL("Incorrect value size");
            } else {
                memcpy(SkTAddOffset<void>(fOwner->writableUniformData(), fVar->offset), &val,
                       szeof(val));
            }
        }

        MeshUniform& operator=(const SkMatrix& val) {
            if (!fVar) {
                SkDEBUGFAIL("Assigning to missing variable");
            } else if (fVar->sizeInBytes() != 9 * sizeof(float)) {
                SkDEBUGFAIL("Incorrect value size");
            } else {
                float* data =
                        SkTAddOffset<float>(fOwner->writableUniformData(), (ptrdiff_t)fVar->offset);
                data[0] = val.get(0);
                data[1] = val.get(3);
                data[2] = val.get(6);
                data[3] = val.get(1);
                data[4] = val.get(4);
                data[5] = val.get(7);
                data[6] = val.get(2);
                data[7] = val.get(5);
                data[8] = val.get(8);
            }
            return *this;
        }

        template <typename T>
        bool set(const T val[], const int count) {
            static_assert(std::is_trivially_copyable<T>::value, "Value must be trivial copyable");
            if (!fVar) {
                SkDEBUGFAIL("Assigning to missing variable");
                return false;
            } else if (sizeof(T) * count != fVar->sizeInBytes()) {
                SkDEBUGFAIL("Incorrect value size");
                return false;
            } else {
                memcpy(SkTAddOffset<void>(fOwner->writableUniformData(), fVar->offset), val,
                       sizeof(T) * count);
            }
            return true;
        }

        MeshUniformBuilder* fOwner;
        const SkRuntimeEffect::Uniform* fVar;
    };
    MeshUniform uniform(std::string_view name) { return {this, fMeshSpec->findUniform(name)}; }

    explicit MeshUniformBuilder(sk_sp<SkMeshSpecification> meshSpec) {
        fMeshSpec = sk_sp(meshSpec);
    }

    sk_sp<SkData> fUniforms;

private:
    void* writableUniformData() {
        if (!fUniforms->unique()) {
            fUniforms = SkData::MakeWithCopy(fUniforms->data(), fUniforms->size());
        }
        return fUniforms->writable_data();
    }

    sk_sp<SkMeshSpecification> fMeshSpec;
};

struct MeshWrapper {
    SkMesh mesh;
    MeshUniformBuilder builder;
};
#endif  // FRAMEWORKS_BASE_LIBS_HWUI_JNI_MESH_H_