/* * 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 #include #include #include #include class MeshUniformBuilder { public: struct MeshUniform { template std::enable_if_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( reinterpret_cast(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( reinterpret_cast(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 bool set(const T val[], const int count) { static_assert(std::is_trivially_copyable::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( reinterpret_cast(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 meshSpec) { fMeshSpec = sk_sp(meshSpec); fUniforms = (SkData::MakeZeroInitialized(meshSpec->uniformSize())); } sk_sp fUniforms; private: void* writableUniformData() { if (!fUniforms->unique()) { fUniforms = SkData::MakeWithCopy(fUniforms->data(), fUniforms->size()); } return fUniforms->writable_data(); } sk_sp fMeshSpec; }; class Mesh { public: Mesh(const sk_sp& meshSpec, int mode, std::vector&& vertexBufferData, jint vertexCount, jint vertexOffset, std::unique_ptr 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& meshSpec, int mode, std::vector&& vertexBufferData, jint vertexCount, jint vertexOffset, std::vector&& indexBuffer, jint indexCount, jint indexOffset, std::unique_ptr 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 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(mVertexBufferData.data()), mVertexBufferData.size()); auto meshMode = SkMesh::Mode(mMode); if (!mIndexBufferData.empty()) { auto ib = SkMesh::MakeIndexBuffer( context, reinterpret_cast(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 mMeshSpec; int mMode = 0; std::vector mVertexBufferData; size_t mVertexCount = 0; size_t mVertexOffset = 0; std::vector mIndexBufferData; size_t mIndexCount = 0; size_t mIndexOffset = 0; std::unique_ptr mBuilder; SkRect mBounds{}; mutable SkMesh mMesh{}; mutable bool mIsDirty = true; mutable GrDirectContext::DirectContextID mGenerationId = GrDirectContext::DirectContextID(); }; #endif // MESH_H_