1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <Mesh.h>
18 #include <SkMesh.h>
19 #include <jni.h>
20 
21 #include <utility>
22 
23 #include "BufferUtils.h"
24 #include "GraphicsJNI.h"
25 #include "graphics_jni_helpers.h"
26 
27 #define gIndexByteSize 2
28 
29 namespace android {
30 
make(JNIEnv * env,jobject,jlong meshSpec,jint mode,jobject vertexBuffer,jboolean isDirect,jint vertexCount,jint vertexOffset,jfloat left,jfloat top,jfloat right,jfloat bottom)31 static jlong make(JNIEnv* env, jobject, jlong meshSpec, jint mode, jobject vertexBuffer,
32                   jboolean isDirect, jint vertexCount, jint vertexOffset, jfloat left, jfloat top,
33                   jfloat right, jfloat bottom) {
34     auto skMeshSpec = sk_ref_sp(reinterpret_cast<SkMeshSpecification*>(meshSpec));
35     size_t bufferSize = vertexCount * skMeshSpec->stride();
36     auto buffer = copyJavaNioBufferToVector(env, vertexBuffer, bufferSize, isDirect);
37     if (env->ExceptionCheck()) {
38         return 0;
39     }
40     auto skRect = SkRect::MakeLTRB(left, top, right, bottom);
41     auto meshPtr = new Mesh(skMeshSpec, static_cast<SkMesh::Mode>(mode), std::move(buffer),
42                             vertexCount, vertexOffset, skRect);
43     auto [valid, msg] = meshPtr->validate();
44     if (!valid) {
45         jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", msg.c_str());
46     }
47     return reinterpret_cast<jlong>(meshPtr);
48 }
49 
makeIndexed(JNIEnv * env,jobject,jlong meshSpec,jint mode,jobject vertexBuffer,jboolean isVertexDirect,jint vertexCount,jint vertexOffset,jobject indexBuffer,jboolean isIndexDirect,jint indexCount,jint indexOffset,jfloat left,jfloat top,jfloat right,jfloat bottom)50 static jlong makeIndexed(JNIEnv* env, jobject, jlong meshSpec, jint mode, jobject vertexBuffer,
51                          jboolean isVertexDirect, jint vertexCount, jint vertexOffset,
52                          jobject indexBuffer, jboolean isIndexDirect, jint indexCount,
53                          jint indexOffset, jfloat left, jfloat top, jfloat right, jfloat bottom) {
54     auto skMeshSpec = sk_ref_sp(reinterpret_cast<SkMeshSpecification*>(meshSpec));
55     auto vertexBufferSize = vertexCount * skMeshSpec->stride();
56     auto indexBufferSize = indexCount * gIndexByteSize;
57     auto vBuf = copyJavaNioBufferToVector(env, vertexBuffer, vertexBufferSize, isVertexDirect);
58     if (env->ExceptionCheck()) {
59         return 0;
60     }
61     auto iBuf = copyJavaNioBufferToVector(env, indexBuffer, indexBufferSize, isIndexDirect);
62     if (env->ExceptionCheck()) {
63         return 0;
64     }
65     auto skRect = SkRect::MakeLTRB(left, top, right, bottom);
66     auto meshPtr =
67             new Mesh(skMeshSpec, static_cast<SkMesh::Mode>(mode), std::move(vBuf), vertexCount,
68                      vertexOffset, std::move(iBuf), indexCount, indexOffset, skRect);
69     auto [valid, msg] = meshPtr->validate();
70     if (!valid) {
71         jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", msg.c_str());
72     }
73 
74     return reinterpret_cast<jlong>(meshPtr);
75 }
76 
ThrowIAEFmt(JNIEnv * env,const char * fmt,...)77 static inline int ThrowIAEFmt(JNIEnv* env, const char* fmt, ...) {
78     va_list args;
79     va_start(args, fmt);
80     int ret = jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", fmt, args);
81     va_end(args);
82     return ret;
83 }
84 
isIntUniformType(const SkRuntimeEffect::Uniform::Type & type)85 static bool isIntUniformType(const SkRuntimeEffect::Uniform::Type& type) {
86     switch (type) {
87         case SkRuntimeEffect::Uniform::Type::kFloat:
88         case SkRuntimeEffect::Uniform::Type::kFloat2:
89         case SkRuntimeEffect::Uniform::Type::kFloat3:
90         case SkRuntimeEffect::Uniform::Type::kFloat4:
91         case SkRuntimeEffect::Uniform::Type::kFloat2x2:
92         case SkRuntimeEffect::Uniform::Type::kFloat3x3:
93         case SkRuntimeEffect::Uniform::Type::kFloat4x4:
94             return false;
95         case SkRuntimeEffect::Uniform::Type::kInt:
96         case SkRuntimeEffect::Uniform::Type::kInt2:
97         case SkRuntimeEffect::Uniform::Type::kInt3:
98         case SkRuntimeEffect::Uniform::Type::kInt4:
99             return true;
100     }
101 }
102 
nativeUpdateFloatUniforms(JNIEnv * env,MeshUniformBuilder * builder,const char * uniformName,const float values[],int count,bool isColor)103 static void nativeUpdateFloatUniforms(JNIEnv* env, MeshUniformBuilder* builder,
104                                       const char* uniformName, const float values[], int count,
105                                       bool isColor) {
106     MeshUniformBuilder::MeshUniform uniform = builder->uniform(uniformName);
107     if (uniform.fVar == nullptr) {
108         ThrowIAEFmt(env, "unable to find uniform named %s", uniformName);
109     } else if (isColor != ((uniform.fVar->flags & SkRuntimeEffect::Uniform::kColor_Flag) != 0)) {
110         if (isColor) {
111             jniThrowExceptionFmt(
112                     env, "java/lang/IllegalArgumentException",
113                     "attempting to set a color uniform using the non-color specific APIs: %s %x",
114                     uniformName, uniform.fVar->flags);
115         } else {
116             ThrowIAEFmt(env,
117                         "attempting to set a non-color uniform using the setColorUniform APIs: %s",
118                         uniformName);
119         }
120     } else if (isIntUniformType(uniform.fVar->type)) {
121         ThrowIAEFmt(env, "attempting to set a int uniform using the setUniform APIs: %s",
122                     uniformName);
123     } else if (!uniform.set<float>(values, count)) {
124         ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]",
125                     uniform.fVar->sizeInBytes(), sizeof(float) * count);
126     }
127 }
128 
updateFloatUniforms(JNIEnv * env,jobject,jlong meshWrapper,jstring uniformName,jfloat value1,jfloat value2,jfloat value3,jfloat value4,jint count)129 static void updateFloatUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring uniformName,
130                                 jfloat value1, jfloat value2, jfloat value3, jfloat value4,
131                                 jint count) {
132     auto* wrapper = reinterpret_cast<Mesh*>(meshWrapper);
133     ScopedUtfChars name(env, uniformName);
134     const float values[4] = {value1, value2, value3, value4};
135     nativeUpdateFloatUniforms(env, wrapper->uniformBuilder(), name.c_str(), values, count, false);
136 }
137 
updateFloatArrayUniforms(JNIEnv * env,jobject,jlong meshWrapper,jstring jUniformName,jfloatArray jvalues,jboolean isColor)138 static void updateFloatArrayUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring jUniformName,
139                                      jfloatArray jvalues, jboolean isColor) {
140     auto wrapper = reinterpret_cast<Mesh*>(meshWrapper);
141     ScopedUtfChars name(env, jUniformName);
142     AutoJavaFloatArray autoValues(env, jvalues, 0, kRO_JNIAccess);
143     nativeUpdateFloatUniforms(env, wrapper->uniformBuilder(), name.c_str(), autoValues.ptr(),
144                               autoValues.length(), isColor);
145 }
146 
nativeUpdateIntUniforms(JNIEnv * env,MeshUniformBuilder * builder,const char * uniformName,const int values[],int count)147 static void nativeUpdateIntUniforms(JNIEnv* env, MeshUniformBuilder* builder,
148                                     const char* uniformName, const int values[], int count) {
149     MeshUniformBuilder::MeshUniform uniform = builder->uniform(uniformName);
150     if (uniform.fVar == nullptr) {
151         ThrowIAEFmt(env, "unable to find uniform named %s", uniformName);
152     } else if (!isIntUniformType(uniform.fVar->type)) {
153         ThrowIAEFmt(env, "attempting to set a non-int uniform using the setIntUniform APIs: %s",
154                     uniformName);
155     } else if (!uniform.set<int>(values, count)) {
156         ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]",
157                     uniform.fVar->sizeInBytes(), sizeof(float) * count);
158     }
159 }
160 
updateIntUniforms(JNIEnv * env,jobject,jlong meshWrapper,jstring uniformName,jint value1,jint value2,jint value3,jint value4,jint count)161 static void updateIntUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring uniformName,
162                               jint value1, jint value2, jint value3, jint value4, jint count) {
163     auto wrapper = reinterpret_cast<Mesh*>(meshWrapper);
164     ScopedUtfChars name(env, uniformName);
165     const int values[4] = {value1, value2, value3, value4};
166     nativeUpdateIntUniforms(env, wrapper->uniformBuilder(), name.c_str(), values, count);
167 }
168 
updateIntArrayUniforms(JNIEnv * env,jobject,jlong meshWrapper,jstring uniformName,jintArray values)169 static void updateIntArrayUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring uniformName,
170                                    jintArray values) {
171     auto wrapper = reinterpret_cast<Mesh*>(meshWrapper);
172     ScopedUtfChars name(env, uniformName);
173     AutoJavaIntArray autoValues(env, values, 0);
174     nativeUpdateIntUniforms(env, wrapper->uniformBuilder(), name.c_str(), autoValues.ptr(),
175                             autoValues.length());
176 }
177 
MeshWrapper_destroy(Mesh * wrapper)178 static void MeshWrapper_destroy(Mesh* wrapper) {
179     delete wrapper;
180 }
181 
getMeshFinalizer(JNIEnv *,jobject)182 static jlong getMeshFinalizer(JNIEnv*, jobject) {
183     return static_cast<jlong>(reinterpret_cast<uintptr_t>(&MeshWrapper_destroy));
184 }
185 
186 static const JNINativeMethod gMeshMethods[] = {
187         {"nativeGetFinalizer", "()J", (void*)getMeshFinalizer},
188         {"nativeMake", "(JILjava/nio/Buffer;ZIIFFFF)J", (void*)make},
189         {"nativeMakeIndexed", "(JILjava/nio/Buffer;ZIILjava/nio/ShortBuffer;ZIIFFFF)J",
190          (void*)makeIndexed},
191         {"nativeUpdateUniforms", "(JLjava/lang/String;[FZ)V", (void*)updateFloatArrayUniforms},
192         {"nativeUpdateUniforms", "(JLjava/lang/String;FFFFI)V", (void*)updateFloatUniforms},
193         {"nativeUpdateUniforms", "(JLjava/lang/String;[I)V", (void*)updateIntArrayUniforms},
194         {"nativeUpdateUniforms", "(JLjava/lang/String;IIIII)V", (void*)updateIntUniforms}};
195 
register_android_graphics_Mesh(JNIEnv * env)196 int register_android_graphics_Mesh(JNIEnv* env) {
197     android::RegisterMethodsOrDie(env, "android/graphics/Mesh", gMeshMethods, NELEM(gMeshMethods));
198     return 0;
199 }
200 
201 }  // namespace android