Merge "Defer Meshed creation" into udc-dev am: c949def472 am: 02b5d9cdee

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21553643

Change-Id: I8720ed979fbb3cc8ac166317c31f4054168e5c07
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Nader Jawad
2023-02-28 04:42:27 +00:00
committed by Automerger Merge Worker
19 changed files with 861 additions and 519 deletions

View File

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

View File

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

View File

@@ -52,4 +52,5 @@ X(DrawShadowRec)
X(DrawVectorDrawable)
X(DrawRippleDrawable)
X(DrawWebView)
X(DrawMesh)
X(DrawSkMesh)
X(DrawMesh)

102
libs/hwui/Mesh.cpp Normal file
View File

@@ -0,0 +1,102 @@
/*
* 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
libs/hwui/Mesh.h Normal file
View File

@@ -0,0 +1,208 @@
/*
* 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 MESH_H_
#define MESH_H_
#include <GrDirectContext.h>
#include <SkMesh.h>
#include <jni.h>
#include <log/log.h>
#include <utility>
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) {
LOG_FATAL("Assigning to missing variable");
} else if (sizeof(val) != fVar->sizeInBytes()) {
LOG_FATAL("Incorrect value size");
} else {
void* dst = reinterpret_cast<void*>(
reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + fVar->offset);
memcpy(dst, &val, sizeof(val));
}
}
MeshUniform& operator=(const SkMatrix& val) {
if (!fVar) {
LOG_FATAL("Assigning to missing variable");
} else if (fVar->sizeInBytes() != 9 * sizeof(float)) {
LOG_FATAL("Incorrect value size");
} else {
float* data = reinterpret_cast<float*>(
reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + 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) {
LOG_FATAL("Assigning to missing variable");
return false;
} else if (sizeof(T) * count != fVar->sizeInBytes()) {
LOG_FATAL("Incorrect value size");
return false;
} else {
void* dst = reinterpret_cast<void*>(
reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + fVar->offset);
memcpy(dst, 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);
fUniforms = (SkData::MakeZeroInitialized(meshSpec->uniformSize()));
}
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;
};
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 // MESH_H_

View File

@@ -24,6 +24,7 @@
#include <experimental/type_traits>
#include <utility>
#include "Mesh.h"
#include "SkAndroidFrameworkUtils.h"
#include "SkBlendMode.h"
#include "SkCanvas.h"
@@ -502,14 +503,14 @@ struct DrawVertices final : Op {
c->drawVertices(vertices, mode, paint);
}
};
struct DrawMesh final : Op {
static const auto kType = Type::DrawMesh;
DrawMesh(const SkMesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint)
struct DrawSkMesh final : Op {
static const auto kType = Type::DrawSkMesh;
DrawSkMesh(const SkMesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint)
: cpuMesh(mesh), blender(std::move(blender)), paint(paint) {
isGpuBased = false;
}
SkMesh cpuMesh;
const SkMesh& cpuMesh;
mutable SkMesh gpuMesh;
sk_sp<SkBlender> blender;
SkPaint paint;
@@ -517,6 +518,7 @@ struct DrawMesh final : Op {
mutable GrDirectContext::DirectContextID contextId;
void draw(SkCanvas* c, const SkMatrix&) const {
GrDirectContext* directContext = c->recordingContext()->asDirectContext();
GrDirectContext::DirectContextID id = directContext->directContextID();
if (!isGpuBased || contextId != id) {
sk_sp<SkMesh::VertexBuffer> vb =
@@ -543,6 +545,18 @@ struct DrawMesh final : Op {
c->drawMesh(gpuMesh, blender, paint);
}
};
struct DrawMesh final : Op {
static const auto kType = Type::DrawMesh;
DrawMesh(const Mesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint)
: mesh(mesh), blender(std::move(blender)), paint(paint) {}
const Mesh& mesh;
sk_sp<SkBlender> blender;
SkPaint paint;
void draw(SkCanvas* c, const SkMatrix&) const { c->drawMesh(mesh.getSkMesh(), blender, paint); }
};
struct DrawAtlas final : Op {
static const auto kType = Type::DrawAtlas;
DrawAtlas(const SkImage* atlas, int count, SkBlendMode mode, const SkSamplingOptions& sampling,
@@ -859,6 +873,10 @@ void DisplayListData::drawVertices(const SkVertices* vert, SkBlendMode mode, con
}
void DisplayListData::drawMesh(const SkMesh& mesh, const sk_sp<SkBlender>& blender,
const SkPaint& paint) {
this->push<DrawSkMesh>(0, mesh, blender, paint);
}
void DisplayListData::drawMesh(const Mesh& mesh, const sk_sp<SkBlender>& blender,
const SkPaint& paint) {
this->push<DrawMesh>(0, mesh, blender, paint);
}
void DisplayListData::drawAtlas(const SkImage* atlas, const SkRSXform xforms[], const SkRect texs[],
@@ -1205,6 +1223,9 @@ void RecordingCanvas::onDrawMesh(const SkMesh& mesh, sk_sp<SkBlender> blender,
const SkPaint& paint) {
fDL->drawMesh(mesh, blender, paint);
}
void RecordingCanvas::drawMesh(const Mesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint) {
fDL->drawMesh(mesh, blender, paint);
}
void RecordingCanvas::onDrawAtlas2(const SkImage* atlas, const SkRSXform xforms[],
const SkRect texs[], const SkColor colors[], int count,
SkBlendMode bmode, const SkSamplingOptions& sampling,
@@ -1223,5 +1244,14 @@ void RecordingCanvas::drawWebView(skiapipeline::FunctorDrawable* drawable) {
fDL->drawWebView(drawable);
}
[[nodiscard]] const SkMesh& DrawMeshPayload::getSkMesh() const {
LOG_FATAL_IF(!meshWrapper && !mesh, "One of Mesh or Mesh must be non-null");
if (meshWrapper) {
return meshWrapper->getSkMesh();
} else {
return *mesh;
}
}
} // namespace uirenderer
} // namespace android

View File

@@ -28,6 +28,7 @@
#include <log/log.h>
#include <cstdlib>
#include <utility>
#include <vector>
#include "CanvasTransform.h"
@@ -40,6 +41,7 @@
enum class SkBlendMode;
class SkRRect;
class Mesh;
namespace android {
namespace uirenderer {
@@ -66,6 +68,18 @@ struct DisplayListOp {
static_assert(sizeof(DisplayListOp) == 4);
class DrawMeshPayload {
public:
explicit DrawMeshPayload(const SkMesh* mesh) : mesh(mesh) {}
explicit DrawMeshPayload(const Mesh* meshWrapper) : meshWrapper(meshWrapper) {}
[[nodiscard]] const SkMesh& getSkMesh() const;
private:
const SkMesh* mesh = nullptr;
const Mesh* meshWrapper = nullptr;
};
struct DrawImagePayload {
explicit DrawImagePayload(Bitmap& bitmap)
: image(bitmap.makeImage()), palette(bitmap.palette()) {
@@ -143,6 +157,7 @@ private:
void drawDRRect(const SkRRect&, const SkRRect&, const SkPaint&);
void drawMesh(const SkMesh&, const sk_sp<SkBlender>&, const SkPaint&);
void drawMesh(const Mesh&, const sk_sp<SkBlender>&, const SkPaint&);
void drawAnnotation(const SkRect&, const char*, SkData*);
void drawDrawable(SkDrawable*, const SkMatrix*);
@@ -247,6 +262,7 @@ public:
SkBlendMode, const SkSamplingOptions&, const SkRect*, const SkPaint*) override;
void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override;
void drawMesh(const Mesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint);
void drawVectorDrawable(VectorDrawableRoot* tree);
void drawWebView(skiapipeline::FunctorDrawable*);

107
libs/hwui/SafeMath.h Normal file
View File

@@ -0,0 +1,107 @@
/*
* 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 SkSafeMath_DEFINED
#define SkSafeMath_DEFINED
#include <cstddef>
#include <cstdint>
#include <limits>
// Copy of Skia's SafeMath API used to validate Mesh parameters to support
// deferred creation of SkMesh instances on RenderThread.
// SafeMath always check that a series of operations do not overflow.
// This must be correct for all platforms, because this is a check for safety at runtime.
class SafeMath {
public:
SafeMath() = default;
bool ok() const { return fOK; }
explicit operator bool() const { return fOK; }
size_t mul(size_t x, size_t y) {
return sizeof(size_t) == sizeof(uint64_t) ? mul64(x, y) : mul32(x, y);
}
size_t add(size_t x, size_t y) {
size_t result = x + y;
fOK &= result >= x;
return result;
}
/**
* Return a + b, unless this result is an overflow/underflow. In those cases, fOK will
* be set to false, and it is undefined what this returns.
*/
int addInt(int a, int b) {
if (b < 0 && a < std::numeric_limits<int>::min() - b) {
fOK = false;
return a;
} else if (b > 0 && a > std::numeric_limits<int>::max() - b) {
fOK = false;
return a;
}
return a + b;
}
// These saturate to their results
static size_t Add(size_t x, size_t y) {
SafeMath tmp;
size_t sum = tmp.add(x, y);
return tmp.ok() ? sum : SIZE_MAX;
}
static size_t Mul(size_t x, size_t y) {
SafeMath tmp;
size_t prod = tmp.mul(x, y);
return tmp.ok() ? prod : SIZE_MAX;
}
private:
uint32_t mul32(uint32_t x, uint32_t y) {
uint64_t bx = x;
uint64_t by = y;
uint64_t result = bx * by;
fOK &= result >> 32 == 0;
// Overflow information is capture in fOK. Return the result modulo 2^32.
return (uint32_t)result;
}
uint64_t mul64(uint64_t x, uint64_t y) {
if (x <= std::numeric_limits<uint64_t>::max() >> 32 &&
y <= std::numeric_limits<uint64_t>::max() >> 32) {
return x * y;
} else {
auto hi = [](uint64_t x) { return x >> 32; };
auto lo = [](uint64_t x) { return x & 0xFFFFFFFF; };
uint64_t lx_ly = lo(x) * lo(y);
uint64_t hx_ly = hi(x) * lo(y);
uint64_t lx_hy = lo(x) * hi(y);
uint64_t hx_hy = hi(x) * hi(y);
uint64_t result = 0;
result = this->add(lx_ly, (hx_ly << 32));
result = this->add(result, (lx_hy << 32));
fOK &= (hx_hy + (hx_ly >> 32) + (lx_hy >> 32)) == 0;
return result;
}
}
bool fOK = true;
};
#endif // SkSafeMath_DEFINED

