Loading graphics/java/android/graphics/Mesh.java +0 −2 Original line number Diff line number Diff line Loading @@ -341,7 +341,6 @@ public class Mesh { * @hide so only calls from module can utilize it */ long getNativeWrapperInstance() { nativeUpdateMesh(mNativeMeshWrapper, mIsIndexed); return mNativeMeshWrapper; } Loading Loading @@ -383,5 +382,4 @@ public class Mesh { private static native void nativeUpdateUniforms(long builder, String uniformName, int[] values); private static native void nativeUpdateMesh(long nativeMeshWrapper, boolean mIsIndexed); } libs/hwui/Android.bp +2 −1 Original line number Diff line number Diff line Loading @@ -332,6 +332,7 @@ cc_defaults { "jni/android_graphics_Matrix.cpp", "jni/android_graphics_Picture.cpp", "jni/android_graphics_DisplayListCanvas.cpp", "jni/android_graphics_Mesh.cpp", "jni/android_graphics_RenderNode.cpp", "jni/android_nio_utils.cpp", "jni/android_util_PathParser.cpp", Loading @@ -351,7 +352,6 @@ cc_defaults { "jni/ImageDecoder.cpp", "jni/Interpolator.cpp", "jni/MeshSpecification.cpp", "jni/Mesh.cpp", "jni/MaskFilter.cpp", "jni/NinePatch.cpp", "jni/NinePatchPeeker.cpp", Loading Loading @@ -538,6 +538,7 @@ cc_defaults { "Interpolator.cpp", "LightingInfo.cpp", "Matrix.cpp", "Mesh.cpp", "MemoryPolicy.cpp", "PathParser.cpp", "Properties.cpp", Loading libs/hwui/DisplayListOps.in +2 −1 Original line number Diff line number Diff line Loading @@ -52,4 +52,5 @@ X(DrawShadowRec) X(DrawVectorDrawable) X(DrawRippleDrawable) X(DrawWebView) X(DrawSkMesh) X(DrawMesh) No newline at end of file libs/hwui/Mesh.cpp 0 → 100644 +102 −0 Original line number Diff line number Diff line /* * 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 "Mesh.h" #include <GLES/gl.h> #include <SkMesh.h> #include "SafeMath.h" static size_t min_vcount_for_mode(SkMesh::Mode mode) { switch (mode) { case SkMesh::Mode::kTriangles: return 3; case SkMesh::Mode::kTriangleStrip: return 3; } } // Re-implementation of SkMesh::validate to validate user side that their mesh is valid. std::tuple<bool, SkString> Mesh::validate() { #define FAIL_MESH_VALIDATE(...) return std::make_tuple(false, SkStringPrintf(__VA_ARGS__)) if (!mMeshSpec) { FAIL_MESH_VALIDATE("MeshSpecification is required."); } if (mVertexBufferData.empty()) { FAIL_MESH_VALIDATE("VertexBuffer is required."); } auto meshStride = mMeshSpec->stride(); auto meshMode = SkMesh::Mode(mMode); SafeMath sm; size_t vsize = sm.mul(meshStride, mVertexCount); if (sm.add(vsize, mVertexOffset) > mVertexBufferData.size()) { FAIL_MESH_VALIDATE( "The vertex buffer offset and vertex count reads beyond the end of the" " vertex buffer."); } if (mVertexOffset % meshStride != 0) { FAIL_MESH_VALIDATE("The vertex offset (%zu) must be a multiple of the vertex stride (%zu).", mVertexOffset, meshStride); } if (size_t uniformSize = mMeshSpec->uniformSize()) { if (!mBuilder->fUniforms || mBuilder->fUniforms->size() < uniformSize) { FAIL_MESH_VALIDATE("The uniform data is %zu bytes but must be at least %zu.", mBuilder->fUniforms->size(), uniformSize); } } auto modeToStr = [](SkMesh::Mode m) { switch (m) { case SkMesh::Mode::kTriangles: return "triangles"; case SkMesh::Mode::kTriangleStrip: return "triangle-strip"; } }; if (!mIndexBufferData.empty()) { if (mIndexCount < min_vcount_for_mode(meshMode)) { FAIL_MESH_VALIDATE("%s mode requires at least %zu indices but index count is %zu.", modeToStr(meshMode), min_vcount_for_mode(meshMode), mIndexCount); } size_t isize = sm.mul(sizeof(uint16_t), mIndexCount); if (sm.add(isize, mIndexOffset) > mIndexBufferData.size()) { FAIL_MESH_VALIDATE( "The index buffer offset and index count reads beyond the end of the" " index buffer."); } // If we allow 32 bit indices then this should enforce 4 byte alignment in that case. if (!SkIsAlign2(mIndexOffset)) { FAIL_MESH_VALIDATE("The index offset must be a multiple of 2."); } } else { if (mVertexCount < min_vcount_for_mode(meshMode)) { FAIL_MESH_VALIDATE("%s mode requires at least %zu vertices but vertex count is %zu.", modeToStr(meshMode), min_vcount_for_mode(meshMode), mVertexCount); } SkASSERT(!fICount); SkASSERT(!fIOffset); } if (!sm.ok()) { FAIL_MESH_VALIDATE("Overflow"); } #undef FAIL_MESH_VALIDATE return {true, {}}; } libs/hwui/jni/Mesh.h→libs/hwui/Mesh.h +208 −0 Original line number Diff line number Diff line Loading @@ -14,171 +14,15 @@ * limitations under the License. */ #ifndef FRAMEWORKS_BASE_LIBS_HWUI_JNI_MESH_H_ #define FRAMEWORKS_BASE_LIBS_HWUI_JNI_MESH_H_ #ifndef MESH_H_ #define MESH_H_ #include <GrDirectContext.h> #include <SkMesh.h> #include <jni.h> #include <log/log.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; }; #include <utility> class MeshUniformBuilder { public: Loading Loading @@ -258,8 +102,107 @@ private: sk_sp<SkMeshSpecification> fMeshSpec; }; struct MeshWrapper { SkMesh mesh; MeshUniformBuilder builder; class Mesh { public: Mesh(const sk_sp<SkMeshSpecification>& meshSpec, int mode, const void* vertexBuffer, size_t vertexBufferSize, jint vertexCount, jint vertexOffset, std::unique_ptr<MeshUniformBuilder> builder, const SkRect& bounds) : mMeshSpec(meshSpec) , mMode(mode) , mVertexCount(vertexCount) , mVertexOffset(vertexOffset) , mBuilder(std::move(builder)) , mBounds(bounds) { copyToVector(mVertexBufferData, vertexBuffer, vertexBufferSize); } Mesh(const sk_sp<SkMeshSpecification>& meshSpec, int mode, const void* vertexBuffer, size_t vertexBufferSize, jint vertexCount, jint vertexOffset, const void* indexBuffer, size_t indexBufferSize, jint indexCount, jint indexOffset, std::unique_ptr<MeshUniformBuilder> builder, const SkRect& bounds) : mMeshSpec(meshSpec) , mMode(mode) , mVertexCount(vertexCount) , mVertexOffset(vertexOffset) , mIndexCount(indexCount) , mIndexOffset(indexOffset) , mBuilder(std::move(builder)) , mBounds(bounds) { copyToVector(mVertexBufferData, vertexBuffer, vertexBufferSize); copyToVector(mIndexBufferData, indexBuffer, indexBufferSize); } Mesh(Mesh&&) = default; Mesh& operator=(Mesh&&) = default; [[nodiscard]] std::tuple<bool, SkString> validate(); void updateSkMesh(GrDirectContext* context) const { GrDirectContext::DirectContextID genId = GrDirectContext::DirectContextID(); if (context) { genId = context->directContextID(); } if (mIsDirty || genId != mGenerationId) { auto vb = SkMesh::MakeVertexBuffer( context, reinterpret_cast<const void*>(mVertexBufferData.data()), mVertexBufferData.size()); auto meshMode = SkMesh::Mode(mMode); if (!mIndexBufferData.empty()) { auto ib = SkMesh::MakeIndexBuffer( context, reinterpret_cast<const void*>(mIndexBufferData.data()), mIndexBufferData.size()); mMesh = SkMesh::MakeIndexed(mMeshSpec, meshMode, vb, mVertexCount, mVertexOffset, ib, mIndexCount, mIndexOffset, mBuilder->fUniforms, mBounds) .mesh; } else { mMesh = SkMesh::Make(mMeshSpec, meshMode, vb, mVertexCount, mVertexOffset, mBuilder->fUniforms, mBounds) .mesh; } mIsDirty = false; mGenerationId = genId; } } SkMesh& getSkMesh() const { LOG_FATAL_IF(mIsDirty, "Attempt to obtain SkMesh when Mesh is dirty, did you " "forget to call updateSkMesh with a GrDirectContext? " "Defensively creating a CPU mesh"); return mMesh; } void markDirty() { mIsDirty = true; } MeshUniformBuilder* uniformBuilder() { return mBuilder.get(); } private: void copyToVector(std::vector<uint8_t>& dst, const void* src, size_t srcSize) { if (src) { dst.resize(srcSize); memcpy(dst.data(), src, srcSize); } } sk_sp<SkMeshSpecification> mMeshSpec; int mMode = 0; std::vector<uint8_t> mVertexBufferData; size_t mVertexCount = 0; size_t mVertexOffset = 0; std::vector<uint8_t> mIndexBufferData; size_t mIndexCount = 0; size_t mIndexOffset = 0; std::unique_ptr<MeshUniformBuilder> mBuilder; SkRect mBounds{}; mutable SkMesh mMesh{}; mutable bool mIsDirty = true; mutable GrDirectContext::DirectContextID mGenerationId = GrDirectContext::DirectContextID(); }; #endif // FRAMEWORKS_BASE_LIBS_HWUI_JNI_MESH_H_ #endif // MESH_H_ Loading
graphics/java/android/graphics/Mesh.java +0 −2 Original line number Diff line number Diff line Loading @@ -341,7 +341,6 @@ public class Mesh { * @hide so only calls from module can utilize it */ long getNativeWrapperInstance() { nativeUpdateMesh(mNativeMeshWrapper, mIsIndexed); return mNativeMeshWrapper; } Loading Loading @@ -383,5 +382,4 @@ public class Mesh { private static native void nativeUpdateUniforms(long builder, String uniformName, int[] values); private static native void nativeUpdateMesh(long nativeMeshWrapper, boolean mIsIndexed); }
libs/hwui/Android.bp +2 −1 Original line number Diff line number Diff line Loading @@ -332,6 +332,7 @@ cc_defaults { "jni/android_graphics_Matrix.cpp", "jni/android_graphics_Picture.cpp", "jni/android_graphics_DisplayListCanvas.cpp", "jni/android_graphics_Mesh.cpp", "jni/android_graphics_RenderNode.cpp", "jni/android_nio_utils.cpp", "jni/android_util_PathParser.cpp", Loading @@ -351,7 +352,6 @@ cc_defaults { "jni/ImageDecoder.cpp", "jni/Interpolator.cpp", "jni/MeshSpecification.cpp", "jni/Mesh.cpp", "jni/MaskFilter.cpp", "jni/NinePatch.cpp", "jni/NinePatchPeeker.cpp", Loading Loading @@ -538,6 +538,7 @@ cc_defaults { "Interpolator.cpp", "LightingInfo.cpp", "Matrix.cpp", "Mesh.cpp", "MemoryPolicy.cpp", "PathParser.cpp", "Properties.cpp", Loading
libs/hwui/DisplayListOps.in +2 −1 Original line number Diff line number Diff line Loading @@ -52,4 +52,5 @@ X(DrawShadowRec) X(DrawVectorDrawable) X(DrawRippleDrawable) X(DrawWebView) X(DrawSkMesh) X(DrawMesh) No newline at end of file
libs/hwui/Mesh.cpp 0 → 100644 +102 −0 Original line number Diff line number Diff line /* * 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 "Mesh.h" #include <GLES/gl.h> #include <SkMesh.h> #include "SafeMath.h" static size_t min_vcount_for_mode(SkMesh::Mode mode) { switch (mode) { case SkMesh::Mode::kTriangles: return 3; case SkMesh::Mode::kTriangleStrip: return 3; } } // Re-implementation of SkMesh::validate to validate user side that their mesh is valid. std::tuple<bool, SkString> Mesh::validate() { #define FAIL_MESH_VALIDATE(...) return std::make_tuple(false, SkStringPrintf(__VA_ARGS__)) if (!mMeshSpec) { FAIL_MESH_VALIDATE("MeshSpecification is required."); } if (mVertexBufferData.empty()) { FAIL_MESH_VALIDATE("VertexBuffer is required."); } auto meshStride = mMeshSpec->stride(); auto meshMode = SkMesh::Mode(mMode); SafeMath sm; size_t vsize = sm.mul(meshStride, mVertexCount); if (sm.add(vsize, mVertexOffset) > mVertexBufferData.size()) { FAIL_MESH_VALIDATE( "The vertex buffer offset and vertex count reads beyond the end of the" " vertex buffer."); } if (mVertexOffset % meshStride != 0) { FAIL_MESH_VALIDATE("The vertex offset (%zu) must be a multiple of the vertex stride (%zu).", mVertexOffset, meshStride); } if (size_t uniformSize = mMeshSpec->uniformSize()) { if (!mBuilder->fUniforms || mBuilder->fUniforms->size() < uniformSize) { FAIL_MESH_VALIDATE("The uniform data is %zu bytes but must be at least %zu.", mBuilder->fUniforms->size(), uniformSize); } } auto modeToStr = [](SkMesh::Mode m) { switch (m) { case SkMesh::Mode::kTriangles: return "triangles"; case SkMesh::Mode::kTriangleStrip: return "triangle-strip"; } }; if (!mIndexBufferData.empty()) { if (mIndexCount < min_vcount_for_mode(meshMode)) { FAIL_MESH_VALIDATE("%s mode requires at least %zu indices but index count is %zu.", modeToStr(meshMode), min_vcount_for_mode(meshMode), mIndexCount); } size_t isize = sm.mul(sizeof(uint16_t), mIndexCount); if (sm.add(isize, mIndexOffset) > mIndexBufferData.size()) { FAIL_MESH_VALIDATE( "The index buffer offset and index count reads beyond the end of the" " index buffer."); } // If we allow 32 bit indices then this should enforce 4 byte alignment in that case. if (!SkIsAlign2(mIndexOffset)) { FAIL_MESH_VALIDATE("The index offset must be a multiple of 2."); } } else { if (mVertexCount < min_vcount_for_mode(meshMode)) { FAIL_MESH_VALIDATE("%s mode requires at least %zu vertices but vertex count is %zu.", modeToStr(meshMode), min_vcount_for_mode(meshMode), mVertexCount); } SkASSERT(!fICount); SkASSERT(!fIOffset); } if (!sm.ok()) { FAIL_MESH_VALIDATE("Overflow"); } #undef FAIL_MESH_VALIDATE return {true, {}}; }
libs/hwui/jni/Mesh.h→libs/hwui/Mesh.h +208 −0 Original line number Diff line number Diff line Loading @@ -14,171 +14,15 @@ * limitations under the License. */ #ifndef FRAMEWORKS_BASE_LIBS_HWUI_JNI_MESH_H_ #define FRAMEWORKS_BASE_LIBS_HWUI_JNI_MESH_H_ #ifndef MESH_H_ #define MESH_H_ #include <GrDirectContext.h> #include <SkMesh.h> #include <jni.h> #include <log/log.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; }; #include <utility> class MeshUniformBuilder { public: Loading Loading @@ -258,8 +102,107 @@ private: sk_sp<SkMeshSpecification> fMeshSpec; }; struct MeshWrapper { SkMesh mesh; MeshUniformBuilder builder; class Mesh { public: Mesh(const sk_sp<SkMeshSpecification>& meshSpec, int mode, const void* vertexBuffer, size_t vertexBufferSize, jint vertexCount, jint vertexOffset, std::unique_ptr<MeshUniformBuilder> builder, const SkRect& bounds) : mMeshSpec(meshSpec) , mMode(mode) , mVertexCount(vertexCount) , mVertexOffset(vertexOffset) , mBuilder(std::move(builder)) , mBounds(bounds) { copyToVector(mVertexBufferData, vertexBuffer, vertexBufferSize); } Mesh(const sk_sp<SkMeshSpecification>& meshSpec, int mode, const void* vertexBuffer, size_t vertexBufferSize, jint vertexCount, jint vertexOffset, const void* indexBuffer, size_t indexBufferSize, jint indexCount, jint indexOffset, std::unique_ptr<MeshUniformBuilder> builder, const SkRect& bounds) : mMeshSpec(meshSpec) , mMode(mode) , mVertexCount(vertexCount) , mVertexOffset(vertexOffset) , mIndexCount(indexCount) , mIndexOffset(indexOffset) , mBuilder(std::move(builder)) , mBounds(bounds) { copyToVector(mVertexBufferData, vertexBuffer, vertexBufferSize); copyToVector(mIndexBufferData, indexBuffer, indexBufferSize); } Mesh(Mesh&&) = default; Mesh& operator=(Mesh&&) = default; [[nodiscard]] std::tuple<bool, SkString> validate(); void updateSkMesh(GrDirectContext* context) const { GrDirectContext::DirectContextID genId = GrDirectContext::DirectContextID(); if (context) { genId = context->directContextID(); } if (mIsDirty || genId != mGenerationId) { auto vb = SkMesh::MakeVertexBuffer( context, reinterpret_cast<const void*>(mVertexBufferData.data()), mVertexBufferData.size()); auto meshMode = SkMesh::Mode(mMode); if (!mIndexBufferData.empty()) { auto ib = SkMesh::MakeIndexBuffer( context, reinterpret_cast<const void*>(mIndexBufferData.data()), mIndexBufferData.size()); mMesh = SkMesh::MakeIndexed(mMeshSpec, meshMode, vb, mVertexCount, mVertexOffset, ib, mIndexCount, mIndexOffset, mBuilder->fUniforms, mBounds) .mesh; } else { mMesh = SkMesh::Make(mMeshSpec, meshMode, vb, mVertexCount, mVertexOffset, mBuilder->fUniforms, mBounds) .mesh; } mIsDirty = false; mGenerationId = genId; } } SkMesh& getSkMesh() const { LOG_FATAL_IF(mIsDirty, "Attempt to obtain SkMesh when Mesh is dirty, did you " "forget to call updateSkMesh with a GrDirectContext? " "Defensively creating a CPU mesh"); return mMesh; } void markDirty() { mIsDirty = true; } MeshUniformBuilder* uniformBuilder() { return mBuilder.get(); } private: void copyToVector(std::vector<uint8_t>& dst, const void* src, size_t srcSize) { if (src) { dst.resize(srcSize); memcpy(dst.data(), src, srcSize); } } sk_sp<SkMeshSpecification> mMeshSpec; int mMode = 0; std::vector<uint8_t> mVertexBufferData; size_t mVertexCount = 0; size_t mVertexOffset = 0; std::vector<uint8_t> mIndexBufferData; size_t mIndexCount = 0; size_t mIndexOffset = 0; std::unique_ptr<MeshUniformBuilder> mBuilder; SkRect mBounds{}; mutable SkMesh mMesh{}; mutable bool mIsDirty = true; mutable GrDirectContext::DirectContextID mGenerationId = GrDirectContext::DirectContextID(); }; #endif // FRAMEWORKS_BASE_LIBS_HWUI_JNI_MESH_H_ #endif // MESH_H_