blob: 13e3c8e7bf771630b3420063c4bd549f529d3990 [file] [log] [blame]
Nader Jawad5f0a8002023-02-21 17:00:51 -08001/*
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef MESH_H_
18#define MESH_H_
19
20#include <GrDirectContext.h>
21#include <SkMesh.h>
22#include <jni.h>
23#include <log/log.h>
24
25#include <utility>
26
27class MeshUniformBuilder {
28public:
29 struct MeshUniform {
30 template <typename T>
31 std::enable_if_t<std::is_trivially_copyable<T>::value, MeshUniform> operator=(
32 const T& val) {
33 if (!fVar) {
34 LOG_FATAL("Assigning to missing variable");
35 } else if (sizeof(val) != fVar->sizeInBytes()) {
36 LOG_FATAL("Incorrect value size");
37 } else {
38 void* dst = reinterpret_cast<void*>(
39 reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + fVar->offset);
40 memcpy(dst, &val, sizeof(val));
41 }
42 }
43
44 MeshUniform& operator=(const SkMatrix& val) {
45 if (!fVar) {
46 LOG_FATAL("Assigning to missing variable");
47 } else if (fVar->sizeInBytes() != 9 * sizeof(float)) {
48 LOG_FATAL("Incorrect value size");
49 } else {
50 float* data = reinterpret_cast<float*>(
51 reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + fVar->offset);
52 data[0] = val.get(0);
53 data[1] = val.get(3);
54 data[2] = val.get(6);
55 data[3] = val.get(1);
56 data[4] = val.get(4);
57 data[5] = val.get(7);
58 data[6] = val.get(2);
59 data[7] = val.get(5);
60 data[8] = val.get(8);
61 }
62 return *this;
63 }
64
65 template <typename T>
66 bool set(const T val[], const int count) {
67 static_assert(std::is_trivially_copyable<T>::value, "Value must be trivial copyable");
68 if (!fVar) {
69 LOG_FATAL("Assigning to missing variable");
70 return false;
71 } else if (sizeof(T) * count != fVar->sizeInBytes()) {
72 LOG_FATAL("Incorrect value size");
73 return false;
74 } else {
75 void* dst = reinterpret_cast<void*>(
76 reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + fVar->offset);
77 memcpy(dst, val, sizeof(T) * count);
78 }
79 return true;
80 }
81
82 MeshUniformBuilder* fOwner;
83 const SkRuntimeEffect::Uniform* fVar;
84 };
85 MeshUniform uniform(std::string_view name) { return {this, fMeshSpec->findUniform(name)}; }
86
87 explicit MeshUniformBuilder(sk_sp<SkMeshSpecification> meshSpec) {
88 fMeshSpec = sk_sp(meshSpec);
89 fUniforms = (SkData::MakeZeroInitialized(meshSpec->uniformSize()));
90 }
91
92 sk_sp<SkData> fUniforms;
93
94private:
95 void* writableUniformData() {
96 if (!fUniforms->unique()) {
97 fUniforms = SkData::MakeWithCopy(fUniforms->data(), fUniforms->size());
98 }
99 return fUniforms->writable_data();
100 }
101
102 sk_sp<SkMeshSpecification> fMeshSpec;
103};
104
105class Mesh {
106public:
Nader Jawad8c1d7aa2023-03-02 15:59:11 -0800107 Mesh(const sk_sp<SkMeshSpecification>& meshSpec, int mode,
108 std::vector<uint8_t>&& vertexBufferData, jint vertexCount, jint vertexOffset,
Nader Jawad5f0a8002023-02-21 17:00:51 -0800109 std::unique_ptr<MeshUniformBuilder> builder, const SkRect& bounds)
110 : mMeshSpec(meshSpec)
111 , mMode(mode)
Nader Jawad8c1d7aa2023-03-02 15:59:11 -0800112 , mVertexBufferData(std::move(vertexBufferData))
Nader Jawad5f0a8002023-02-21 17:00:51 -0800113 , mVertexCount(vertexCount)
114 , mVertexOffset(vertexOffset)
115 , mBuilder(std::move(builder))
Nader Jawad8c1d7aa2023-03-02 15:59:11 -0800116 , mBounds(bounds) {}
Nader Jawad5f0a8002023-02-21 17:00:51 -0800117
Nader Jawad8c1d7aa2023-03-02 15:59:11 -0800118 Mesh(const sk_sp<SkMeshSpecification>& meshSpec, int mode,
119 std::vector<uint8_t>&& vertexBufferData, jint vertexCount, jint vertexOffset,
120 std::vector<uint8_t>&& indexBuffer, jint indexCount, jint indexOffset,
Nader Jawad5f0a8002023-02-21 17:00:51 -0800121 std::unique_ptr<MeshUniformBuilder> builder, const SkRect& bounds)
122 : mMeshSpec(meshSpec)
123 , mMode(mode)
Nader Jawad8c1d7aa2023-03-02 15:59:11 -0800124 , mVertexBufferData(std::move(vertexBufferData))
Nader Jawad5f0a8002023-02-21 17:00:51 -0800125 , mVertexCount(vertexCount)
126 , mVertexOffset(vertexOffset)
Nader Jawad8c1d7aa2023-03-02 15:59:11 -0800127 , mIndexBufferData(std::move(indexBuffer))
Nader Jawad5f0a8002023-02-21 17:00:51 -0800128 , mIndexCount(indexCount)
129 , mIndexOffset(indexOffset)
130 , mBuilder(std::move(builder))
Nader Jawad8c1d7aa2023-03-02 15:59:11 -0800131 , mBounds(bounds) {}
Nader Jawad5f0a8002023-02-21 17:00:51 -0800132
133 Mesh(Mesh&&) = default;
134
135 Mesh& operator=(Mesh&&) = default;
136
137 [[nodiscard]] std::tuple<bool, SkString> validate();
138
139 void updateSkMesh(GrDirectContext* context) const {
140 GrDirectContext::DirectContextID genId = GrDirectContext::DirectContextID();
141 if (context) {
142 genId = context->directContextID();
143 }
144
145 if (mIsDirty || genId != mGenerationId) {
146 auto vb = SkMesh::MakeVertexBuffer(
147 context, reinterpret_cast<const void*>(mVertexBufferData.data()),
148 mVertexBufferData.size());
149 auto meshMode = SkMesh::Mode(mMode);
150 if (!mIndexBufferData.empty()) {
151 auto ib = SkMesh::MakeIndexBuffer(
152 context, reinterpret_cast<const void*>(mIndexBufferData.data()),
153 mIndexBufferData.size());
154 mMesh = SkMesh::MakeIndexed(mMeshSpec, meshMode, vb, mVertexCount, mVertexOffset,
155 ib, mIndexCount, mIndexOffset, mBuilder->fUniforms,
156 mBounds)
157 .mesh;
158 } else {
159 mMesh = SkMesh::Make(mMeshSpec, meshMode, vb, mVertexCount, mVertexOffset,
160 mBuilder->fUniforms, mBounds)
161 .mesh;
162 }
163 mIsDirty = false;
164 mGenerationId = genId;
165 }
166 }
167
168 SkMesh& getSkMesh() const {
169 LOG_FATAL_IF(mIsDirty,
170 "Attempt to obtain SkMesh when Mesh is dirty, did you "
171 "forget to call updateSkMesh with a GrDirectContext? "
172 "Defensively creating a CPU mesh");
173 return mMesh;
174 }
175
176 void markDirty() { mIsDirty = true; }
177
178 MeshUniformBuilder* uniformBuilder() { return mBuilder.get(); }
179
180private:
Nader Jawad5f0a8002023-02-21 17:00:51 -0800181 sk_sp<SkMeshSpecification> mMeshSpec;
182 int mMode = 0;
183
184 std::vector<uint8_t> mVertexBufferData;
185 size_t mVertexCount = 0;
186 size_t mVertexOffset = 0;
187
188 std::vector<uint8_t> mIndexBufferData;
189 size_t mIndexCount = 0;
190 size_t mIndexOffset = 0;
191
192 std::unique_ptr<MeshUniformBuilder> mBuilder;
193 SkRect mBounds{};
194
195 mutable SkMesh mMesh{};
196 mutable bool mIsDirty = true;
197 mutable GrDirectContext::DirectContextID mGenerationId = GrDirectContext::DirectContextID();
198};
199#endif // MESH_H_