<commit_before>/*
 * Molch, an implementation of the axolotl ratchet based on libsodium
 *
 * ISC License
 *
 * Copyright (C) 2015-2016 1984not Security GmbH
 * Author: Max Bruckner (FSMaxB)
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sodium.h>
#include <exception>
#include <memory>
#include <sstream>

#include "master-keys.hpp"
#include "spiced-random.hpp"
#include "sodium-wrappers.hpp"
#include "gsl.hpp"

namespace Molch {
	MasterKeys& MasterKeys::move(MasterKeys&& master_keys) noexcept {
		//move the private keys
		this->private_keys = std::move(master_keys.private_keys);
		this->private_identity_key = master_keys.private_identity_key;
		this->private_signing_key = master_keys.private_signing_key;

		this->public_identity_key = master_keys.public_identity_key;
		this->public_signing_key = master_keys.public_signing_key;

		return *this;
	}

	MasterKeys::MasterKeys(MasterKeys&& master_keys) noexcept {
		this->move(std::move(master_keys));
	}

	MasterKeys& MasterKeys::operator=(MasterKeys&& master_keys) noexcept {
		this->move(std::move(master_keys));
		return *this;
	}

	MasterKeys::MasterKeys([[maybe_unused]] uninitialized_t uninitialized) noexcept {}

	result<MasterKeys> MasterKeys::create(const std::optional<span<const std::byte>> low_entropy_seed) {
		MasterKeys keys(uninitialized_t::uninitialized);
		keys.init();
		if (low_entropy_seed.has_value()) {
			OUTCOME_TRY(keys.generate(low_entropy_seed.value()));
		} else {
			OUTCOME_TRY(keys.generate());
		}

		return keys;
	}

	result<MasterKeys> MasterKeys::import(
			const ProtobufCKey& public_signing_key,
			const ProtobufCKey& private_signing_key,
			const ProtobufCKey& public_identity_key,
			const ProtobufCKey& private_identity_key) {
		MasterKeys keys(uninitialized_t::uninitialized);
		keys.init();

		if ((keys.private_signing_key == nullptr) || (keys.private_identity_key == nullptr)) {
			return Error(status_type::INCORRECT_DATA, "One of the private key pointers is nullptr.");
		}

		{
			ReadWriteUnlocker unlocker(keys);

			//copy the keys
			keys.public_signing_key = PublicSigningKey{public_signing_key};
			*keys.private_signing_key = PrivateSigningKey{private_signing_key};
			keys.public_identity_key = PublicKey{public_identity_key};
			*keys.private_identity_key = PrivateKey{private_identity_key};
		}

		return keys;
	}

	void MasterKeys::init() {
		//allocate the private key storage
		this->private_keys = std::unique_ptr<PrivateMasterKeyStorage,SodiumDeleter<PrivateMasterKeyStorage>>(sodium_malloc<PrivateMasterKeyStorage>(1));

		//initialize the Buffers
		//private, initialize with pointers to private key storage
		this->private_identity_key = &this->private_keys->identity_key;
		this->private_signing_key = &this->private_keys->signing_key;

		//lock the private key storage
		this->lock();
	}

	result<void> MasterKeys::generate() noexcept {
		ReadWriteUnlocker unlocker{*this};

		//generate the signing keypair
		OUTCOME_TRY(crypto_sign_keypair(
				this->public_signing_key,
				*this->private_signing_key));
		this->public_signing_key.empty = false;
		this->private_signing_key->empty = false;

		//generate the identity keypair
		OUTCOME_TRY(crypto_box_keypair(this->public_identity_key, *this->private_identity_key));
		this->public_identity_key.empty = false;
		this->private_identity_key->empty = false;

		return outcome::success();
	}

	result<void> MasterKeys::generate(const span<const std::byte> low_entropy_seed) noexcept {
		FulfillOrFail(!low_entropy_seed.empty());

		ReadWriteUnlocker unlocker{*this};

		SodiumBuffer high_entropy_seed{
				crypto_sign_SEEDBYTES + crypto_box_SEEDBYTES,
				crypto_sign_SEEDBYTES + crypto_box_SEEDBYTES};
		spiced_random(high_entropy_seed, low_entropy_seed);

		//generate the signing keypair
		OUTCOME_TRY(crypto_sign_seed_keypair(
				this->public_signing_key,
				*this->private_signing_key,
				span<std::byte>(high_entropy_seed).subspan(0, crypto_sign_SEEDBYTES)));
		this->public_signing_key.empty = false;
		this->private_signing_key->empty = false;

		//generate the identity keypair
		OUTCOME_TRY(crypto_box_seed_keypair(
				this->public_identity_key,
				*this->private_identity_key,
				span<const std::byte>{high_entropy_seed}.subspan(crypto_sign_SEEDBYTES)));
		this->public_identity_key.empty = false;
		this->private_identity_key->empty = false;

		return outcome::success();
	}

	const PublicSigningKey& MasterKeys::getSigningKey() const noexcept {
		return this->public_signing_key;
	}

	result<const PrivateSigningKey*> MasterKeys::getPrivateSigningKey() const noexcept {
		if (this->private_signing_key == nullptr) {
			return Error(status_type::INCORRECT_DATA, "The private signing key pointer doesn't point to anything.");
		}

		return this->private_signing_key;
	}

	const PublicKey& MasterKeys::getIdentityKey() const noexcept {
		return this->public_identity_key;
	}

	result<const PrivateKey*> MasterKeys::getPrivateIdentityKey() const noexcept {
		if (this->private_identity_key == nullptr) {
			return Error(status_type::INCORRECT_DATA, "The private identity key pointer doesn't point to anything.");
		}

		return this->private_identity_key;
	}

	/*
	 * Sign a piece of data. Returns the data and signature in one output buffer.
	 */
	result<Buffer> MasterKeys::sign(const span<const std::byte> data) const {
		Unlocker unlocker{*this};
		const auto buffer_size{data.size() + SIGNATURE_SIZE};
		Buffer signed_data(buffer_size, buffer_size);
		OUTCOME_TRY(crypto_sign(
				signed_data,
				data,
				*this->private_signing_key));

		return std::move(signed_data);
	}

	result<MasterKeys::ExportedMasterKeys> MasterKeys::exportProtobuf(Arena& arena) const {
		Unlocker unlocker{*this};

		ExportedMasterKeys exported;

		OUTCOME_TRY(public_signing_key, this->public_signing_key.exportProtobuf(arena));
		exported.public_signing_key = public_signing_key;
		OUTCOME_TRY(private_signing_key, this->private_signing_key->exportProtobuf(arena));
		exported.private_signing_key = private_signing_key;
		OUTCOME_TRY(public_identity_key, this->public_identity_key.exportProtobuf(arena));
		exported.public_identity_key = public_identity_key;
		OUTCOME_TRY(private_identity_key, this->private_identity_key->exportProtobuf(arena));
		exported.private_identity_key = private_identity_key;

		return exported;
	}

	void MasterKeys::lock() const noexcept {
		sodium_mprotect_noaccess(this->private_keys.get());
	}

	void MasterKeys::unlock() const noexcept {
		sodium_mprotect_readonly(this->private_keys.get());
	}

	void MasterKeys::unlock_readwrite() const noexcept {
		sodium_mprotect_readwrite(this->private_keys.get());
	}

	std::ostream& MasterKeys::print(std::ostream& stream) const {
		Unlocker unlocker{*this};

		stream << "Public Signing Key:\n";
		this->public_signing_key.printHex(stream) << '\n';
		stream << "Private Signing Key:\n";
		this->private_signing_key->printHex(stream) << '\n';
		stream << "Public Identity Key:\n";
		this->public_identity_key.printHex(stream) << '\n';
		stream << "Private Identity Key:\n";
		this->private_identity_key->printHex(stream) << '\n';

		return stream;
	}

	MasterKeys::Unlocker::Unlocker(const MasterKeys& keys) noexcept : keys{keys} {
		this->keys.unlock();
	}

	MasterKeys::Unlocker::~Unlocker() noexcept {
		this->keys.lock();
	}

	MasterKeys::ReadWriteUnlocker::ReadWriteUnlocker(const MasterKeys& keys) noexcept : keys{keys} {
		this->keys.unlock_readwrite();
	}

	MasterKeys::ReadWriteUnlocker::~ReadWriteUnlocker() noexcept {
		this->keys.lock();
	}
}
<commit_msg>Fix MasterKeys::lock<commit_after>/*
 * Molch, an implementation of the axolotl ratchet based on libsodium
 *
 * ISC License
 *
 * Copyright (C) 2015-2016 1984not Security GmbH
 * Author: Max Bruckner (FSMaxB)
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sodium.h>
#include <exception>
#include <memory>
#include <sstream>

#include "master-keys.hpp"
#include "spiced-random.hpp"
#include "sodium-wrappers.hpp"
#include "gsl.hpp"

namespace Molch {
	MasterKeys& MasterKeys::move(MasterKeys&& master_keys) noexcept {
		//move the private keys
		this->private_keys = std::move(master_keys.private_keys);
		this->private_identity_key = master_keys.private_identity_key;
		this->private_signing_key = master_keys.private_signing_key;

		this->public_identity_key = master_keys.public_identity_key;
		this->public_signing_key = master_keys.public_signing_key;

		return *this;
	}

	MasterKeys::MasterKeys(MasterKeys&& master_keys) noexcept {
		this->move(std::move(master_keys));
	}

	MasterKeys& MasterKeys::operator=(MasterKeys&& master_keys) noexcept {
		this->move(std::move(master_keys));
		return *this;
	}

	MasterKeys::MasterKeys([[maybe_unused]] uninitialized_t uninitialized) noexcept {}

	result<MasterKeys> MasterKeys::create(const std::optional<span<const std::byte>> low_entropy_seed) {
		MasterKeys keys(uninitialized_t::uninitialized);
		keys.init();
		if (low_entropy_seed.has_value()) {
			OUTCOME_TRY(keys.generate(low_entropy_seed.value()));
		} else {
			OUTCOME_TRY(keys.generate());
		}

		return keys;
	}

	result<MasterKeys> MasterKeys::import(
			const ProtobufCKey& public_signing_key,
			const ProtobufCKey& private_signing_key,
			const ProtobufCKey& public_identity_key,
			const ProtobufCKey& private_identity_key) {
		MasterKeys keys(uninitialized_t::uninitialized);
		keys.init();

		if ((keys.private_signing_key == nullptr) || (keys.private_identity_key == nullptr)) {
			return Error(status_type::INCORRECT_DATA, "One of the private key pointers is nullptr.");
		}

		{
			ReadWriteUnlocker unlocker(keys);

			//copy the keys
			keys.public_signing_key = PublicSigningKey{public_signing_key};
			*keys.private_signing_key = PrivateSigningKey{private_signing_key};
			keys.public_identity_key = PublicKey{public_identity_key};
			*keys.private_identity_key = PrivateKey{private_identity_key};
		}

		return keys;
	}

	void MasterKeys::init() {
		//allocate the private key storage
		this->private_keys = std::unique_ptr<PrivateMasterKeyStorage,SodiumDeleter<PrivateMasterKeyStorage>>(sodium_malloc<PrivateMasterKeyStorage>(1));

		//initialize the Buffers
		//private, initialize with pointers to private key storage
		this->private_identity_key = &this->private_keys->identity_key;
		this->private_signing_key = &this->private_keys->signing_key;

		//lock the private key storage
		this->lock();
	}

	result<void> MasterKeys::generate() noexcept {
		ReadWriteUnlocker unlocker{*this};

		//generate the signing keypair
		OUTCOME_TRY(crypto_sign_keypair(
				this->public_signing_key,
				*this->private_signing_key));
		this->public_signing_key.empty = false;
		this->private_signing_key->empty = false;

		//generate the identity keypair
		OUTCOME_TRY(crypto_box_keypair(this->public_identity_key, *this->private_identity_key));
		this->public_identity_key.empty = false;
		this->private_identity_key->empty = false;

		return outcome::success();
	}

	result<void> MasterKeys::generate(const span<const std::byte> low_entropy_seed) noexcept {
		FulfillOrFail(!low_entropy_seed.empty());

		ReadWriteUnlocker unlocker{*this};

		SodiumBuffer high_entropy_seed{
				crypto_sign_SEEDBYTES + crypto_box_SEEDBYTES,
				crypto_sign_SEEDBYTES + crypto_box_SEEDBYTES};
		spiced_random(high_entropy_seed, low_entropy_seed);

		//generate the signing keypair
		OUTCOME_TRY(crypto_sign_seed_keypair(
				this->public_signing_key,
				*this->private_signing_key,
				span<std::byte>(high_entropy_seed).subspan(0, crypto_sign_SEEDBYTES)));
		this->public_signing_key.empty = false;
		this->private_signing_key->empty = false;

		//generate the identity keypair
		OUTCOME_TRY(crypto_box_seed_keypair(
				this->public_identity_key,
				*this->private_identity_key,
				span<const std::byte>{high_entropy_seed}.subspan(crypto_sign_SEEDBYTES)));
		this->public_identity_key.empty = false;
		this->private_identity_key->empty = false;

		return outcome::success();
	}

	const PublicSigningKey& MasterKeys::getSigningKey() const noexcept {
		return this->public_signing_key;
	}

	result<const PrivateSigningKey*> MasterKeys::getPrivateSigningKey() const noexcept {
		if (this->private_signing_key == nullptr) {
			return Error(status_type::INCORRECT_DATA, "The private signing key pointer doesn't point to anything.");
		}

		return this->private_signing_key;
	}

	const PublicKey& MasterKeys::getIdentityKey() const noexcept {
		return this->public_identity_key;
	}

	result<const PrivateKey*> MasterKeys::getPrivateIdentityKey() const noexcept {
		if (this->private_identity_key == nullptr) {
			return Error(status_type::INCORRECT_DATA, "The private identity key pointer doesn't point to anything.");
		}

		return this->private_identity_key;
	}

	/*
	 * Sign a piece of data. Returns the data and signature in one output buffer.
	 */
	result<Buffer> MasterKeys::sign(const span<const std::byte> data) const {
		Unlocker unlocker{*this};
		const auto buffer_size{data.size() + SIGNATURE_SIZE};
		Buffer signed_data(buffer_size, buffer_size);
		OUTCOME_TRY(crypto_sign(
				signed_data,
				data,
				*this->private_signing_key));

		return std::move(signed_data);
	}

	result<MasterKeys::ExportedMasterKeys> MasterKeys::exportProtobuf(Arena& arena) const {
		Unlocker unlocker{*this};

		ExportedMasterKeys exported;

		OUTCOME_TRY(public_signing_key, this->public_signing_key.exportProtobuf(arena));
		exported.public_signing_key = public_signing_key;
		OUTCOME_TRY(private_signing_key, this->private_signing_key->exportProtobuf(arena));
		exported.private_signing_key = private_signing_key;
		OUTCOME_TRY(public_identity_key, this->public_identity_key.exportProtobuf(arena));
		exported.public_identity_key = public_identity_key;
		OUTCOME_TRY(private_identity_key, this->private_identity_key->exportProtobuf(arena));
		exported.private_identity_key = private_identity_key;

		return exported;
	}

	void MasterKeys::lock() const noexcept {
		if (this->private_keys) {
			sodium_mprotect_noaccess(this->private_keys.get());
		}
	}

	void MasterKeys::unlock() const noexcept {
		sodium_mprotect_readonly(this->private_keys.get());
	}

	void MasterKeys::unlock_readwrite() const noexcept {
		sodium_mprotect_readwrite(this->private_keys.get());
	}

	std::ostream& MasterKeys::print(std::ostream& stream) const {
		Unlocker unlocker{*this};

		stream << "Public Signing Key:\n";
		this->public_signing_key.printHex(stream) << '\n';
		stream << "Private Signing Key:\n";
		this->private_signing_key->printHex(stream) << '\n';
		stream << "Public Identity Key:\n";
		this->public_identity_key.printHex(stream) << '\n';
		stream << "Private Identity Key:\n";
		this->private_identity_key->printHex(stream) << '\n';

		return stream;
	}

	MasterKeys::Unlocker::Unlocker(const MasterKeys& keys) noexcept : keys{keys} {
		this->keys.unlock();
	}

	MasterKeys::Unlocker::~Unlocker() noexcept {
		this->keys.lock();
	}

	MasterKeys::ReadWriteUnlocker::ReadWriteUnlocker(const MasterKeys& keys) noexcept : keys{keys} {
		this->keys.unlock_readwrite();
	}

	MasterKeys::ReadWriteUnlocker::~ReadWriteUnlocker() noexcept {
		this->keys.lock();
	}
}
<|endoftext|>


