Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c949def4 authored by Nader Jawad's avatar Nader Jawad Committed by Android (Google) Code Review
Browse files

Merge "Defer Meshed creation" into udc-dev

parents 643dc76f 5f0a800a
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -341,7 +341,6 @@ public class Mesh {
     * @hide so only calls from module can utilize it
     */
    long getNativeWrapperInstance() {
        nativeUpdateMesh(mNativeMeshWrapper, mIsIndexed);
        return mNativeMeshWrapper;
    }

@@ -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);
}
+2 −1
Original line number Diff line number Diff line
@@ -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",
@@ -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",
@@ -538,6 +538,7 @@ cc_defaults {
        "Interpolator.cpp",
        "LightingInfo.cpp",
        "Matrix.cpp",
        "Mesh.cpp",
        "MemoryPolicy.cpp",
        "PathParser.cpp",
        "Properties.cpp",
+2 −1
Original line number Diff line number Diff line
@@ -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, {}};
}
+208 −0
Original line number Diff line number Diff line
@@ -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:
@@ -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