diff options
author | 2022-11-30 03:08:14 +0000 | |
---|---|---|
committer | 2022-11-30 03:08:14 +0000 | |
commit | fb11cf828915e76f4df7e1dee7a3f0325f219d37 (patch) | |
tree | 34e8460f4220624f0a57bb7e1a6ad3f1dbbf5519 | |
parent | 966144cc5e8827fc4ac2e1e9ba45fb642a4d3dfd (diff) | |
parent | 4804746c35a42bf98c64d8e6c9a60919dab362d6 (diff) |
Merge "Exposed SkMesh"
-rw-r--r-- | graphics/java/android/graphics/Mesh.java | 317 | ||||
-rw-r--r-- | graphics/java/android/graphics/MeshSpecification.java | 2 | ||||
-rw-r--r-- | libs/hwui/jni/Mesh.cpp | 209 | ||||
-rw-r--r-- | libs/hwui/jni/Mesh.h | 261 |
4 files changed, 788 insertions, 1 deletions
diff --git a/graphics/java/android/graphics/Mesh.java b/graphics/java/android/graphics/Mesh.java new file mode 100644 index 000000000000..f0a0cf40ffa9 --- /dev/null +++ b/graphics/java/android/graphics/Mesh.java @@ -0,0 +1,317 @@ +/* + * 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); +} diff --git a/graphics/java/android/graphics/MeshSpecification.java b/graphics/java/android/graphics/MeshSpecification.java index b27c5e0ab728..45c13affac14 100644 --- a/graphics/java/android/graphics/MeshSpecification.java +++ b/graphics/java/android/graphics/MeshSpecification.java @@ -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)} diff --git a/libs/hwui/jni/Mesh.cpp b/libs/hwui/jni/Mesh.cpp new file mode 100644 index 000000000000..6dba6c1a966b --- /dev/null +++ b/libs/hwui/jni/Mesh.cpp @@ -0,0 +1,209 @@ +/* + * 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 <GLES/gl.h> +#include <Mesh.h> +#include <SkMesh.h> + +#include "GraphicsJNI.h" +#include "graphics_jni_helpers.h" + +namespace android { + +sk_sp<SkMesh::VertexBuffer> genVertexBuffer(JNIEnv* env, jobject buffer, int size, + jboolean isDirect) { + auto buff = ScopedJavaNioBuffer(env, buffer, size, isDirect); + auto vertexBuffer = SkMesh::MakeVertexBuffer(nullptr, buff.data(), size); + return vertexBuffer; +} + +sk_sp<SkMesh::IndexBuffer> genIndexBuffer(JNIEnv* env, jobject buffer, int size, + jboolean isDirect) { + auto buff = ScopedJavaNioBuffer(env, buffer, size, isDirect); + auto indexBuffer = SkMesh::MakeIndexBuffer(nullptr, buff.data(), size); + return indexBuffer; +} + +static jlong make(JNIEnv* env, jobject, jlong meshSpec, jint mode, jobject vertexBuffer, + jboolean isDirect, jint vertexCount, jint vertexOffset, jint left, jint top, + jint right, jint bottom) { + auto skMeshSpec = sk_ref_sp(reinterpret_cast<SkMeshSpecification*>(meshSpec)); + sk_sp<SkMesh::VertexBuffer> skVertexBuffer = + genVertexBuffer(env, vertexBuffer, skMeshSpec->attributes().size_bytes(), isDirect); + auto skRect = SkRect::MakeLTRB(left, top, right, bottom); + auto mesh = SkMesh::Make(skMeshSpec, SkMesh::Mode(mode), skVertexBuffer, vertexCount, + vertexOffset, nullptr, skRect); + auto meshPtr = std::make_unique<MeshWrapper>(MeshWrapper{mesh, MeshUniformBuilder(skMeshSpec)}); + return reinterpret_cast<jlong>(meshPtr.release()); +} + +static jlong makeIndexed(JNIEnv* env, jobject, jlong meshSpec, jint mode, jobject vertexBuffer, + jboolean isVertexDirect, jint vertexCount, jint vertexOffset, + jobject indexBuffer, jboolean isIndexDirect, jint indexCount, + jint indexOffset, jint left, jint top, jint right, jint bottom) { + auto skMeshSpec = sk_ref_sp(reinterpret_cast<SkMeshSpecification*>(meshSpec)); + sk_sp<SkMesh::VertexBuffer> skVertexBuffer = genVertexBuffer( + env, vertexBuffer, skMeshSpec->attributes().size_bytes(), isVertexDirect); + 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); + auto meshPtr = std::make_unique<MeshWrapper>(MeshWrapper{mesh, MeshUniformBuilder(skMeshSpec)}); + return reinterpret_cast<jlong>(meshPtr.release()); +} + +static void updateMesh(JNIEnv* env, jobject, jlong meshWrapper, jboolean indexed) { + auto wrapper = reinterpret_cast<MeshWrapper*>(meshWrapper); + auto mesh = wrapper->mesh; + if (indexed) { + wrapper->mesh = SkMesh::MakeIndexed( + sk_ref_sp(mesh.spec()), mesh.mode(), sk_ref_sp(mesh.vertexBuffer()), + mesh.vertexCount(), mesh.vertexOffset(), sk_ref_sp(mesh.indexBuffer()), + mesh.indexCount(), mesh.indexOffset(), wrapper->builder.fUniforms, mesh.bounds()); + } else { + wrapper->mesh = SkMesh::Make( + sk_ref_sp(mesh.spec()), mesh.mode(), sk_ref_sp(mesh.vertexBuffer()), + mesh.vertexCount(), mesh.vertexOffset(), wrapper->builder.fUniforms, mesh.bounds()); + } +} + +static inline int ThrowIAEFmt(JNIEnv* env, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + int ret = jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", fmt, args); + va_end(args); + return ret; +} + +static bool isIntUniformType(const SkRuntimeEffect::Uniform::Type& type) { + switch (type) { + case SkRuntimeEffect::Uniform::Type::kFloat: + case SkRuntimeEffect::Uniform::Type::kFloat2: + case SkRuntimeEffect::Uniform::Type::kFloat3: + case SkRuntimeEffect::Uniform::Type::kFloat4: + case SkRuntimeEffect::Uniform::Type::kFloat2x2: + case SkRuntimeEffect::Uniform::Type::kFloat3x3: + case SkRuntimeEffect::Uniform::Type::kFloat4x4: + return false; + case SkRuntimeEffect::Uniform::Type::kInt: + case SkRuntimeEffect::Uniform::Type::kInt2: + case SkRuntimeEffect::Uniform::Type::kInt3: + case SkRuntimeEffect::Uniform::Type::kInt4: + return true; + } +} + +static void nativeUpdateFloatUniforms(JNIEnv* env, MeshUniformBuilder* builder, + const char* uniformName, const float values[], int count, + bool isColor) { + MeshUniformBuilder::MeshUniform uniform = builder->uniform(uniformName); + if (uniform.fVar == nullptr) { + ThrowIAEFmt(env, "unable to find uniform named %s", uniformName); + } else if (isColor != ((uniform.fVar->flags & SkRuntimeEffect::Uniform::kColor_Flag) != 0)) { + if (isColor) { + jniThrowExceptionFmt( + env, "java/lang/IllegalArgumentException", + "attempting to set a color uniform using the non-color specific APIs: %s %x", + uniformName, uniform.fVar->flags); + } else { + ThrowIAEFmt(env, + "attempting to set a non-color uniform using the setColorUniform APIs: %s", + uniformName); + } + } else if (isIntUniformType(uniform.fVar->type)) { + ThrowIAEFmt(env, "attempting to set a int uniform using the setUniform APIs: %s", + uniformName); + } else if (!uniform.set<float>(values, count)) { + ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]", + uniform.fVar->sizeInBytes(), sizeof(float) * count); + } +} + +static void updateFloatUniforms(JNIEnv* env, jobject, jlong uniBuilder, jstring uniformName, + jfloat value1, jfloat value2, jfloat value3, jfloat value4, + jint count) { + auto* builder = reinterpret_cast<MeshUniformBuilder*>(uniBuilder); + ScopedUtfChars name(env, uniformName); + const float values[4] = {value1, value2, value3, value4}; + nativeUpdateFloatUniforms(env, builder, name.c_str(), values, count, false); +} + +static void updateFloatArrayUniforms(JNIEnv* env, jobject, jlong uniBuilder, jstring jUniformName, + jfloatArray jvalues, jboolean isColor) { + auto builder = reinterpret_cast<MeshUniformBuilder*>(uniBuilder); + ScopedUtfChars name(env, jUniformName); + AutoJavaFloatArray autoValues(env, jvalues, 0, kRO_JNIAccess); + nativeUpdateFloatUniforms(env, builder, name.c_str(), autoValues.ptr(), autoValues.length(), + isColor); +} + +static void nativeUpdateIntUniforms(JNIEnv* env, MeshUniformBuilder* builder, + const char* uniformName, const int values[], int count) { + MeshUniformBuilder::MeshUniform uniform = builder->uniform(uniformName); + if (uniform.fVar == nullptr) { + ThrowIAEFmt(env, "unable to find uniform named %s", uniformName); + } else if (!isIntUniformType(uniform.fVar->type)) { + ThrowIAEFmt(env, "attempting to set a non-int uniform using the setIntUniform APIs: %s", + uniformName); + } else if (!uniform.set<int>(values, count)) { + ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]", + uniform.fVar->sizeInBytes(), sizeof(float) * count); + } +} + +static void updateIntUniforms(JNIEnv* env, jobject, jlong uniBuilder, jstring uniformName, + jint value1, jint value2, jint value3, jint value4, jint count) { + auto builder = reinterpret_cast<MeshUniformBuilder*>(uniBuilder); + ScopedUtfChars name(env, uniformName); + const int values[4] = {value1, value2, value3, value4}; + nativeUpdateIntUniforms(env, builder, name.c_str(), values, count); +} + +static void updateIntArrayUniforms(JNIEnv* env, jobject, jlong uniBuilder, jstring uniformName, + jintArray values) { + auto builder = reinterpret_cast<MeshUniformBuilder*>(uniBuilder); + ScopedUtfChars name(env, uniformName); + AutoJavaIntArray autoValues(env, values, 0); + nativeUpdateIntUniforms(env, builder, name.c_str(), autoValues.ptr(), autoValues.length()); +} + +static void MeshWrapper_destroy(MeshWrapper* wrapper) { + delete wrapper; +} + +static jlong getMeshFinalizer(JNIEnv*, jobject) { + return static_cast<jlong>(reinterpret_cast<uintptr_t>(&MeshWrapper_destroy)); +} + +static const JNINativeMethod gMeshMethods[] = { + {"nativeGetFinalizer", "()J", (void*)getMeshFinalizer}, + {"nativeMake", "(JILjava/nio/Buffer;ZIIIIII)J", (void*)make}, + {"nativeMakeIndexed", "(JILjava/nio/Buffer;ZIILjava/nio/ShortBuffer;ZIIIIII)J", + (void*)makeIndexed}, + {"nativeUpdateMesh", "(JZ)V", (void*)updateMesh}, + {"nativeUpdateUniforms", "(JLjava/lang/String;[FZ)V", (void*)updateFloatArrayUniforms}, + {"nativeUpdateUniforms", "(JLjava/lang/String;FFFFI)V", (void*)updateFloatUniforms}, + {"nativeUpdateUniforms", "(JLjava/lang/String;[I)V", (void*)updateIntArrayUniforms}, + {"nativeUpdateUniforms", "(JLjava/lang/String;IIIII)V", (void*)updateIntUniforms}}; + +int register_android_graphics_Mesh(JNIEnv* env) { + android::RegisterMethodsOrDie(env, "android/graphics/Mesh", gMeshMethods, NELEM(gMeshMethods)); + return 0; +} + +} // namespace android diff --git a/libs/hwui/jni/Mesh.h b/libs/hwui/jni/Mesh.h new file mode 100644 index 000000000000..aa014a5e4f61 --- /dev/null +++ b/libs/hwui/jni/Mesh.h @@ -0,0 +1,261 @@ +/* + * 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_ |