Exposed SkMesh

Exposed skMesh through Mesh API, utilizing makeVertexBuffer,
makeIndexBuffer, make, and makeIndexed. Uniforms are set through
set<>Uniform methods, where the native mesh will then be updated upon
each call to set<>Uniform.

Bug: b/254354591
Test: built hwui
Change-Id: Ia100f00a1e29198e30dfd25999f8bd8db9fb3ca0
This commit is contained in:
Angel Aguayo
2022-10-27 00:45:15 +00:00
parent c719979691
commit 4804746c35
4 changed files with 788 additions and 1 deletions

View File

@@ -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);
}

View File

@@ -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)}

209
libs/hwui/jni/Mesh.cpp Normal file
View File

@@ -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

261
libs/hwui/jni/Mesh.h Normal file
View File

@@ -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_