<commit_before>/*
 * Copyright 2018 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "src/core/SkStrikeCache.h"

#include <cctype>

#include "include/core/SkGraphics.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkTraceMemoryDump.h"
#include "include/core/SkTypeface.h"
#include "include/private/SkMutex.h"
#include "include/private/SkTemplates.h"
#include "src/core/SkGlyphRunPainter.h"
#include "src/core/SkScalerCache.h"

bool gSkUseThreadLocalStrikeCaches_IAcknowledgeThisIsIncrediblyExperimental = false;

SkStrikeCache* SkStrikeCache::GlobalStrikeCache() {
#if !defined(SK_BUILD_FOR_IOS)
    if (gSkUseThreadLocalStrikeCaches_IAcknowledgeThisIsIncrediblyExperimental) {
        static thread_local auto* cache = new SkStrikeCache;
        return cache;
    }
#endif
    static auto* cache = new SkStrikeCache;
    return cache;
}

SkStrikeCache::ExclusiveStrikePtr::ExclusiveStrikePtr(sk_sp<Strike> strike)
    : fStrike{std::move(strike)} {}

SkStrikeCache::ExclusiveStrikePtr::ExclusiveStrikePtr()
    : fStrike{nullptr} {}

SkStrikeCache::ExclusiveStrikePtr::ExclusiveStrikePtr(ExclusiveStrikePtr&& o)
    : fStrike{std::move(o.fStrike)} {
    o.fStrike = nullptr;
}

SkStrikeCache::ExclusiveStrikePtr&
SkStrikeCache::ExclusiveStrikePtr::operator = (ExclusiveStrikePtr&& that) {
    fStrike = std::move(that.fStrike);
    return *this;
}

SkStrike* SkStrikeCache::ExclusiveStrikePtr::get() const {
    return fStrike.get();
}

SkStrike* SkStrikeCache::ExclusiveStrikePtr::operator -> () const {
    return this->get();
}

SkStrike& SkStrikeCache::ExclusiveStrikePtr::operator *  () const {
    return *this->get();
}

SkStrikeCache::ExclusiveStrikePtr::operator bool () const {
    return fStrike != nullptr;
}

bool operator == (const SkStrikeCache::ExclusiveStrikePtr& lhs,
                  const SkStrikeCache::ExclusiveStrikePtr& rhs) {
    return lhs.fStrike == rhs.fStrike;
}

bool operator == (const SkStrikeCache::ExclusiveStrikePtr& lhs, decltype(nullptr)) {
    return lhs.fStrike == nullptr;
}

bool operator == (decltype(nullptr), const SkStrikeCache::ExclusiveStrikePtr& rhs) {
    return nullptr == rhs.fStrike;
}

SkStrikeCache::~SkStrikeCache() {
    Strike* strike = fHead;
    while (strike) {
        Strike* next = strike->fNext;
        strike->unref();
        strike = next;
    }
}

SkExclusiveStrikePtr SkStrikeCache::findOrCreateStrikeExclusive(
        const SkDescriptor& desc, const SkScalerContextEffects& effects, const SkTypeface& typeface)
{
    return SkExclusiveStrikePtr(this->findOrCreateStrike(desc, effects, typeface));
}

auto SkStrikeCache::findOrCreateStrike(const SkDescriptor& desc,
                                       const SkScalerContextEffects& effects,
                                       const SkTypeface& typeface) -> sk_sp<Strike> {
    SkAutoSpinlock ac(fLock);
    sk_sp<Strike> strike = this->internalFindStrikeOrNull(desc);
    if (strike == nullptr) {
        auto scaler = typeface.createScalerContext(effects, &desc);
        strike = this->internalCreateStrike(desc, std::move(scaler));
    }
    return strike;
}

SkScopedStrikeForGPU SkStrikeCache::findOrCreateScopedStrike(const SkDescriptor& desc,
                                                             const SkScalerContextEffects& effects,
                                                             const SkTypeface& typeface) {
    return SkScopedStrikeForGPU{this->findOrCreateStrike(desc, effects, typeface).release()};
}

void SkStrikeCache::PurgeAll() {
    GlobalStrikeCache()->purgeAll();
}

void SkStrikeCache::Dump() {
    SkDebugf("GlyphCache [     used    budget ]\n");
    SkDebugf("    bytes  [ %8zu  %8zu ]\n",
             SkGraphics::GetFontCacheUsed(), SkGraphics::GetFontCacheLimit());
    SkDebugf("    count  [ %8zu  %8zu ]\n",
             SkGraphics::GetFontCacheCountUsed(), SkGraphics::GetFontCacheCountLimit());

    int counter = 0;

    auto visitor = [&counter](const Strike& strike) {
        const SkScalerContextRec& rec = strike.fScalerCache.getScalerContext()->getRec();

        SkDebugf("index %d\n", counter);
        SkDebugf("%s", rec.dump().c_str());
        counter += 1;
    };

    GlobalStrikeCache()->forEachStrike(visitor);
}

namespace {
    const char gGlyphCacheDumpName[] = "skia/sk_glyph_cache";
}  // namespace

void SkStrikeCache::DumpMemoryStatistics(SkTraceMemoryDump* dump) {
    dump->dumpNumericValue(gGlyphCacheDumpName, "size", "bytes", SkGraphics::GetFontCacheUsed());
    dump->dumpNumericValue(gGlyphCacheDumpName, "budget_size", "bytes",
                           SkGraphics::GetFontCacheLimit());
    dump->dumpNumericValue(gGlyphCacheDumpName, "glyph_count", "objects",
                           SkGraphics::GetFontCacheCountUsed());
    dump->dumpNumericValue(gGlyphCacheDumpName, "budget_glyph_count", "objects",
                           SkGraphics::GetFontCacheCountLimit());

    if (dump->getRequestedDetails() == SkTraceMemoryDump::kLight_LevelOfDetail) {
        dump->setMemoryBacking(gGlyphCacheDumpName, "malloc", nullptr);
        return;
    }

    auto visitor = [&dump](const Strike& strike) {
        const SkTypeface* face = strike.fScalerCache.getScalerContext()->getTypeface();
        const SkScalerContextRec& rec = strike.fScalerCache.getScalerContext()->getRec();

        SkString fontName;
        face->getFamilyName(&fontName);
        // Replace all special characters with '_'.
        for (size_t index = 0; index < fontName.size(); ++index) {
            if (!std::isalnum(fontName[index])) {
                fontName[index] = '_';
            }
        }

        SkString dumpName = SkStringPrintf(
                "%s/%s_%d/%p", gGlyphCacheDumpName, fontName.c_str(), rec.fFontID, &strike);

        dump->dumpNumericValue(dumpName.c_str(),
                               "size", "bytes", strike.fMemoryUsed);
        dump->dumpNumericValue(dumpName.c_str(),
                               "glyph_count", "objects",
                               strike.fScalerCache.countCachedGlyphs());
        dump->setMemoryBacking(dumpName.c_str(), "malloc", nullptr);
    };

    GlobalStrikeCache()->forEachStrike(visitor);
}

SkExclusiveStrikePtr SkStrikeCache::findStrikeExclusive(const SkDescriptor& desc) {
    SkAutoSpinlock ac(fLock);
    return SkExclusiveStrikePtr(this->internalFindStrikeOrNull(desc));
}

auto SkStrikeCache::internalFindStrikeOrNull(const SkDescriptor& desc) -> sk_sp<Strike> {
    for (Strike* strike = fHead; strike != nullptr; strike = strike->fNext) {
        if (strike->fScalerCache.getDescriptor() == desc) {
            if (fHead != strike) {
                // Make most recently used
                strike->fPrev->fNext = strike->fNext;
                if (strike->fNext != nullptr) {
                    strike->fNext->fPrev = strike->fPrev;
                } else {
                    fTail = strike->fPrev;
                }
                fHead->fPrev = strike;
                strike->fNext = fHead;
                strike->fPrev = nullptr;
                fHead = strike;
            }

            return sk_ref_sp(strike);
        }
    }

    return nullptr;
}

SkExclusiveStrikePtr SkStrikeCache::createStrikeExclusive(
        const SkDescriptor& desc,
        std::unique_ptr<SkScalerContext> scaler,
        SkFontMetrics* maybeMetrics,
        std::unique_ptr<SkStrikePinner> pinner)
{
    SkAutoSpinlock ac(fLock);
    return SkExclusiveStrikePtr(
            this->internalCreateStrike(desc, std::move(scaler), maybeMetrics, std::move(pinner)));
}

auto SkStrikeCache::internalCreateStrike(
        const SkDescriptor& desc,
        std::unique_ptr<SkScalerContext> scaler,
        SkFontMetrics* maybeMetrics,
        std::unique_ptr<SkStrikePinner> pinner) -> sk_sp<Strike> {
    auto strike =
            sk_make_sp<Strike>(this, desc, std::move(scaler), maybeMetrics, std::move(pinner));
    this->internalAttachToHead(strike);
    return strike;
}

void SkStrikeCache::purgeAll() {
    SkAutoSpinlock ac(fLock);
    this->internalPurge(fTotalMemoryUsed);
}

size_t SkStrikeCache::getTotalMemoryUsed() const {
    SkAutoSpinlock ac(fLock);
    return fTotalMemoryUsed;
}

int SkStrikeCache::getCacheCountUsed() const {
    SkAutoSpinlock ac(fLock);
    return fCacheCount;
}

int SkStrikeCache::getCacheCountLimit() const {
    SkAutoSpinlock ac(fLock);
    return fCacheCountLimit;
}

size_t SkStrikeCache::setCacheSizeLimit(size_t newLimit) {
    static const size_t minLimit = 256 * 1024;
    if (newLimit < minLimit) {
        newLimit = minLimit;
    }

    SkAutoSpinlock ac(fLock);

    size_t prevLimit = fCacheSizeLimit;
    fCacheSizeLimit = newLimit;
    this->internalPurge();
    return prevLimit;
}

size_t  SkStrikeCache::getCacheSizeLimit() const {
    SkAutoSpinlock ac(fLock);
    return fCacheSizeLimit;
}

int SkStrikeCache::setCacheCountLimit(int newCount) {
    if (newCount < 0) {
        newCount = 0;
    }

    SkAutoSpinlock ac(fLock);

    int prevCount = fCacheCountLimit;
    fCacheCountLimit = newCount;
    this->internalPurge();
    return prevCount;
}

int SkStrikeCache::getCachePointSizeLimit() const {
    SkAutoSpinlock ac(fLock);
    return fPointSizeLimit;
}

int SkStrikeCache::setCachePointSizeLimit(int newLimit) {
    if (newLimit < 0) {
        newLimit = 0;
    }

    SkAutoSpinlock ac(fLock);

    int prevLimit = fPointSizeLimit;
    fPointSizeLimit = newLimit;
    return prevLimit;
}

void SkStrikeCache::forEachStrike(std::function<void(const Strike&)> visitor) const {
    SkAutoSpinlock ac(fLock);

    this->validate();

    for (Strike* strike = fHead; strike != nullptr; strike = strike->fNext) {
        visitor(*strike);
    }
}

size_t SkStrikeCache::internalPurge(size_t minBytesNeeded) {
    this->validate();

    size_t bytesNeeded = 0;
    if (fTotalMemoryUsed > fCacheSizeLimit) {
        bytesNeeded = fTotalMemoryUsed - fCacheSizeLimit;
    }
    bytesNeeded = std::max(bytesNeeded, minBytesNeeded);
    if (bytesNeeded) {
        // no small purges!
        bytesNeeded = std::max(bytesNeeded, fTotalMemoryUsed >> 2);
    }

    int countNeeded = 0;
    if (fCacheCount > fCacheCountLimit) {
        countNeeded = fCacheCount - fCacheCountLimit;
        // no small purges!
        countNeeded = std::max(countNeeded, fCacheCount >> 2);
    }

    // early exit
    if (!countNeeded && !bytesNeeded) {
        return 0;
    }

    size_t  bytesFreed = 0;
    int     countFreed = 0;

    // Start at the tail and proceed backwards deleting; the list is in LRU
    // order, with unimportant entries at the tail.
    Strike* strike = fTail;
    while (strike != nullptr && (bytesFreed < bytesNeeded || countFreed < countNeeded)) {
        Strike* prev = strike->fPrev;

        // Only delete if the strike is not pinned.
        if (strike->fPinner == nullptr || strike->fPinner->canDelete()) {
            bytesFreed += strike->fMemoryUsed;
            countFreed += 1;
            this->internalRemoveStrike(strike);
        }
        strike = prev;
    }

    this->validate();

#ifdef SPEW_PURGE_STATUS
    if (countFreed) {
        SkDebugf("purging %dK from font cache [%d entries]\n",
                 (int)(bytesFreed >> 10), countFreed);
    }
#endif

    return bytesFreed;
}

void SkStrikeCache::internalAttachToHead(sk_sp<Strike> strike) {
    SkASSERT(nullptr == strike->fPrev && nullptr == strike->fNext);

    fCacheCount += 1;
    fTotalMemoryUsed += strike->fMemoryUsed;

    if (fHead) {
        fHead->fPrev = strike.get();
        strike->fNext = fHead;
    }

    if (fTail == nullptr) {
        fTail = strike.get();
    }

    fHead = strike.release(); // Transfer ownership of strike to the cache list.
}

void SkStrikeCache::internalRemoveStrike(Strike* strike) {
    SkASSERT(fCacheCount > 0);
    fCacheCount -= 1;
    fTotalMemoryUsed -= strike->fMemoryUsed;

    if (strike->fPrev) {
        strike->fPrev->fNext = strike->fNext;
    } else {
        fHead = strike->fNext;
    }
    if (strike->fNext) {
        strike->fNext->fPrev = strike->fPrev;
    } else {
        fTail = strike->fPrev;
    }
    strike->fPrev = strike->fNext = nullptr;
    strike->fRemoved = true;
    strike->unref();
}

void SkStrikeCache::validate() const {
#ifdef SK_DEBUG
    size_t computedBytes = 0;
    int computedCount = 0;

    const Strike* strike = fHead;
    while (strike != nullptr) {
        computedBytes += strike->fMemoryUsed;
        computedCount += 1;
        strike = strike->fNext;
    }

    // Can't use SkASSERTF because it looses thread annotations.
    if (fCacheCount != computedCount) {
        SkDebugf("fCacheCount: %d, computedCount: %d", fCacheCount, computedCount);
        SK_ABORT("fCacheCount != computedCount");
    }
    if (fTotalMemoryUsed != computedBytes) {
        SkDebugf("fTotalMemoryUsed: %d, computedBytes: %d", fTotalMemoryUsed, computedBytes);
        SK_ABORT("fTotalMemoryUsed == computedBytes");
    }
#endif
}

void SkStrikeCache::Strike::updateDelta(size_t increase) {
    if (increase != 0) {
        SkAutoSpinlock lock{fStrikeCache->fLock};
        fMemoryUsed += increase;
        if (!fRemoved) {
            fStrikeCache->fTotalMemoryUsed += increase;
        }
    }
}
<commit_msg>Roll Skia from cf8c53436c77 to b43313646796 (1 revision) am: 58ebf05ec5 am: 614cb53c20 am: b4ec41f71e<commit_after>/*
 * Copyright 2018 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "src/core/SkStrikeCache.h"

#include <cctype>

#include "include/core/SkGraphics.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkTraceMemoryDump.h"
#include "include/core/SkTypeface.h"
#include "include/private/SkMutex.h"
#include "include/private/SkTemplates.h"
#include "src/core/SkGlyphRunPainter.h"
#include "src/core/SkScalerCache.h"

bool gSkUseThreadLocalStrikeCaches_IAcknowledgeThisIsIncrediblyExperimental = false;

SkStrikeCache* SkStrikeCache::GlobalStrikeCache() {
#if !defined(SK_BUILD_FOR_IOS)
    if (gSkUseThreadLocalStrikeCaches_IAcknowledgeThisIsIncrediblyExperimental) {
        static thread_local auto* cache = new SkStrikeCache;
        return cache;
    }
#endif
    static auto* cache = new SkStrikeCache;
    return cache;
}

SkStrikeCache::ExclusiveStrikePtr::ExclusiveStrikePtr(sk_sp<Strike> strike)
    : fStrike{std::move(strike)} {}

SkStrikeCache::ExclusiveStrikePtr::ExclusiveStrikePtr()
    : fStrike{nullptr} {}

SkStrikeCache::ExclusiveStrikePtr::ExclusiveStrikePtr(ExclusiveStrikePtr&& o)
    : fStrike{std::move(o.fStrike)} {
    o.fStrike = nullptr;
}

SkStrikeCache::ExclusiveStrikePtr&
SkStrikeCache::ExclusiveStrikePtr::operator = (ExclusiveStrikePtr&& that) {
    fStrike = std::move(that.fStrike);
    return *this;
}

SkStrike* SkStrikeCache::ExclusiveStrikePtr::get() const {
    return fStrike.get();
}

SkStrike* SkStrikeCache::ExclusiveStrikePtr::operator -> () const {
    return this->get();
}

SkStrike& SkStrikeCache::ExclusiveStrikePtr::operator *  () const {
    return *this->get();
}

SkStrikeCache::ExclusiveStrikePtr::operator bool () const {
    return fStrike != nullptr;
}

bool operator == (const SkStrikeCache::ExclusiveStrikePtr& lhs,
                  const SkStrikeCache::ExclusiveStrikePtr& rhs) {
    return lhs.fStrike == rhs.fStrike;
}

bool operator == (const SkStrikeCache::ExclusiveStrikePtr& lhs, decltype(nullptr)) {
    return lhs.fStrike == nullptr;
}

bool operator == (decltype(nullptr), const SkStrikeCache::ExclusiveStrikePtr& rhs) {
    return nullptr == rhs.fStrike;
}

SkStrikeCache::~SkStrikeCache() {
    Strike* strike = fHead;
    while (strike) {
        Strike* next = strike->fNext;
        strike->unref();
        strike = next;
    }
}

SkExclusiveStrikePtr SkStrikeCache::findOrCreateStrikeExclusive(
        const SkDescriptor& desc, const SkScalerContextEffects& effects, const SkTypeface& typeface)
{
    return SkExclusiveStrikePtr(this->findOrCreateStrike(desc, effects, typeface));
}

auto SkStrikeCache::findOrCreateStrike(const SkDescriptor& desc,
                                       const SkScalerContextEffects& effects,
                                       const SkTypeface& typeface) -> sk_sp<Strike> {
    SkAutoSpinlock ac(fLock);
    sk_sp<Strike> strike = this->internalFindStrikeOrNull(desc);
    if (strike == nullptr) {
        auto scaler = typeface.createScalerContext(effects, &desc);
        strike = this->internalCreateStrike(desc, std::move(scaler));
    }
    this->internalPurge();
    return strike;
}

SkScopedStrikeForGPU SkStrikeCache::findOrCreateScopedStrike(const SkDescriptor& desc,
                                                             const SkScalerContextEffects& effects,
                                                             const SkTypeface& typeface) {
    return SkScopedStrikeForGPU{this->findOrCreateStrike(desc, effects, typeface).release()};
}

void SkStrikeCache::PurgeAll() {
    GlobalStrikeCache()->purgeAll();
}

void SkStrikeCache::Dump() {
    SkDebugf("GlyphCache [     used    budget ]\n");
    SkDebugf("    bytes  [ %8zu  %8zu ]\n",
             SkGraphics::GetFontCacheUsed(), SkGraphics::GetFontCacheLimit());
    SkDebugf("    count  [ %8zu  %8zu ]\n",
             SkGraphics::GetFontCacheCountUsed(), SkGraphics::GetFontCacheCountLimit());

    int counter = 0;

    auto visitor = [&counter](const Strike& strike) {
        const SkScalerContextRec& rec = strike.fScalerCache.getScalerContext()->getRec();

        SkDebugf("index %d\n", counter);
        SkDebugf("%s", rec.dump().c_str());
        counter += 1;
    };

    GlobalStrikeCache()->forEachStrike(visitor);
}

namespace {
    const char gGlyphCacheDumpName[] = "skia/sk_glyph_cache";
}  // namespace

void SkStrikeCache::DumpMemoryStatistics(SkTraceMemoryDump* dump) {
    dump->dumpNumericValue(gGlyphCacheDumpName, "size", "bytes", SkGraphics::GetFontCacheUsed());
    dump->dumpNumericValue(gGlyphCacheDumpName, "budget_size", "bytes",
                           SkGraphics::GetFontCacheLimit());
    dump->dumpNumericValue(gGlyphCacheDumpName, "glyph_count", "objects",
                           SkGraphics::GetFontCacheCountUsed());
    dump->dumpNumericValue(gGlyphCacheDumpName, "budget_glyph_count", "objects",
                           SkGraphics::GetFontCacheCountLimit());

    if (dump->getRequestedDetails() == SkTraceMemoryDump::kLight_LevelOfDetail) {
        dump->setMemoryBacking(gGlyphCacheDumpName, "malloc", nullptr);
        return;
    }

    auto visitor = [&dump](const Strike& strike) {
        const SkTypeface* face = strike.fScalerCache.getScalerContext()->getTypeface();
        const SkScalerContextRec& rec = strike.fScalerCache.getScalerContext()->getRec();

        SkString fontName;
        face->getFamilyName(&fontName);
        // Replace all special characters with '_'.
        for (size_t index = 0; index < fontName.size(); ++index) {
            if (!std::isalnum(fontName[index])) {
                fontName[index] = '_';
            }
        }

        SkString dumpName = SkStringPrintf(
                "%s/%s_%d/%p", gGlyphCacheDumpName, fontName.c_str(), rec.fFontID, &strike);

        dump->dumpNumericValue(dumpName.c_str(),
                               "size", "bytes", strike.fMemoryUsed);
        dump->dumpNumericValue(dumpName.c_str(),
                               "glyph_count", "objects",
                               strike.fScalerCache.countCachedGlyphs());
        dump->setMemoryBacking(dumpName.c_str(), "malloc", nullptr);
    };

    GlobalStrikeCache()->forEachStrike(visitor);
}

SkExclusiveStrikePtr SkStrikeCache::findStrikeExclusive(const SkDescriptor& desc) {
    SkAutoSpinlock ac(fLock);
    sk_sp<SkStrike> result = this->internalFindStrikeOrNull(desc);
    this->internalPurge();
    return SkExclusiveStrikePtr(result);
}

auto SkStrikeCache::internalFindStrikeOrNull(const SkDescriptor& desc) -> sk_sp<Strike> {
    for (Strike* strike = fHead; strike != nullptr; strike = strike->fNext) {
        if (strike->fScalerCache.getDescriptor() == desc) {
            if (fHead != strike) {
                // Make most recently used
                strike->fPrev->fNext = strike->fNext;
                if (strike->fNext != nullptr) {
                    strike->fNext->fPrev = strike->fPrev;
                } else {
                    fTail = strike->fPrev;
                }
                fHead->fPrev = strike;
                strike->fNext = fHead;
                strike->fPrev = nullptr;
                fHead = strike;
            }

            return sk_ref_sp(strike);
        }
    }

    return nullptr;
}

SkExclusiveStrikePtr SkStrikeCache::createStrikeExclusive(
        const SkDescriptor& desc,
        std::unique_ptr<SkScalerContext> scaler,
        SkFontMetrics* maybeMetrics,
        std::unique_ptr<SkStrikePinner> pinner)
{
    SkAutoSpinlock ac(fLock);
    return SkExclusiveStrikePtr(
            this->internalCreateStrike(desc, std::move(scaler), maybeMetrics, std::move(pinner)));
}

auto SkStrikeCache::internalCreateStrike(
        const SkDescriptor& desc,
        std::unique_ptr<SkScalerContext> scaler,
        SkFontMetrics* maybeMetrics,
        std::unique_ptr<SkStrikePinner> pinner) -> sk_sp<Strike> {
    auto strike =
            sk_make_sp<Strike>(this, desc, std::move(scaler), maybeMetrics, std::move(pinner));
    this->internalAttachToHead(strike);
    return strike;
}

void SkStrikeCache::purgeAll() {
    SkAutoSpinlock ac(fLock);
    this->internalPurge(fTotalMemoryUsed);
}

size_t SkStrikeCache::getTotalMemoryUsed() const {
    SkAutoSpinlock ac(fLock);
    return fTotalMemoryUsed;
}

int SkStrikeCache::getCacheCountUsed() const {
    SkAutoSpinlock ac(fLock);
    return fCacheCount;
}

int SkStrikeCache::getCacheCountLimit() const {
    SkAutoSpinlock ac(fLock);
    return fCacheCountLimit;
}

size_t SkStrikeCache::setCacheSizeLimit(size_t newLimit) {
    SkAutoSpinlock ac(fLock);

    size_t prevLimit = fCacheSizeLimit;
    fCacheSizeLimit = newLimit;
    this->internalPurge();
    return prevLimit;
}

size_t  SkStrikeCache::getCacheSizeLimit() const {
    SkAutoSpinlock ac(fLock);
    return fCacheSizeLimit;
}

int SkStrikeCache::setCacheCountLimit(int newCount) {
    if (newCount < 0) {
        newCount = 0;
    }

    SkAutoSpinlock ac(fLock);

    int prevCount = fCacheCountLimit;
    fCacheCountLimit = newCount;
    this->internalPurge();
    return prevCount;
}

int SkStrikeCache::getCachePointSizeLimit() const {
    SkAutoSpinlock ac(fLock);
    return fPointSizeLimit;
}

int SkStrikeCache::setCachePointSizeLimit(int newLimit) {
    if (newLimit < 0) {
        newLimit = 0;
    }

    SkAutoSpinlock ac(fLock);

    int prevLimit = fPointSizeLimit;
    fPointSizeLimit = newLimit;
    return prevLimit;
}

void SkStrikeCache::forEachStrike(std::function<void(const Strike&)> visitor) const {
    SkAutoSpinlock ac(fLock);

    this->validate();

    for (Strike* strike = fHead; strike != nullptr; strike = strike->fNext) {
        visitor(*strike);
    }
}

size_t SkStrikeCache::internalPurge(size_t minBytesNeeded) {
    this->validate();

    size_t bytesNeeded = 0;
    if (fTotalMemoryUsed > fCacheSizeLimit) {
        bytesNeeded = fTotalMemoryUsed - fCacheSizeLimit;
    }
    bytesNeeded = std::max(bytesNeeded, minBytesNeeded);
    if (bytesNeeded) {
        // no small purges!
        bytesNeeded = std::max(bytesNeeded, fTotalMemoryUsed >> 2);
    }

    int countNeeded = 0;
    if (fCacheCount > fCacheCountLimit) {
        countNeeded = fCacheCount - fCacheCountLimit;
        // no small purges!
        countNeeded = std::max(countNeeded, fCacheCount >> 2);
    }

    // early exit
    if (!countNeeded && !bytesNeeded) {
        return 0;
    }

    size_t  bytesFreed = 0;
    int     countFreed = 0;

    // Start at the tail and proceed backwards deleting; the list is in LRU
    // order, with unimportant entries at the tail.
    Strike* strike = fTail;
    while (strike != nullptr && (bytesFreed < bytesNeeded || countFreed < countNeeded)) {
        Strike* prev = strike->fPrev;

        // Only delete if the strike is not pinned.
        if (strike->fPinner == nullptr || strike->fPinner->canDelete()) {
            bytesFreed += strike->fMemoryUsed;
            countFreed += 1;
            this->internalRemoveStrike(strike);
        }
        strike = prev;
    }

    this->validate();

#ifdef SPEW_PURGE_STATUS
    if (countFreed) {
        SkDebugf("purging %dK from font cache [%d entries]\n",
                 (int)(bytesFreed >> 10), countFreed);
    }
#endif

    return bytesFreed;
}

void SkStrikeCache::internalAttachToHead(sk_sp<Strike> strike) {
    SkASSERT(nullptr == strike->fPrev && nullptr == strike->fNext);

    fCacheCount += 1;
    fTotalMemoryUsed += strike->fMemoryUsed;

    if (fHead) {
        fHead->fPrev = strike.get();
        strike->fNext = fHead;
    }

    if (fTail == nullptr) {
        fTail = strike.get();
    }

    fHead = strike.release(); // Transfer ownership of strike to the cache list.
}

void SkStrikeCache::internalRemoveStrike(Strike* strike) {
    SkASSERT(fCacheCount > 0);
    fCacheCount -= 1;
    fTotalMemoryUsed -= strike->fMemoryUsed;

    if (strike->fPrev) {
        strike->fPrev->fNext = strike->fNext;
    } else {
        fHead = strike->fNext;
    }
    if (strike->fNext) {
        strike->fNext->fPrev = strike->fPrev;
    } else {
        fTail = strike->fPrev;
    }
    strike->fPrev = strike->fNext = nullptr;
    strike->fRemoved = true;
    strike->unref();
}

void SkStrikeCache::validate() const {
#ifdef SK_DEBUG
    size_t computedBytes = 0;
    int computedCount = 0;

    const Strike* strike = fHead;
    while (strike != nullptr) {
        computedBytes += strike->fMemoryUsed;
        computedCount += 1;
        strike = strike->fNext;
    }

    // Can't use SkASSERTF because it looses thread annotations.
    if (fCacheCount != computedCount) {
        SkDebugf("fCacheCount: %d, computedCount: %d", fCacheCount, computedCount);
        SK_ABORT("fCacheCount != computedCount");
    }
    if (fTotalMemoryUsed != computedBytes) {
        SkDebugf("fTotalMemoryUsed: %d, computedBytes: %d", fTotalMemoryUsed, computedBytes);
        SK_ABORT("fTotalMemoryUsed == computedBytes");
    }
#endif
}

void SkStrikeCache::Strike::updateDelta(size_t increase) {
    if (increase != 0) {
        SkAutoSpinlock lock{fStrikeCache->fLock};
        fMemoryUsed += increase;
        if (!fRemoved) {
            fStrikeCache->fTotalMemoryUsed += increase;
        }
    }
}
<|endoftext|>


<commit_before>/* bzflag
 * Copyright (c) 1993-2013 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

// interface header
#include "ShotManager.h"
#include "TimeKeeper.h"
#include "vectors.h"

namespace Shots
{
	//----------------Manager

	double Manager::DeadShotCacheTime = 10.0;

	Manager::Manager()
	{
		Logics[std::string("")] = new FlightLogic();
		LastGUID = INVALID_SHOT_GUID;
	}

	Manager::~Manager()
	{
		for (FlightLogicMap::iterator itr = Logics.begin(); itr != Logics.end(); itr++)
			delete((itr++)->second);

		Logics.clear();

		for (ShotList::iterator itr = LiveShots.begin(); itr != LiveShots.end(); itr++)
			delete(*itr);

		LiveShots.clear();

		for (ShotList::iterator itr = RecentlyDeadShots.begin(); itr != RecentlyDeadShots.end(); itr++)
			delete(*itr);

		RecentlyDeadShots.clear();
	}

	void Manager::Init()
	{
		Logics[Flags::GuidedMissile->flagAbbv] = new GuidedMissileLogic();
		Logics[Flags::SuperBullet->flagAbbv] = new SuperBulletLogic();
	}

	void Manager::SetFlightLogic(const char* flagCode, FlightLogic* logic)
	{
		std::string code;
		if (flagCode)
			code = flagCode;

		if (Logics.find(code) != Logics.end())
			delete(Logics.find(code)->second);

		Logics[code] = logic;
	}

	uint32_t Manager::AddShot( const FiringInfo &info, PlayerId UNUSED(shooter) )
	{
		FlightLogic* logic = NULL;
		if(Logics.find(info.flagType->flagAbbv) != Logics.end())
			logic = Logics[info.flagType->flagAbbv];

		if (!logic)
			logic = Logics[""];

		Shot *shot = new Shot(NewGUID(),info,*logic);
		LiveShots.push_back(shot);
		return shot->GetGUID();
	}

	void Manager::RemoveShot (uint32_t shotID)
	{
		ShotList::iterator itr = LiveShots.begin();
		while ( itr != LiveShots.end())
		{
			if ((*itr)->GetGUID() == shotID)
			{
				(*itr)->End();
				RecentlyDeadShots.push_back((*itr));
				itr = LiveShots.erase(itr);
				return;
			}
			else
				itr++;
		}
	}

	void Manager::RemovePlayer( PlayerId player )
	{
		ShotList::iterator itr = LiveShots.begin();
		while ( itr != LiveShots.end())
		{
			if ((*itr)->GetPlayerID() == player)
			{
				(*itr)->End();
				itr = LiveShots.erase(itr);
			}
			else
				itr++;
		}
	}

	void Manager::SetShotTarget( uint32_t shotID, PlayerId target )
	{
		Shot* shot = FindByID(shotID);
		if (shot)
			shot->Retarget(target);
	}

	uint32_t Manager::FindShot (PlayerId shooter, uint16_t localShotID)
	{
		for (ShotList::iterator itr = LiveShots.begin(); itr != LiveShots.end(); itr++)
		{
			if ((*itr)->GetPlayerID() == shooter && (*itr)->Info.shot.id == localShotID)
				return (*itr)->GetGUID();
		}
		return 0;
	}

	uint32_t Manager::NewGUID()
	{
		LastGUID++;

		if (LastGUID == INVALID_SHOT_GUID) // handle the rollover
			LastGUID = INVALID_SHOT_GUID + 1;

		return LastGUID;
	}

	Shot* Manager::FindByID (uint32_t shotID)
	{
		for (ShotList::iterator itr = LiveShots.begin(); itr != LiveShots.end(); itr++)
		{
			if ((*itr)->GetGUID() == shotID)
				return *itr;
		}
		return NULL;
	}

	void Manager::Update()
	{
		ShotList::iterator itr = LiveShots.begin();
		while ( itr != LiveShots.end())
		{
			if ((*itr)->Update())
			{
				Shot* shot = *itr;
				itr = LiveShots.erase(itr);
				shot->End();
				RecentlyDeadShots.push_back(shot);
			}
			else
				itr++;
		}

		double now = TimeKeeper::getCurrent().getSeconds();

		itr = RecentlyDeadShots.begin();

		while ( itr != RecentlyDeadShots.end())
		{
			if (now - (*itr)->GetLastUpdateTime() >= Manager::DeadShotCacheTime)
				itr = RecentlyDeadShots.erase(itr);
			else
				itr++;
		}
	}

	ShotList Manager::LiveShotsForPlayer( PlayerId player )
	{
		ShotList list;

		for (ShotList::iterator itr = LiveShots.begin(); itr != LiveShots.end(); itr++)
		{
			if ((*itr)->GetPlayerID() == player)
				list.push_back(*itr);
		}

		return list;
	}

	//----------------Shot

	Shot::Shot(uint32_t guid, const FiringInfo &info, FlightLogic& logic): GUID(guid), Logic(logic), LastUpdateTime(-1.0), Info(info)
	{
		StartTime = TimeKeeper::getCurrent().getSeconds();
		LifeTime = info.lifetime;
		Target = NoPlayer;
	}

	bool Shot::Update()
	{
		if (TimeKeeper::getCurrent().getSeconds() - StartTime >= LifeTime || Logic.Update(*this))
			return true;

		LastUpdateTime = TimeKeeper::getCurrent().getSeconds();
		return false;
	}

	void Shot::End()
	{
		Logic.End(*this);
		LastUpdateTime = TimeKeeper::getCurrent().getSeconds();
	}

	void Shot::Retarget(PlayerId target)
	{
		Logic.Retarget(*this,target);
		Target = target;
	}

	fvec3 FlightLogic::ProjectShotLocation( Shot &shot, double deltaT )
	{
		fvec3 vec;
		vec.x = shot.LastUpdatePosition.x + (shot.Info.shot.vel[0] * (float)deltaT);
		vec.y = shot.LastUpdatePosition.y + (shot.Info.shot.vel[1] * (float)deltaT);
		vec.z = shot.LastUpdatePosition.z + (shot.Info.shot.vel[2] * (float)deltaT);

		return vec;
	}
	//----------------GuidedMissileLogic

	bool GuidedMissileLogic::Update ( Shot& /*shot*/ )
	{
		// compute the distance to target and knock that sucker down
		return false;
	}

	void GuidedMissileLogic::End (  Shot& /*shot*/ )
	{

	}