View File

@@ -39,21 +39,22 @@
#include <SkShader.h>
#include <SkTextBlob.h>
#include <SkVertices.h>
#include <log/log.h>
#include <ui/FatVector.h>
#include <memory>
#include <optional>
#include <utility>
#include "CanvasProperty.h"
#include "Mesh.h"
#include "NinePatchUtils.h"
#include "VectorDrawable.h"
#include "hwui/Bitmap.h"
#include "hwui/MinikinUtils.h"
#include "hwui/PaintFilter.h"
#include <log/log.h>
#include "pipeline/skia/AnimatedDrawables.h"
#include "pipeline/skia/HolePunch.h"
#include <ui/FatVector.h>
namespace android {
@@ -572,8 +573,14 @@ void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, cons
applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawVertices(vertices, mode, p); });
}
void SkiaCanvas::drawMesh(const SkMesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint) {
mCanvas->drawMesh(mesh, blender, paint);
void SkiaCanvas::drawMesh(const Mesh& mesh, sk_sp<SkBlender> blender, const Paint& paint) {
GrDirectContext* context = nullptr;
auto recordingContext = mCanvas->recordingContext();
if (recordingContext) {
context = recordingContext->asDirectContext();
}
mesh.updateSkMesh(context);
mCanvas->drawMesh(mesh.getSkMesh(), blender, paint);
}
// ----------------------------------------------------------------------------

View File

@@ -129,8 +129,7 @@ public:
float sweepAngle, bool useCenter, const Paint& paint) override;
virtual void drawPath(const SkPath& path, const Paint& paint) override;
virtual void drawVertices(const SkVertices*, SkBlendMode, const Paint& paint) override;
virtual void drawMesh(const SkMesh& mesh, sk_sp<SkBlender> blender,
const SkPaint& paint) override;
virtual void drawMesh(const Mesh& mesh, sk_sp<SkBlender> blender, const Paint& paint) override;
virtual void drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) override;
virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* paint) override;

