200 lines
7.1 KiB
C
200 lines
7.1 KiB
C
|
|
/*
|
||
|
|
* 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,
|
||
|
|
std::vector<uint8_t>&& vertexBufferData, jint vertexCount, jint vertexOffset,
|
||
|
|
std::unique_ptr<MeshUniformBuilder> builder, const SkRect& bounds)
|
||
|
|
: mMeshSpec(meshSpec)
|
||
|
|
, mMode(mode)
|
||
|
|
, mVertexBufferData(std::move(vertexBufferData))
|
||
|
|
, mVertexCount(vertexCount)
|
||
|
|
, mVertexOffset(vertexOffset)
|
||
|
|
, mBuilder(std::move(builder))
|
||
|
|
, mBounds(bounds) {}
|
||
|
|
|
||
|
|
Mesh(const sk_sp<SkMeshSpecification>& meshSpec, int mode,
|
||
|
|
std::vector<uint8_t>&& vertexBufferData, jint vertexCount, jint vertexOffset,
|
||
|
|
std::vector<uint8_t>&& indexBuffer, jint indexCount, jint indexOffset,
|
||
|
|
std::unique_ptr<MeshUniformBuilder> builder, const SkRect& bounds)
|
||
|
|
: mMeshSpec(meshSpec)
|
||
|
|
, mMode(mode)
|
||
|
|
, mVertexBufferData(std::move(vertexBufferData))
|
||
|
|
, mVertexCount(vertexCount)
|
||
|
|
, mVertexOffset(vertexOffset)
|
||
|
|
, mIndexBufferData(std::move(indexBuffer))
|
||
|
|
, mIndexCount(indexCount)
|
||
|
|
, mIndexOffset(indexOffset)
|
||
|
|
, mBuilder(std::move(builder))
|
||
|
|
, mBounds(bounds) {}
|
||
|
|
|
||
|
|
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:
|
||
|
|
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_
|