//----------------SuperBulletLogic
	bool SuperBulletLogic::Update ( Shot& shot )
	{
		shot.LastUpdatePosition = ProjectShotLocation(shot,shot.GetLastUpdateTime() - shot.GetStartTime());
		return false;
	}

}

// Local Variables: ***
// mode: C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<commit_msg>get rid of erroneous double increment (crashed bzfs on exit)<commit_after>/* bzflag
 * Copyright (c) 1993-2013 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

// interface header
#include "ShotManager.h"
#include "TimeKeeper.h"
#include "vectors.h"

namespace Shots
{
	//----------------Manager

	double Manager::DeadShotCacheTime = 10.0;

	Manager::Manager()
	{
		Logics[std::string("")] = new FlightLogic();
		LastGUID = INVALID_SHOT_GUID;
	}

	Manager::~Manager()
	{
		for (FlightLogicMap::iterator itr = Logics.begin(); itr != Logics.end(); itr++)
			delete(itr->second);

		Logics.clear();

		for (ShotList::iterator itr = LiveShots.begin(); itr != LiveShots.end(); itr++)
			delete(*itr);

		LiveShots.clear();

		for (ShotList::iterator itr = RecentlyDeadShots.begin(); itr != RecentlyDeadShots.end(); itr++)
			delete(*itr);

		RecentlyDeadShots.clear();
	}

