Compare commits

..

4 Commits

Author SHA1 Message Date
Kevin Ring fbbaf4452a Add tests for credit filtering.
cesium-native / Quick Checks (push) Has been cancelled Details
cesium-native / Linting (push) Has been cancelled Details
cesium-native / Documentation (push) Has been cancelled Details
cesium-native / ${{matrix.platform}} / ${{matrix.build_type}} (Debug, windows-2022) (push) Has been cancelled Details
cesium-native / ${{matrix.platform}} / ${{matrix.build_type}} (RelWithDebInfo, windows-2022) (push) Has been cancelled Details
cesium-native / ${{matrix.platform}} / ${{matrix.compiler}} / ${{matrix.build_type}} (Debug, clang, macos-13) (push) Has been cancelled Details
cesium-native / ${{matrix.platform}} / ${{matrix.compiler}} / ${{matrix.build_type}} (Debug, clang, ubuntu-22.04) (push) Has been cancelled Details
cesium-native / ${{matrix.platform}} / ${{matrix.compiler}} / ${{matrix.build_type}} (Debug, gcc, ubuntu-24.04) (push) Has been cancelled Details
cesium-native / ${{matrix.platform}} / ${{matrix.compiler}} / ${{matrix.build_type}} (RelWithDebInfo, clang, macos-13) (push) Has been cancelled Details
cesium-native / ${{matrix.platform}} / ${{matrix.compiler}} / ${{matrix.build_type}} (RelWithDebInfo, clang, ubuntu-22.04) (push) Has been cancelled Details
cesium-native / ${{matrix.platform}} / ${{matrix.compiler}} / ${{matrix.build_type}} (RelWithDebInfo, gcc, ubuntu-24.04) (push) Has been cancelled Details
cesium-native / Emscripten v${{matrix.version}} ${{matrix.memory}}bit memory (32, 3.1.39) (push) Has been cancelled Details
cesium-native / Emscripten v${{matrix.version}} ${{matrix.memory}}bit memory (32, 4.0.13) (push) Has been cancelled Details
cesium-native / Emscripten v${{matrix.version}} ${{matrix.memory}}bit memory (64, 4.0.13) (push) Has been cancelled Details
2025-11-21 19:18:54 +11:00
Kevin Ring fd8aaef4be Fix Doxygen error. 2025-11-21 18:50:34 +11:00
Kevin Ring 32e509eb95 Simplify removed credit tracking.
There's no good reason this was so complicated before.
2025-11-21 18:01:52 +11:00
Kevin Ring 002d00e3f8 Add credit filtering option to getSnapshot. 2025-11-21 17:41:34 +11:00
4 changed files with 358 additions and 75 deletions

View File

@ -8,13 +8,13 @@
- The constructor parameters for `RasterOverlayTileProvider` and `QuadtreeRasterOverlayTileProvider` have changed.
- The `getCredit` method has been removed from `RasterOverlayCreditProvider`. Use `getCredits` instead.
- Removed unused property `RasterOverlayOptions::subTileCacheBytes`.
- The `CreditSystem` would previously treat credits with identical text as the same credit, even if they came from different sources. Now, credits from different `CreditSource` instances are treated as different credits.
##### Additions :tada:
- Added the concept of a `CreditSource`. Every `Credit` in a `CreditSystem` has a source, and these can be mapped back to `Tileset` and `RasterOverlayTileProvider` (via their `getCreditSource` methods) in order to determine which dataset created which credits.
- Added `TilesetViewGroup::isCreditReferenced`, which can be used to determine if a particular view group references a particular `Credit`.
- Added `CreditReferencer::isCreditReferenced`, which can be used to determine if the referencer is currently referencing a particular `Credit`.
- `CreditSystem::getSnapshot` now takes an optional parameter specifying if and how to filter `Credits` with identical HTML strings.
### v0.54.0 - 2025-11-17

View File