View File

@@ -16,18 +16,17 @@
#pragma once
#include <cutils/compiler.h>
#include <utils/Functor.h>
#include <SaveFlags.h>
#include <androidfw/ResourceTypes.h>
#include "Properties.h"
#include "pipeline/skia/AnimatedDrawables.h"
#include "utils/Macros.h"
#include <SkBitmap.h>
#include <SkCanvas.h>
#include <SkMatrix.h>
#include <androidfw/ResourceTypes.h>
#include <cutils/compiler.h>
#include <utils/Functor.h>
#include "Properties.h"
#include "pipeline/skia/AnimatedDrawables.h"
#include "utils/Macros.h"
class SkAnimatedImage;
enum class SkBlendMode;
@@ -35,6 +34,7 @@ class SkCanvasState;
class SkRRect;
class SkRuntimeShaderBuilder;
class SkVertices;
class Mesh;
namespace minikin {
class Font;
@@ -227,7 +227,7 @@ public:
float sweepAngle, bool useCenter, const Paint& paint) = 0;
virtual void drawPath(const SkPath& path, const Paint& paint) = 0;
virtual void drawVertices(const SkVertices*, SkBlendMode, const Paint& paint) = 0;
virtual void drawMesh(const SkMesh& mesh, sk_sp<SkBlender>, const SkPaint& paint) = 0;
virtual void drawMesh(const Mesh& mesh, sk_sp<SkBlender>, const Paint& paint) = 0;
// Bitmap-based
virtual void drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) = 0;