	void Manager::Init()
	{
		Logics[Flags::GuidedMissile->flagAbbv] = new GuidedMissileLogic();
		Logics[Flags::SuperBullet->flagAbbv] = new SuperBulletLogic();
	}

	void Manager::SetFlightLogic(const char* flagCode, FlightLogic* logic)
	{
		std::string code;
		if (flagCode)
			code = flagCode;

		if (Logics.find(code) != Logics.end())
			delete(Logics.find(code)->second);

		Logics[code] = logic;
	}

	uint32_t Manager::AddShot( const FiringInfo &info, PlayerId UNUSED(shooter) )
	{
		FlightLogic* logic = NULL;
		if(Logics.find(info.flagType->flagAbbv) != Logics.end())
			logic = Logics[info.flagType->flagAbbv];

		if (!logic)
			logic = Logics[""];

		Shot *shot = new Shot(NewGUID(),info,*logic);
		LiveShots.push_back(shot);
		return shot->GetGUID();
	}

	void Manager::RemoveShot (uint32_t shotID)
	{
		ShotList::iterator itr = LiveShots.begin();
		while ( itr != LiveShots.end())
		{
			if ((*itr)->GetGUID() == shotID)
			{
				(*itr)->End();
				RecentlyDeadShots.push_back((*itr));
				itr = LiveShots.erase(itr);
				return;
			}
			else
				itr++;
		}
	}

