Merge "Exposed SkMesh"
This commit is contained in:
317
graphics/java/android/graphics/Mesh.java
Normal file
317
graphics/java/android/graphics/Mesh.java
Normal 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);
|
||||
}
|
||||
@@ -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
209
libs/hwui/jni/Mesh.cpp
Normal 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
261
libs/hwui/jni/Mesh.h
Normal 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_
|
||||
Reference in New Issue
Block a user