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, mode, std::move(buffer), vertexCount, vertexOffset,
42 std::make_unique<MeshUniformBuilder>(skMeshSpec), 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 = new Mesh(skMeshSpec, mode, std::move(vBuf), vertexCount, vertexOffset,
67 std::move(iBuf), indexCount, indexOffset,
68 std::make_unique<MeshUniformBuilder>(skMeshSpec), 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 wrapper->markDirty();
137 }
138
updateFloatArrayUniforms(JNIEnv * env,jobject,jlong meshWrapper,jstring jUniformName,jfloatArray jvalues,jboolean isColor)139 static void updateFloatArrayUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring jUniformName,
140 jfloatArray jvalues, jboolean isColor) {
141 auto wrapper = reinterpret_cast<Mesh*>(meshWrapper);
142 ScopedUtfChars name(env, jUniformName);
143 AutoJavaFloatArray autoValues(env, jvalues, 0, kRO_JNIAccess);
144 nativeUpdateFloatUniforms(env, wrapper->uniformBuilder(), name.c_str(), autoValues.ptr(),
145 autoValues.length(), isColor);
146 wrapper->markDirty();
147 }
148
nativeUpdateIntUniforms(JNIEnv * env,MeshUniformBuilder * builder,const char * uniformName,const int values[],int count)149 static void nativeUpdateIntUniforms(JNIEnv* env, MeshUniformBuilder* builder,
150 const char* uniformName, const int values[], int count) {
151 MeshUniformBuilder::MeshUniform uniform = builder->uniform(uniformName);
152 if (uniform.fVar == nullptr) {
153 ThrowIAEFmt(env, "unable to find uniform named %s", uniformName);
154 } else if (!isIntUniformType(uniform.fVar->type)) {
155 ThrowIAEFmt(env, "attempting to set a non-int uniform using the setIntUniform APIs: %s",
156 uniformName);
157 } else if (!uniform.set<int>(values, count)) {
158 ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]",
159 uniform.fVar->sizeInBytes(), sizeof(float) * count);
160 }
161 }
162
updateIntUniforms(JNIEnv * env,jobject,jlong meshWrapper,jstring uniformName,jint value1,jint value2,jint value3,jint value4,jint count)163 static void updateIntUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring uniformName,
164 jint value1, jint value2, jint value3, jint value4, jint count) {
165 auto wrapper = reinterpret_cast<Mesh*>(meshWrapper);
166 ScopedUtfChars name(env, uniformName);
167 const int values[4] = {value1, value2, value3, value4};
168 nativeUpdateIntUniforms(env, wrapper->uniformBuilder(), name.c_str(), values, count);
169 wrapper->markDirty();
170 }
171
updateIntArrayUniforms(JNIEnv * env,jobject,jlong meshWrapper,jstring uniformName,jintArray values)172 static void updateIntArrayUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring uniformName,
173 jintArray values) {
174 auto wrapper = reinterpret_cast<Mesh*>(meshWrapper);
175 ScopedUtfChars name(env, uniformName);
176 AutoJavaIntArray autoValues(env, values, 0);
177 nativeUpdateIntUniforms(env, wrapper->uniformBuilder(), name.c_str(), autoValues.ptr(),
178 autoValues.length());
179 wrapper->markDirty();
180 }
181
MeshWrapper_destroy(Mesh * wrapper)182 static void MeshWrapper_destroy(Mesh* wrapper) {
183 delete wrapper;
184 }
185
getMeshFinalizer(JNIEnv *,jobject)186 static jlong getMeshFinalizer(JNIEnv*, jobject) {
187 return static_cast<jlong>(reinterpret_cast<uintptr_t>(&MeshWrapper_destroy));
188 }
189
190 static const JNINativeMethod gMeshMethods[] = {
191 {"nativeGetFinalizer", "()J", (void*)getMeshFinalizer},
192 {"nativeMake", "(JILjava/nio/Buffer;ZIIFFFF)J", (void*)make},
193 {"nativeMakeIndexed", "(JILjava/nio/Buffer;ZIILjava/nio/ShortBuffer;ZIIFFFF)J",
194 (void*)makeIndexed},
195 {"nativeUpdateUniforms", "(JLjava/lang/String;[FZ)V", (void*)updateFloatArrayUniforms},
196 {"nativeUpdateUniforms", "(JLjava/lang/String;FFFFI)V", (void*)updateFloatUniforms},
197 {"nativeUpdateUniforms", "(JLjava/lang/String;[I)V", (void*)updateIntArrayUniforms},
198 {"nativeUpdateUniforms", "(JLjava/lang/String;IIIII)V", (void*)updateIntUniforms}};
199
register_android_graphics_Mesh(JNIEnv * env)200 int register_android_graphics_Mesh(JNIEnv* env) {
201 android::RegisterMethodsOrDie(env, "android/graphics/Mesh", gMeshMethods, NELEM(gMeshMethods));
202 return 0;
203 }
204
205 } // namespace android