	void Manager::RemovePlayer( PlayerId player )
	{
		ShotList::iterator itr = LiveShots.begin();
		while ( itr != LiveShots.end())
		{
			if ((*itr)->GetPlayerID() == player)
			{
				(*itr)->End();
				itr = LiveShots.erase(itr);
			}
			else
				itr++;
		}
	}

	void Manager::SetShotTarget( uint32_t shotID, PlayerId target )
	{
		Shot* shot = FindByID(shotID);
		if (shot)
			shot->Retarget(target);
	}

	uint32_t Manager::FindShot (PlayerId shooter, uint16_t localShotID)
	{
		for (ShotList::iterator itr = LiveShots.begin(); itr != LiveShots.end(); itr++)
		{
			if ((*itr)->GetPlayerID() == shooter && (*itr)->Info.shot.id == localShotID)
				return (*itr)->GetGUID();
		}
		return 0;
	}

	uint32_t Manager::NewGUID()
	{
		LastGUID++;

		if (LastGUID == INVALID_SHOT_GUID) // handle the rollover
			LastGUID = INVALID_SHOT_GUID + 1;

		return LastGUID;
	}

	Shot* Manager::FindByID (uint32_t shotID)
	{
		for (ShotList::iterator itr = LiveShots.begin(); itr != LiveShots.end(); itr++)
		{
			if ((*itr)->GetGUID() == shotID)
				return *itr;
		}
		return NULL;
	}

	void Manager::Update()
	{
		ShotList::iterator itr = LiveShots.begin();
		while ( itr != LiveShots.end())
		{
			if ((*itr)->Update())
			{
				Shot* shot = *itr;
				itr = LiveShots.erase(itr);
				shot->End();
				RecentlyDeadShots.push_back(shot);
			}
			else
				itr++;
		}

		double now = TimeKeeper::getCurrent().getSeconds();

		itr = RecentlyDeadShots.begin();

		while ( itr != RecentlyDeadShots.end())
		{
			if (now - (*itr)->GetLastUpdateTime() >= Manager::DeadShotCacheTime)
				itr = RecentlyDeadShots.erase(itr);
			else
				itr++;
		}
	}

	ShotList Manager::LiveShotsForPlayer( PlayerId player )
	{
		ShotList list;

		for (ShotList::iterator itr = LiveShots.begin(); itr != LiveShots.end(); itr++)
		{
			if ((*itr)->GetPlayerID() == player)
				list.push_back(*itr);
		}

		return list;
	}

	//----------------Shot

	Shot::Shot(uint32_t guid, const FiringInfo &info, FlightLogic& logic): GUID(guid), Logic(logic), LastUpdateTime(-1.0), Info(info)
	{
		StartTime = TimeKeeper::getCurrent().getSeconds();
		LifeTime = info.lifetime;
		Target = NoPlayer;
	}

	bool Shot::Update()
	{
		if (TimeKeeper::getCurrent().getSeconds() - StartTime >= LifeTime || Logic.Update(*this))
			return true;

		LastUpdateTime = TimeKeeper::getCurrent().getSeconds();
		return false;
	}

	void Shot::End()
	{
		Logic.End(*this);
		LastUpdateTime = TimeKeeper::getCurrent().getSeconds();
	}

	void Shot::Retarget(PlayerId target)
	{
		Logic.Retarget(*this,target);
		Target = target;
	}

	fvec3 FlightLogic::ProjectShotLocation( Shot &shot, double deltaT )
	{
		fvec3 vec;
		vec.x = shot.LastUpdatePosition.x + (shot.Info.shot.vel[0] * (float)deltaT);
		vec.y = shot.LastUpdatePosition.y + (shot.Info.shot.vel[1] * (float)deltaT);
		vec.z = shot.LastUpdatePosition.z + (shot.Info.shot.vel[2] * (float)deltaT);

		return vec;
	}
	//----------------GuidedMissileLogic

