* Fix a crash by setting not exist material parameters on ubershader * gltfio: add getFeatureMap, remove logic for dummy textures Co-authored-by: Philip Rideout <philiprideout@gmail.com>
166 lines
6.3 KiB
C++
166 lines
6.3 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.
|
|
*/
|
|
|
|
#include "ArchiveCache.h"
|
|
|
|
#include <uberz/ReadableArchive.h>
|
|
#include <utils/memalign.h>
|
|
|
|
#include <zstd.h>
|
|
|
|
using namespace utils;
|
|
using namespace filament::uberz;
|
|
|
|
namespace filament::gltfio {
|
|
|
|
|
|
// Set this to a certain spec index to find out why it was deemed unsuitable.
|
|
// To find the spec index of interest, try invoking uberz with the verbose flag.
|
|
constexpr static int DEBUG_SPEC_INDEX = -1;
|
|
|
|
constexpr static void debugSuitability(int index, const char* msg) {
|
|
if constexpr(DEBUG_SPEC_INDEX > 0) {
|
|
if (index == DEBUG_SPEC_INDEX) {
|
|
slog.d << "Spec " << DEBUG_SPEC_INDEX
|
|
<< " is unsuitable due to " << msg << io::endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool strIsEqual(const CString& a, const char* b) {
|
|
return strncmp(a.c_str(), b, a.size()) == 0;
|
|
}
|
|
|
|
void ArchiveCache::load(const void* archiveData, uint64_t archiveByteCount) {
|
|
assert_invariant(mArchive == nullptr && "Do not call load() twice");
|
|
const uint64_t decompSize = ZSTD_getFrameContentSize(archiveData, archiveByteCount);
|
|
if (decompSize == ZSTD_CONTENTSIZE_UNKNOWN || decompSize == ZSTD_CONTENTSIZE_ERROR) {
|
|
PANIC_POSTCONDITION("Decompression error.");
|
|
}
|
|
uint64_t* basePointer = (uint64_t*) utils::aligned_alloc(decompSize, 8);
|
|
ZSTD_decompress(basePointer, decompSize, archiveData, archiveByteCount);
|
|
mArchive = (ReadableArchive*) basePointer;
|
|
convertOffsetsToPointers(mArchive);
|
|
mMaterials = FixedCapacityVector<Material*>(mArchive->specsCount, nullptr);
|
|
}
|
|
|
|
// This loops though all ubershaders and returns the first one that meets the given requirements.
|
|
Material* ArchiveCache::getMaterial(const ArchiveRequirements& reqs) {
|
|
assert_invariant(mArchive && "Please call load() before requesting any materials.");
|
|
|
|
for (uint64_t i = 0; i < mArchive->specsCount; ++i) {
|
|
const ArchiveSpec& spec = mArchive->specs[i];
|
|
if (spec.blendingMode != INVALID_BLENDING && spec.blendingMode != reqs.blendingMode) {
|
|
debugSuitability(i, "blend mode mismatch.");
|
|
continue;
|
|
}
|
|
if (spec.shadingModel != INVALID_SHADING_MODEL && spec.shadingModel != reqs.shadingModel) {
|
|
debugSuitability(i, "material model.");
|
|
continue;
|
|
}
|
|
bool specIsSuitable = true;
|
|
|
|
// For each feature required by the mesh, this ubershader is suitable only if it includes a
|
|
// feature flag for it and the feature flag is either OPTIONAL or REQUIRED.
|
|
for (const auto& req : reqs.features) {
|
|
const CString& meshRequirement = req.first;
|
|
if (req.second == false) {
|
|
continue;
|
|
}
|
|
bool found = false;
|
|
for (uint64_t j = 0; j < spec.flagsCount && !found; ++j) {
|
|
const ArchiveFlag& flag = spec.flags[j];
|
|
if (strIsEqual(meshRequirement, flag.name)) {
|
|
if (flag.value != ArchiveFeature::UNSUPPORTED) {
|
|
found = true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
debugSuitability(i, meshRequirement.c_str());
|
|
specIsSuitable = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If this ubershader requires a certain feature to be enabled in the glTF, but the glTF
|
|
// mesh doesn't have it, then this ubershader is not suitable. This occurs very rarely, so
|
|
// it intentionally comes after the other suitability check.
|
|
for (uint64_t j = 0; j < spec.flagsCount && specIsSuitable; ++j) {
|
|
ArchiveFlag& flag = spec.flags[j];
|
|
if (UTILS_UNLIKELY(flag.value == ArchiveFeature::REQUIRED)) {
|
|
// This allocates a new CString just to make a robin_map lookup, but this is rare
|
|
// because almost none of our feature flags are REQUIRED.
|
|
auto iter = reqs.features.find(CString(flag.name));
|
|
if (iter == reqs.features.end() || iter.value() == false) {
|
|
debugSuitability(i, flag.name);
|
|
specIsSuitable = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (specIsSuitable) {
|
|
if (mMaterials[i] == nullptr) {
|
|
mMaterials[i] = Material::Builder()
|
|
.package(spec.package, spec.packageByteCount)
|
|
.build(mEngine);
|
|
}
|
|
return mMaterials[i];
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
Material* ArchiveCache::getDefaultMaterial() {
|
|
assert_invariant(mArchive && "Please call load() before requesting any materials.");
|
|
assert_invariant(!mMaterials.empty() && "Archive must have at least one material.");
|
|
if (mMaterials[0] == nullptr) {
|
|
mMaterials[0] = Material::Builder()
|
|
.package(mArchive->specs[0].package, mArchive->specs[0].packageByteCount)
|
|
.build(mEngine);
|
|
}
|
|
return mMaterials[0];
|
|
}
|
|
|
|
void ArchiveCache::destroyMaterials() {
|
|
for (auto mat : mMaterials) mEngine.destroy(mat);
|
|
mMaterials.clear();
|
|
}
|
|
|
|
FeatureMap ArchiveCache::getFeatureMap(Material* material) const {
|
|
FeatureMap features;
|
|
for (size_t specIndex = 0; specIndex < mMaterials.size(); ++specIndex) {
|
|
if (material == mMaterials[specIndex]) {
|
|
const ArchiveSpec& spec = mArchive->specs[specIndex];
|
|
for (uint64_t j = 0; j < spec.flagsCount; ++j) {
|
|
const ArchiveFlag& flag = spec.flags[j];
|
|
features[flag.name] = flag.value;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return features;
|
|
}
|
|
|
|
ArchiveCache::~ArchiveCache() {
|
|
assert_invariant(mMaterials.size() == 0 &&
|
|
"Please call destroyMaterials explicitly to ensure correct destruction order");
|
|
utils::aligned_free(mArchive);
|
|
}
|
|
|
|
} // namespace filament::gltfio
|