Merge "Defer Meshed creation" into udc-dev am: c949def472
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21553643 Change-Id: I23fee940a77051aa5eca0cba4d4d1089d56c7782 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
102
libs/hwui/Mesh.cpp
Normal 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
208
libs/hwui/Mesh.h
Normal 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_
|
||||
@@ -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
|
||||
|
||||
@@ -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
107
libs/hwui/SafeMath.h
Normal 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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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,
|
||||
|
||||
350
libs/hwui/jni/android_graphics_Mesh.cpp
Normal file
350
libs/hwui/jni/android_graphics_Mesh.cpp
Normal 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
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user