	bool GuidedMissileLogic::Update ( Shot& /*shot*/ )
	{
		// compute the distance to target and knock that sucker down
		return false;
	}

	void GuidedMissileLogic::End (  Shot& /*shot*/ )
	{

	}

//----------------SuperBulletLogic
	bool SuperBulletLogic::Update ( Shot& shot )
	{
		shot.LastUpdatePosition = ProjectShotLocation(shot,shot.GetLastUpdateTime() - shot.GetStartTime());
		return false;
	}

}

// Local Variables: ***
// mode: C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<|endoftext|>


<commit_before>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2005 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * OpenSceneGraph Public License for more details.
*/
#include <osg/GL>
#include <osg/PolygonOffset>
#include <osg/Notify>

using namespace osg;

static float s_FactorMultipler = 1.0f;
static float s_UnitMultipler = 1.0f;
static bool s_MultiplerSet = false;

void PolygonOffset::setFactorMultiplier(float multiplier)
{
    s_MultiplerSet = true;
    s_FactorMultipler = multiplier;
}

float PolygonOffset::getFactorMultiplier()
{
    return s_FactorMultipler;
}

void PolygonOffset::setUnitMultiplier(float multiplier)
{
    s_MultiplerSet = true;
    s_UnitMultipler = multiplier;
}

float PolygonOffset::getUnitMultiplier()
{
    return s_UnitMultipler;
}

bool PolygonOffset::areUnitAndMultipliersSet()
{
    return s_MultiplerSet;
}

void PolygonOffset::setFactorAndUnitMultipliersUsingBestGuessForDriver()
{
    s_MultiplerSet = true;
    // osg::notify(osg::NOTICE)<<"PolygonOffset::setFactorAndUnitMultipliersUsingBestGuessForDriver()"<<std::endl;
}

PolygonOffset::PolygonOffset():
    _factor(0.0f),
    _units(0.0f)
{
}

PolygonOffset::PolygonOffset(float factor, float units):
    _factor(factor),
    _units(units)
{
}

PolygonOffset::~PolygonOffset()
{
}

void PolygonOffset::apply(State&) const
{
    if (!s_MultiplerSet) setFactorAndUnitMultipliersUsingBestGuessForDriver();

    glPolygonOffset(_factor * s_FactorMultipler,
                    _units * s_UnitMultipler);
}
<commit_msg>Implemented Radeon workaround for polygonoffset<commit_after>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2005 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * OpenSceneGraph Public License for more details.
*/
#include <osg/GL>
#include <osg/PolygonOffset>
#include <osg/Notify>

using namespace osg;

static float s_FactorMultipler = 1.0f;
static float s_UnitMultipler = 1.0f;
static bool s_MultiplerSet = false;

void PolygonOffset::setFactorMultiplier(float multiplier)
{
    s_MultiplerSet = true;
    s_FactorMultipler = multiplier;
}

float PolygonOffset::getFactorMultiplier()
{
    return s_FactorMultipler;
}

void PolygonOffset::setUnitMultiplier(float multiplier)
{
    s_MultiplerSet = true;
    s_UnitMultipler = multiplier;
}

float PolygonOffset::getUnitMultiplier()
{
    return s_UnitMultipler;
}

bool PolygonOffset::areUnitAndMultipliersSet()
{
    return s_MultiplerSet;
}

void PolygonOffset::setFactorAndUnitMultipliersUsingBestGuessForDriver()
{
    s_MultiplerSet = true;
    // osg::notify(osg::NOTICE)<<"PolygonOffset::setFactorAndUnitMultipliersUsingBestGuessForDriver()"<<std::endl;

    const GLubyte* renderer = glGetString(GL_RENDERER);
    if (renderer)
    {
        if ((strstr((const char*)renderer,"Radeon")!=0) ||
            (strstr((const char*)renderer,"RADEON")!=0))
        {
            setFactorMultiplier(256.0f);
            setUnitMultiplier(256.0f);
            osg::notify(osg::INFO)<<"PolygonOffset::setFactorAndUnitMultipliersUsingBestGuessForDriver() apply ATI workaround."<<std::endl;
        }
    }
}

PolygonOffset::PolygonOffset():
    _factor(0.0f),
    _units(0.0f)
{
}

PolygonOffset::PolygonOffset(float factor, float units):
    _factor(factor),
    _units(units)
{
}

PolygonOffset::~PolygonOffset()
{
}

void PolygonOffset::apply(State&) const
{
    if (!s_MultiplerSet) setFactorAndUnitMultipliersUsingBestGuessForDriver();

    glPolygonOffset(_factor * s_FactorMultipler,
                    _units * s_UnitMultipler);
}
<|endoftext|>


<commit_before>#include <maya/MDGModifier.h>
#include <maya/MDagModifier.h>
#include <maya/MFnDagNode.h>
#include <maya/MFnPhongShader.h>
#include <maya/MFileObject.h>
#include <maya/MSelectionList.h>
#include <maya/MPlugArray.h>
#include <maya/MStatus.h>
#include <maya/MGlobal.h>

#include "assetManager.h"
#include "common.h"
#include "util.h"

std::vector<AssetManager*> AssetManager::myManagers;

AssetManager*
AssetManager::createManager(const MString& filePath)
{
    AssetManager* m = new AssetManager(filePath);
    myManagers.push_back(m);
    m->init();
    return m;
}

AssetManager::AssetManager()
{
}

AssetManager::AssetManager(const MString& filePath)
    : myFilePath(filePath)
{
}

AssetManager::~AssetManager()
{
    // TODO: destroy stuff
}

MObject
AssetManager::getAssetNode()
{
    return myAssetNode;
}

void
AssetManager::init()
{
    MDGModifier dg;
    MDagModifier dag;
    MFnDagNode fnDag;
    MStatus stat;
    MPlug src;
    MPlug dest;

    try
    {

        // Create the asset node
        myAssetNode = dg.createNode("hAsset", &stat);
        Util::checkMayaStatus(stat);
        MPlug plug( myAssetNode, AssetNodeAttributes::fileNameAttr);
        stat = dg.newPlugValueString(plug, myFilePath);

        MFileObject file;
        file.setRawFullName( myFilePath );
        MString fileName = file.resolvedName();
        int dotIndex = fileName.rindexW('.');
        MString assetName = fileName.substringW(0, dotIndex-1);
        assetName += "_asset";
        MFnDependencyNode fnDN( myAssetNode );
        fnDN.setName(assetName, &stat);
        Util::checkMayaStatus(stat);

        myAssetTransform = fnDag.create("transform", assetName + "_transform", MObject::kNullObj, &stat);
        Util::checkMayaStatus(stat);

        MString t = "time1";
	MObject timeNode = Util::findNodeByName(t);
	src = MFnDependencyNode(timeNode).findPlug("outTime");
	dest = MPlug( myAssetNode, AssetNodeAttributes::timeInput );
	dg.connect(src, dest);

        // DGModifier do it
        stat = dg.doIt();
        Util::checkMayaStatus(stat);

        plug.setLocked(true);

        stat = update();
        Util::checkMayaStatus(stat);

        // Select the newly created asset
        stat = MGlobal::select( myAssetNode, MGlobal::kReplaceList );
        Util::checkMayaStatus(stat);

    }
    catch (MayaError& e)
    {
        cerr << e.what() << endl;
    }

}

MStatus
AssetManager::update()
{
    cerr << "AssetManager::update() called" << endl;
    MStatus stat;
    try
    {
        // Create the objs/meshes
        MPlug objectsPlug( myAssetNode, AssetNodeAttributes::objects );
        int objCount = objectsPlug.evaluateNumElements(&stat);
        Util::checkMayaStatus(stat);

	MString title = "Houdini";
	MString status = "Composing Objects...";
	Util::showProgressWindow( title, status, 0 );
        for (int i=0; i<objCount; i++)
        {
	    Util::updateProgressWindow( status, (int) ( (float)i*100.0f / (float) objCount) );
            MPlug elemPlug = objectsPlug[i];
            createObjectNodeGroup(elemPlug, myAssetTransform);
        }
	Util::hideProgressWindow();

        // Instancers
        MPlug instancersPlug(myAssetNode, AssetNodeAttributes::instancers);
        int instCount = instancersPlug.numElements(&stat);
        Util::checkMayaStatus(stat);

	status = "Instancing Objects...";
	Util::showProgressWindow( title, status, 0 );
        for (int i=0; i<instCount; i++)
        {
	    Util::updateProgressWindow( status, (int) ( (float)i*100.0f / (float) instCount) );
            MPlug elemPlug = instancersPlug[i];
            createInstNodeGroup(elemPlug, myAssetTransform);
        }
	Util::hideProgressWindow();

        // TODO: delete extra node groups

        return MS::kSuccess;
    }
    catch (MayaError& e)
    {
        cerr << e.what() << endl;
        return MS::kFailure;
    }
}

ObjectNodeGroup*
AssetManager::getObjectGroup(MPlug& plug)
{
    try
    {
        int index = plug.logicalIndex();
        // This throws an exception if index is out of bounds
        ObjectNodeGroup* ret = myObjectNodeGroups.at(index);
        return ret;
    }
    catch (...)
    {
        return NULL;
    }
}

InstNodeGroup*
AssetManager::getInstGroup(MPlug& plug)
{
    try
    {
        int index = plug.logicalIndex();
        // This throws an exception if index is out of bounds
        InstNodeGroup* ret = myInstNodeGroups.at(index);
        return ret;
    }
    catch (...)
    {
        return NULL;
    }
}

MStatus
AssetManager::createObjectNodeGroup(MPlug& plug, MObject& assetTransform)
{
    ObjectNodeGroup* objGroup = getObjectGroup(plug);
    if (objGroup == NULL)
    {
        objGroup = new ObjectNodeGroup();
        objGroup->plug = plug;
        objGroup->assetTransform = assetTransform;
        myObjectNodeGroups.push_back(objGroup);
    }

    return objGroup->update();
}

MStatus
AssetManager::createInstNodeGroup(MPlug& plug, MObject& assetTransform)
{
    InstNodeGroup* instGroup = getInstGroup(plug);
    if (instGroup == NULL)
    {
        instGroup = new InstNodeGroup();
        instGroup->plug = plug;
        instGroup->assetTransform = assetTransform;
        myInstNodeGroups.push_back(instGroup);
    }

    return instGroup->update();
}

MStatus
ObjectNodeGroup::update()
{
    MStatus stat;

    stat = updateNodes();
    stat = updateConnections();
    return stat;
}

