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