@ -326,4 +326,158 @@ TEST_CASE("Test CreditSystem with CreditSources") {
CHECK(snapshot.currentCredits[0] == credit1);
CHECK(snapshot.removedCredits.empty());
}
SUBCASE("getSnapshot") {
SUBCASE("None filtering mode") {
SUBCASE("includes all Credits from all sources") {
CreditSource sourceC(creditSystem);
Credit credit0 = creditSystem.createCredit(sourceA, html0);
Credit credit1 = creditSystem.createCredit(sourceB, html0);
Credit credit2 = creditSystem.createCredit(sourceC, html0);
creditSystem.addCreditReference(credit0);
creditSystem.addCreditReference(credit1);
creditSystem.addCreditReference(credit2);
const CreditsSnapshot& snapshot =
creditSystem.getSnapshot(CreditFilteringMode::None);
REQUIRE(snapshot.currentCredits.size() == 3);
CHECK(snapshot.currentCredits[0] == credit0);
CHECK(snapshot.currentCredits[1] == credit1);
CHECK(snapshot.currentCredits[2] == credit2);
CHECK(snapshot.removedCredits.empty());
}
}
SUBCASE("UniqueHtmlAndShowOnScreen filtering mode") {
SUBCASE(
"filters out credits with identical HTML and showOnScreen values") {
CreditSource sourceC(creditSystem);
Credit credit0 = creditSystem.createCredit(sourceA, html0, true);
Credit credit1 = creditSystem.createCredit(sourceB, html0, true);
Credit credit2 = creditSystem.createCredit(sourceC, html0, false);
creditSystem.addCreditReference(credit0);
creditSystem.addCreditReference(credit1);
creditSystem.addCreditReference(credit2);
const CreditsSnapshot& snapshot = creditSystem.getSnapshot(
CreditFilteringMode::UniqueHtmlAndShowOnScreen);
REQUIRE(snapshot.currentCredits.size() == 2);
CHECK(snapshot.currentCredits[0] == credit0);
CHECK(snapshot.currentCredits[1] == credit2);
CHECK(snapshot.removedCredits.empty());
}
SUBCASE("reference count is the sum of collapsed credits") {
CreditSource sourceC(creditSystem);
Credit credit0 = creditSystem.createCredit(sourceA, html0, false);
Credit credit1 = creditSystem.createCredit(sourceB, html0, true);
Credit credit2 = creditSystem.createCredit(sourceC, html0, true);
creditSystem.addCreditReference(credit0);
creditSystem.addCreditReference(credit0);
creditSystem.addCreditReference(credit1);
creditSystem.addCreditReference(credit2);
creditSystem.addCreditReference(credit2);
const CreditsSnapshot& snapshot = creditSystem.getSnapshot(
CreditFilteringMode::UniqueHtmlAndShowOnScreen);
// credit0 has a reference count of 2. credit1 and credit2 are collapsed
// into one credit with a reference count of 3 and represented by
// credit1. So credit1 should be shown before credit0.
REQUIRE(snapshot.currentCredits.size() == 2);
CHECK(snapshot.currentCredits[0] == credit1);
CHECK(snapshot.currentCredits[1] == credit0);
CHECK(snapshot.removedCredits.empty());
}
}
SUBCASE("UniqueHtml filtering mode") {
SUBCASE(
"filters out credits with identical HTML from different sources") {
CreditSource sourceC(creditSystem);
Credit credit0 = creditSystem.createCredit(sourceA, html0);
Credit credit1 = creditSystem.createCredit(sourceB, html0);
Credit credit2 = creditSystem.createCredit(sourceC, html0);
creditSystem.addCreditReference(credit0);
creditSystem.addCreditReference(credit1);
creditSystem.addCreditReference(credit2);
const CreditsSnapshot& snapshot =
creditSystem.getSnapshot(CreditFilteringMode::UniqueHtml);
REQUIRE(snapshot.currentCredits.size() == 1);
CHECK(snapshot.currentCredits[0] == credit0);
CHECK(snapshot.removedCredits.empty());
}
SUBCASE("includes the Credit with showOnScreen=true if one exists.") {
CreditSource sourceC(creditSystem);
Credit credit0 = creditSystem.createCredit(sourceA, html0, false);
Credit credit1 = creditSystem.createCredit(sourceB, html0, true);
Credit credit2 = creditSystem.createCredit(sourceC, html0, false);
creditSystem.addCreditReference(credit0);
creditSystem.addCreditReference(credit1);
creditSystem.addCreditReference(credit2);
const CreditsSnapshot& snapshot =
creditSystem.getSnapshot(CreditFilteringMode::UniqueHtml);
REQUIRE(snapshot.currentCredits.size() == 1);
CHECK(snapshot.currentCredits[0] == credit1);
CHECK(snapshot.removedCredits.empty());
}
SUBCASE("includes the first of multiple Credits with showOnScreen=true") {
CreditSource sourceC(creditSystem);
Credit credit0 = creditSystem.createCredit(sourceA, html0, true);
Credit credit1 = creditSystem.createCredit(sourceB, html0, true);
Credit credit2 = creditSystem.createCredit(sourceC, html0, false);
creditSystem.addCreditReference(credit0);
creditSystem.addCreditReference(credit1);
creditSystem.addCreditReference(credit2);
const CreditsSnapshot& snapshot =
creditSystem.getSnapshot(CreditFilteringMode::UniqueHtml);
REQUIRE(snapshot.currentCredits.size() == 1);
CHECK(snapshot.currentCredits[0] == credit0);
CHECK(snapshot.removedCredits.empty());
}
SUBCASE("reference count is the sum of collapsed credits") {
CreditSource sourceC(creditSystem);
Credit credit0 = creditSystem.createCredit(sourceA, html0);
Credit credit1 = creditSystem.createCredit(sourceB, html1);
Credit credit2 = creditSystem.createCredit(sourceC, html1);
creditSystem.addCreditReference(credit0);
creditSystem.addCreditReference(credit0);
creditSystem.addCreditReference(credit1);
creditSystem.addCreditReference(credit2);
creditSystem.addCreditReference(credit2);
const CreditsSnapshot& snapshot =
creditSystem.getSnapshot(CreditFilteringMode::UniqueHtml);
// credit0 has a reference count of 2. credit1 and credit2 are collapsed
// into one credit with a reference count of 3 and represented by
// credit1. So credit1 should be shown before credit0.
REQUIRE(snapshot.currentCredits.size() == 2);
CHECK(snapshot.currentCredits[0] == credit1);
CHECK(snapshot.currentCredits[1] == credit0);
CHECK(snapshot.removedCredits.empty());
}
}
}
// Refeference count for sorting is the sum of collapsed credit reference
// counts.
}