// Check if necessary nodes exist, and if not, will create them
MStatus
ObjectNodeGroup::updateNodes()
{
    MStatus stat;
    MFnDagNode fnDag;
    MDGModifier dg;
    MDagModifier dag;
    MString cmd, result;

    try
    {

        // Check to see if the object transform has been created, if so,
        // then add the part transform under it, if not, then create the
        // object transform then add the part transform
        MString partName = plug.child(AssetNodeAttributes::objectName).asString();

        int usIndex = partName.rindexW('_');
        MString objName = partName.substringW(0, usIndex-1);

        MString objFullPath = "|" + MFnDependencyNode(assetTransform).name() +
                              "|" + objName;
        cerr << objFullPath << endl;
        cmd = "objExists " + objFullPath;
        int exists = 0;
        stat = MGlobal::executeCommand(cmd, exists);
        Util::checkMayaStatus(stat);
        cerr << exists << endl;

        MObject objNode;
        if (exists)
        {
            objNode = Util::findNodeByName(objFullPath);
        }
        else
        {
            objNode = fnDag.create("transform", objName, assetTransform, &stat);
            Util::checkMayaStatus(stat);
        }
        objectTransform = objNode;

        if (partTransform.isNull())
        {
            cerr << "createing mesh" << endl;
            // Creates a mesh node and its parent, returns the parent to me.
            partTransform = fnDag.create("mesh", partName, MObject::kNullObj, &stat);
            Util::checkMayaStatus(stat);
            meshNode = MFnDagNode(partTransform).child(0, &stat);
            Util::checkMayaStatus(stat);
            stat = dag.reparentNode(partTransform, objectTransform);
            Util::checkMayaStatus(stat);
            stat = dag.doIt();
            Util::checkMayaStatus(stat);
        }

        // Materials
        MPlug materialPlug = plug.child(AssetNodeAttributes::material);
        if (materialNode.isNull())
        {
            cerr << "createing mateiral" << endl;
            cmd = "shadingNode -asShader phong";
            result = Util::executeCommand(cmd); // phong node
            materialNode = Util::findNodeByName(result);
        }

        if (seNode.isNull())
        {
            cerr << "createing se: " << MFnDagNode(partTransform).fullPathName() << endl;
            stat = MGlobal::selectByName(MFnDagNode(partTransform).fullPathName(), MGlobal::kReplaceList);
            Util::checkMayaStatus(stat);

            cmd = "hyperShade -assign " + MFnDependencyNode(materialNode).name();
            result = Util::executeCommand(cmd); // shadingEngine node

            MPlugArray connectedPlugs;
            bool hasConnections = MFnDependencyNode(materialNode).findPlug("outColor").connectedTo(
                    connectedPlugs, false, true, &stat);
            Util::checkMayaStatus(stat);
            if (hasConnections)
            {
                seNode = connectedPlugs[0].node();
                cerr << "seNode: " << MFnDependencyNode(seNode).name() << endl;
            }
        }

        bool matExists = materialPlug.child(AssetNodeAttributes::materialExists).asBool();
        if (matExists)
        {
            MString texturePath = materialPlug.child(AssetNodeAttributes::texturePath).asString();
            if (texturePath != "")
            {
                if (fileNode.isNull())
                {
                    cmd = "shadingNode -asTexture file";
                    result = Util::executeCommand(cmd); // file node
                    fileNode = Util::findNodeByName(result);
                }
            }
            else
            {
                if (!fileNode.isNull())
                {
                    stat = dg.deleteNode(fileNode);
                    Util::checkMayaStatus(stat);
                }
            }
        }
        cerr << "check" << endl;
        stat = dg.doIt();
        Util::checkMayaStatus(stat);

        return MS::kSuccess;
    }
    catch (MayaError& e)
    {
        cerr << e.what() << endl;
        return MS::kFailure;
    }
}

//Once you know all the nodes exist, then make sure all proper connections exist
MStatus
ObjectNodeGroup::updateConnections()
{
    MDGModifier dg;
    MPlug src, dest;
    MStatus stat;

    try
    {
        // Transform
        MPlug transformPlug = plug.child(AssetNodeAttributes::transform);

        src = transformPlug.child(AssetNodeAttributes::translateAttr);
        cerr << src.name() << endl;
        dest = MFnDependencyNode(partTransform).findPlug("translate", true);
        stat = dg.connect(src, dest);
        Util::checkMayaStatus(stat);

        src = transformPlug.child(AssetNodeAttributes::rotateAttr);
        dest = MFnDependencyNode(partTransform).findPlug("rotate", true);
        stat = dg.connect(src, dest);
        Util::checkMayaStatus(stat);

        src = transformPlug.child(AssetNodeAttributes::scaleAttr);
        dest = MFnDependencyNode(partTransform).findPlug("scale", true);
        stat = dg.connect(src, dest);
        Util::checkMayaStatus(stat);

        // Mesh
        src = plug.child(AssetNodeAttributes::mesh);
        dest = MFnDependencyNode(meshNode).findPlug("inMesh", true);
        stat = dg.connect(src, dest);
        Util::checkMayaStatus(stat);

        // Materials
        MPlug materialPlug = plug.child(AssetNodeAttributes::material);
        bool matExists = materialPlug.child(AssetNodeAttributes::materialExists).asBool();
        if (matExists)
        {
            src = materialPlug.child(AssetNodeAttributes::specularAttr);
            dest = MFnDependencyNode(materialNode).findPlug("specularColor");
            stat = dg.connect(src, dest);
            Util::checkMayaStatus(stat);

            src = materialPlug.child(AssetNodeAttributes::alphaAttr);
            dest = MFnDependencyNode(materialNode).findPlug("transparency");
            stat = dg.connect(src, dest);
            Util::checkMayaStatus(stat);

            MString texturePath = materialPlug.child(AssetNodeAttributes::texturePath).asString();
            cerr << "texturePath: " << texturePath << endl;
            if (texturePath == "")
            {
                src = materialPlug.child(AssetNodeAttributes::diffuseAttr);
                dest = MFnDependencyNode(materialNode).findPlug("color");
                stat = dg.connect(src, dest);
                Util::checkMayaStatus(stat);

                src = materialPlug.child(AssetNodeAttributes::ambientAttr);
                dest = MFnDependencyNode(materialNode).findPlug("ambientColor");
                stat = dg.connect(src, dest);
                Util::checkMayaStatus(stat);
            }
            else
            {
                src = materialPlug.child(AssetNodeAttributes::texturePath);
                dest = MFnDependencyNode(fileNode).findPlug("fileTextureName");
                stat = dg.connect(src, dest);
                Util::checkMayaStatus(stat);

                src = MFnDependencyNode(fileNode).findPlug("outColor");
                dest = MFnDependencyNode(materialNode).findPlug("color");
                stat = dg.connect(src, dest);
                Util::checkMayaStatus(stat);
            }
        }
       Util::checkMayaStatus(stat);

        stat = dg.doIt();
        Util::checkMayaStatus(stat);

        return MS::kSuccess;
    }
    catch (MayaError& e)
    {
        cerr << e.what() << endl;
        return MS::kFailure;
    }

}

MStatus
InstNodeGroup::update()
{
    MStatus stat;

    stat = updateNodes();
    stat = updateConnections();
    return stat;
}

MStatus
InstNodeGroup::updateNodes()
{
    MStatus stat;
    MDagModifier dag;
    MFnDependencyNode fnDN;

    try
    {
        // Create the instancer node
        if (instancerNode.isNull())
        {
            instancerNode = fnDN.create("instancer", "instancer", &stat);
            Util::checkMayaStatus(stat);
        }

        // Set the rotation units to radians
        stat = dag.newPlugValueInt(MFnDependencyNode(instancerNode).findPlug("rau"), 1);
        Util::checkMayaStatus(stat);

        stat = dag.reparentNode(instancerNode, assetTransform);
        Util::checkMayaStatus(stat);

        stat = dag.doIt();
        Util::checkMayaStatus(stat);

        return MS::kSuccess;
    }
    catch (MayaError& e)
    {
        cerr << e.what() << endl;
        return MS::kFailure;
    }

}

MStatus
InstNodeGroup::updateConnections()
{
    MStatus stat;
    MPlug src, dest;
    MDGModifier dg;

    try
    {
        // Connect the input points
        src = plug.child(AssetNodeAttributes::instancerData);
        dest = MFnDependencyNode(instancerNode).findPlug("inputPoints");
        stat = dg.connect(src, dest);
        Util::checkMayaStatus(stat);

        // Connect the instanced objects
        MPlug instancedNames = plug.child(AssetNodeAttributes::instancedObjectNames);
        int ionCount = instancedNames.numElements();
        for (int i=0; i<ionCount; i++)
        {
            MString name = "|" + MFnDependencyNode(assetTransform).name() +
                "|" + instancedNames[i].asString();

            src = MFnDependencyNode(Util::findNodeByName(name)).findPlug("matrix");
            dest = MFnDependencyNode(instancerNode).findPlug("inputHierarchy").elementByLogicalIndex(i);
            stat = dg.connect(src, dest);
            Util::checkMayaStatus(stat);

            MString cmd = "hide " + name;

	    //TODO: this hide command sometimes "fails" to execute, in that Maya reports an error from the
	    //execution of the command, but it actually does succeed in hiding the objects.  Might be
	    // a Maya bug or perhaps we are not executing the commands at a good time for Maya (this should
	    // be happening immediately after a compute
            Util::executeCommand(cmd);
        }

        stat = dg.doIt();
        Util::checkMayaStatus(stat);

        return MS::kSuccess;
    }
    catch (MayaError& e)
    {
        cerr << e.what() << endl;
        return MS::kFailure;
    }
}
<commit_msg>Fix some MEL command erroring out<commit_after>#include <maya/MDGModifier.h>
#include <maya/MDagModifier.h>
#include <maya/MFnDagNode.h>
#include <maya/MFnPhongShader.h>
#include <maya/MFileObject.h>
#include <maya/MSelectionList.h>
#include <maya/MPlugArray.h>
#include <maya/MStatus.h>
#include <maya/MGlobal.h>

#include "assetManager.h"
#include "common.h"
#include "util.h"

std::vector<AssetManager*> AssetManager::myManagers;

AssetManager*
AssetManager::createManager(const MString& filePath)
{
    AssetManager* m = new AssetManager(filePath);
    myManagers.push_back(m);
    m->init();
    return m;
}

AssetManager::AssetManager()
{
}

AssetManager::AssetManager(const MString& filePath)
    : myFilePath(filePath)
{
}

AssetManager::~AssetManager()
{
    // TODO: destroy stuff
}

MObject
AssetManager::getAssetNode()
{
    return myAssetNode;
}

void
AssetManager::init()
{
    MDGModifier dg;
    MDagModifier dag;
    MFnDagNode fnDag;
    MStatus stat;
    MPlug src;
    MPlug dest;

    try
    {

        // Create the asset node
        myAssetNode = dg.createNode("hAsset", &stat);
        Util::checkMayaStatus(stat);
        MPlug plug( myAssetNode, AssetNodeAttributes::fileNameAttr);
        stat = dg.newPlugValueString(plug, myFilePath);

        MFileObject file;
        file.setRawFullName( myFilePath );
        MString fileName = file.resolvedName();
        int dotIndex = fileName.rindexW('.');
        MString assetName = fileName.substringW(0, dotIndex-1);
        assetName += "_asset";
        MFnDependencyNode fnDN( myAssetNode );
        fnDN.setName(assetName, &stat);
        Util::checkMayaStatus(stat);

        myAssetTransform = fnDag.create("transform", assetName + "_transform", MObject::kNullObj, &stat);
        Util::checkMayaStatus(stat);

        MString t = "time1";
	MObject timeNode = Util::findNodeByName(t);
	src = MFnDependencyNode(timeNode).findPlug("outTime");
	dest = MPlug( myAssetNode, AssetNodeAttributes::timeInput );
	dg.connect(src, dest);

        // DGModifier do it
        stat = dg.doIt();
        Util::checkMayaStatus(stat);

        plug.setLocked(true);

        stat = update();
        Util::checkMayaStatus(stat);

        // Select the newly created asset
        stat = MGlobal::select( myAssetNode, MGlobal::kReplaceList );
        Util::checkMayaStatus(stat);

    }
    catch (MayaError& e)
    {
        cerr << e.what() << endl;
    }

}

