blob: 109aac321b6e6f504ae750e6b7385e4136e258ec [file] [log] [blame]
Angel Aguayo48047462022-10-27 00:45:15 +00001/*
2 * Copyright (C) 2022 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 <GLES/gl.h>
18#include <Mesh.h>
19#include <SkMesh.h>
20
21#include "GraphicsJNI.h"
22#include "graphics_jni_helpers.h"
23
24namespace android {
25
26sk_sp<SkMesh::VertexBuffer> genVertexBuffer(JNIEnv* env, jobject buffer, int size,
27 jboolean isDirect) {
28 auto buff = ScopedJavaNioBuffer(env, buffer, size, isDirect);
29 auto vertexBuffer = SkMesh::MakeVertexBuffer(nullptr, buff.data(), size);
30 return vertexBuffer;
31}
32
33sk_sp<SkMesh::IndexBuffer> genIndexBuffer(JNIEnv* env, jobject buffer, int size,
34 jboolean isDirect) {
35 auto buff = ScopedJavaNioBuffer(env, buffer, size, isDirect);
36 auto indexBuffer = SkMesh::MakeIndexBuffer(nullptr, buff.data(), size);
37 return indexBuffer;
38}
39
40static jlong make(JNIEnv* env, jobject, jlong meshSpec, jint mode, jobject vertexBuffer,
41 jboolean isDirect, jint vertexCount, jint vertexOffset, jint left, jint top,
42 jint right, jint bottom) {
43 auto skMeshSpec = sk_ref_sp(reinterpret_cast<SkMeshSpecification*>(meshSpec));
44 sk_sp<SkMesh::VertexBuffer> skVertexBuffer =
Angel Aguayo90c46ee2022-11-08 23:42:09 +000045 genVertexBuffer(env, vertexBuffer, vertexCount * skMeshSpec->stride(), isDirect);
Angel Aguayo48047462022-10-27 00:45:15 +000046 auto skRect = SkRect::MakeLTRB(left, top, right, bottom);
47 auto mesh = SkMesh::Make(skMeshSpec, SkMesh::Mode(mode), skVertexBuffer, vertexCount,
48 vertexOffset, nullptr, skRect);
49 auto meshPtr = std::make_unique<MeshWrapper>(MeshWrapper{mesh, MeshUniformBuilder(skMeshSpec)});
50 return reinterpret_cast<jlong>(meshPtr.release());
51}
52
53static jlong makeIndexed(JNIEnv* env, jobject, jlong meshSpec, jint mode, jobject vertexBuffer,
54 jboolean isVertexDirect, jint vertexCount, jint vertexOffset,
55 jobject indexBuffer, jboolean isIndexDirect, jint indexCount,
56 jint indexOffset, jint left, jint top, jint right, jint bottom) {
57 auto skMeshSpec = sk_ref_sp(reinterpret_cast<SkMeshSpecification*>(meshSpec));
Angel Aguayo90c46ee2022-11-08 23:42:09 +000058 sk_sp<SkMesh::VertexBuffer> skVertexBuffer =
59 genVertexBuffer(env, vertexBuffer, vertexCount * skMeshSpec->stride(), isVertexDirect);
Angel Aguayo48047462022-10-27 00:45:15 +000060 sk_sp<SkMesh::IndexBuffer> skIndexBuffer =
61 genIndexBuffer(env, indexBuffer, indexCount * gIndexByteSize, isIndexDirect);
62 auto skRect = SkRect::MakeLTRB(left, top, right, bottom);
63 auto mesh = SkMesh::MakeIndexed(skMeshSpec, SkMesh::Mode(mode), skVertexBuffer, vertexCount,
64 vertexOffset, skIndexBuffer, indexCount, indexOffset, nullptr,
65 skRect);
66 auto meshPtr = std::make_unique<MeshWrapper>(MeshWrapper{mesh, MeshUniformBuilder(skMeshSpec)});
67 return reinterpret_cast<jlong>(meshPtr.release());
68}
69
70static void updateMesh(JNIEnv* env, jobject, jlong meshWrapper, jboolean indexed) {
71 auto wrapper = reinterpret_cast<MeshWrapper*>(meshWrapper);
72 auto mesh = wrapper->mesh;
73 if (indexed) {
74 wrapper->mesh = SkMesh::MakeIndexed(
75 sk_ref_sp(mesh.spec()), mesh.mode(), sk_ref_sp(mesh.vertexBuffer()),
76 mesh.vertexCount(), mesh.vertexOffset(), sk_ref_sp(mesh.indexBuffer()),
77 mesh.indexCount(), mesh.indexOffset(), wrapper->builder.fUniforms, mesh.bounds());
78 } else {
79 wrapper->mesh = SkMesh::Make(
80 sk_ref_sp(mesh.spec()), mesh.mode(), sk_ref_sp(mesh.vertexBuffer()),
81 mesh.vertexCount(), mesh.vertexOffset(), wrapper->builder.fUniforms, mesh.bounds());
82 }
83}
84
85static inline int ThrowIAEFmt(JNIEnv* env, const char* fmt, ...) {
86 va_list args;
87 va_start(args, fmt);
88 int ret = jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", fmt, args);
89 va_end(args);
90 return ret;
91}
92
93static bool isIntUniformType(const SkRuntimeEffect::Uniform::Type& type) {
94 switch (type) {
95 case SkRuntimeEffect::Uniform::Type::kFloat:
96 case SkRuntimeEffect::Uniform::Type::kFloat2:
97 case SkRuntimeEffect::Uniform::Type::kFloat3:
98 case SkRuntimeEffect::Uniform::Type::kFloat4:
99 case SkRuntimeEffect::Uniform::Type::kFloat2x2:
100 case SkRuntimeEffect::Uniform::Type::kFloat3x3:
101 case SkRuntimeEffect::Uniform::Type::kFloat4x4:
102 return false;
103 case SkRuntimeEffect::Uniform::Type::kInt:
104 case SkRuntimeEffect::Uniform::Type::kInt2:
105 case SkRuntimeEffect::Uniform::Type::kInt3:
106 case SkRuntimeEffect::Uniform::Type::kInt4:
107 return true;
108 }
109}
110
111static void nativeUpdateFloatUniforms(JNIEnv* env, MeshUniformBuilder* builder,
112 const char* uniformName, const float values[], int count,
113 bool isColor) {
114 MeshUniformBuilder::MeshUniform uniform = builder->uniform(uniformName);
115 if (uniform.fVar == nullptr) {
116 ThrowIAEFmt(env, "unable to find uniform named %s", uniformName);
117 } else if (isColor != ((uniform.fVar->flags & SkRuntimeEffect::Uniform::kColor_Flag) != 0)) {
118 if (isColor) {
119 jniThrowExceptionFmt(
120 env, "java/lang/IllegalArgumentException",
121 "attempting to set a color uniform using the non-color specific APIs: %s %x",
122 uniformName, uniform.fVar->flags);
123 } else {
124 ThrowIAEFmt(env,
125 "attempting to set a non-color uniform using the setColorUniform APIs: %s",
126 uniformName);
127 }
128 } else if (isIntUniformType(uniform.fVar->type)) {
129 ThrowIAEFmt(env, "attempting to set a int uniform using the setUniform APIs: %s",
130 uniformName);
131 } else if (!uniform.set<float>(values, count)) {
132 ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]",
133 uniform.fVar->sizeInBytes(), sizeof(float) * count);
134 }
135}
136
137static void updateFloatUniforms(JNIEnv* env, jobject, jlong uniBuilder, jstring uniformName,
138 jfloat value1, jfloat value2, jfloat value3, jfloat value4,
139 jint count) {
140 auto* builder = reinterpret_cast<MeshUniformBuilder*>(uniBuilder);
141 ScopedUtfChars name(env, uniformName);
142 const float values[4] = {value1, value2, value3, value4};
143 nativeUpdateFloatUniforms(env, builder, name.c_str(), values, count, false);
144}
145
146static void updateFloatArrayUniforms(JNIEnv* env, jobject, jlong uniBuilder, jstring jUniformName,
147 jfloatArray jvalues, jboolean isColor) {
148 auto builder = reinterpret_cast<MeshUniformBuilder*>(uniBuilder);
149 ScopedUtfChars name(env, jUniformName);
150 AutoJavaFloatArray autoValues(env, jvalues, 0, kRO_JNIAccess);
151 nativeUpdateFloatUniforms(env, builder, name.c_str(), autoValues.ptr(), autoValues.length(),
152 isColor);
153}
154
155static void nativeUpdateIntUniforms(JNIEnv* env, MeshUniformBuilder* builder,
156 const char* uniformName, const int values[], int count) {
157 MeshUniformBuilder::MeshUniform uniform = builder->uniform(uniformName);
158 if (uniform.fVar == nullptr) {
159 ThrowIAEFmt(env, "unable to find uniform named %s", uniformName);
160 } else if (!isIntUniformType(uniform.fVar->type)) {
161 ThrowIAEFmt(env, "attempting to set a non-int uniform using the setIntUniform APIs: %s",
162 uniformName);
163 } else if (!uniform.set<int>(values, count)) {
164 ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]",
165 uniform.fVar->sizeInBytes(), sizeof(float) * count);
166 }
167}
168
169static void updateIntUniforms(JNIEnv* env, jobject, jlong uniBuilder, jstring uniformName,
170 jint value1, jint value2, jint value3, jint value4, jint count) {
171 auto builder = reinterpret_cast<MeshUniformBuilder*>(uniBuilder);
172 ScopedUtfChars name(env, uniformName);
173 const int values[4] = {value1, value2, value3, value4};
174 nativeUpdateIntUniforms(env, builder, name.c_str(), values, count);
175}
176
177static void updateIntArrayUniforms(JNIEnv* env, jobject, jlong uniBuilder, jstring uniformName,
178 jintArray values) {
179 auto builder = reinterpret_cast<MeshUniformBuilder*>(uniBuilder);
180 ScopedUtfChars name(env, uniformName);
181 AutoJavaIntArray autoValues(env, values, 0);
182 nativeUpdateIntUniforms(env, builder, name.c_str(), autoValues.ptr(), autoValues.length());
183}
184
185static void MeshWrapper_destroy(MeshWrapper* wrapper) {
186 delete wrapper;
187}
188
189static jlong getMeshFinalizer(JNIEnv*, jobject) {
190 return static_cast<jlong>(reinterpret_cast<uintptr_t>(&MeshWrapper_destroy));
191}
192
193static const JNINativeMethod gMeshMethods[] = {
194 {"nativeGetFinalizer", "()J", (void*)getMeshFinalizer},
195 {"nativeMake", "(JILjava/nio/Buffer;ZIIIIII)J", (void*)make},
196 {"nativeMakeIndexed", "(JILjava/nio/Buffer;ZIILjava/nio/ShortBuffer;ZIIIIII)J",
197 (void*)makeIndexed},
198 {"nativeUpdateMesh", "(JZ)V", (void*)updateMesh},
199 {"nativeUpdateUniforms", "(JLjava/lang/String;[FZ)V", (void*)updateFloatArrayUniforms},
200 {"nativeUpdateUniforms", "(JLjava/lang/String;FFFFI)V", (void*)updateFloatUniforms},
201 {"nativeUpdateUniforms", "(JLjava/lang/String;[I)V", (void*)updateIntArrayUniforms},
202 {"nativeUpdateUniforms", "(JLjava/lang/String;IIIII)V", (void*)updateIntUniforms}};
203
204int register_android_graphics_Mesh(JNIEnv* env) {
205 android::RegisterMethodsOrDie(env, "android/graphics/Mesh", gMeshMethods, NELEM(gMeshMethods));
206 return 0;
207}
208
209} // namespace android