View File

@ -3,6 +3,7 @@
#include <CesiumUtility/Library.h>
#include <cstdint>
#include <limits>
#include <memory>
#include <string>
#include <unordered_map>
@ -13,6 +14,43 @@ namespace CesiumUtility {
class CreditSystem;
/**
* @brief Specifies how credit system snapshots should handle multiple
* credits with the same HTML string.
*/
enum class CreditFilteringMode : uint8_t {
/**
* @brief No filtering is performed. Each unique @ref Credit is reported.
*/
None = 0,
/**
* @brief Credits are filtered so that each reported credit has a combination
* of HTML string and @ref CreditSystem::shouldBeShownOnScreen value that is
* unique from all the other reported credits.
*
* If multiple credits have the same HTML string but different
* @ref CreditSystem::shouldBeShownOnScreen values, they will be reported as
* separate credits.
*
* It is unspecified which of the multiple credits with the same properties
* will be reported.
*/
UniqueHtmlAndShowOnScreen = 1,
/**
* @brief Credits with identical HTML strings are reported as one Credit even
* if they have a different @ref CreditSource or @ref
* CreditSystem::shouldBeShownOnScreen value.
*
* It is unspecified which of the multiple credits with the same source will
* be reported. However, it is guaranteed that if any of the multiple credits
* has @ref CreditSystem::shouldBeShownOnScreen set to `true`, the reported
* credit will also have it set to `true`.
*/
UniqueHtml = 2
};
/**
* @brief Represents an HTML string that should be shown on screen to attribute
* third parties for used data, imagery, etc. Acts as a handle into a
@ -116,8 +154,15 @@ struct CreditsSnapshot {
*/
class CESIUMUTILITY_API CreditSystem final {
public:
/**
* @brief Constructs a new instance.
*/
CreditSystem() noexcept = default;
~CreditSystem() noexcept;
CreditSystem(const CreditSystem&) = delete;
CreditSystem& operator=(const CreditSystem&) = delete;
/**
* @brief Inserts a credit string.
*
@ -227,8 +272,13 @@ public:
* The snapshot will include a sorted list of credits that are currently
* active, as well as a list of credits that have been removed since the last
* snapshot.
*
* @param filteringMode Specifies how multiple credits with the same HTML
* string should be reported in the snapshot.
*/
const CreditsSnapshot& getSnapshot() noexcept;
const CreditsSnapshot& getSnapshot(
CreditFilteringMode filteringMode =
CreditFilteringMode::UniqueHtml) noexcept;
/**
* @brief Gets the default credit source used when no other source is
@ -240,6 +290,17 @@ public:
const CreditSource& getDefaultCreditSource() const noexcept;
private:
struct CreditRecord {
std::string html{};
bool showOnScreen{false};
int32_t referenceCount{0};
bool shownLastSnapshot{0};
uint32_t generation{0};
const CreditSource* pSource{nullptr};
uint32_t previousCreditWithSameHtml{INVALID_CREDIT_INDEX};
uint32_t nextCreditWithSameHtml{INVALID_CREDIT_INDEX};
};
void addBulkReferences(
const std::vector<int32_t>& references,
const std::vector<uint32_t>& generations) noexcept;
@ -250,22 +311,20 @@ private:
void createCreditSource(CreditSource& creditSource) noexcept;
void destroyCreditSource(CreditSource& creditSource) noexcept;
uint32_t filterCreditForSnapshot(
CreditFilteringMode filteringMode,
const CreditRecord& record) noexcept;
const std::string INVALID_CREDIT_MESSAGE =
"Error: Invalid Credit, cannot get HTML string.";
struct CreditRecord {
std::string html{};
bool showOnScreen{false};
int32_t referenceCount{0};
bool shownLastSnapshot{0};
uint32_t generation{0};
const CreditSource* pSource{nullptr};
};
static const uint32_t INVALID_CREDIT_INDEX{
std::numeric_limits<uint32_t>::max()};
std::vector<CreditSource*> _creditSources;
std::vector<CreditRecord> _credits;
std::vector<Credit> _creditsToNoLongerShowThisSnapshot;
CreditsSnapshot _snapshot;
std::vector<int32_t> _referenceCountScratch;
// Each entry in this vector is an index into _credits that is unused and can
// be reused for a new credit.

View File

@ -61,12 +61,18 @@ Credit CreditSystem::createCredit(
std::string&& html,
bool showOnScreen) {
// If this credit already exists, return a Credit handle to it
uint32_t lastCreditWithSameHtml = INVALID_CREDIT_INDEX;
for (size_t id = 0; id < this->_credits.size(); ++id) {
CreditRecord& record = this->_credits[id];
if (record.pSource == &source && record.html == html) {
if (record.html == html) {
if (record.pSource == &source) {
// Override the existing credit's showOnScreen value.
record.showOnScreen = showOnScreen;
return Credit(uint32_t(id), record.generation);
} else if (record.pSource != nullptr) {
// Same HTML, but different source.
lastCreditWithSameHtml = uint32_t(id);
}
}
}
@ -90,6 +96,14 @@ Credit CreditSystem::createCredit(
// slot was destroyed.
record.pSource = &source;
if (lastCreditWithSameHtml != INVALID_CREDIT_INDEX) {
record.previousCreditWithSameHtml = lastCreditWithSameHtml;
record.nextCreditWithSameHtml = INVALID_CREDIT_INDEX;
CreditRecord& lastRecord = this->_credits[size_t(lastCreditWithSameHtml)];
CESIUM_ASSERT(lastRecord.nextCreditWithSameHtml == INVALID_CREDIT_INDEX);
lastRecord.nextCreditWithSameHtml = uint32_t(creditIndex);
}
return Credit(uint32_t(creditIndex), record.generation);
}
@ -147,19 +161,6 @@ bool CreditSystem::addCreditReference(Credit credit) {
++record.referenceCount;
// If this is the first reference to this credit, and it was shown last frame,
// make sure this credit doesn't exist in _creditsToNoLongerShowThisSnapshot.
if (record.shownLastSnapshot && record.referenceCount == 1) {
this->_creditsToNoLongerShowThisSnapshot.erase(
std::remove_if(
this->_creditsToNoLongerShowThisSnapshot.begin(),
this->_creditsToNoLongerShowThisSnapshot.end(),
[id = credit._id](const Credit& candidate) {
return candidate._id == id;
}),
this->_creditsToNoLongerShowThisSnapshot.end());
}
return true;
}
@ -174,18 +175,18 @@ bool CreditSystem::removeCreditReference(Credit credit) {
CESIUM_ASSERT(record.referenceCount > 0);
--record.referenceCount;
// If this was the last reference to this credit, and it was shown last frame,
// add this credit to _creditsToNoLongerShowThisSnapshot.
if (record.shownLastSnapshot && record.referenceCount == 0) {
this->_creditsToNoLongerShowThisSnapshot.emplace_back(credit);
}
return true;
}
const CreditsSnapshot& CreditSystem::getSnapshot() noexcept {
const CreditsSnapshot&
CreditSystem::getSnapshot(CreditFilteringMode filteringMode) noexcept {
std::vector<Credit>& currentCredits = this->_snapshot.currentCredits;
std::vector<Credit>& removedCredits = this->_snapshot.removedCredits;
currentCredits.clear();
removedCredits.clear();
std::vector<int32_t>& effectiveReferenceCounts = this->_referenceCountScratch;
effectiveReferenceCounts.assign(this->_credits.size(), 0);
for (size_t i = 0; i < this->_credits.size(); ++i) {
CreditRecord& record = this->_credits[i];
@ -194,23 +195,38 @@ const CreditsSnapshot& CreditSystem::getSnapshot() noexcept {
// count of zero.
if (record.referenceCount > 0) {
CESIUM_ASSERT(record.pSource != nullptr);
currentCredits.emplace_back(Credit(uint32_t(i), record.generation));
record.shownLastSnapshot = true;
// This credit is active, but it may be filtered out in favor of another
// credit with the same HTML.
uint32_t filteredInFavorOf =
this->filterCreditForSnapshot(filteringMode, record);
if (filteredInFavorOf != INVALID_CREDIT_INDEX) {
// Credit filtered out in favor of another credit.
// That other credit inherits this credit's reference count.
effectiveReferenceCounts[filteredInFavorOf] += record.referenceCount;
if (record.shownLastSnapshot) {
removedCredits.emplace_back(Credit(uint32_t(i), record.generation));
record.shownLastSnapshot = false;
}
} else {
// This credit is not filtered.
currentCredits.emplace_back(Credit(uint32_t(i), record.generation));
effectiveReferenceCounts[i] += record.referenceCount;
record.shownLastSnapshot = true;
}
} else if (record.shownLastSnapshot) {
removedCredits.emplace_back(Credit(uint32_t(i), record.generation));
record.shownLastSnapshot = false;
}
}
this->_creditsToNoLongerShowThisSnapshot.swap(this->_snapshot.removedCredits);
this->_creditsToNoLongerShowThisSnapshot.clear();
// sort credits based on the number of occurrences
std::sort(
currentCredits.begin(),
currentCredits.end(),
[this](const Credit& a, const Credit& b) {
int32_t aCounts = this->_credits[a._id].referenceCount;
int32_t bCounts = this->_credits[b._id].referenceCount;
[&](const Credit& a, const Credit& b) {
int32_t aCounts = effectiveReferenceCounts[a._id];
int32_t bCounts = effectiveReferenceCounts[b._id];
if (aCounts == bCounts)
return a._id < b._id;
else
@ -238,20 +254,6 @@ void CreditSystem::addBulkReferences(
int32_t referencesToAdd = references[i];
record.referenceCount += referencesToAdd;
// If this is the first reference to this credit, and it was shown last
// frame, make sure this credit doesn't exist in
// _creditsToNoLongerShowThisSnapshot.
if (record.shownLastSnapshot && record.referenceCount == referencesToAdd) {
this->_creditsToNoLongerShowThisSnapshot.erase(
std::remove_if(
this->_creditsToNoLongerShowThisSnapshot.begin(),
this->_creditsToNoLongerShowThisSnapshot.end(),
[i = uint32_t(i)](const Credit& candidate) {
return candidate._id == i;
}),
this->_creditsToNoLongerShowThisSnapshot.end());
}
}
}
@ -270,13 +272,6 @@ void CreditSystem::releaseBulkReferences(
CESIUM_ASSERT(record.referenceCount >= referencesToRemove);
record.referenceCount -= referencesToRemove;
// If this was the last reference to this credit, and it was shown last
// frame, add this credit to _creditsToNoLongerShowThisSnapshot.
if (record.shownLastSnapshot && record.referenceCount == 0) {
this->_creditsToNoLongerShowThisSnapshot.emplace_back(
Credit(uint32_t(i), record.generation));
}
}
}
@ -291,17 +286,24 @@ void CreditSystem::destroyCreditSource(CreditSource& creditSource) noexcept {
++record.generation;
this->_unusedCreditRecords.emplace_back(&record - this->_credits.data());
if (record.referenceCount > 0) {
record.referenceCount = 0;
} else {
this->_creditsToNoLongerShowThisSnapshot.erase(
std::remove_if(
this->_creditsToNoLongerShowThisSnapshot.begin(),
this->_creditsToNoLongerShowThisSnapshot.end(),
[id = uint32_t(&record - this->_credits.data())](
const Credit& candidate) { return candidate._id == id; }),
this->_creditsToNoLongerShowThisSnapshot.end());
// Delete this record from the linked list of credits with the same HTML.
if (record.nextCreditWithSameHtml != INVALID_CREDIT_INDEX) {
CreditRecord& nextRecord =
this->_credits[record.nextCreditWithSameHtml];
nextRecord.previousCreditWithSameHtml =
record.previousCreditWithSameHtml;
record.nextCreditWithSameHtml = INVALID_CREDIT_INDEX;
}
if (record.previousCreditWithSameHtml != INVALID_CREDIT_INDEX) {
CreditRecord& previousRecord =
this->_credits[record.previousCreditWithSameHtml];
previousRecord.nextCreditWithSameHtml = record.nextCreditWithSameHtml;
record.previousCreditWithSameHtml = INVALID_CREDIT_INDEX;
}
record.referenceCount = 0;
record.shownLastSnapshot = false;
}
}
@ -313,4 +315,72 @@ void CreditSystem::destroyCreditSource(CreditSource& creditSource) noexcept {
this->_creditSources.end());
}
uint32_t CreditSystem::filterCreditForSnapshot(
CreditFilteringMode filteringMode,
const CreditRecord& record) noexcept {
if (filteringMode == CreditFilteringMode::None) {
return CreditSystem::INVALID_CREDIT_INDEX;
} else if (filteringMode == CreditFilteringMode::UniqueHtmlAndShowOnScreen) {
uint32_t currentCreditIndex = record.previousCreditWithSameHtml;
while (currentCreditIndex != INVALID_CREDIT_INDEX) {
const CreditRecord& otherRecord =
this->_credits[size_t(currentCreditIndex)];
// If the other credit has the same showOnScreen value, filter this one
// out in favor of it.
if (otherRecord.showOnScreen == record.showOnScreen) {
return currentCreditIndex;
}
currentCreditIndex = otherRecord.previousCreditWithSameHtml;
}
return CreditSystem::INVALID_CREDIT_INDEX;
} else {
CESIUM_ASSERT(filteringMode == CreditFilteringMode::UniqueHtml);
// In this filtering mode, we need to find the first credit in the linked
// list with showOnScreen=true. Or if they're all showOnScreen=false, return
// the first one. Unlike `UniqueHtmlAndShowOnScreen`, the Credit we want may
// occur after the current one.
// Walk backwards to find the first credit in the linked list.
uint32_t previousCreditIndex = uint32_t(&record - this->_credits.data());
uint32_t firstCreditIndex;
do {
firstCreditIndex = previousCreditIndex;
const CreditRecord& otherRecord =
this->_credits[size_t(firstCreditIndex)];
previousCreditIndex = otherRecord.previousCreditWithSameHtml;
} while (previousCreditIndex != INVALID_CREDIT_INDEX);
// Walk forward from the first credit to find one with showOnScreen=true (if
// any).
uint32_t currentCreditIndex = firstCreditIndex;
while (currentCreditIndex != INVALID_CREDIT_INDEX) {
const CreditRecord& otherRecord =
this->_credits[size_t(currentCreditIndex)];
if (otherRecord.showOnScreen) {
// Found the first credit with showOnScreen=true. Filter this credit out
// in favor of it. Unless the currentCreditIndex points to the same
// credit we started with!
return currentCreditIndex == uint32_t(&record - this->_credits.data())
? CreditSystem::INVALID_CREDIT_INDEX
: currentCreditIndex;
}
currentCreditIndex = otherRecord.nextCreditWithSameHtml;
}
// Reached the end of the linked list without finding any credit with
// showOnScreen=true. So return the first credit in the list. Unless the
// firstCreditIndex points to the same credit we started with!
return firstCreditIndex == uint32_t(&record - this->_credits.data())
? CreditSystem::INVALID_CREDIT_INDEX
: firstCreditIndex;
}
}
} // namespace CesiumUtility