MStatus
AssetManager::update()
{
    cerr << "AssetManager::update() called" << endl;
    MStatus stat;
    try
    {
        // Create the objs/meshes
        MPlug objectsPlug( myAssetNode, AssetNodeAttributes::objects );
        int objCount = objectsPlug.evaluateNumElements(&stat);
        Util::checkMayaStatus(stat);

	MString title = "Houdini";
	MString status = "Composing Objects...";
	Util::showProgressWindow( title, status, 0 );
        for (int i=0; i<objCount; i++)
        {
	    Util::updateProgressWindow( status, (int) ( (float)i*100.0f / (float) objCount) );
            MPlug elemPlug = objectsPlug[i];
            createObjectNodeGroup(elemPlug, myAssetTransform);
        }
	Util::hideProgressWindow();

        // Instancers
        MPlug instancersPlug(myAssetNode, AssetNodeAttributes::instancers);
        int instCount = instancersPlug.numElements(&stat);
        Util::checkMayaStatus(stat);

	status = "Instancing Objects...";
	Util::showProgressWindow( title, status, 0 );
        for (int i=0; i<instCount; i++)
        {
	    Util::updateProgressWindow( status, (int) ( (float)i*100.0f / (float) instCount) );
            MPlug elemPlug = instancersPlug[i];
            createInstNodeGroup(elemPlug, myAssetTransform);
        }
	Util::hideProgressWindow();

        // TODO: delete extra node groups

        return MS::kSuccess;
    }
    catch (MayaError& e)
    {
        cerr << e.what() << endl;
        return MS::kFailure;
    }
}

ObjectNodeGroup*
AssetManager::getObjectGroup(MPlug& plug)
{
    try
    {
        int index = plug.logicalIndex();
        // This throws an exception if index is out of bounds
        ObjectNodeGroup* ret = myObjectNodeGroups.at(index);
        return ret;
    }
    catch (...)
    {
        return NULL;
    }
}

InstNodeGroup*
AssetManager::getInstGroup(MPlug& plug)
{
    try
    {
        int index = plug.logicalIndex();
        // This throws an exception if index is out of bounds
        InstNodeGroup* ret = myInstNodeGroups.at(index);
        return ret;
    }
    catch (...)
    {
        return NULL;
    }
}

MStatus
AssetManager::createObjectNodeGroup(MPlug& plug, MObject& assetTransform)
{
    ObjectNodeGroup* objGroup = getObjectGroup(plug);
    if (objGroup == NULL)
    {
        objGroup = new ObjectNodeGroup();
        objGroup->plug = plug;
        objGroup->assetTransform = assetTransform;
        myObjectNodeGroups.push_back(objGroup);
    }

    return objGroup->update();
}

MStatus
AssetManager::createInstNodeGroup(MPlug& plug, MObject& assetTransform)
{
    InstNodeGroup* instGroup = getInstGroup(plug);
    if (instGroup == NULL)
    {
        instGroup = new InstNodeGroup();
        instGroup->plug = plug;
        instGroup->assetTransform = assetTransform;
        myInstNodeGroups.push_back(instGroup);
    }

    return instGroup->update();
}

MStatus
ObjectNodeGroup::update()
{
    MStatus stat;

    stat = updateNodes();
    stat = updateConnections();
    return stat;
}

// Check if necessary nodes exist, and if not, will create them
MStatus
ObjectNodeGroup::updateNodes()
{
    MStatus stat;
    MFnDagNode fnDag;
    MDGModifier dg;
    MDagModifier dag;
    MString cmd, result;

    try
    {

        // Check to see if the object transform has been created, if so,
        // then add the part transform under it, if not, then create the
        // object transform then add the part transform
        MString partName = plug.child(AssetNodeAttributes::objectName).asString();

        int usIndex = partName.rindexW('_');
        MString objName = partName.substringW(0, usIndex-1);

        MString objFullPath = "|" + MFnDependencyNode(assetTransform).name() +
                              "|" + objName;
        cerr << objFullPath << endl;
        cmd = "objExists " + objFullPath;
        int exists = 0;
        stat = MGlobal::executeCommand(cmd, exists);
        Util::checkMayaStatus(stat);
        cerr << exists << endl;

        MObject objNode;
        if (exists)
        {
            objNode = Util::findNodeByName(objFullPath);
        }
        else
        {
            objNode = fnDag.create("transform", objName, assetTransform, &stat);
            Util::checkMayaStatus(stat);
        }
        objectTransform = objNode;

        if (partTransform.isNull())
        {
            cerr << "createing mesh" << endl;
            // Creates a mesh node and its parent, returns the parent to me.
            partTransform = fnDag.create("mesh", partName, MObject::kNullObj, &stat);
            Util::checkMayaStatus(stat);
            meshNode = MFnDagNode(partTransform).child(0, &stat);
            Util::checkMayaStatus(stat);
            stat = dag.reparentNode(partTransform, objectTransform);
            Util::checkMayaStatus(stat);
            stat = dag.doIt();
            Util::checkMayaStatus(stat);
        }

        // Materials
        MPlug materialPlug = plug.child(AssetNodeAttributes::material);
        if (materialNode.isNull())
        {
            cerr << "createing mateiral" << endl;
            cmd = "shadingNode -asShader phong";
            result = Util::executeCommand(cmd); // phong node
            materialNode = Util::findNodeByName(result);
        }

        if (seNode.isNull())
        {
            cerr << "createing se: " << MFnDagNode(partTransform).fullPathName() << endl;
            stat = MGlobal::selectByName(MFnDagNode(partTransform).fullPathName(), MGlobal::kReplaceList);
            Util::checkMayaStatus(stat);

            cmd = "hyperShade -assign " + MFnDependencyNode(materialNode).name();
	    stat = MGlobal::executeCommand(cmd);
            Util::checkMayaStatus(stat);

            MPlugArray connectedPlugs;
            bool hasConnections = MFnDependencyNode(materialNode).findPlug("outColor").connectedTo(
                    connectedPlugs, false, true, &stat);
            Util::checkMayaStatus(stat);
            if (hasConnections)
            {
                seNode = connectedPlugs[0].node();
                cerr << "seNode: " << MFnDependencyNode(seNode).name() << endl;
            }
        }

        bool matExists = materialPlug.child(AssetNodeAttributes::materialExists).asBool();
        if (matExists)
        {
            MString texturePath = materialPlug.child(AssetNodeAttributes::texturePath).asString();
            if (texturePath != "")
            {
                if (fileNode.isNull())
                {
                    cmd = "shadingNode -asTexture file";
                    result = Util::executeCommand(cmd); // file node
                    fileNode = Util::findNodeByName(result);
                }
            }
            else
            {
                if (!fileNode.isNull())
                {
                    stat = dg.deleteNode(fileNode);
                    Util::checkMayaStatus(stat);
                }
            }
        }
        cerr << "check" << endl;
        stat = dg.doIt();
        Util::checkMayaStatus(stat);

        return MS::kSuccess;
    }
    catch (MayaError& e)
    {
        cerr << e.what() << endl;
        return MS::kFailure;
    }
}

//Once you know all the nodes exist, then make sure all proper connections exist
MStatus
ObjectNodeGroup::updateConnections()
{
    MDGModifier dg;
    MPlug src, dest;
    MStatus stat;

    try
    {
        // Transform
        MPlug transformPlug = plug.child(AssetNodeAttributes::transform);

        src = transformPlug.child(AssetNodeAttributes::translateAttr);
        cerr << src.name() << endl;
        dest = MFnDependencyNode(partTransform).findPlug("translate", true);
        stat = dg.connect(src, dest);
        Util::checkMayaStatus(stat);

        src = transformPlug.child(AssetNodeAttributes::rotateAttr);
        dest = MFnDependencyNode(partTransform).findPlug("rotate", true);
        stat = dg.connect(src, dest);
        Util::checkMayaStatus(stat);

        src = transformPlug.child(AssetNodeAttributes::scaleAttr);
        dest = MFnDependencyNode(partTransform).findPlug("scale", true);
        stat = dg.connect(src, dest);
        Util::checkMayaStatus(stat);

        // Mesh
        src = plug.child(AssetNodeAttributes::mesh);
        dest = MFnDependencyNode(meshNode).findPlug("inMesh", true);
        stat = dg.connect(src, dest);
        Util::checkMayaStatus(stat);

        // Materials
        MPlug materialPlug = plug.child(AssetNodeAttributes::material);
        bool matExists = materialPlug.child(AssetNodeAttributes::materialExists).asBool();
        if (matExists)
        {
            src = materialPlug.child(AssetNodeAttributes::specularAttr);
            dest = MFnDependencyNode(materialNode).findPlug("specularColor");
            stat = dg.connect(src, dest);
            Util::checkMayaStatus(stat);

            src = materialPlug.child(AssetNodeAttributes::alphaAttr);
            dest = MFnDependencyNode(materialNode).findPlug("transparency");
            stat = dg.connect(src, dest);
            Util::checkMayaStatus(stat);

            MString texturePath = materialPlug.child(AssetNodeAttributes::texturePath).asString();
            cerr << "texturePath: " << texturePath << endl;
            if (texturePath == "")
            {
                src = materialPlug.child(AssetNodeAttributes::diffuseAttr);
                dest = MFnDependencyNode(materialNode).findPlug("color");
                stat = dg.connect(src, dest);
                Util::checkMayaStatus(stat);

                src = materialPlug.child(AssetNodeAttributes::ambientAttr);
                dest = MFnDependencyNode(materialNode).findPlug("ambientColor");
                stat = dg.connect(src, dest);
                Util::checkMayaStatus(stat);
            }
            else
            {
                src = materialPlug.child(AssetNodeAttributes::texturePath);
                dest = MFnDependencyNode(fileNode).findPlug("fileTextureName");
                stat = dg.connect(src, dest);
                Util::checkMayaStatus(stat);

                src = MFnDependencyNode(fileNode).findPlug("outColor");
                dest = MFnDependencyNode(materialNode).findPlug("color");
                stat = dg.connect(src, dest);
                Util::checkMayaStatus(stat);
            }
        }
       Util::checkMayaStatus(stat);

        stat = dg.doIt();
        Util::checkMayaStatus(stat);

        return MS::kSuccess;
    }
    catch (MayaError& e)
    {
        cerr << e.what() << endl;
        return MS::kFailure;
    }

}

MStatus
InstNodeGroup::update()
{
    MStatus stat;

    stat = updateNodes();
    stat = updateConnections();
    return stat;
}

MStatus
InstNodeGroup::updateNodes()
{
    MStatus stat;
    MDagModifier dag;
    MFnDependencyNode fnDN;

    try
    {
        // Create the instancer node
        if (instancerNode.isNull())
        {
            instancerNode = fnDN.create("instancer", "instancer", &stat);
            Util::checkMayaStatus(stat);
        }

        // Set the rotation units to radians
        stat = dag.newPlugValueInt(MFnDependencyNode(instancerNode).findPlug("rau"), 1);
        Util::checkMayaStatus(stat);

        stat = dag.reparentNode(instancerNode, assetTransform);
        Util::checkMayaStatus(stat);

        stat = dag.doIt();
        Util::checkMayaStatus(stat);

        return MS::kSuccess;
    }
    catch (MayaError& e)
    {
        cerr << e.what() << endl;
        return MS::kFailure;
    }

}

MStatus
InstNodeGroup::updateConnections()
{
    MStatus stat;
    MPlug src, dest;
    MDGModifier dg;

    try
    {
        // Connect the input points
        src = plug.child(AssetNodeAttributes::instancerData);
        dest = MFnDependencyNode(instancerNode).findPlug("inputPoints");
        stat = dg.connect(src, dest);
        Util::checkMayaStatus(stat);

        // Connect the instanced objects
        MPlug instancedNames = plug.child(AssetNodeAttributes::instancedObjectNames);
        int ionCount = instancedNames.numElements();
        for (int i=0; i<ionCount; i++)
        {
            MString name = "|" + MFnDependencyNode(assetTransform).name() +
                "|" + instancedNames[i].asString();

            src = MFnDependencyNode(Util::findNodeByName(name)).findPlug("matrix");
            dest = MFnDependencyNode(instancerNode).findPlug("inputHierarchy").elementByLogicalIndex(i);
            stat = dg.connect(src, dest);
            Util::checkMayaStatus(stat);

            MString cmd = "hide " + name;

	    stat = MGlobal::executeCommand(cmd);
	    Util::checkMayaStatus(stat);
        }

        stat = dg.doIt();
        Util::checkMayaStatus(stat);

        return MS::kSuccess;
    }
    catch (MayaError& e)
    {
        cerr << e.what() << endl;
        return MS::kFailure;
    }
}
<|endoftext|>