View File

@@ -1,227 +0,0 @@
/*
* 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, jfloat left, jfloat top,
jfloat right, jfloat bottom) {
auto skMeshSpec = sk_ref_sp(reinterpret_cast<SkMeshSpecification*>(meshSpec));
sk_sp<SkMesh::VertexBuffer> skVertexBuffer =
genVertexBuffer(env, vertexBuffer, vertexCount * skMeshSpec->stride(), isDirect);
auto skRect = SkRect::MakeLTRB(left, top, right, bottom);
auto meshResult = SkMesh::Make(
skMeshSpec, SkMesh::Mode(mode), skVertexBuffer, vertexCount, vertexOffset,
SkData::MakeWithCopy(skMeshSpec->uniforms().data(), skMeshSpec->uniformSize()), skRect);
if (!meshResult.error.isEmpty()) {
jniThrowException(env, "java/lang/IllegalArgumentException", meshResult.error.c_str());
}
auto meshPtr = std::make_unique<MeshWrapper>(
MeshWrapper{meshResult.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, jfloat left, jfloat top, jfloat right, jfloat bottom) {
auto skMeshSpec = sk_ref_sp(reinterpret_cast<SkMeshSpecification*>(meshSpec));
sk_sp<SkMesh::VertexBuffer> skVertexBuffer =
genVertexBuffer(env, vertexBuffer, vertexCount * skMeshSpec->stride(), isVertexDirect);
sk_sp<SkMesh::IndexBuffer> skIndexBuffer =
genIndexBuffer(env, indexBuffer, indexCount * gIndexByteSize, isIndexDirect);
auto skRect = SkRect::MakeLTRB(left, top, right, bottom);
auto meshResult = SkMesh::MakeIndexed(
skMeshSpec, SkMesh::Mode(mode), skVertexBuffer, vertexCount, vertexOffset,
skIndexBuffer, indexCount, indexOffset,
SkData::MakeWithCopy(skMeshSpec->uniforms().data(), skMeshSpec->uniformSize()), skRect);
if (!meshResult.error.isEmpty()) {
jniThrowException(env, "java/lang/IllegalArgumentException", meshResult.error.c_str());
}
auto meshPtr = std::make_unique<MeshWrapper>(
MeshWrapper{meshResult.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())
.mesh;
} 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())
.mesh;
}
}
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 meshWrapper, jstring uniformName,
jfloat value1, jfloat value2, jfloat value3, jfloat value4,
jint count) {
auto* wrapper = reinterpret_cast<MeshWrapper*>(meshWrapper);
ScopedUtfChars name(env, uniformName);
const float values[4] = {value1, value2, value3, value4};
nativeUpdateFloatUniforms(env, &wrapper->builder, name.c_str(), values, count, false);
}
static void updateFloatArrayUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring jUniformName,
jfloatArray jvalues, jboolean isColor) {
auto wrapper = reinterpret_cast<MeshWrapper*>(meshWrapper);
ScopedUtfChars name(env, jUniformName);
AutoJavaFloatArray autoValues(env, jvalues, 0, kRO_JNIAccess);
nativeUpdateFloatUniforms(env, &wrapper->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 meshWrapper, jstring uniformName,
jint value1, jint value2, jint value3, jint value4, jint count) {
auto wrapper = reinterpret_cast<MeshWrapper*>(meshWrapper);
ScopedUtfChars name(env, uniformName);
const int values[4] = {value1, value2, value3, value4};
nativeUpdateIntUniforms(env, &wrapper->builder, name.c_str(), values, count);
}
static void updateIntArrayUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring uniformName,
jintArray values) {
auto wrapper = reinterpret_cast<MeshWrapper*>(meshWrapper);
ScopedUtfChars name(env, uniformName);
AutoJavaIntArray autoValues(env, values, 0);
nativeUpdateIntUniforms(env, &wrapper->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;ZIIFFFF)J", (void*)make},
{"nativeMakeIndexed", "(JILjava/nio/Buffer;ZIILjava/nio/ShortBuffer;ZIIFFFF)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

View File

@@ -1,265 +0,0 @@
/*
* 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 <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;
};
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) {
LOG_FATAL("Assigning to missing variable");
} else if (sizeof(val) != fVar->sizeInBytes()) {
LOG_FATAL("Incorrect value size");
} else {
void* dst = reinterpret_cast<void*>(
reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + fVar->offset);
memcpy(dst, &val, sizeof(val));
}
}
MeshUniform& operator=(const SkMatrix& val) {
if (!fVar) {
LOG_FATAL("Assigning to missing variable");
} else if (fVar->sizeInBytes() != 9 * sizeof(float)) {
LOG_FATAL("Incorrect value size");
} else {
float* data = reinterpret_cast<float*>(
reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + 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) {
LOG_FATAL("Assigning to missing variable");
return false;
} else if (sizeof(T) * count != fVar->sizeInBytes()) {
LOG_FATAL("Incorrect value size");
return false;
} else {
void* dst = reinterpret_cast<void*>(
reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + fVar->offset);
memcpy(dst, 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);
fUniforms = (SkData::MakeZeroInitialized(meshSpec->uniformSize()));
}
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_

View File

@@ -21,7 +21,6 @@
#else
#define __ANDROID_API_P__ 28
#endif
#include <Mesh.h>
#include <androidfw/ResourceTypes.h>
#include <hwui/Canvas.h>
#include <hwui/Paint.h>
@@ -446,10 +445,10 @@ static void drawVertices(JNIEnv* env, jobject, jlong canvasHandle,
static void drawMesh(JNIEnv* env, jobject, jlong canvasHandle, jlong meshHandle, jint modeHandle,
jlong paintHandle) {
const SkMesh mesh = reinterpret_cast<MeshWrapper*>(meshHandle)->mesh;
const Mesh* mesh = reinterpret_cast<Mesh*>(meshHandle);
SkBlendMode blendMode = static_cast<SkBlendMode>(modeHandle);
SkPaint* paint = reinterpret_cast<Paint*>(paintHandle);
get_canvas(canvasHandle)->drawMesh(mesh, SkBlender::Mode(blendMode), *paint);
Paint* paint = reinterpret_cast<Paint*>(paintHandle);
get_canvas(canvasHandle)->drawMesh(*mesh, SkBlender::Mode(blendMode), *paint);
}
static void drawNinePatch(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,

View File

@@ -0,0 +1,350 @@
/*
* Copyright (C) 2023 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 <GrDirectContext.h>
#include <Mesh.h>
#include <SkMesh.h>
#include <jni.h>
#include <log/log.h>
#include <utility>
#include "GraphicsJNI.h"
#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, size_t 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(size_t 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;
};
namespace android {
static jlong make(JNIEnv* env, jobject, jlong meshSpec, jint mode, jobject vertexBuffer,
jboolean isDirect, jint vertexCount, jint vertexOffset, jfloat left, jfloat top,
jfloat right, jfloat bottom) {
auto skMeshSpec = sk_ref_sp(reinterpret_cast<SkMeshSpecification*>(meshSpec));
size_t bufferSize = vertexCount * skMeshSpec->stride();
auto buff = ScopedJavaNioBuffer(env, vertexBuffer, bufferSize, isDirect);
auto skRect = SkRect::MakeLTRB(left, top, right, bottom);
auto meshPtr = new Mesh(skMeshSpec, mode, buff.data(), bufferSize, vertexCount, vertexOffset,
std::make_unique<MeshUniformBuilder>(skMeshSpec), skRect);
auto [valid, msg] = meshPtr->validate();
if (!valid) {
jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", msg.c_str());
}
return reinterpret_cast<jlong>(meshPtr);
}
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, jfloat left, jfloat top, jfloat right, jfloat bottom) {
auto skMeshSpec = sk_ref_sp(reinterpret_cast<SkMeshSpecification*>(meshSpec));
auto vertexBufferSize = vertexCount * skMeshSpec->stride();
auto indexBufferSize = indexCount * gIndexByteSize;
auto vBuf = ScopedJavaNioBuffer(env, vertexBuffer, vertexBufferSize, isVertexDirect);
auto iBuf = ScopedJavaNioBuffer(env, indexBuffer, indexBufferSize, isIndexDirect);
auto skRect = SkRect::MakeLTRB(left, top, right, bottom);
auto meshPtr = new Mesh(skMeshSpec, mode, vBuf.data(), vertexBufferSize, vertexCount,
vertexOffset, iBuf.data(), indexBufferSize, indexCount, indexOffset,
std::make_unique<MeshUniformBuilder>(skMeshSpec), skRect);
auto [valid, msg] = meshPtr->validate();
if (!valid) {
jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", msg.c_str());
}
return reinterpret_cast<jlong>(meshPtr);
}
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 meshWrapper, jstring uniformName,
jfloat value1, jfloat value2, jfloat value3, jfloat value4,
jint count) {
auto* wrapper = reinterpret_cast<Mesh*>(meshWrapper);
ScopedUtfChars name(env, uniformName);
const float values[4] = {value1, value2, value3, value4};
nativeUpdateFloatUniforms(env, wrapper->uniformBuilder(), name.c_str(), values, count, false);
wrapper->markDirty();
}
static void updateFloatArrayUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring jUniformName,
jfloatArray jvalues, jboolean isColor) {
auto wrapper = reinterpret_cast<Mesh*>(meshWrapper);
ScopedUtfChars name(env, jUniformName);
AutoJavaFloatArray autoValues(env, jvalues, 0, kRO_JNIAccess);
nativeUpdateFloatUniforms(env, wrapper->uniformBuilder(), name.c_str(), autoValues.ptr(),
autoValues.length(), isColor);
wrapper->markDirty();
}
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 meshWrapper, jstring uniformName,
jint value1, jint value2, jint value3, jint value4, jint count) {
auto wrapper = reinterpret_cast<Mesh*>(meshWrapper);
ScopedUtfChars name(env, uniformName);
const int values[4] = {value1, value2, value3, value4};
nativeUpdateIntUniforms(env, wrapper->uniformBuilder(), name.c_str(), values, count);
wrapper->markDirty();
}
static void updateIntArrayUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring uniformName,
jintArray values) {
auto wrapper = reinterpret_cast<Mesh*>(meshWrapper);
ScopedUtfChars name(env, uniformName);
AutoJavaIntArray autoValues(env, values, 0);
nativeUpdateIntUniforms(env, wrapper->uniformBuilder(), name.c_str(), autoValues.ptr(),
autoValues.length());
wrapper->markDirty();
}
static void MeshWrapper_destroy(Mesh* 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;ZIIFFFF)J", (void*)make},
{"nativeMakeIndexed", "(JILjava/nio/Buffer;ZIILjava/nio/ShortBuffer;ZIIFFFF)J",
(void*)makeIndexed},
{"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

View File

@@ -23,6 +23,7 @@
#else
#include "DamageAccumulator.h"
#endif
#include "TreeInfo.h"
#include "VectorDrawable.h"
#ifdef __ANDROID__
#include "renderthread/CanvasContext.h"
@@ -102,6 +103,12 @@ bool SkiaDisplayList::prepareListAndChildren(
info.prepareTextures = false;
info.canvasContext.unpinImages();
}
auto grContext = info.canvasContext.getGrContext();
for (auto mesh : mMeshes) {
mesh->updateSkMesh(grContext);
}
#endif
bool hasBackwardProjectedNodesHere = false;
@@ -168,6 +175,7 @@ void SkiaDisplayList::reset() {
mDisplayList.reset();
mMeshes.clear();
mMutableImages.clear();
mVectorDrawables.clear();
mAnimatedImages.clear();

View File

@@ -18,6 +18,7 @@
#include <deque>
#include "Mesh.h"
#include "RecordingCanvas.h"
#include "RenderNodeDrawable.h"
#include "TreeInfo.h"
@@ -167,6 +168,7 @@ public:
std::deque<RenderNodeDrawable> mChildNodes;
std::deque<FunctorDrawable*> mChildFunctors;
std::vector<SkImage*> mMutableImages;
std::vector<const Mesh*> mMeshes;
private:
std::vector<Pair<VectorDrawableRoot*, SkMatrix>> mVectorDrawables;

View File

@@ -321,6 +321,11 @@ double SkiaRecordingCanvas::drawAnimatedImage(AnimatedImageDrawable* animatedIma
return 0;
}
void SkiaRecordingCanvas::drawMesh(const Mesh& mesh, sk_sp<SkBlender> blender, const Paint& paint) {
mDisplayList->mMeshes.push_back(&mesh);
mRecorder.drawMesh(mesh, blender, paint);
}
} // namespace skiapipeline
} // namespace uirenderer
} // namespace android

View File

@@ -81,6 +81,7 @@ public:
virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override;
virtual void enableZ(bool enableZ) override;
virtual void drawMesh(const Mesh& mesh, sk_sp<SkBlender> blender, const Paint& paint) override;
virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) override;
virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override;