diff options
Diffstat (limited to 'src/decoder/tools/astc_inspector_cli.cc')
-rw-r--r-- | src/decoder/tools/astc_inspector_cli.cc | 785 |
1 files changed, 785 insertions, 0 deletions
diff --git a/src/decoder/tools/astc_inspector_cli.cc b/src/decoder/tools/astc_inspector_cli.cc new file mode 100644 index 0000000..105f574 --- /dev/null +++ b/src/decoder/tools/astc_inspector_cli.cc @@ -0,0 +1,785 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// astc_inspector_cli collects the various statistics of a stream of ASTC data +// stored in an ASTC file. +// +// Example usage: +// To dump statistics about an ASTC file, use: +// astc_inspector_cli <filename> +// +// To dump statistics on a specific block in an ASTC file, use: +// astc_inspector_cli <filename> <number> + +#include <algorithm> +#include <array> +#include <fstream> +#include <functional> +#include <iomanip> +#include <iostream> +#include <memory> +#include <numeric> +#include <sstream> +#include <string> +#include <unordered_map> +#include <unordered_set> +#include <vector> + +#include "src/base/string_utils.h" +#include "src/decoder/astc_file.h" +#include "src/decoder/endpoint_codec.h" +#include "src/decoder/intermediate_astc_block.h" +#include "src/decoder/partition.h" +#include "src/decoder/quantization.h" +#include "src/decoder/weight_infill.h" + +using astc_codec::ASTCFile; +using astc_codec::ColorEndpointMode; +using astc_codec::IntermediateBlockData; +using astc_codec::PhysicalASTCBlock; +using astc_codec::RgbaColor; +using astc_codec::VoidExtentData; +using astc_codec::base::Optional; + +namespace { + +constexpr int kNumEndpointModes = + static_cast<int>(ColorEndpointMode::kNumColorEndpointModes); +constexpr std::array<const char*, kNumEndpointModes> kModeStrings {{ + "kLDRLumaDirect", "kLDRLumaBaseOffset", "kHDRLumaLargeRange", + "kHDRLumaSmallRange", "kLDRLumaAlphaDirect", "kLDRLumaAlphaBaseOffset", + "kLDRRGBBaseScale", "kHDRRGBBaseScale", "kLDRRGBDirect", + "kLDRRGBBaseOffset", "kLDRRGBBaseScaleTwoA", "kHDRRGBDirect", + "kLDRRGBADirect", "kLDRRGBABaseOffset", "kHDRRGBDirectLDRAlpha", + "kHDRRGBDirectHDRAlpha" }}; + +//////////////////////////////////////////////////////////////////////////////// +// +// A generic stat that should be tracked via an instance of ASTCFileStats. +class Stat { + public: + explicit Stat(const std::vector<IntermediateBlockData>* blocks, size_t total) + : blocks_(blocks), total_(total) { } + virtual ~Stat() { } + + virtual std::ostream& PrintToStream(std::ostream& out) const = 0; + + protected: + // Utility function to iterate over all of the blocks that are not void-extent + // blocks. FoldFn optionally allows a value to accumulate. It should be of the + // type: + // (const IntermediateBlockData&, T x) -> T + template<typename T, typename FoldFn> + T IterateBlocks(T initial, FoldFn f) const { + T result = initial; + for (const auto& block : *blocks_) { + result = f(block, std::move(result)); + } + return result; + } + + size_t NumBlocks() const { return total_; } + + private: + const std::vector<IntermediateBlockData>* const blocks_; + const size_t total_; +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// Computes the number of void extent blocks. +class VoidExtentCount : public Stat { + public: + VoidExtentCount(const std::vector<IntermediateBlockData>* blocks, + size_t total, std::string description) + : Stat(blocks, total), description_(std::move(description)), + count_(total - blocks->size()) { } + + std::ostream& PrintToStream(std::ostream& out) const override { + return out << description_ << ": " << count_ + << " (" << (count_ * 100 / NumBlocks()) << "%)" << std::endl; + }; + + private: + const std::string description_; + const size_t count_; +}; + +////////////////////////////////////////////////////////////////////////////// +// +// Computes a per-block stat and reports it as an average over all blocks. +class PerBlockAverage : public Stat { + public: + PerBlockAverage(const std::vector<IntermediateBlockData>* blocks, + size_t total, std::string description, + const std::function<int(const IntermediateBlockData&)> &fn) + : Stat(blocks, total), + description_(std::move(description)) { + int sum = 0; + size_t count = 0; + for (const auto& block : *blocks) { + sum += fn(block); + ++count; + } + average_ = sum / count; + } + + std::ostream& PrintToStream(std::ostream& out) const override { + return out << description_ << ": " << average_ << std::endl; + } + + private: + size_t average_; + std::string description_; +}; + +////////////////////////////////////////////////////////////////////////////// +// +// Computes a per-block true or false value and reports how many blocks return +// true with a percentage of total blocks. +class PerBlockPredicate : public Stat { + public: + PerBlockPredicate(const std::vector<IntermediateBlockData>* blocks, + size_t total, std::string description, + const std::function<bool(const IntermediateBlockData&)> &fn) + : Stat(blocks, total), + description_(std::move(description)), + count_(std::count_if(blocks->begin(), blocks->end(), fn)) { } + + std::ostream& PrintToStream(std::ostream& out) const override { + return out << description_ << ": " << count_ + << " (" << (count_ * 100 / NumBlocks()) << "%)" << std::endl; + }; + + private: + const std::string description_; + const size_t count_; +}; + +////////////////////////////////////////////////////////////////////////////// +// +// Returns a histogram of the number of occurrences of each endpoint mode in +// the list of blocks. Note, due to multi-subset blocks, the sum of these +// values will not match the total number of blocks. +class ModeCountsStat : public Stat { + public: + explicit ModeCountsStat(const std::vector<IntermediateBlockData>* blocks, + size_t total) + : Stat(blocks, total), + mode_counts_(IterateBlocks<ModeArray>( + {}, [](const IntermediateBlockData& data, ModeArray&& m) { + auto result = m; + for (const auto& ep : data.endpoints) { + result[static_cast<int>(ep.mode)]++; + } + return result; + })) { } + + std::ostream& PrintToStream(std::ostream& out) const override { + const size_t total_modes_used = + std::accumulate(mode_counts_.begin(), mode_counts_.end(), 0); + + out << "Endpoint modes used: " << std::endl; + for (size_t i = 0; i < kNumEndpointModes; ++i) { + out << " "; + out << std::setw(30) << std::left << std::setfill('.') << kModeStrings[i]; + out << std::setw(8) << std::right << std::setfill('.') << mode_counts_[i]; + + std::stringstream pct; + pct << " (" << (mode_counts_[i] * 100 / total_modes_used) << "%)"; + + out << std::setw(6) << std::right << std::setfill(' ') << pct.str(); + out << std::endl; + } + + return out; + } + + private: + using ModeArray = std::array<int, kNumEndpointModes>; + const ModeArray mode_counts_; +}; + +////////////////////////////////////////////////////////////////////////////// +// +// Counts the number of unique endpoints used across all blocks. +class UniqueEndpointsCount : public Stat { + public: + explicit UniqueEndpointsCount( + const std::vector<IntermediateBlockData>* blocks, size_t total) + : Stat(blocks, total), + unique_endpoints_(IterateBlocks<UniqueEndpointSet>( + UniqueEndpointSet(), + [](const IntermediateBlockData& data, UniqueEndpointSet&& eps) { + UniqueEndpointSet result(eps); + for (const auto& ep : data.endpoints) { + RgbaColor ep_one, ep_two; + DecodeColorsForMode(ep.colors, data.endpoint_range.value(), + ep.mode, &ep_one, &ep_two); + result.insert(PackEndpoint(ep_one)); + result.insert(PackEndpoint(ep_two)); + } + return result; + })) { } + + std::ostream& PrintToStream(std::ostream& out) const override { + out << "Num unique endpoints: " << unique_endpoints_.size() << std::endl; + return out; + } + + private: + static uint32_t PackEndpoint(const RgbaColor& color) { + uint32_t result = 0; + for (const int& c : color) { + constexpr int kSaturatedChannelValue = 0xFF; + assert(c >= 0); + assert(c <= kSaturatedChannelValue); + result <<= 8; + result |= c; + } + return result; + } + + using UniqueEndpointSet = std::unordered_set<uint32_t>; + const UniqueEndpointSet unique_endpoints_; +}; + +////////////////////////////////////////////////////////////////////////////// +// +// Computes a histogram of the number of occurrences of 1-4 subset partitions. +class PartitionCountStat : public Stat { + public: + explicit PartitionCountStat(const std::vector<IntermediateBlockData>* blocks, + size_t total) + : Stat(blocks, total) + , part_counts_(IterateBlocks<PartCount>( + {}, [](const IntermediateBlockData& data, PartCount&& m) { + PartCount result = m; + result[data.endpoints.size() - 1]++; + return result; + })) { } + + std::ostream& PrintToStream(std::ostream& out) const override { + out << "Num partitions used: " << std::endl; + for (size_t i = 0; i < part_counts_.size(); ++i) { + out << " " << i + 1 << ": " << part_counts_[i] << std::endl; + } + return out; + } + + private: + using PartCount = std::array<int, 4>; + const PartCount part_counts_; +}; + +////////////////////////////////////////////////////////////////////////////// +// +// For each block that uses dual-plane mode, computes and stores the dual-plane +// channels in a vector. Outputs the number of each channel used across all +// blocks +class DualChannelStat : public Stat { + private: + static constexpr auto kNumDualPlaneChannels = + std::tuple_size<astc_codec::Endpoint>::value; + using CountsArray = std::array<int, kNumDualPlaneChannels>; + + public: + explicit DualChannelStat(const std::vector<IntermediateBlockData>* blocks, + size_t total) + : Stat(blocks, total), + dual_channels_(IterateBlocks( + std::vector<int>(), + [](const IntermediateBlockData& data, std::vector<int>&& input) { + auto result = input; + if (data.dual_plane_channel) { + result.push_back(data.dual_plane_channel.value()); + } + return result; + })) { } + + std::ostream& PrintToStream(std::ostream& out) const override { + // Similar to the number of partitions, the number of dual plane blocks + // can be determined by parsing the next four fields and summing them. + const int num_dual_plane_blocks = dual_channels_.size(); + out << "Number of dual-plane blocks: " << num_dual_plane_blocks + << " (" << (num_dual_plane_blocks * 100) / NumBlocks() << "%)" + << std::endl; + + CountsArray counts = GetCounts(); + assert(counts.size() == kNumDualPlaneChannels); + + for (size_t i = 0; i < counts.size(); ++i) { + out << " " << i << ": " << counts[i] << std::endl; + } + return out; + } + + private: + CountsArray GetCounts() const { + CountsArray counts; + for (size_t i = 0; i < kNumDualPlaneChannels; ++i) { + counts[i] = + std::count_if(dual_channels_.begin(), dual_channels_.end(), + [i](int channel) { return i == channel; }); + } + return counts; + } + + const std::vector<int> dual_channels_; +}; + + +// Stores the intermediate block representations of the blocks associated with +// an ASTCFile. Also provides various facilities for extracting aggregate data +// from these blocks. +class ASTCFileStats { + public: + explicit ASTCFileStats(const std::unique_ptr<ASTCFile>& astc_file) { + const size_t total = astc_file->NumBlocks(); + + for (size_t block_idx = 0; block_idx < astc_file->NumBlocks(); ++block_idx) { + const PhysicalASTCBlock pb = astc_file->GetBlock(block_idx); + assert(!pb.IsIllegalEncoding()); + if (pb.IsIllegalEncoding()) { + std::cerr << "WARNING: Block " << block_idx << " has illegal encoding." << std::endl; + continue; + } + + if (!pb.IsVoidExtent()) { + Optional<IntermediateBlockData> block = UnpackIntermediateBlock(pb); + if (!block) { + std::cerr << "WARNING: Block " << block_idx << " failed to unpack." << std::endl; + continue; + } + + blocks_.push_back(block.value()); + } + } + + stats_.emplace_back(new UniqueEndpointsCount(&blocks_, total)); + stats_.emplace_back(new VoidExtentCount( + &blocks_, total, "Num void extent blocks")); + + stats_.emplace_back(new PerBlockAverage( + &blocks_, total, "Average weight range", + [](const IntermediateBlockData& b) { return b.weight_range; })); + + stats_.emplace_back(new PerBlockAverage( + &blocks_, total, "Average number of weights", + [](const IntermediateBlockData& b) { return b.weights.size(); })); + + stats_.emplace_back(new PerBlockPredicate( + &blocks_, total, "Num blocks that use blue contract mode", + [](const IntermediateBlockData& block) { + for (const auto& ep : block.endpoints) { + if (UsesBlueContract( + block.endpoint_range.valueOr(255), ep.mode, ep.colors)) { + return true; + } + } + + return false; + })); + + stats_.emplace_back(new ModeCountsStat(&blocks_, total)); + + stats_.emplace_back(new PerBlockPredicate( + &blocks_, total, "Num multi-part blocks", + [](const IntermediateBlockData& block) { + return block.endpoints.size() > 1; + })); + stats_.emplace_back(new PartitionCountStat(&blocks_, total)); + + stats_.emplace_back(new DualChannelStat(&blocks_, total)); + } + + // Returns a sorted list of pairs of the form (part_id, count) where the + // |part_id| is the partition ID used for 2-subset blocks, and |count| is the + // number of times that particular ID was used. + std::vector<std::pair<int, int>> ComputePartIDHistogram() const { + std::vector<int> part_ids(1 << 11, 0); + std::iota(part_ids.begin(), part_ids.end(), 0); + + // The histogram will then pair IDs with counts so that we can sort by + // the number of instances later on. + std::vector<std::pair<int, int>> part_id_histogram; + std::transform(part_ids.begin(), part_ids.end(), + std::back_inserter(part_id_histogram), + [](const int& x) { return std::make_pair(x, 0); }); + + // Actually count the IDs in the list of blocks. + for (const auto& block : blocks_) { + if (block.endpoints.size() == 2) { + const int id = block.partition_id.value(); + assert(part_id_histogram[id].first == id); + part_id_histogram[id].second++; + } + } + + struct OrderBySecondGreater { + typedef std::pair<int, int> PairType; + bool operator()(const PairType& lhs, const PairType& rhs) { + return lhs.second > rhs.second; + } + }; + + // Sort by descending numbers of occurrence for each partition ID + std::sort(part_id_histogram.begin(), part_id_histogram.end(), + OrderBySecondGreater()); + + return part_id_histogram; + } + + // Weights range from 2x2 - 12x12. For simplicity define buckets for every + // pair in [0, 12]^2. + constexpr static int kResolutionBuckets = 13; + // Returns a linear array of buckets over all pairs of grid resolutions, + // x-major in memory. + std::vector<int> ComputeWeightResolutionHistogram() const { + // Allocate one bucket for every grid resolution. + std::vector<int> resolution_histogram( + kResolutionBuckets * kResolutionBuckets, 0); + + // Count the weight resolutions in the list of blocks. + for (const auto& block : blocks_) { + const int dim_x = block.weight_grid_dim_x; + const int dim_y = block.weight_grid_dim_y; + assert(dim_x > 0); + assert(dim_x < kResolutionBuckets); + assert(dim_y > 0); + assert(dim_y < kResolutionBuckets); + ++resolution_histogram[dim_x + dim_y * kResolutionBuckets]; + } + + return resolution_histogram; + } + + // Runs through each defined statistic and prints it out to stdout. Also + // prints a histogram of partition ids used for the given blocks. + void PrintStats() const { + for (const auto& stat : stats_) { + stat->PrintToStream(std::cout); + } + + // We also want to find if there are any 2-subset partition IDs that are + // used disproportionately often. Since partition IDs are 11 bits long, we + // can have as many as (1 << 11) used IDs in a given sequence of blocks. + const auto part_id_histogram = ComputePartIDHistogram(); + const int total_part_ids = std::accumulate( + part_id_histogram.begin(), part_id_histogram.end(), 0, + [](const int& x, const std::pair<int, int>& hist) { + return x + hist.second; + }); + + if (total_part_ids > 0) { + // Display numbers until we either: + // A. Display the top 90% of used partitions + // B. Reach a point where the remaining partition IDs constitute < 1% of + // the total number of IDs used. + const auto prepare_part_entry = []() -> std::ostream& { + return std::cout << std::setw(6) << std::left << std::setfill('.'); + }; + int part_accum = 0; + std::cout << "Two subset partition ID histogram: " << std::endl; + std::cout << " "; + prepare_part_entry() << "ID" << "Count" << std::endl; + for (const auto& hist : part_id_histogram) { + part_accum += hist.second; + if ((hist.second * 100 / total_part_ids) < 1 || + (100 * (total_part_ids - part_accum)) / total_part_ids < 10) { + const int num_to_display = (total_part_ids - part_accum); + std::cout << " rest: " << num_to_display + << " (" << (num_to_display * 100 / total_part_ids) + << "%)" << std::endl; + break; + } else { + std::cout << " "; + prepare_part_entry() << hist.first << hist.second + << " (" << (hist.second * 100 / total_part_ids) + << "%)" << std::endl; + } + } + } + + // Build the 2D histogram of resolutions. + std::vector<int> weight_histogram = ComputeWeightResolutionHistogram(); + // Labels the weight resolution table. + std::cout << "Weight resolutions:" << std::endl; + const auto prepare_weight_entry = []() -> std::ostream& { + return std::cout << std::setw(6) << std::left << std::setfill(' '); + }; + prepare_weight_entry() << "H W"; + for (int resolution_x = 2; resolution_x < kResolutionBuckets; + ++resolution_x) { + prepare_weight_entry() << resolution_x; + } + std::cout << std::endl; + + // Displays table; skips rows/cols {0, 1} since they will always be empty. + for (int resolution_y = 2; resolution_y < kResolutionBuckets; + ++resolution_y) { + prepare_weight_entry() << resolution_y; + for (int resolution_x = 2; resolution_x < kResolutionBuckets; + ++resolution_x) { + const int count = + weight_histogram[resolution_x + resolution_y * kResolutionBuckets]; + prepare_weight_entry(); + if (!count) { + std::cout << "*"; + } else { + std::cout << count; + } + } + std::cout << std::endl; + } + } + + size_t NumBlocks() const { return blocks_.size(); } + + private: + std::vector<std::unique_ptr<Stat>> stats_; + std::vector<IntermediateBlockData> blocks_; +}; + +std::ostream& operator<<(std::ostream& stream, const RgbaColor& color) { + stream << "{"; + constexpr int kNumChannels = std::tuple_size<RgbaColor>::value; + for (int i = 0; i < kNumChannels; ++i) { + stream << color[i]; + if (i < (kNumChannels - 1)) { + stream << ", "; + } + } + return stream << "}"; +} + +void PrintStatsForBlock(const PhysicalASTCBlock& pb, + astc_codec::Footprint footprint) { + const auto print_void_extent = [&pb](const VoidExtentData& void_extent_data) { + std::cout << "Void extent block:" << std::endl; + std::cout << " 16-bit RGBA: {" + << void_extent_data.r << ", " + << void_extent_data.g << ", " + << void_extent_data.b << ", " + << void_extent_data.a << "}" << std::endl; + if (pb.VoidExtentCoords()) { + std::cout << " Extent (S): {" + << void_extent_data.coords[0] << ", " + << void_extent_data.coords[1] << "}" << std::endl; + std::cout << " Extent (T): {" + << void_extent_data.coords[2] << ", " + << void_extent_data.coords[3] << "}" << std::endl; + } else { + std::cout << " No valid extent data" << std::endl; + } + }; + + const auto print_endpoint_data = + [](ColorEndpointMode mode, int endpoint_range, + const std::vector<int>& encoded_vals) { + std::cout << " Endpoint mode: " + << kModeStrings[static_cast<int>(mode)] << std::endl; + std::cout << " Uses blue-contract mode: " + << (UsesBlueContract(endpoint_range, mode, encoded_vals) + ? "true" : "false") + << std::endl; + + RgbaColor endpoint_low, endpoint_high; + DecodeColorsForMode(encoded_vals, endpoint_range, mode, + &endpoint_low, &endpoint_high); + + std::cout << " Low endpoint: " << endpoint_low << std::endl; + std::cout << " High endpoint: " << endpoint_high << std::endl; + }; + + const auto print_color_data = + [&print_endpoint_data, &footprint](const IntermediateBlockData& ib_data) { + const int endpoint_range = ib_data.endpoint_range.value(); + std::cout << "Endpoint range: " << endpoint_range << std::endl; + + const int num_parts = ib_data.endpoints.size(); + if (ib_data.partition_id.hasValue()) { + const int part_id = ib_data.partition_id.value(); + std::cout << "Parititon ID: " << part_id << std::endl; + + const auto part = GetASTCPartition(footprint, num_parts, part_id); + assert(part.assignment.size() == footprint.Height() * footprint.Width()); + + std::cout << "Assignment:" << std::endl; + for (int y = 0; y < footprint.Height(); ++y) { + std::cout << " "; + for (int x = 0; x < footprint.Width(); ++x) { + const int texel_index = y * footprint.Width() + x; + std::cout << " " << part.assignment[texel_index]; + } + std::cout << std::endl; + } + } else { + std::cout << "Single partition" << std::endl; + } + + int endpoint_index = 0; + for (const auto& ep_data : ib_data.endpoints) { + if (num_parts == 1) { + std::cout << "Endpoints:" << std::endl; + } else { + std::cout << "Endpoint " << (endpoint_index++) << ": " << std::endl; + } + print_endpoint_data(ep_data.mode, endpoint_range, ep_data.colors); + } + + if (ib_data.dual_plane_channel) { + std::cout << "Dual plane channel: " + << ib_data.dual_plane_channel.value() << std::endl; + } else { + std::cout << "Single plane" << std::endl; + } + }; + + const auto print_weight_data = + [&footprint](const IntermediateBlockData& ib_data) { + std::cout << "Weight grid dimensions: " + << ib_data.weight_grid_dim_x << "x" << ib_data.weight_grid_dim_y + << std::endl; + std::cout << "Weight range: " << ib_data.weight_range << std::endl; + + std::cout << "Encoded weight grid: " << std::endl; + int weight_idx = 0; + for (int j = 0; j < ib_data.weight_grid_dim_y; ++j) { + std::cout << " "; + for (int i = 0; i < ib_data.weight_grid_dim_x; ++i) { + std::cout << std::setw(3) << std::left << std::setfill(' ') + << ib_data.weights[weight_idx++]; + } + std::cout << std::endl; + } + + std::cout << "Actual weight grid: " << std::endl; + std::vector<int> actual_weights = ib_data.weights; + for (auto& weight : actual_weights) { + weight = astc_codec::UnquantizeWeightFromRange( + weight, ib_data.weight_range); + } + + actual_weights = astc_codec::InfillWeights( + actual_weights, footprint, ib_data.weight_grid_dim_x, + ib_data.weight_grid_dim_y); + + weight_idx = 0; + for (int j = 0; j < footprint.Height(); ++j) { + std::cout << " "; + for (int i = 0; i < footprint.Width(); ++i) { + std::cout << std::setw(3) << std::left << std::setfill(' ') + << actual_weights[weight_idx++]; + } + std::cout << std::endl; + } + }; + + if (pb.IsVoidExtent()) { + Optional<VoidExtentData> ve = astc_codec::UnpackVoidExtent(pb); + if (!ve) { + std::cerr << "ERROR: Failed to unpack void extent block." << std::endl; + } else { + print_void_extent(ve.value()); + } + } else { + Optional<IntermediateBlockData> ib = + astc_codec::UnpackIntermediateBlock(pb); + if (!ib) { + std::cerr << "ERROR: Failed to unpack intermediate block." << std::endl; + } else { + const auto& ib_data = ib.value(); + print_color_data(ib_data); + print_weight_data(ib_data); + } + } +} + +} // namespace + +int main(int argc, char* argv[]) { + bool error = false; + + std::string filename; + size_t block_index = 0; + bool has_block_index = false; + + if (argc >= 2) { + filename = argv[1]; + + if (argc == 3) { + int32_t param = astc_codec::base::ParseInt32(argv[2], -1); + if (param < 0) { + std::cerr << "ERROR: Invalid block index." << std::endl; + error = true; + } else { + block_index = static_cast<size_t>(param); + has_block_index = true; + } + } else if (argc != 2) { + std::cerr << "ERROR: Too many parameters." << std::endl; + error = true; + } + } else { + error = true; + } + + if (error) { + std::cout << ((argc >= 0) ? argv[0] : "astc_inspector_cli") + << " <filename> [<block index>]" << std::endl + << std::endl + << "Collects the various statistics of a stream of ASTC data " + << "stored in an ASTC file." << std::endl + << std::endl + << " filename ASTC file path." << std::endl + << " block index If specified, show detailed information about a block" + << std::endl; + return 1; + } + + std::string error_string; + std::unique_ptr<ASTCFile> astc_file = ASTCFile::LoadFile(argv[1], &error_string); + if (!astc_file) { + std::cerr << "ERROR: " << error_string << std::endl; + return 2; + } + + if (has_block_index) { + Optional<astc_codec::Footprint> footprint = + astc_codec::Footprint::Parse(astc_file->GetFootprintString().c_str()); + if (!footprint) { + std::cerr << "ERROR: Invalid footprint \"" << astc_file->GetFootprintString() << "\"" << std::endl; + return 3; + } + + PrintStatsForBlock(astc_file->GetBlock(block_index), footprint.value()); + } else { + std::cout << "Dimensions: " << astc_file->GetWidth() << "x" + << astc_file->GetHeight() << ", depth " << astc_file->GetDepth() + << std::endl; + + ASTCFileStats stats(astc_file); + + std::cout << std::endl + << "Total bits used: " << 128 * astc_file->NumBlocks() + << " (" << astc_file->NumBlocks() << " blocks, " + << (astc_file->NumBlocks() * 16) << " bytes)" + << std::endl << std::endl; + + stats.PrintStats(); + } + + return 0; +} |