Cleanup.
5
3rdparty/astc-codec/.gitignore
vendored
@@ -1,5 +0,0 @@
|
|||||||
bazel-*
|
|
||||||
.bazelrc
|
|
||||||
build
|
|
||||||
.vs
|
|
||||||
.vscode
|
|
||||||
29
3rdparty/astc-codec/BUILD.bazel
vendored
@@ -1,29 +0,0 @@
|
|||||||
# 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.
|
|
||||||
|
|
||||||
licenses(["notice"])
|
|
||||||
|
|
||||||
cc_library(
|
|
||||||
name = "api",
|
|
||||||
hdrs = ["include/astc-codec/astc-codec.h"],
|
|
||||||
visibility = ["//src/decoder:__pkg__"],
|
|
||||||
)
|
|
||||||
|
|
||||||
cc_library(
|
|
||||||
name = "astc_codec",
|
|
||||||
hdrs = ["include/astc-codec/astc-codec.h"],
|
|
||||||
includes = ["include"],
|
|
||||||
visibility = ["//visibility:public"],
|
|
||||||
deps = ["//src/decoder:codec"],
|
|
||||||
)
|
|
||||||
46
3rdparty/astc-codec/CMakeLists.txt
vendored
@@ -1,46 +0,0 @@
|
|||||||
# 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.
|
|
||||||
cmake_minimum_required(VERSION 3.1.0)
|
|
||||||
project(astc-codec)
|
|
||||||
|
|
||||||
option(OPTION_ASTC_TESTS "Build all the unit tests." ON)
|
|
||||||
|
|
||||||
# TODO add support for the fuzzer, it has some additional dependencies we are not
|
|
||||||
# yet bringing in.
|
|
||||||
option(OPTION_BUILD_FUZZER "Build the fuzzer tests." OFF)
|
|
||||||
|
|
||||||
set (CMAKE_CXX_STANDARD 11)
|
|
||||||
if(OPTION_ASTC_TESTS)
|
|
||||||
enable_testing()
|
|
||||||
|
|
||||||
# No need to build gmock if an external project defines it.
|
|
||||||
if(NOT TARGET gmock_main)
|
|
||||||
# We use the approach suggested by https://crascit.com/2015/07/25/cmake-gtest/ to download gtest.
|
|
||||||
include(ExternalProject)
|
|
||||||
# Download and unpack googletest at configure time
|
|
||||||
configure_file(GoogleTest-CMakeLists.txt.in googletest-download/CMakeLists.txt)
|
|
||||||
execute_process(COMMAND "${CMAKE_COMMAND}" -G "${CMAKE_GENERATOR}" .
|
|
||||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/googletest-download")
|
|
||||||
execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/googletest-download")
|
|
||||||
|
|
||||||
# Prevent GoogleTest from overriding our compiler/linker options when building with Visual Studio
|
|
||||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
|
||||||
|
|
||||||
# Add googletest directly to our build. This adds the following targets: gtest, gtest_main, gmock and gmock_main
|
|
||||||
add_subdirectory("${CMAKE_BINARY_DIR}/googletest-src" "${CMAKE_BINARY_DIR}/googletest-build")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_subdirectory(src/base)
|
|
||||||
add_subdirectory(src/decoder)
|
|
||||||
28
3rdparty/astc-codec/CONTRIBUTING.md
vendored
@@ -1,28 +0,0 @@
|
|||||||
# How to Contribute
|
|
||||||
|
|
||||||
We'd love to accept your patches and contributions to this project. There are
|
|
||||||
just a few small guidelines you need to follow.
|
|
||||||
|
|
||||||
## Contributor License Agreement
|
|
||||||
|
|
||||||
Contributions to this project must be accompanied by a Contributor License
|
|
||||||
Agreement. You (or your employer) retain the copyright to your contribution;
|
|
||||||
this simply gives us permission to use and redistribute your contributions as
|
|
||||||
part of the project. Head over to <https://cla.developers.google.com/> to see
|
|
||||||
your current agreements on file or to sign a new one.
|
|
||||||
|
|
||||||
You generally only need to submit a CLA once, so if you've already submitted one
|
|
||||||
(even if it was for a different project), you probably don't need to do it
|
|
||||||
again.
|
|
||||||
|
|
||||||
## Code reviews
|
|
||||||
|
|
||||||
All submissions, including submissions by project members, require review. We
|
|
||||||
use GitHub pull requests for this purpose. Consult
|
|
||||||
[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
|
|
||||||
information on using pull requests.
|
|
||||||
|
|
||||||
## Community Guidelines
|
|
||||||
|
|
||||||
This project follows [Google's Open Source Community
|
|
||||||
Guidelines](https://opensource.google.com/conduct/).
|
|
||||||
15
3rdparty/astc-codec/GoogleTest-CMakeLists.txt.in
vendored
@@ -1,15 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 2.8.2)
|
|
||||||
|
|
||||||
project(googletest-download NONE)
|
|
||||||
|
|
||||||
include(ExternalProject)
|
|
||||||
ExternalProject_Add(googletest
|
|
||||||
GIT_REPOSITORY https://github.com/google/googletest.git
|
|
||||||
GIT_TAG "release-1.8.1"
|
|
||||||
SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-src"
|
|
||||||
BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-build"
|
|
||||||
CONFIGURE_COMMAND ""
|
|
||||||
BUILD_COMMAND ""
|
|
||||||
INSTALL_COMMAND ""
|
|
||||||
TEST_COMMAND ""
|
|
||||||
)
|
|
||||||
71
3rdparty/astc-codec/README.md
vendored
@@ -1,71 +0,0 @@
|
|||||||
# astc-codec
|
|
||||||
|
|
||||||
astc-codec is a software ASTC decoder implementation, which supports the ASTC
|
|
||||||
LDR profile.
|
|
||||||
|
|
||||||
Example usage:
|
|
||||||
|
|
||||||
```
|
|
||||||
#include <astc-codec/astc-codec.h>
|
|
||||||
|
|
||||||
// ...
|
|
||||||
|
|
||||||
std::vector<uint8_t> astc = LoadMyASTCData();
|
|
||||||
const size_t width = 640;
|
|
||||||
const size_t height = 480;
|
|
||||||
|
|
||||||
std::vector<uint8_t> result;
|
|
||||||
result.resize(width * height * 4);
|
|
||||||
|
|
||||||
bool success = astc_codec::ASTCDecompressToRGBA(
|
|
||||||
astc.data(), astc.size(), width, height, astc_codec::FootprintType::k4x4,
|
|
||||||
result.data(), result.size(), /* stride */ width * 4);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
### With bazel
|
|
||||||
|
|
||||||
Install [Bazel](https://bazel.build/), and then run:
|
|
||||||
|
|
||||||
```
|
|
||||||
bazel build :astc_codec -c opt
|
|
||||||
```
|
|
||||||
|
|
||||||
astc-codec has been tested on Mac and Linux.
|
|
||||||
|
|
||||||
### Run Tests
|
|
||||||
|
|
||||||
```
|
|
||||||
bazel test //...
|
|
||||||
```
|
|
||||||
|
|
||||||
### With CMake
|
|
||||||
|
|
||||||
Install [CMake](https://cmake.org/), and the run:
|
|
||||||
|
|
||||||
```
|
|
||||||
mkdir build && cd build && cmake .. && make
|
|
||||||
```
|
|
||||||
|
|
||||||
Or open the project in your favorite IDE and import CMakeLists.txt.
|
|
||||||
|
|
||||||
### Run Tests
|
|
||||||
|
|
||||||
In the build directory, execute:
|
|
||||||
|
|
||||||
```
|
|
||||||
ctest
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
See [CONTRIBUTING.md](CONTRIBUTING.md) for important contributing requirements.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
astc-codec project is licensed under the Apache License Version 2.0. You can
|
|
||||||
find a copy of it in [LICENSE](LICENSE).
|
|
||||||
|
|
||||||
This is not an officially supported Google product.
|
|
||||||
37
3rdparty/astc-codec/WORKSPACE
vendored
@@ -1,37 +0,0 @@
|
|||||||
# 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.
|
|
||||||
|
|
||||||
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
|
|
||||||
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
|
||||||
|
|
||||||
git_repository(
|
|
||||||
name = "gtest",
|
|
||||||
remote = "https://github.com/google/googletest.git",
|
|
||||||
commit = "ba96d0b1161f540656efdaed035b3c062b60e006",
|
|
||||||
)
|
|
||||||
|
|
||||||
http_archive(
|
|
||||||
name = "honggfuzz",
|
|
||||||
url = "https://github.com/google/honggfuzz/archive/1.7.zip",
|
|
||||||
sha256 = "9d420326979fed4a065fa6176d5e09bd513cd2820fe216ae8b684aa6780d72b2",
|
|
||||||
build_file = "//third_party:honggfuzz.BUILD",
|
|
||||||
strip_prefix = "honggfuzz-1.7",
|
|
||||||
)
|
|
||||||
|
|
||||||
http_archive(
|
|
||||||
name = "benchmark",
|
|
||||||
url = "https://github.com/google/benchmark/archive/v1.4.1.zip",
|
|
||||||
sha256 = "61ae07eb5d4a0b02753419eb17a82b7d322786bb36ab62bd3df331a4d47c00a7",
|
|
||||||
strip_prefix = "benchmark-1.4.1",
|
|
||||||
)
|
|
||||||
38
3rdparty/astc-codec/cmake-format.json
vendored
@@ -1,38 +0,0 @@
|
|||||||
{
|
|
||||||
"line_width": 120,
|
|
||||||
"dangle_parens": false,
|
|
||||||
"first_comment_is_literal": true,
|
|
||||||
"algorithm_order": [
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
2,
|
|
||||||
3
|
|
||||||
],
|
|
||||||
"command_case": "lower",
|
|
||||||
"additional_commands": {
|
|
||||||
"foo": {
|
|
||||||
"flags": [
|
|
||||||
"BAR",
|
|
||||||
"BAZ"
|
|
||||||
],
|
|
||||||
"kwargs": {
|
|
||||||
"HEADERS": "*",
|
|
||||||
"DEPENDS": "*",
|
|
||||||
"SOURCES": "*"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"separate_fn_name_with_space": false,
|
|
||||||
"always_wrap": [],
|
|
||||||
"separate_ctrl_name_with_space": false,
|
|
||||||
"max_subargs_per_line": 5,
|
|
||||||
"fence_pattern": "^\\s*([`~]{3}[`~]*)(.*)$",
|
|
||||||
"enable_markup": true,
|
|
||||||
"ruler_pattern": "^\\s*[^\\w\\s]{3}.*[^\\w\\s]{3}$",
|
|
||||||
"tab_size": 2,
|
|
||||||
"keyword_case": "unchanged",
|
|
||||||
"enum_char": ".",
|
|
||||||
"literal_comment_pattern": null,
|
|
||||||
"bullet_char": "*",
|
|
||||||
"line_ending": "unix"
|
|
||||||
}
|
|
||||||
4
3rdparty/astc-codec/src/.clang-format
vendored
@@ -1,4 +0,0 @@
|
|||||||
BasedOnStyle: Google
|
|
||||||
AllowShortCaseLabelsOnASingleLine: true
|
|
||||||
AllowShortFunctionsOnASingleLine: Inline
|
|
||||||
SpaceAfterTemplateKeyword: false
|
|
||||||
49
3rdparty/astc-codec/src/base/BUILD.bazel
vendored
@@ -1,49 +0,0 @@
|
|||||||
# 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.
|
|
||||||
|
|
||||||
licenses(["notice"])
|
|
||||||
|
|
||||||
cc_library(
|
|
||||||
name = "base",
|
|
||||||
hdrs = [
|
|
||||||
"bit_stream.h",
|
|
||||||
"bottom_n.h",
|
|
||||||
"math_utils.h",
|
|
||||||
"optional.h",
|
|
||||||
"string_utils.h",
|
|
||||||
"type_traits.h",
|
|
||||||
"uint128.h",
|
|
||||||
"utils.h",
|
|
||||||
],
|
|
||||||
features = ["-parse_headers"],
|
|
||||||
visibility = ["//src/decoder:__pkg__"],
|
|
||||||
)
|
|
||||||
|
|
||||||
cc_test(
|
|
||||||
name = "base_test",
|
|
||||||
srcs = [
|
|
||||||
"test/bit_stream_test.cpp",
|
|
||||||
"test/bottom_n_test.cpp",
|
|
||||||
"test/math_utils_test.cpp",
|
|
||||||
"test/optional_test.cpp",
|
|
||||||
"test/string_utils_test.cpp",
|
|
||||||
"test/type_traits_test.cpp",
|
|
||||||
"test/uint128_test.cpp",
|
|
||||||
],
|
|
||||||
deps = [
|
|
||||||
"@gtest//:gtest_main",
|
|
||||||
":base",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
27
3rdparty/astc-codec/src/base/CMakeLists.txt
vendored
@@ -1,27 +0,0 @@
|
|||||||
# 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.
|
|
||||||
add_library(base INTERFACE)
|
|
||||||
target_include_directories(base INTERFACE ../..)
|
|
||||||
|
|
||||||
if(OPTION_ASTC_TESTS)
|
|
||||||
add_executable(base_test
|
|
||||||
test/bit_stream_test.cpp
|
|
||||||
test/bottom_n_test.cpp
|
|
||||||
test/math_utils_test.cpp
|
|
||||||
test/optional_test.cpp
|
|
||||||
test/string_utils_test.cpp
|
|
||||||
test/type_traits_test.cpp
|
|
||||||
test/uint128_test.cpp)
|
|
||||||
target_link_libraries(base_test base gmock_main)
|
|
||||||
add_test(NAME base_test COMMAND base_test WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
|
|
||||||
endif()
|
|
||||||
@@ -1,141 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
#include "src/base/bit_stream.h"
|
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
namespace astc_codec {
|
|
||||||
namespace base {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
static constexpr uint64_t kAllBits = 0xFFFFFFFFFFFFFFFF;
|
|
||||||
static constexpr uint64_t k40Bits = 0x000000FFFFFFFFFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BitStream, Decode) {
|
|
||||||
{
|
|
||||||
BitStream<uint64_t> stream(0, 1);
|
|
||||||
|
|
||||||
uint64_t bits = kAllBits;
|
|
||||||
EXPECT_TRUE(stream.GetBits(1, &bits));
|
|
||||||
EXPECT_EQ(bits, 0);
|
|
||||||
EXPECT_FALSE(stream.GetBits(1, &bits));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
BitStream<uint64_t> stream(0b1010101010101010, 32);
|
|
||||||
EXPECT_EQ(stream.Bits(), 32);
|
|
||||||
|
|
||||||
uint64_t bits = 0;
|
|
||||||
EXPECT_TRUE(stream.GetBits(1, &bits));
|
|
||||||
EXPECT_EQ(bits, 0);
|
|
||||||
|
|
||||||
EXPECT_TRUE(stream.GetBits(3, &bits));
|
|
||||||
EXPECT_EQ(bits, 0b101);
|
|
||||||
|
|
||||||
EXPECT_TRUE(stream.GetBits(8, &bits));
|
|
||||||
EXPECT_EQ(bits, 0b10101010);
|
|
||||||
|
|
||||||
EXPECT_EQ(stream.Bits(), 20);
|
|
||||||
|
|
||||||
EXPECT_TRUE(stream.GetBits(20, &bits));
|
|
||||||
EXPECT_EQ(bits, 0b1010);
|
|
||||||
EXPECT_EQ(stream.Bits(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
BitStream<uint64_t> stream(kAllBits, 64);
|
|
||||||
EXPECT_EQ(stream.Bits(), 64);
|
|
||||||
|
|
||||||
uint64_t bits = 0;
|
|
||||||
EXPECT_TRUE(stream.GetBits(64, &bits));
|
|
||||||
EXPECT_EQ(bits, kAllBits);
|
|
||||||
EXPECT_EQ(stream.Bits(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
BitStream<uint64_t> stream(kAllBits, 64);
|
|
||||||
EXPECT_EQ(stream.Bits(), 64);
|
|
||||||
|
|
||||||
uint64_t bits = 0;
|
|
||||||
EXPECT_TRUE(stream.GetBits(40, &bits));
|
|
||||||
EXPECT_EQ(bits, k40Bits);
|
|
||||||
EXPECT_EQ(stream.Bits(), 24);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
BitStream<uint64_t> stream(kAllBits, 32);
|
|
||||||
|
|
||||||
uint64_t bits = 0;
|
|
||||||
EXPECT_TRUE(stream.GetBits(0, &bits));
|
|
||||||
EXPECT_EQ(bits, 0);
|
|
||||||
EXPECT_TRUE(stream.GetBits(32, &bits));
|
|
||||||
EXPECT_EQ(bits, k40Bits & 0xFFFFFFFF);
|
|
||||||
EXPECT_TRUE(stream.GetBits(0, &bits));
|
|
||||||
EXPECT_EQ(bits, 0);
|
|
||||||
EXPECT_EQ(stream.Bits(), 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BitStream, Encode) {
|
|
||||||
{
|
|
||||||
BitStream<uint64_t> stream;
|
|
||||||
|
|
||||||
stream.PutBits(0, 1);
|
|
||||||
stream.PutBits(0b11, 2);
|
|
||||||
EXPECT_EQ(stream.Bits(), 3);
|
|
||||||
|
|
||||||
uint64_t bits = 0;
|
|
||||||
EXPECT_TRUE(stream.GetBits(3, &bits));
|
|
||||||
EXPECT_EQ(bits, 0b110);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
BitStream<uint64_t> stream;
|
|
||||||
|
|
||||||
uint64_t bits = 0;
|
|
||||||
stream.PutBits(kAllBits, 64);
|
|
||||||
EXPECT_EQ(stream.Bits(), 64);
|
|
||||||
|
|
||||||
EXPECT_TRUE(stream.GetBits(64, &bits));
|
|
||||||
EXPECT_EQ(bits, kAllBits);
|
|
||||||
EXPECT_EQ(stream.Bits(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
BitStream<uint64_t> stream;
|
|
||||||
stream.PutBits(kAllBits, 40);
|
|
||||||
|
|
||||||
uint64_t bits = 0;
|
|
||||||
EXPECT_TRUE(stream.GetBits(40, &bits));
|
|
||||||
EXPECT_EQ(bits, k40Bits);
|
|
||||||
EXPECT_EQ(stream.Bits(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
BitStream<uint64_t> stream;
|
|
||||||
stream.PutBits(0, 0);
|
|
||||||
stream.PutBits(kAllBits, 32);
|
|
||||||
stream.PutBits(0, 0);
|
|
||||||
|
|
||||||
uint64_t bits = 0;
|
|
||||||
EXPECT_TRUE(stream.GetBits(32, &bits));
|
|
||||||
EXPECT_EQ(bits, k40Bits & 0xFFFFFFFF);
|
|
||||||
EXPECT_EQ(stream.Bits(), 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace base
|
|
||||||
} // namespace astc_codec
|
|
||||||
108
3rdparty/astc-codec/src/base/test/bottom_n_test.cpp
vendored
@@ -1,108 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
#include "src/base/bottom_n.h"
|
|
||||||
|
|
||||||
#include <gmock/gmock.h>
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
namespace astc_codec {
|
|
||||||
namespace base {
|
|
||||||
|
|
||||||
using ::testing::ElementsAre;
|
|
||||||
|
|
||||||
template<typename T, size_t N>
|
|
||||||
static void pushAll(BottomN<T>& heap, const T (&arr)[N]) {
|
|
||||||
for (auto i : arr) {
|
|
||||||
heap.Push(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BottomN, Sort) {
|
|
||||||
{
|
|
||||||
BottomN<int> heap(10);
|
|
||||||
EXPECT_TRUE(heap.Empty());
|
|
||||||
int list[] = { 1,2 };
|
|
||||||
pushAll(heap, list);
|
|
||||||
|
|
||||||
EXPECT_EQ(heap.Size(), 2);
|
|
||||||
EXPECT_FALSE(heap.Empty());
|
|
||||||
EXPECT_THAT(heap.Pop(), ElementsAre(1, 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
BottomN<int> heap(6);
|
|
||||||
int list[] = {1, 4, 3, 2, 2, 1};
|
|
||||||
pushAll(heap, list);
|
|
||||||
|
|
||||||
EXPECT_EQ(heap.Size(), 6);
|
|
||||||
EXPECT_THAT(heap.Pop(), ElementsAre(1, 1, 2, 2, 3, 4));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(BottomN, Bounds) {
|
|
||||||
{
|
|
||||||
BottomN<int> heap(4);
|
|
||||||
int list[] = { 1, 2, 3, 4 };
|
|
||||||
pushAll(heap, list);
|
|
||||||
EXPECT_EQ(heap.Size(), 4);
|
|
||||||
|
|
||||||
heap.Push(0);
|
|
||||||
EXPECT_EQ(heap.Size(), 4);
|
|
||||||
|
|
||||||
EXPECT_THAT(heap.Pop(), ElementsAre(0, 1, 2, 3));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
BottomN<int> heap(4);
|
|
||||||
int list[] = { 4, 3, 2,1 };
|
|
||||||
pushAll(heap, list);
|
|
||||||
EXPECT_EQ(heap.Size(), 4);
|
|
||||||
|
|
||||||
int list2[] = { 4,4,4,4 };
|
|
||||||
pushAll(heap, list2);
|
|
||||||
EXPECT_EQ(heap.Size(), 4);
|
|
||||||
|
|
||||||
EXPECT_THAT(heap.Pop(), ElementsAre(1, 2, 3, 4));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
BottomN<int> heap(4);
|
|
||||||
int list[] = { 4, 3, 2, 1 };
|
|
||||||
pushAll(heap, list);
|
|
||||||
EXPECT_EQ(heap.Size(), 4);
|
|
||||||
|
|
||||||
int list2[] = { 5, 5, 5, 5 };
|
|
||||||
pushAll(heap, list2);
|
|
||||||
EXPECT_EQ(heap.Size(), 4);
|
|
||||||
|
|
||||||
EXPECT_THAT(heap.Pop(), ElementsAre(1, 2, 3, 4));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
BottomN<int> heap(4);
|
|
||||||
int list[] = { 4, 3, 2, 1 };
|
|
||||||
pushAll(heap, list);
|
|
||||||
EXPECT_EQ(heap.Size(), 4);
|
|
||||||
|
|
||||||
int list2[] = { 0, 0, 0, 0 };
|
|
||||||
pushAll(heap, list2);
|
|
||||||
EXPECT_EQ(heap.Size(), 4);
|
|
||||||
|
|
||||||
EXPECT_THAT(heap.Pop(), ElementsAre(0, 0, 0, 0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace base
|
|
||||||
} // namespace astc_codec
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
#include "src/base/math_utils.h"
|
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
namespace astc_codec {
|
|
||||||
namespace base {
|
|
||||||
|
|
||||||
TEST(MathUtils, Log2Floor) {
|
|
||||||
EXPECT_EQ(-1, Log2Floor(0));
|
|
||||||
|
|
||||||
for (int i = 0; i < 32; i++) {
|
|
||||||
uint32_t n = 1U << i;
|
|
||||||
EXPECT_EQ(i, Log2Floor(n));
|
|
||||||
if (n > 2) {
|
|
||||||
EXPECT_EQ(i - 1, Log2Floor(n - 1));
|
|
||||||
EXPECT_EQ(i, Log2Floor(n + 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(MathUtils, CountOnes) {
|
|
||||||
EXPECT_EQ(0, CountOnes(0));
|
|
||||||
EXPECT_EQ(1, CountOnes(1));
|
|
||||||
EXPECT_EQ(32, CountOnes(static_cast<uint32_t>(~0U)));
|
|
||||||
EXPECT_EQ(1, CountOnes(0x8000000));
|
|
||||||
|
|
||||||
for (int i = 0; i < 32; i++) {
|
|
||||||
EXPECT_EQ(1, CountOnes(1U << i));
|
|
||||||
EXPECT_EQ(31, CountOnes(static_cast<uint32_t>(~0U) ^ (1U << i)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(MathUtils, ReverseBits) {
|
|
||||||
EXPECT_EQ(ReverseBits(0u), 0u);
|
|
||||||
EXPECT_EQ(ReverseBits(1u), 1u << 31);
|
|
||||||
EXPECT_EQ(ReverseBits(0xffffffff), 0xffffffff);
|
|
||||||
EXPECT_EQ(ReverseBits(0x00000001), 0x80000000);
|
|
||||||
EXPECT_EQ(ReverseBits(0x80000000), 0x00000001);
|
|
||||||
EXPECT_EQ(ReverseBits(0xaaaaaaaa), 0x55555555);
|
|
||||||
EXPECT_EQ(ReverseBits(0x55555555), 0xaaaaaaaa);
|
|
||||||
EXPECT_EQ(ReverseBits(0x7d5d7f53), 0xcafebabe);
|
|
||||||
EXPECT_EQ(ReverseBits(0xcafebabe), 0x7d5d7f53);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(MathUtils, GetBits) {
|
|
||||||
EXPECT_EQ(GetBits(0u, 0, 1), 0u);
|
|
||||||
EXPECT_EQ(GetBits(0u, 0, 32), 0u);
|
|
||||||
EXPECT_EQ(GetBits(0x00000001u, 0, 1), 0x00000001);
|
|
||||||
EXPECT_EQ(GetBits(0x00000001u, 0, 32), 0x00000001);
|
|
||||||
EXPECT_EQ(GetBits(0x00000001u, 1, 31), 0x00000000);
|
|
||||||
EXPECT_EQ(GetBits(0x00000001u, 31, 1), 0x00000000);
|
|
||||||
|
|
||||||
EXPECT_DEBUG_DEATH(GetBits(0x00000000u, 1, 32), "");
|
|
||||||
EXPECT_DEBUG_DEATH(GetBits(0x00000000u, 32, 0), "");
|
|
||||||
EXPECT_DEBUG_DEATH(GetBits(0x00000000u, 32, 1), "");
|
|
||||||
|
|
||||||
EXPECT_EQ(GetBits(0XFFFFFFFFu, 0, 4), 0x0000000F);
|
|
||||||
EXPECT_EQ(GetBits(0XFFFFFFFFu, 16, 16), 0xFFFF);
|
|
||||||
EXPECT_EQ(GetBits(0x80000000u, 31, 1), 1);
|
|
||||||
EXPECT_EQ(GetBits(0xCAFEBABEu, 24, 8), 0xCA);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace base
|
|
||||||
} // namespace astc_codec
|
|
||||||
481
3rdparty/astc-codec/src/base/test/optional_test.cpp
vendored
@@ -1,481 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
#include "src/base/optional.h"
|
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace astc_codec {
|
|
||||||
namespace base {
|
|
||||||
|
|
||||||
TEST(Optional, TypeProperties) {
|
|
||||||
// Making sure optional has the correct alignment and doesn't waste too much
|
|
||||||
// space
|
|
||||||
|
|
||||||
static_assert(sizeof(Optional<bool>) == 2, "bad Optional<bool> size");
|
|
||||||
static_assert(std::alignment_of<Optional<bool>>::value ==
|
|
||||||
std::alignment_of<bool>::value,
|
|
||||||
"bad Optional<bool> alignment");
|
|
||||||
|
|
||||||
static_assert(sizeof(Optional<char>) == 2, "bad Optional<char> size");
|
|
||||||
static_assert(std::alignment_of<Optional<char>>::value ==
|
|
||||||
std::alignment_of<char>::value,
|
|
||||||
"bad Optional<char> alignment");
|
|
||||||
|
|
||||||
static_assert(sizeof(Optional<int16_t>) == 4, "bad Optional<int16_t> size");
|
|
||||||
static_assert(std::alignment_of<Optional<int16_t>>::value ==
|
|
||||||
std::alignment_of<int16_t>::value,
|
|
||||||
"bad Optional<int16_t> alignment");
|
|
||||||
|
|
||||||
static_assert(sizeof(Optional<int32_t>) == 8, "bad Optional<int32_t> size");
|
|
||||||
static_assert(std::alignment_of<Optional<int32_t>>::value ==
|
|
||||||
std::alignment_of<int32_t>::value,
|
|
||||||
"bad Optional<int32_t> alignment");
|
|
||||||
|
|
||||||
static_assert(sizeof(Optional<int64_t>) == 16, "bad Optional<int64_t> size");
|
|
||||||
static_assert(std::alignment_of<Optional<int64_t>>::value ==
|
|
||||||
std::alignment_of<int64_t>::value,
|
|
||||||
"bad Optional<int64_t> alignment");
|
|
||||||
|
|
||||||
struct S128 {
|
|
||||||
int64_t data[2];
|
|
||||||
};
|
|
||||||
|
|
||||||
static_assert(sizeof(Optional<S128>) == 3 * sizeof(int64_t),
|
|
||||||
"bad Optional<S128> size");
|
|
||||||
static_assert(std::alignment_of<Optional<S128>>::value ==
|
|
||||||
std::alignment_of<S128>::value,
|
|
||||||
"bad Optional<S128> alignment");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Optional, ConstructFromValue) {
|
|
||||||
{
|
|
||||||
Optional<int> o;
|
|
||||||
EXPECT_FALSE(o);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
Optional<int> o = {};
|
|
||||||
EXPECT_FALSE(o);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
Optional<int> o = kNullopt;
|
|
||||||
EXPECT_FALSE(o);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
Optional<int> o(1);
|
|
||||||
EXPECT_TRUE(o);
|
|
||||||
EXPECT_EQ(1, *o);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// check the std::decay<> constructor
|
|
||||||
Optional<int> o = static_cast<const short&>(1);
|
|
||||||
EXPECT_TRUE(o);
|
|
||||||
EXPECT_EQ(1, *o);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
Optional<int> o = 1;
|
|
||||||
EXPECT_TRUE(o);
|
|
||||||
EXPECT_EQ(1, *o);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
Optional<int> o{1};
|
|
||||||
EXPECT_TRUE(o);
|
|
||||||
EXPECT_EQ(1, *o);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
short val = 10;
|
|
||||||
Optional<int> o = val;
|
|
||||||
EXPECT_TRUE(o);
|
|
||||||
EXPECT_EQ(10, *o);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
Optional<std::vector<int>> o(kInplace, 10);
|
|
||||||
EXPECT_TRUE(o);
|
|
||||||
EXPECT_EQ((std::vector<int>(10)), *o);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
Optional<std::vector<int>> o(kInplace, {1, 2, 3, 4});
|
|
||||||
EXPECT_TRUE(o);
|
|
||||||
EXPECT_EQ((std::vector<int>{1, 2, 3, 4}), *o);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Optional, ConstructFromOptional) {
|
|
||||||
{
|
|
||||||
Optional<int> o = Optional<int>();
|
|
||||||
EXPECT_FALSE(o);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
Optional<short> o2;
|
|
||||||
Optional<int> o(o2);
|
|
||||||
EXPECT_FALSE(o);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
Optional<short> o2 = 42;
|
|
||||||
Optional<int> o(o2);
|
|
||||||
EXPECT_TRUE(o);
|
|
||||||
EXPECT_EQ(42, *o);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
Optional<int> o(Optional<int>(1));
|
|
||||||
EXPECT_TRUE(o);
|
|
||||||
EXPECT_EQ(1, *o);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
Optional<int> o2 = 2;
|
|
||||||
Optional<int> o = o2;
|
|
||||||
EXPECT_TRUE(o);
|
|
||||||
EXPECT_EQ(2, *o);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
Optional<std::vector<int>> o2 = std::vector<int>{20, 30, 40};
|
|
||||||
Optional<std::vector<int>> o = o2;
|
|
||||||
EXPECT_TRUE(o);
|
|
||||||
EXPECT_EQ((std::vector<int>{20, 30, 40}), *o);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Optional, Assign) {
|
|
||||||
{
|
|
||||||
Optional<int> o;
|
|
||||||
o = 1;
|
|
||||||
EXPECT_TRUE(o);
|
|
||||||
EXPECT_EQ(1, *o);
|
|
||||||
|
|
||||||
o = 2;
|
|
||||||
EXPECT_TRUE(o);
|
|
||||||
EXPECT_EQ(2, *o);
|
|
||||||
|
|
||||||
o = kNullopt;
|
|
||||||
EXPECT_FALSE(o);
|
|
||||||
|
|
||||||
o = Optional<int>(10);
|
|
||||||
EXPECT_TRUE(o);
|
|
||||||
EXPECT_EQ(10, *o);
|
|
||||||
|
|
||||||
Optional<int> o2;
|
|
||||||
o = o2;
|
|
||||||
EXPECT_FALSE(o);
|
|
||||||
|
|
||||||
o = 2u;
|
|
||||||
EXPECT_TRUE(o);
|
|
||||||
EXPECT_EQ(2, *o);
|
|
||||||
|
|
||||||
o = Optional<short>();
|
|
||||||
EXPECT_FALSE(o);
|
|
||||||
|
|
||||||
o = Optional<short>(20);
|
|
||||||
EXPECT_TRUE(o);
|
|
||||||
EXPECT_EQ(20, *o);
|
|
||||||
|
|
||||||
Optional<short> o3(200);
|
|
||||||
o = o3;
|
|
||||||
EXPECT_TRUE(o);
|
|
||||||
EXPECT_EQ(200, *o);
|
|
||||||
|
|
||||||
o = {};
|
|
||||||
EXPECT_FALSE(o);
|
|
||||||
|
|
||||||
// check the std::decay<> assignment
|
|
||||||
o = static_cast<const short&>(1);
|
|
||||||
EXPECT_TRUE(o);
|
|
||||||
EXPECT_EQ(1, *o);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Optional, MakeOptional) {
|
|
||||||
{
|
|
||||||
auto o = makeOptional(1);
|
|
||||||
static_assert(std::is_same<decltype(o), Optional<int>>::value,
|
|
||||||
"Bad type deduction in makeOptional()");
|
|
||||||
EXPECT_TRUE(o);
|
|
||||||
EXPECT_EQ(1, *o);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto o = makeOptional(std::vector<char>{'1', '2'});
|
|
||||||
static_assert(std::is_same<decltype(o), Optional<std::vector<char>>>::value,
|
|
||||||
"Bad type deduction in makeOptional()");
|
|
||||||
EXPECT_TRUE(o);
|
|
||||||
EXPECT_EQ((std::vector<char>{'1', '2'}), *o);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// check std::decay<> in the factory function
|
|
||||||
auto o = makeOptional("String");
|
|
||||||
static_assert(std::is_same<decltype(o), Optional<const char*>>::value,
|
|
||||||
"Bad type deduction in makeOptional()");
|
|
||||||
EXPECT_TRUE(o);
|
|
||||||
EXPECT_STREQ("String", *o);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto o = makeOptional<std::string>("String");
|
|
||||||
static_assert(std::is_same<decltype(o), Optional<std::string>>::value,
|
|
||||||
"Bad type deduction in makeOptional()");
|
|
||||||
EXPECT_TRUE(o);
|
|
||||||
EXPECT_STREQ("String", o->c_str());
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto o = makeOptional<std::string>(5, 'b');
|
|
||||||
static_assert(std::is_same<decltype(o), Optional<std::string>>::value,
|
|
||||||
"Bad type deduction in makeOptional()");
|
|
||||||
EXPECT_TRUE(o);
|
|
||||||
EXPECT_STREQ("bbbbb", o->c_str());
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto o = makeOptional<std::string>();
|
|
||||||
static_assert(std::is_same<decltype(o), Optional<std::string>>::value,
|
|
||||||
"Bad type deduction in makeOptional()");
|
|
||||||
EXPECT_TRUE(o);
|
|
||||||
EXPECT_STREQ("", o->c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Optional, Move) {
|
|
||||||
auto o = makeOptional(std::unique_ptr<int>(new int(10)));
|
|
||||||
{
|
|
||||||
decltype(o) o2 = std::move(o);
|
|
||||||
EXPECT_TRUE(o);
|
|
||||||
EXPECT_TRUE(o2);
|
|
||||||
EXPECT_FALSE(bool(*o));
|
|
||||||
EXPECT_TRUE(bool(*o2));
|
|
||||||
EXPECT_EQ(10, **o2);
|
|
||||||
|
|
||||||
decltype(o) o3;
|
|
||||||
o3 = std::move(o2);
|
|
||||||
EXPECT_TRUE(o2);
|
|
||||||
EXPECT_TRUE(o3);
|
|
||||||
EXPECT_FALSE(bool(*o2));
|
|
||||||
EXPECT_TRUE(bool(*o3));
|
|
||||||
EXPECT_EQ(10, **o3);
|
|
||||||
|
|
||||||
o3 = std::move(o2);
|
|
||||||
EXPECT_TRUE(o2);
|
|
||||||
EXPECT_TRUE(o3);
|
|
||||||
EXPECT_FALSE(bool(*o2));
|
|
||||||
EXPECT_FALSE(bool(*o3));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
decltype(o) o1;
|
|
||||||
decltype(o) o2 = std::move(o1);
|
|
||||||
EXPECT_FALSE(o1);
|
|
||||||
EXPECT_FALSE(o2);
|
|
||||||
|
|
||||||
o2 = std::move(o1);
|
|
||||||
EXPECT_FALSE(o1);
|
|
||||||
EXPECT_FALSE(o2);
|
|
||||||
|
|
||||||
decltype(o) o3{kInplace, new int(20)};
|
|
||||||
o3 = std::move(o1);
|
|
||||||
EXPECT_FALSE(o1);
|
|
||||||
EXPECT_FALSE(o3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Optional, Value) {
|
|
||||||
auto o = makeOptional(1);
|
|
||||||
EXPECT_EQ(1, o.value());
|
|
||||||
EXPECT_EQ(1, o.valueOr(2));
|
|
||||||
|
|
||||||
o = kNullopt;
|
|
||||||
EXPECT_EQ(2, o.valueOr(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Optional, Clear) {
|
|
||||||
auto o = makeOptional(1);
|
|
||||||
o.clear();
|
|
||||||
EXPECT_FALSE(o);
|
|
||||||
|
|
||||||
o.clear();
|
|
||||||
EXPECT_FALSE(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Optional, Emplace) {
|
|
||||||
auto o = makeOptional(std::vector<int>{1, 2, 3, 4});
|
|
||||||
o.emplace(3, 1);
|
|
||||||
EXPECT_TRUE(o);
|
|
||||||
EXPECT_EQ((std::vector<int>{1, 1, 1}), *o);
|
|
||||||
EXPECT_EQ(3U, o->capacity());
|
|
||||||
|
|
||||||
o.clear();
|
|
||||||
o.emplace({1, 2});
|
|
||||||
EXPECT_TRUE(o);
|
|
||||||
EXPECT_EQ((std::vector<int>{1, 2}), *o);
|
|
||||||
EXPECT_EQ(2U, o->capacity());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Optional, Reset) {
|
|
||||||
auto o = makeOptional(std::vector<int>{1, 2, 3, 4});
|
|
||||||
o.reset(std::vector<int>{4, 3});
|
|
||||||
EXPECT_TRUE(o);
|
|
||||||
EXPECT_EQ((std::vector<int>{4, 3}), *o);
|
|
||||||
EXPECT_EQ(2U, o->capacity());
|
|
||||||
|
|
||||||
o.clear();
|
|
||||||
o.reset(std::vector<int>{1});
|
|
||||||
EXPECT_EQ((std::vector<int>{1}), *o);
|
|
||||||
EXPECT_EQ(1U, o->capacity());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Optional, CompareEqual) {
|
|
||||||
EXPECT_TRUE(makeOptional(1) == makeOptional(1));
|
|
||||||
EXPECT_TRUE(makeOptional(1) == 1);
|
|
||||||
EXPECT_TRUE(1 == makeOptional(1));
|
|
||||||
EXPECT_FALSE(makeOptional(1) == makeOptional(2));
|
|
||||||
EXPECT_FALSE(makeOptional(2) == 1);
|
|
||||||
EXPECT_FALSE(2 == makeOptional(1));
|
|
||||||
EXPECT_TRUE(makeOptional(1) != makeOptional(2));
|
|
||||||
EXPECT_TRUE(makeOptional(1) != 2);
|
|
||||||
EXPECT_TRUE(1 != makeOptional(2));
|
|
||||||
|
|
||||||
EXPECT_FALSE(makeOptional(1) == kNullopt);
|
|
||||||
EXPECT_FALSE(makeOptional(1) == Optional<int>());
|
|
||||||
EXPECT_FALSE(kNullopt == makeOptional(1));
|
|
||||||
EXPECT_FALSE(Optional<int>() == makeOptional(1));
|
|
||||||
EXPECT_TRUE(makeOptional(1) != kNullopt);
|
|
||||||
EXPECT_TRUE(makeOptional(1) != Optional<int>());
|
|
||||||
EXPECT_TRUE(kNullopt != makeOptional(1));
|
|
||||||
EXPECT_TRUE(Optional<int>() != makeOptional(1));
|
|
||||||
|
|
||||||
EXPECT_TRUE(kNullopt == Optional<int>());
|
|
||||||
EXPECT_TRUE(kNullopt == Optional<char*>());
|
|
||||||
EXPECT_FALSE(kNullopt != Optional<int>());
|
|
||||||
EXPECT_FALSE(kNullopt != Optional<char*>());
|
|
||||||
EXPECT_TRUE(Optional<int>() == Optional<int>());
|
|
||||||
EXPECT_FALSE(Optional<int>() != Optional<int>());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Optional, CompareLess) {
|
|
||||||
EXPECT_TRUE(makeOptional(1) < makeOptional(2));
|
|
||||||
EXPECT_TRUE(1 < makeOptional(2));
|
|
||||||
EXPECT_TRUE(makeOptional(1) < 2);
|
|
||||||
|
|
||||||
EXPECT_FALSE(makeOptional(1) < makeOptional(1));
|
|
||||||
EXPECT_FALSE(1 < makeOptional(1));
|
|
||||||
EXPECT_FALSE(makeOptional(1) < 1);
|
|
||||||
EXPECT_FALSE(makeOptional(2) < makeOptional(1));
|
|
||||||
EXPECT_FALSE(2 < makeOptional(1));
|
|
||||||
EXPECT_FALSE(makeOptional(2) < 1);
|
|
||||||
|
|
||||||
EXPECT_TRUE(kNullopt < makeOptional(2));
|
|
||||||
EXPECT_TRUE(Optional<int>() < makeOptional(2));
|
|
||||||
EXPECT_TRUE(Optional<int>() < 2);
|
|
||||||
EXPECT_FALSE(makeOptional(2) < kNullopt);
|
|
||||||
EXPECT_FALSE(makeOptional(2) < Optional<int>());
|
|
||||||
EXPECT_FALSE(2 < Optional<int>());
|
|
||||||
|
|
||||||
EXPECT_FALSE(kNullopt < Optional<int>());
|
|
||||||
EXPECT_FALSE(Optional<int>() < kNullopt);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Optional, Destruction) {
|
|
||||||
// create a reference counting class to check if we delete everything
|
|
||||||
// we've created
|
|
||||||
struct Track {
|
|
||||||
Track(int& val) : mVal(val) { ++mVal.get(); }
|
|
||||||
Track(std::initializer_list<int*> vals) : mVal(**vals.begin()) {
|
|
||||||
++mVal.get();
|
|
||||||
}
|
|
||||||
Track(const Track& other) : mVal(other.mVal) { ++mVal.get(); }
|
|
||||||
Track(Track&& other) : mVal(other.mVal) { ++mVal.get(); }
|
|
||||||
Track& operator=(const Track& other) {
|
|
||||||
--mVal.get();
|
|
||||||
mVal = other.mVal;
|
|
||||||
++mVal.get();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
Track& operator=(Track&& other) {
|
|
||||||
--mVal.get();
|
|
||||||
mVal = other.mVal;
|
|
||||||
++mVal.get();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
~Track() { --mVal.get(); }
|
|
||||||
|
|
||||||
std::reference_wrapper<int> mVal;
|
|
||||||
};
|
|
||||||
|
|
||||||
int counter = 0;
|
|
||||||
{
|
|
||||||
auto o = makeOptional(Track(counter));
|
|
||||||
EXPECT_EQ(1, counter);
|
|
||||||
}
|
|
||||||
EXPECT_EQ(0, counter);
|
|
||||||
|
|
||||||
{
|
|
||||||
auto o = makeOptional(Track(counter));
|
|
||||||
EXPECT_EQ(1, counter);
|
|
||||||
o.clear();
|
|
||||||
EXPECT_EQ(0, counter);
|
|
||||||
}
|
|
||||||
EXPECT_EQ(0, counter);
|
|
||||||
|
|
||||||
{
|
|
||||||
auto o = makeOptional(Track(counter));
|
|
||||||
EXPECT_EQ(1, counter);
|
|
||||||
int counter2 = 0;
|
|
||||||
o.emplace(counter2);
|
|
||||||
EXPECT_EQ(0, counter);
|
|
||||||
EXPECT_EQ(1, counter2);
|
|
||||||
o = Track(counter);
|
|
||||||
EXPECT_EQ(1, counter);
|
|
||||||
EXPECT_EQ(0, counter2);
|
|
||||||
|
|
||||||
auto o2 = o;
|
|
||||||
EXPECT_EQ(2, counter);
|
|
||||||
EXPECT_EQ(0, counter2);
|
|
||||||
}
|
|
||||||
EXPECT_EQ(0, counter);
|
|
||||||
|
|
||||||
{
|
|
||||||
auto o = makeOptional(Track(counter));
|
|
||||||
auto o2 = std::move(o);
|
|
||||||
EXPECT_EQ(2, counter);
|
|
||||||
o = o2;
|
|
||||||
EXPECT_EQ(2, counter);
|
|
||||||
}
|
|
||||||
EXPECT_EQ(0, counter);
|
|
||||||
|
|
||||||
int counter2 = 0;
|
|
||||||
{
|
|
||||||
Optional<Track> o;
|
|
||||||
o.emplace(counter);
|
|
||||||
EXPECT_EQ(1, counter);
|
|
||||||
|
|
||||||
o.emplace(counter2);
|
|
||||||
EXPECT_EQ(0, counter);
|
|
||||||
EXPECT_EQ(1, counter2);
|
|
||||||
}
|
|
||||||
EXPECT_EQ(0, counter);
|
|
||||||
EXPECT_EQ(0, counter2);
|
|
||||||
|
|
||||||
{
|
|
||||||
Optional<Track> o;
|
|
||||||
o.emplace({&counter});
|
|
||||||
EXPECT_EQ(1, counter);
|
|
||||||
|
|
||||||
counter2 = 0;
|
|
||||||
o.emplace({&counter2});
|
|
||||||
EXPECT_EQ(0, counter);
|
|
||||||
EXPECT_EQ(1, counter2);
|
|
||||||
}
|
|
||||||
EXPECT_EQ(0, counter);
|
|
||||||
EXPECT_EQ(0, counter2);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace base
|
|
||||||
} // namespace astc_codec
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
#include "src/base/string_utils.h"
|
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include <list>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace astc_codec {
|
|
||||||
namespace base {
|
|
||||||
|
|
||||||
TEST(StringUtils, Split) {
|
|
||||||
std::vector<std::string> results;
|
|
||||||
|
|
||||||
auto testFunc = [&results](std::string&& s) {
|
|
||||||
results.push_back(std::move(s));
|
|
||||||
};
|
|
||||||
|
|
||||||
Split("", "abc", testFunc);
|
|
||||||
EXPECT_EQ(results.size(), 1);
|
|
||||||
|
|
||||||
Split("abc", "", testFunc);
|
|
||||||
EXPECT_EQ(results.size(), 1);
|
|
||||||
|
|
||||||
results.clear();
|
|
||||||
Split("abc", "a", testFunc);
|
|
||||||
EXPECT_EQ(results.size(), 2);
|
|
||||||
EXPECT_EQ(results[0], "");
|
|
||||||
EXPECT_EQ(results[1], "bc");
|
|
||||||
|
|
||||||
results.clear();
|
|
||||||
Split("aaa", "a", testFunc);
|
|
||||||
EXPECT_EQ(4, results.size());
|
|
||||||
EXPECT_EQ("", results[0]);
|
|
||||||
EXPECT_EQ("", results[1]);
|
|
||||||
EXPECT_EQ("", results[2]);
|
|
||||||
EXPECT_EQ("", results[3]);
|
|
||||||
|
|
||||||
results.clear();
|
|
||||||
Split("1a2a3a4", "a", testFunc);
|
|
||||||
EXPECT_EQ(4, results.size());
|
|
||||||
EXPECT_EQ("1", results[0]);
|
|
||||||
EXPECT_EQ("2", results[1]);
|
|
||||||
EXPECT_EQ("3", results[2]);
|
|
||||||
EXPECT_EQ("4", results[3]);
|
|
||||||
|
|
||||||
results.clear();
|
|
||||||
Split("1a2aa3a4", "a", testFunc);
|
|
||||||
EXPECT_EQ(5, results.size());
|
|
||||||
EXPECT_EQ("1", results[0]);
|
|
||||||
EXPECT_EQ("2", results[1]);
|
|
||||||
EXPECT_EQ("", results[2]);
|
|
||||||
EXPECT_EQ("3", results[3]);
|
|
||||||
EXPECT_EQ("4", results[4]);
|
|
||||||
|
|
||||||
results.clear();
|
|
||||||
Split("The quick brown fox jumped over the lazy dog",
|
|
||||||
" ", testFunc);
|
|
||||||
EXPECT_EQ(9, results.size());
|
|
||||||
EXPECT_EQ("The", results[0]);
|
|
||||||
EXPECT_EQ("quick", results[1]);
|
|
||||||
EXPECT_EQ("brown", results[2]);
|
|
||||||
EXPECT_EQ("fox", results[3]);
|
|
||||||
EXPECT_EQ("jumped", results[4]);
|
|
||||||
EXPECT_EQ("over", results[5]);
|
|
||||||
EXPECT_EQ("the", results[6]);
|
|
||||||
EXPECT_EQ("lazy", results[7]);
|
|
||||||
EXPECT_EQ("dog", results[8]);
|
|
||||||
|
|
||||||
results.clear();
|
|
||||||
Split("a; b; c; d", "; ", testFunc);
|
|
||||||
EXPECT_EQ(4, results.size());
|
|
||||||
EXPECT_EQ("a", results[0]);
|
|
||||||
EXPECT_EQ("b", results[1]);
|
|
||||||
EXPECT_EQ("c", results[2]);
|
|
||||||
EXPECT_EQ("d", results[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StringUtils, ParseInt32) {
|
|
||||||
EXPECT_EQ(ParseInt32("0", -1), 0);
|
|
||||||
EXPECT_EQ(ParseInt32("100", -1), 100);
|
|
||||||
EXPECT_EQ(ParseInt32("-100", -1), -100);
|
|
||||||
|
|
||||||
EXPECT_EQ(ParseInt32("", -1), -1);
|
|
||||||
EXPECT_EQ(ParseInt32("a", -1), -1);
|
|
||||||
EXPECT_EQ(ParseInt32("10x1", -1), 10);
|
|
||||||
|
|
||||||
EXPECT_EQ(ParseInt32("2147483647", -1), 2147483647);
|
|
||||||
EXPECT_EQ(ParseInt32("2147483648", -1), 2147483647);
|
|
||||||
|
|
||||||
EXPECT_EQ(ParseInt32("-2147483648", -1), -2147483648);
|
|
||||||
EXPECT_EQ(ParseInt32("-2147483649", -1), -2147483648);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace base
|
|
||||||
} // namespace astc_codec
|
|
||||||
@@ -1,130 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
#include "src/base/type_traits.h"
|
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <functional>
|
|
||||||
#include <list>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace astc_codec {
|
|
||||||
namespace base {
|
|
||||||
|
|
||||||
TEST(TypeTraits, IsCallable) {
|
|
||||||
class C;
|
|
||||||
C* c = nullptr;
|
|
||||||
|
|
||||||
auto lambda = [c](bool) -> C* { return nullptr; };
|
|
||||||
|
|
||||||
static_assert(is_callable_as<void(), void()>::value, "simple function");
|
|
||||||
static_assert(is_callable_as<void (&)(), void()>::value,
|
|
||||||
"function reference");
|
|
||||||
static_assert(is_callable_as<void (*)(), void()>::value, "function pointer");
|
|
||||||
static_assert(is_callable_as<int(C&, C*), int(C&, C*)>::value,
|
|
||||||
"function with arguments and return type");
|
|
||||||
static_assert(is_callable_as<decltype(lambda), C*(bool)>::value, "lambda");
|
|
||||||
static_assert(is_callable_as<std::function<bool(int)>, bool(int)>::value,
|
|
||||||
"std::function");
|
|
||||||
|
|
||||||
static_assert(!is_callable_as<int, void()>::value,
|
|
||||||
"int should not be callable");
|
|
||||||
static_assert(!is_callable_as<C, void()>::value, "incomplete type");
|
|
||||||
static_assert(!is_callable_as<void(), void(int)>::value,
|
|
||||||
"different arguments");
|
|
||||||
static_assert(!is_callable_as<int(), void()>::value,
|
|
||||||
"different return types");
|
|
||||||
static_assert(!is_callable_as<int(), short()>::value,
|
|
||||||
"slightly different return types");
|
|
||||||
static_assert(!is_callable_as<int(int), int(int, int)>::value,
|
|
||||||
"more arguments");
|
|
||||||
static_assert(!is_callable_as<int(int, int), int(int)>::value,
|
|
||||||
"less arguments");
|
|
||||||
|
|
||||||
static_assert(!is_callable_as<int(int), int>::value,
|
|
||||||
"bad required signature");
|
|
||||||
|
|
||||||
static_assert(is_callable_with_args<void(), void()>::value,
|
|
||||||
"simple function");
|
|
||||||
static_assert(is_callable_with_args<void (&)(), void()>::value,
|
|
||||||
"function reference");
|
|
||||||
static_assert(is_callable_with_args<void (*)(), void()>::value,
|
|
||||||
"function pointer");
|
|
||||||
static_assert(is_callable_with_args<int(C&, C*), int(C&, C*)>::value,
|
|
||||||
"function with arguments and return type");
|
|
||||||
static_assert(is_callable_with_args<decltype(lambda), C*(bool)>::value,
|
|
||||||
"lambda");
|
|
||||||
static_assert(
|
|
||||||
is_callable_with_args<std::function<bool(int)>, bool(int)>::value,
|
|
||||||
"std::function");
|
|
||||||
|
|
||||||
static_assert(!is_callable_with_args<int, void()>::value,
|
|
||||||
"int should not be callable");
|
|
||||||
static_assert(!is_callable_with_args<C, void()>::value, "incomplete type");
|
|
||||||
static_assert(!is_callable_with_args<void(), void(int)>::value,
|
|
||||||
"different arguments");
|
|
||||||
static_assert(is_callable_with_args<int(), void()>::value,
|
|
||||||
"different return types are ignored");
|
|
||||||
static_assert(is_callable_with_args<int(), short()>::value,
|
|
||||||
"slightly different return types are ignored");
|
|
||||||
static_assert(!is_callable_with_args<int(int), int(int, int)>::value,
|
|
||||||
"more arguments");
|
|
||||||
static_assert(!is_callable_with_args<int(int, int), int(int)>::value,
|
|
||||||
"less arguments");
|
|
||||||
|
|
||||||
static_assert(!is_callable_with_args<int(int), int>::value,
|
|
||||||
"bad required signature");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(TypeTraits, IsTemplateInstantiation) {
|
|
||||||
static_assert(!is_template_instantiation_of<int, std::vector>::value,
|
|
||||||
"int is not an instance of vector");
|
|
||||||
static_assert(!is_template_instantiation_of<std::list<std::vector<int>>,
|
|
||||||
std::vector>::value,
|
|
||||||
"list is not an instance of vector");
|
|
||||||
|
|
||||||
static_assert(
|
|
||||||
is_template_instantiation_of<std::vector<int>, std::vector>::value,
|
|
||||||
"std::vector<int> is an instance of vector");
|
|
||||||
static_assert(
|
|
||||||
is_template_instantiation_of<std::vector<std::vector<std::vector<int>>>,
|
|
||||||
std::vector>::value,
|
|
||||||
"nested std::vector<> is an instance of vector");
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef _MSC_VER
|
|
||||||
TEST(TypeTraits, IsRange) {
|
|
||||||
static_assert(is_range<std::vector<int>>::value,
|
|
||||||
"vector<> should be detected as a range");
|
|
||||||
static_assert(is_range<const std::list<std::function<void()>>>::value,
|
|
||||||
"const list<> should be detected as a range");
|
|
||||||
static_assert(is_range<std::array<std::vector<int>, 10>>::value,
|
|
||||||
"array<> should be detected as a range");
|
|
||||||
char arr[100];
|
|
||||||
static_assert(is_range<decltype(arr)>::value,
|
|
||||||
"C array should be detected as a range");
|
|
||||||
static_assert(is_range<decltype("string")>::value,
|
|
||||||
"String literal should be detected as a range");
|
|
||||||
|
|
||||||
static_assert(!is_range<int>::value, "int shouldn't be a range");
|
|
||||||
static_assert(!is_range<int*>::value, "int* shouldn't be a range");
|
|
||||||
static_assert(!is_range<const int*>::value,
|
|
||||||
"even const int* shouldn't be a range");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace base
|
|
||||||
} // namespace astc_codec
|
|
||||||
140
3rdparty/astc-codec/src/base/test/uint128_test.cpp
vendored
@@ -1,140 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
#include "src/base/uint128.h"
|
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
namespace astc_codec {
|
|
||||||
namespace base {
|
|
||||||
|
|
||||||
TEST(UInt128, Equality) {
|
|
||||||
const UInt128 zero(0);
|
|
||||||
const UInt128 max64(~0ULL);
|
|
||||||
|
|
||||||
EXPECT_EQ(zero, zero);
|
|
||||||
EXPECT_NE(zero, max64);
|
|
||||||
EXPECT_EQ(zero, UInt128(0));
|
|
||||||
EXPECT_NE(zero, UInt128(1));
|
|
||||||
EXPECT_EQ(max64, max64);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UInt128, Shifting) {
|
|
||||||
const UInt128 max64(~0ULL);
|
|
||||||
const UInt128 upper64(~0ULL, 0);
|
|
||||||
EXPECT_EQ(upper64.HighBits(), ~0ULL);
|
|
||||||
EXPECT_EQ(upper64.LowBits(), 0);
|
|
||||||
|
|
||||||
EXPECT_EQ(upper64 >> 64, max64);
|
|
||||||
|
|
||||||
EXPECT_EQ(UInt128(1) << 1, UInt128(2));
|
|
||||||
EXPECT_EQ(UInt128(0) << 0, UInt128(0));
|
|
||||||
EXPECT_EQ(max64 << 0, max64);
|
|
||||||
EXPECT_EQ(max64 >> 0, max64);
|
|
||||||
EXPECT_EQ(upper64 << 0, upper64);
|
|
||||||
EXPECT_EQ(upper64 >> 0, upper64);
|
|
||||||
|
|
||||||
{
|
|
||||||
const UInt128 bit63 = UInt128(1ULL << 62) << 1;
|
|
||||||
EXPECT_EQ(bit63.LowBits(), 1ULL << 63);
|
|
||||||
EXPECT_EQ(bit63.HighBits(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const UInt128 bit64 = UInt128(1ULL << 63) << 1;
|
|
||||||
EXPECT_EQ(bit64.LowBits(), 0);
|
|
||||||
EXPECT_EQ(bit64.HighBits(), 1);
|
|
||||||
EXPECT_EQ(bit64 >> 1, UInt128(1ULL << 63));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const UInt128 overshift = max64 << 128;
|
|
||||||
EXPECT_EQ(overshift.HighBits(), 0);
|
|
||||||
EXPECT_EQ(overshift.LowBits(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const UInt128 overlap = upper64 >> 32;
|
|
||||||
EXPECT_EQ(overlap.HighBits(), 0x00000000FFFFFFFF);
|
|
||||||
EXPECT_EQ(overlap.LowBits(), 0xFFFFFFFF00000000);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const UInt128 overlap = max64 << 32;
|
|
||||||
EXPECT_EQ(overlap.HighBits(), 0x00000000FFFFFFFF);
|
|
||||||
EXPECT_EQ(overlap.LowBits(), 0xFFFFFFFF00000000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UInt128, LargeShift) {
|
|
||||||
const UInt128 base(0xFF);
|
|
||||||
EXPECT_EQ(base << 64, UInt128(0xFFULL, 0));
|
|
||||||
EXPECT_EQ(base << 72, UInt128(0xFF00ULL, 0));
|
|
||||||
EXPECT_EQ(base << 80, UInt128(0xFF0000ULL, 0));
|
|
||||||
EXPECT_EQ(base << 88, UInt128(0xFF000000ULL, 0));
|
|
||||||
EXPECT_EQ(base << 96, UInt128(0xFF00000000ULL, 0));
|
|
||||||
EXPECT_EQ(base << 104, UInt128(0xFF0000000000ULL, 0));
|
|
||||||
EXPECT_EQ(base << 112, UInt128(0xFF000000000000ULL, 0));
|
|
||||||
EXPECT_EQ(base << 120, UInt128(0xFF00000000000000ULL, 0));
|
|
||||||
|
|
||||||
const UInt128 upper(0xFF00000000000000ULL, 0);
|
|
||||||
EXPECT_EQ(upper >> 64, UInt128(0, 0xFF00000000000000ULL));
|
|
||||||
EXPECT_EQ(upper >> 72, UInt128(0, 0xFF000000000000ULL));
|
|
||||||
EXPECT_EQ(upper >> 80, UInt128(0, 0xFF0000000000ULL));
|
|
||||||
EXPECT_EQ(upper >> 88, UInt128(0, 0xFF00000000ULL));
|
|
||||||
EXPECT_EQ(upper >> 96, UInt128(0, 0xFF000000ULL));
|
|
||||||
EXPECT_EQ(upper >> 104, UInt128(0, 0xFF0000ULL));
|
|
||||||
EXPECT_EQ(upper >> 112, UInt128(0, 0xFF00ULL));
|
|
||||||
EXPECT_EQ(upper >> 120, UInt128(0, 0xFFULL));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UInt128, BooleanOperators) {
|
|
||||||
const UInt128 allOnes(~0ULL, ~0ULL);
|
|
||||||
EXPECT_EQ(allOnes.HighBits(), ~0ULL);
|
|
||||||
EXPECT_EQ(allOnes.LowBits(), ~0ULL);
|
|
||||||
|
|
||||||
EXPECT_EQ(~allOnes, UInt128(0));
|
|
||||||
EXPECT_EQ(~UInt128(0), allOnes);
|
|
||||||
|
|
||||||
EXPECT_EQ(UInt128(0xFFFF00) & UInt128(0x00FFFF), UInt128(0x00FF00));
|
|
||||||
EXPECT_EQ(UInt128(0xFFFF00) | UInt128(0x00FFFF), UInt128(0xFFFFFF));
|
|
||||||
EXPECT_EQ(UInt128(0xFFFF00) ^ UInt128(0x00FFFF), UInt128(0xFF00FF));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UInt128, Addition) {
|
|
||||||
const UInt128 bit63(1ULL << 63);
|
|
||||||
|
|
||||||
EXPECT_EQ(UInt128(1) + 1, UInt128(2));
|
|
||||||
EXPECT_EQ(bit63 + bit63, UInt128(1) << 64);
|
|
||||||
|
|
||||||
const UInt128 carryUp = UInt128(~0ULL) + 1;
|
|
||||||
EXPECT_EQ(carryUp.HighBits(), 1);
|
|
||||||
EXPECT_EQ(carryUp.LowBits(), 0);
|
|
||||||
|
|
||||||
const UInt128 allOnes(~0ULL, ~0ULL);
|
|
||||||
EXPECT_EQ(allOnes + 1, UInt128(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UInt128, Subtraction) {
|
|
||||||
const UInt128 bit64 = UInt128(1) << 64;
|
|
||||||
EXPECT_EQ(bit64 - 1, UInt128(~0ULL));
|
|
||||||
|
|
||||||
EXPECT_EQ(UInt128(1) - 1, UInt128(0));
|
|
||||||
|
|
||||||
const UInt128 allOnes(~0ULL, ~0ULL);
|
|
||||||
EXPECT_EQ(UInt128(0) - 1, allOnes);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace base
|
|
||||||
} // namespace astc_codec
|
|
||||||
246
3rdparty/astc-codec/src/decoder/BUILD.bazel
vendored
@@ -1,246 +0,0 @@
|
|||||||
# 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.
|
|
||||||
|
|
||||||
licenses(["notice"])
|
|
||||||
|
|
||||||
cc_library(
|
|
||||||
name = "footprint",
|
|
||||||
srcs = ["footprint.cc"],
|
|
||||||
hdrs = ["footprint.h"],
|
|
||||||
deps = [
|
|
||||||
"//:api",
|
|
||||||
"//src/base",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
cc_library(
|
|
||||||
name = "astc_utils",
|
|
||||||
srcs = [
|
|
||||||
"astc_file.cc",
|
|
||||||
"endpoint_codec.cc",
|
|
||||||
"integer_sequence_codec.cc",
|
|
||||||
"intermediate_astc_block.cc",
|
|
||||||
"logical_astc_block.cc",
|
|
||||||
"partition.cc",
|
|
||||||
"physical_astc_block.cc",
|
|
||||||
"quantization.cc",
|
|
||||||
"weight_infill.cc",
|
|
||||||
],
|
|
||||||
hdrs = [
|
|
||||||
"astc_file.h",
|
|
||||||
"endpoint_codec.h",
|
|
||||||
"integer_sequence_codec.h",
|
|
||||||
"intermediate_astc_block.h",
|
|
||||||
"logical_astc_block.h",
|
|
||||||
"partition.h",
|
|
||||||
"physical_astc_block.h",
|
|
||||||
"quantization.h",
|
|
||||||
"types.h",
|
|
||||||
"weight_infill.h",
|
|
||||||
],
|
|
||||||
copts = [
|
|
||||||
"-Wno-unused-variable",
|
|
||||||
"-O3",
|
|
||||||
],
|
|
||||||
deps = [
|
|
||||||
":footprint",
|
|
||||||
"//src/base",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
cc_library(
|
|
||||||
name = "codec",
|
|
||||||
srcs = ["codec.cc"],
|
|
||||||
hdrs = ["codec.h"],
|
|
||||||
visibility = ["//:__pkg__"],
|
|
||||||
deps = [
|
|
||||||
":astc_utils",
|
|
||||||
":footprint",
|
|
||||||
"//src/base",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
cc_binary(
|
|
||||||
name = "astc_inspector_cli",
|
|
||||||
srcs = ["tools/astc_inspector_cli.cc"],
|
|
||||||
deps = [
|
|
||||||
":astc_utils",
|
|
||||||
"//src/base",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
################################################################################
|
|
||||||
##
|
|
||||||
## Testing
|
|
||||||
##
|
|
||||||
################################################################################
|
|
||||||
|
|
||||||
cc_library(
|
|
||||||
name = "test",
|
|
||||||
testonly = 1,
|
|
||||||
hdrs = ["test/image_utils.h"],
|
|
||||||
deps = ["@gtest//:gtest"],
|
|
||||||
)
|
|
||||||
|
|
||||||
cc_test(
|
|
||||||
name = "physical_astc_block_test",
|
|
||||||
size = "small",
|
|
||||||
srcs = ["test/physical_astc_block_test.cc"],
|
|
||||||
deps = [
|
|
||||||
":astc_utils",
|
|
||||||
"@gtest//:gtest_main",
|
|
||||||
"//src/base",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
cc_test(
|
|
||||||
name = "partition_test",
|
|
||||||
size = "medium",
|
|
||||||
srcs = ["test/partition_test.cc"],
|
|
||||||
deps = [
|
|
||||||
":astc_utils",
|
|
||||||
"@gtest//:gtest_main",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
cc_test(
|
|
||||||
name = "integer_sequence_codec_test",
|
|
||||||
size = "small",
|
|
||||||
srcs = ["test/integer_sequence_codec_test.cc"],
|
|
||||||
deps = [
|
|
||||||
":astc_utils",
|
|
||||||
"@gtest//:gtest_main",
|
|
||||||
"//src/base",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
cc_test(
|
|
||||||
name = "intermediate_astc_block_test",
|
|
||||||
size = "small",
|
|
||||||
srcs = ["test/intermediate_astc_block_test.cc"],
|
|
||||||
data = glob([
|
|
||||||
"testdata/checkered_*.astc",
|
|
||||||
]),
|
|
||||||
deps = [
|
|
||||||
":astc_utils",
|
|
||||||
":test",
|
|
||||||
"@gtest//:gtest_main",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
cc_test(
|
|
||||||
name = "quantization_test",
|
|
||||||
size = "medium",
|
|
||||||
srcs = ["test/quantization_test.cc"],
|
|
||||||
deps = [
|
|
||||||
":astc_utils",
|
|
||||||
"@gtest//:gtest_main",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
cc_test(
|
|
||||||
name = "weight_infill_test",
|
|
||||||
size = "small",
|
|
||||||
srcs = ["test/weight_infill_test.cc"],
|
|
||||||
deps = [
|
|
||||||
":astc_utils",
|
|
||||||
":footprint",
|
|
||||||
"@gtest//:gtest_main",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
cc_test(
|
|
||||||
name = "endpoint_codec_test",
|
|
||||||
size = "small",
|
|
||||||
srcs = ["test/endpoint_codec_test.cc"],
|
|
||||||
data = [
|
|
||||||
":testdata/checkerboard.astc",
|
|
||||||
],
|
|
||||||
deps = [
|
|
||||||
":astc_utils",
|
|
||||||
":test",
|
|
||||||
"@gtest//:gtest_main",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
cc_test(
|
|
||||||
name = "logical_astc_block_test",
|
|
||||||
size = "large",
|
|
||||||
srcs = ["test/logical_astc_block_test.cc"],
|
|
||||||
data = glob([
|
|
||||||
"testdata/atlas_small_*.astc",
|
|
||||||
"testdata/atlas_small_*.bmp",
|
|
||||||
"testdata/footprint_*.astc",
|
|
||||||
"testdata/footprint_*.bmp",
|
|
||||||
"testdata/rgb_*.astc",
|
|
||||||
"testdata/rgb_*.bmp",
|
|
||||||
]),
|
|
||||||
deps = [
|
|
||||||
":astc_utils",
|
|
||||||
":test",
|
|
||||||
"@gtest//:gtest_main",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
cc_test(
|
|
||||||
name = "codec_test",
|
|
||||||
size = "large",
|
|
||||||
srcs = ["test/codec_test.cc"],
|
|
||||||
data = glob([
|
|
||||||
"testdata/atlas_small_*.astc",
|
|
||||||
"testdata/atlas_small_*.bmp",
|
|
||||||
]),
|
|
||||||
deps = [
|
|
||||||
":codec",
|
|
||||||
":test",
|
|
||||||
"@gtest//:gtest_main",
|
|
||||||
"//:api",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
cc_test(
|
|
||||||
name = "footprint_test",
|
|
||||||
size = "small",
|
|
||||||
srcs = ["test/footprint_test.cc"],
|
|
||||||
deps = [
|
|
||||||
":footprint",
|
|
||||||
"@gtest//:gtest_main",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
cc_test(
|
|
||||||
name = "astc_fuzzer",
|
|
||||||
srcs = ["test/astc_fuzzer.cc"],
|
|
||||||
copts = select({
|
|
||||||
# Clang-only flags. TODO: Find a better way to detect GCC/clang.
|
|
||||||
"@bazel_tools//src/conditions:darwin_x86_64": [
|
|
||||||
"-fsanitize-coverage=trace-pc-guard,indirect-calls,trace-cmp",
|
|
||||||
"-fsanitize-coverage=bb",
|
|
||||||
],
|
|
||||||
"@bazel_tools//src/conditions:darwin": [
|
|
||||||
"-fsanitize-coverage=trace-pc-guard,indirect-calls,trace-cmp",
|
|
||||||
"-fsanitize-coverage=bb",
|
|
||||||
],
|
|
||||||
# GCC-only flags.
|
|
||||||
"//conditions:default": [
|
|
||||||
"-finstrument-functions"
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
deps = [
|
|
||||||
":codec",
|
|
||||||
"@honggfuzz//:honggfuzz",
|
|
||||||
"@benchmark//:benchmark",
|
|
||||||
],
|
|
||||||
linkstatic = 1,
|
|
||||||
)
|
|
||||||
95
3rdparty/astc-codec/src/decoder/CMakeLists.txt
vendored
@@ -1,95 +0,0 @@
|
|||||||
# 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.
|
|
||||||
add_library(footprint footprint.cc)
|
|
||||||
target_link_libraries(footprint base)
|
|
||||||
|
|
||||||
add_library(astc_utils
|
|
||||||
astc_file.cc
|
|
||||||
endpoint_codec.cc
|
|
||||||
integer_sequence_codec.cc
|
|
||||||
intermediate_astc_block.cc
|
|
||||||
logical_astc_block.cc
|
|
||||||
partition.cc
|
|
||||||
physical_astc_block.cc
|
|
||||||
quantization.cc
|
|
||||||
weight_infill.cc)
|
|
||||||
target_link_libraries(astc_utils PRIVATE base footprint)
|
|
||||||
target_include_directories(astc_utils PRIVATE ../..)
|
|
||||||
|
|
||||||
add_library(astc-codec codec.cc)
|
|
||||||
target_link_libraries(astc-codec PRIVATE astc_utils)
|
|
||||||
target_include_directories(astc-codec PUBLIC ../../include)
|
|
||||||
target_include_directories(astc-codec PRIVATE ../..)
|
|
||||||
|
|
||||||
add_executable(astc_inspector_cli tools/astc_inspector_cli.cc)
|
|
||||||
target_include_directories(astc_inspector_cli PRIVATE ../..)
|
|
||||||
target_link_libraries(astc_inspector_cli PRIVATE astc_utils)
|
|
||||||
|
|
||||||
#
|
|
||||||
# Testing
|
|
||||||
#
|
|
||||||
if(OPTION_ASTC_TESTS)
|
|
||||||
# Note that we will execute all the tests in the project directory.
|
|
||||||
# We do this to ensure the unit tests can pick up the required test data
|
|
||||||
|
|
||||||
# Create interface library exposing the root as an include directory
|
|
||||||
add_library(codec_test_dependencies INTERFACE)
|
|
||||||
target_include_directories(codec_test_dependencies INTERFACE ../..)
|
|
||||||
|
|
||||||
add_executable(physical_astc_block_test test/physical_astc_block_test.cc)
|
|
||||||
add_test(NAME physical_astc_block_test COMMAND physical_astc_block_test WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
|
|
||||||
target_link_libraries(physical_astc_block_test astc_utils codec_test_dependencies gmock_main)
|
|
||||||
|
|
||||||
add_executable(partition_test test/partition_test.cc)
|
|
||||||
add_test(NAME partition_test COMMAND partition_test WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
|
|
||||||
target_link_libraries(partition_test PRIVATE astc_utils codec_test_dependencies gmock_main)
|
|
||||||
|
|
||||||
add_executable(integer_sequence_codec_test test/integer_sequence_codec_test.cc)
|
|
||||||
target_link_libraries(integer_sequence_codec_test PRIVATE astc_utils codec_test_dependencies gmock_main)
|
|
||||||
|
|
||||||
add_executable(intermediate_astc_block_test test/intermediate_astc_block_test.cc)
|
|
||||||
add_test(NAME intermediate_astc_block_test COMMAND intermediate_astc_block_test WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
|
|
||||||
target_link_libraries(intermediate_astc_block_test PRIVATE astc_utils codec_test_dependencies gmock_main)
|
|
||||||
|
|
||||||
add_executable(quantization_test test/quantization_test.cc)
|
|
||||||
add_test(NAME quantization_test COMMAND quantization_test WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
|
|
||||||
target_link_libraries(quantization_test PRIVATE astc_utils codec_test_dependencies gmock_main)
|
|
||||||
|
|
||||||
add_executable(weight_infill_test test/weight_infill_test.cc)
|
|
||||||
add_test(NAME weight_infill_test COMMAND weight_infill_test WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
|
|
||||||
target_link_libraries(weight_infill_test PRIVATE astc_utils footprint codec_test_dependencies gmock_main)
|
|
||||||
|
|
||||||
add_executable(endpoint_codec_test test/endpoint_codec_test.cc)
|
|
||||||
add_test(NAME endpoint_codec_test COMMAND endpoint_codec_test WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
|
|
||||||
target_link_libraries(endpoint_codec_test PRIVATE astc_utils codec_test_dependencies gmock_main)
|
|
||||||
|
|
||||||
add_executable(logical_astc_block_test test/logical_astc_block_test.cc)
|
|
||||||
add_test(NAME logical_astc_block_test COMMAND logical_astc_block_test WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
|
|
||||||
target_link_libraries(logical_astc_block_test PRIVATE astc_utils codec_test_dependencies gmock_main)
|
|
||||||
|
|
||||||
add_executable(codec_test test/codec_test.cc)
|
|
||||||
add_test(NAME codec_test COMMAND codec_test WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
|
|
||||||
|
|
||||||
target_link_libraries(codec_test PRIVATE astc-codec codec_test_dependencies gmock_main)
|
|
||||||
|
|
||||||
add_executable(footprint_test test/footprint_test.cc)
|
|
||||||
add_test(NAME footprint_test COMMAND footprint_test WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
|
|
||||||
target_link_libraries(footprint_test PRIVATE footprint codec_test_dependencies gmock_main)
|
|
||||||
|
|
||||||
if(OPTION_BUILD_FUZZER)
|
|
||||||
message(FATAL_ERROR "Not yet supported due to missing dependencies")
|
|
||||||
add_executable(astc_fuzzer test/astc_fuzzer.cc codec_test_dependencies gmock_main)
|
|
||||||
target_link_libraries(astc_fuzzer PRIVATE astc-codec honggfuzz benchmark)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
// 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 fuzzing wrapper to help with fuzz testing.
|
|
||||||
|
|
||||||
#include "src/decoder/codec.h"
|
|
||||||
|
|
||||||
#include <benchmark/benchmark.h>
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
|
|
||||||
std::string error;
|
|
||||||
std::unique_ptr<astc_codec::ASTCFile> file =
|
|
||||||
astc_codec::ASTCFile::LoadFromMemory(reinterpret_cast<const char*>(data),
|
|
||||||
size, &error);
|
|
||||||
if (file) {
|
|
||||||
std::vector<uint8_t> out_buffer(file->GetWidth() * file->GetHeight() * 4);
|
|
||||||
bool result = astc_codec::DecompressToImage(
|
|
||||||
*file, out_buffer.data(), out_buffer.size(), file->GetWidth() * 4);
|
|
||||||
benchmark::DoNotOptimize(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
181
3rdparty/astc-codec/src/decoder/test/codec_test.cc
vendored
@@ -1,181 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
#include "src/decoder/codec.h"
|
|
||||||
#include "include/astc-codec/astc-codec.h"
|
|
||||||
#include "src/decoder/test/image_utils.h"
|
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace astc_codec {
|
|
||||||
|
|
||||||
static void PrintTo(FootprintType footprint, std::ostream* os) {
|
|
||||||
switch (footprint) {
|
|
||||||
case FootprintType::k4x4: *os << "FootprintType::k4x4"; break;
|
|
||||||
case FootprintType::k5x4: *os << "FootprintType::k5x4"; break;
|
|
||||||
case FootprintType::k5x5: *os << "FootprintType::k5x5"; break;
|
|
||||||
case FootprintType::k6x5: *os << "FootprintType::k6x5"; break;
|
|
||||||
case FootprintType::k6x6: *os << "FootprintType::k6x6"; break;
|
|
||||||
case FootprintType::k8x5: *os << "FootprintType::k8x5"; break;
|
|
||||||
case FootprintType::k8x6: *os << "FootprintType::k8x6"; break;
|
|
||||||
case FootprintType::k10x5: *os << "FootprintType::k10x5"; break;
|
|
||||||
case FootprintType::k10x6: *os << "FootprintType::k10x6"; break;
|
|
||||||
case FootprintType::k8x8: *os << "FootprintType::k8x8"; break;
|
|
||||||
case FootprintType::k10x8: *os << "FootprintType::k10x8"; break;
|
|
||||||
case FootprintType::k10x10: *os << "FootprintType::k10x10"; break;
|
|
||||||
case FootprintType::k12x10: *os << "FootprintType::k12x10"; break;
|
|
||||||
case FootprintType::k12x12: *os << "FootprintType::k12x12"; break;
|
|
||||||
default:
|
|
||||||
*os << "<Unexpected FootprintType "
|
|
||||||
<< static_cast<uint32_t>(footprint) << ">";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
using ::testing::TestWithParam;
|
|
||||||
using ::testing::ValuesIn;
|
|
||||||
|
|
||||||
ImageBuffer LoadGoldenImageWithAlpha(std::string basename) {
|
|
||||||
const std::string filename =
|
|
||||||
std::string("src/decoder/testdata/") + basename + ".bmp";
|
|
||||||
ImageBuffer result;
|
|
||||||
LoadGoldenBmp(filename, &result);
|
|
||||||
EXPECT_EQ(result.BytesPerPixel(), 4);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ImageTestParams {
|
|
||||||
std::string image_name;
|
|
||||||
FootprintType footprint;
|
|
||||||
size_t width;
|
|
||||||
size_t height;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void PrintTo(const ImageTestParams& params, std::ostream* os) {
|
|
||||||
*os << "ImageTestParams(" << params.image_name << ", " << params.width << "x"
|
|
||||||
<< params.height << ", ";
|
|
||||||
PrintTo(params.footprint, os);
|
|
||||||
*os << ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(CodecTest, InvalidInput) {
|
|
||||||
const size_t valid_width = 16;
|
|
||||||
const size_t valid_height = 16;
|
|
||||||
const size_t valid_stride = valid_width * 4;
|
|
||||||
|
|
||||||
const std::vector<uint8_t> data(256);
|
|
||||||
std::vector<uint8_t> output(valid_width * valid_height * 4);
|
|
||||||
|
|
||||||
// Invalid footprint.
|
|
||||||
EXPECT_FALSE(ASTCDecompressToRGBA(
|
|
||||||
data.data(), data.size(), valid_width, valid_height,
|
|
||||||
FootprintType::kCount, output.data(), output.size(), valid_stride));
|
|
||||||
|
|
||||||
// Fail for 0 width or height.
|
|
||||||
EXPECT_FALSE(ASTCDecompressToRGBA(data.data(), data.size(), 0, valid_height,
|
|
||||||
FootprintType::k4x4, output.data(),
|
|
||||||
output.size(), valid_stride));
|
|
||||||
EXPECT_FALSE(ASTCDecompressToRGBA(data.data(), data.size(), valid_width, 0,
|
|
||||||
FootprintType::k4x4, output.data(),
|
|
||||||
output.size(), valid_stride));
|
|
||||||
|
|
||||||
// Fail for data size that's not a multiple of block size.
|
|
||||||
EXPECT_FALSE(ASTCDecompressToRGBA(
|
|
||||||
data.data(), data.size() - 1, valid_width, valid_height,
|
|
||||||
FootprintType::k4x4, output.data(), output.size(), valid_stride));
|
|
||||||
// Fail for data size that doesn't match the block count.
|
|
||||||
EXPECT_FALSE(ASTCDecompressToRGBA(
|
|
||||||
data.data(), data.size() - 16, valid_width, valid_height,
|
|
||||||
FootprintType::k4x4, output.data(), output.size(), valid_stride));
|
|
||||||
|
|
||||||
// Fail for invalid stride.
|
|
||||||
EXPECT_FALSE(ASTCDecompressToRGBA(
|
|
||||||
data.data(), data.size(), valid_width, valid_height, FootprintType::k4x4,
|
|
||||||
output.data(), output.size(), valid_stride - 1));
|
|
||||||
|
|
||||||
// Fail for invalid output size.
|
|
||||||
EXPECT_FALSE(ASTCDecompressToRGBA(
|
|
||||||
data.data(), data.size(), valid_width, valid_height, FootprintType::k4x4,
|
|
||||||
output.data(), output.size() - 1, valid_stride));
|
|
||||||
}
|
|
||||||
|
|
||||||
class CodecTest : public TestWithParam<ImageTestParams> {};
|
|
||||||
|
|
||||||
TEST_P(CodecTest, PublicAPI) {
|
|
||||||
const auto& params = GetParam();
|
|
||||||
const std::string astc = LoadASTCFile(params.image_name);
|
|
||||||
|
|
||||||
ImageBuffer our_decoded_image;
|
|
||||||
our_decoded_image.Allocate(params.width, params.height, 4);
|
|
||||||
ASSERT_TRUE(ASTCDecompressToRGBA(
|
|
||||||
reinterpret_cast<const uint8_t*>(astc.data()), astc.size(), params.width,
|
|
||||||
params.height, params.footprint, our_decoded_image.Data().data(),
|
|
||||||
our_decoded_image.DataSize(), our_decoded_image.Stride()));
|
|
||||||
|
|
||||||
// Check that the decoded image is *very* similar to the library decoding
|
|
||||||
// of an ASTC texture. They may not be exact due to differences in how we
|
|
||||||
// convert a 16-bit float to an 8-bit integer.
|
|
||||||
ImageBuffer decoded_image = LoadGoldenImageWithAlpha(params.image_name);
|
|
||||||
CompareSumOfSquaredDifferences(decoded_image, our_decoded_image, 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(CodecTest, DecompressToImage) {
|
|
||||||
const auto& params = GetParam();
|
|
||||||
|
|
||||||
std::string error;
|
|
||||||
std::unique_ptr<ASTCFile> image_file = ASTCFile::LoadFile(
|
|
||||||
std::string("src/decoder/testdata/") + params.image_name + ".astc",
|
|
||||||
&error);
|
|
||||||
ASSERT_TRUE(image_file) << "Failed to load " << params.image_name << ": "
|
|
||||||
<< error;
|
|
||||||
|
|
||||||
ASSERT_TRUE(image_file->GetFootprint());
|
|
||||||
EXPECT_EQ(params.footprint, image_file->GetFootprint().value().Type());
|
|
||||||
|
|
||||||
ImageBuffer our_decoded_image;
|
|
||||||
our_decoded_image.Allocate(image_file->GetWidth(), image_file->GetHeight(),
|
|
||||||
4);
|
|
||||||
|
|
||||||
ASSERT_TRUE(DecompressToImage(*image_file, our_decoded_image.Data().data(),
|
|
||||||
our_decoded_image.DataSize(),
|
|
||||||
our_decoded_image.Stride()));
|
|
||||||
|
|
||||||
// Check that the decoded image is *very* similar to the library decoding
|
|
||||||
// of an ASTC texture. They may not be exact due to differences in how we
|
|
||||||
// convert a 16-bit float to an 8-bit integer.
|
|
||||||
ImageBuffer decoded_image = LoadGoldenImageWithAlpha(params.image_name);
|
|
||||||
CompareSumOfSquaredDifferences(decoded_image, our_decoded_image, 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test to make sure that reading out color values from blocks in a real-world
|
|
||||||
// image isn't terribly wrong, either.
|
|
||||||
std::vector<ImageTestParams> GetTransparentImageTestParams() {
|
|
||||||
return {
|
|
||||||
// image_name astc footprint width height
|
|
||||||
{ "atlas_small_4x4", FootprintType::k4x4, 256, 256 },
|
|
||||||
{ "atlas_small_5x5", FootprintType::k5x5, 256, 256 },
|
|
||||||
{ "atlas_small_6x6", FootprintType::k6x6, 256, 256 },
|
|
||||||
{ "atlas_small_8x8", FootprintType::k8x8, 256, 256 },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(Transparent, CodecTest,
|
|
||||||
ValuesIn(GetTransparentImageTestParams()));
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
} // namespace astc_codec
|
|
||||||
@@ -1,463 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
#include "src/decoder/endpoint_codec.h"
|
|
||||||
#include "src/decoder/intermediate_astc_block.h"
|
|
||||||
#include "src/decoder/test/image_utils.h"
|
|
||||||
|
|
||||||
#include <random>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
#include <gmock/gmock.h>
|
|
||||||
|
|
||||||
namespace astc_codec {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
using ::testing::AllOf;
|
|
||||||
using ::testing::AnyOf;
|
|
||||||
using ::testing::Each;
|
|
||||||
using ::testing::Eq;
|
|
||||||
using ::testing::Ge;
|
|
||||||
using ::testing::Le;
|
|
||||||
using ::testing::Ne;
|
|
||||||
using ::testing::Pointwise;
|
|
||||||
using ::testing::SizeIs;
|
|
||||||
using ::testing::Pair;
|
|
||||||
|
|
||||||
constexpr std::array<EndpointEncodingMode, 6> kEndpointEncodingModes = {{
|
|
||||||
EndpointEncodingMode::kDirectLuma,
|
|
||||||
EndpointEncodingMode::kDirectLumaAlpha,
|
|
||||||
EndpointEncodingMode::kBaseScaleRGB,
|
|
||||||
EndpointEncodingMode::kBaseScaleRGBA,
|
|
||||||
EndpointEncodingMode::kDirectRGB,
|
|
||||||
EndpointEncodingMode::kDirectRGBA }};
|
|
||||||
|
|
||||||
const std::array<std::pair<RgbaColor, RgbaColor>, 3> kBlueContractPairs = {{
|
|
||||||
std::make_pair(RgbaColor{{ 22, 18, 30, 59 }},
|
|
||||||
RgbaColor{{ 162, 148, 155, 59 }}),
|
|
||||||
std::make_pair(RgbaColor{{ 22, 30, 27, 36 }},
|
|
||||||
RgbaColor{{ 228, 221, 207, 36 }}),
|
|
||||||
std::make_pair(RgbaColor{{ 54, 60, 55, 255 }},
|
|
||||||
RgbaColor{{ 23, 30, 27, 255 }})
|
|
||||||
}};
|
|
||||||
|
|
||||||
// Used to directly initialize std::pairs of colors with initializer lists
|
|
||||||
// e.g. MakeColors({{ r, g, b, a }}, {{ r, g, b, a }});
|
|
||||||
std::pair<RgbaColor, RgbaColor> MakeColors(RgbaColor&& a, RgbaColor&& b) {
|
|
||||||
return std::make_pair(a, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns |high| and |low| as they would be decoded using the quantization
|
|
||||||
// factor |quant| for the ColorEndpointMode |mode|.
|
|
||||||
std::pair<RgbaColor, RgbaColor> TestColors(
|
|
||||||
RgbaColor low, RgbaColor high, int quant, EndpointEncodingMode mode) {
|
|
||||||
ColorEndpointMode astc_mode;
|
|
||||||
std::vector<int> encoded;
|
|
||||||
const bool needs_swap =
|
|
||||||
EncodeColorsForMode(low, high, quant, mode, &astc_mode, &encoded);
|
|
||||||
|
|
||||||
RgbaColor decoded_low, decoded_high;
|
|
||||||
DecodeColorsForMode(encoded, quant, astc_mode, &decoded_low, &decoded_high);
|
|
||||||
|
|
||||||
if (needs_swap) {
|
|
||||||
return std::make_pair(decoded_high, decoded_low);
|
|
||||||
} else {
|
|
||||||
return std::make_pair(decoded_low, decoded_high);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if the argument tuple entries only differ by at most x.
|
|
||||||
MATCHER_P(IsCloseTo, x, "") {
|
|
||||||
const auto& a = ::testing::get<0>(arg);
|
|
||||||
const auto& b = ::testing::get<1>(arg);
|
|
||||||
return (a > b) ? ((a - b) <= x) : ((b - a) <= x);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test to make sure that the range of values that we get as they are
|
|
||||||
// quantized remains within what we pass as |quant|.
|
|
||||||
TEST(EndpointCodecTest, QuantRanges) {
|
|
||||||
const RgbaColor low {{ 0, 0, 0, 0 }};
|
|
||||||
const RgbaColor high {{ 255, 255, 255, 255 }};
|
|
||||||
|
|
||||||
std::vector<int> result;
|
|
||||||
for (const auto& mode : kEndpointEncodingModes) {
|
|
||||||
for (int i = 5; i < 256; ++i) {
|
|
||||||
ColorEndpointMode astc_mode;
|
|
||||||
const bool needs_swap =
|
|
||||||
EncodeColorsForMode(low, high, i, mode, &astc_mode, &result);
|
|
||||||
EXPECT_EQ(result.size(), NumValuesForEncodingMode(mode)) << i;
|
|
||||||
EXPECT_EQ(result.size(), NumColorValuesForEndpointMode(astc_mode)) << i;
|
|
||||||
|
|
||||||
// ASTC mode shouldn't use base/offset when endpoints are so far apart.
|
|
||||||
EXPECT_THAT(astc_mode, Ne(ColorEndpointMode::kLDRRGBBaseOffset));
|
|
||||||
EXPECT_THAT(astc_mode, Ne(ColorEndpointMode::kLDRRGBABaseOffset));
|
|
||||||
|
|
||||||
EXPECT_THAT(result, Each(AllOf(Ge(0), Le(i))))
|
|
||||||
<< "Mode: " << static_cast<int>(mode);
|
|
||||||
// We don't care if we need to swap the weights in this test
|
|
||||||
EXPECT_TRUE(needs_swap || !needs_swap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test to make sure that each mode that directly encodes colors can effectively
|
|
||||||
// encode both black and white
|
|
||||||
TEST(EndpointCodecTest, ExtremeDirectEncodings) {
|
|
||||||
const RgbaColor kWhite {{ 255, 255, 255, 255 }};
|
|
||||||
const RgbaColor kBlack {{ 0, 0, 0, 255 }};
|
|
||||||
|
|
||||||
std::vector<int> encoded;
|
|
||||||
for (const auto& mode : kEndpointEncodingModes) {
|
|
||||||
for (int i = 5; i < 256; ++i) {
|
|
||||||
const auto expected = std::make_pair(kWhite, kBlack);
|
|
||||||
EXPECT_EQ(TestColors(kWhite, kBlack, i, mode), expected)
|
|
||||||
<< "Range: " << i << ", Mode: " << static_cast<int>(mode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// According to the spec, this is used for colors close to gray. The values
|
|
||||||
// chosen here were according to the spec.
|
|
||||||
TEST(EndpointCodecTest, UsesBlueContract) {
|
|
||||||
std::vector<int> vals = { 132, 127, 116, 112, 183, 180, 31, 22 };
|
|
||||||
EXPECT_TRUE(UsesBlueContract(255, ColorEndpointMode::kLDRRGBDirect, vals));
|
|
||||||
EXPECT_TRUE(UsesBlueContract(255, ColorEndpointMode::kLDRRGBADirect, vals));
|
|
||||||
|
|
||||||
// For the offset modes the only way to trigger the blue contract mode is if
|
|
||||||
// we force the subtraction in the decoding procedure (See section C.2.14 of
|
|
||||||
// the spec), so we need to set the 7th bit to 1 for all of the odd-numbered
|
|
||||||
// values
|
|
||||||
vals[1] &= 0xBF;
|
|
||||||
vals[3] &= 0xBF;
|
|
||||||
vals[5] &= 0xBF;
|
|
||||||
vals[7] &= 0xBF;
|
|
||||||
|
|
||||||
EXPECT_FALSE(
|
|
||||||
UsesBlueContract(255, ColorEndpointMode::kLDRRGBBaseOffset, vals));
|
|
||||||
EXPECT_FALSE(
|
|
||||||
UsesBlueContract(255, ColorEndpointMode::kLDRRGBABaseOffset, vals));
|
|
||||||
|
|
||||||
vals[1] |= 0x40;
|
|
||||||
vals[3] |= 0x40;
|
|
||||||
vals[5] |= 0x40;
|
|
||||||
vals[7] |= 0x40;
|
|
||||||
|
|
||||||
EXPECT_TRUE(
|
|
||||||
UsesBlueContract(255, ColorEndpointMode::kLDRRGBBaseOffset, vals));
|
|
||||||
EXPECT_TRUE(
|
|
||||||
UsesBlueContract(255, ColorEndpointMode::kLDRRGBABaseOffset, vals));
|
|
||||||
|
|
||||||
// All other LDR endpoint modes should return no blue contract
|
|
||||||
for (int max_val : { 255, 127, 11 }) {
|
|
||||||
for (auto mode : { ColorEndpointMode::kLDRLumaDirect,
|
|
||||||
ColorEndpointMode::kLDRLumaBaseOffset,
|
|
||||||
ColorEndpointMode::kLDRLumaAlphaDirect,
|
|
||||||
ColorEndpointMode::kLDRLumaAlphaBaseOffset,
|
|
||||||
ColorEndpointMode::kLDRRGBBaseScale,
|
|
||||||
ColorEndpointMode::kLDRRGBBaseScaleTwoA }) {
|
|
||||||
EXPECT_FALSE(UsesBlueContract(max_val, mode, vals));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that encoding and decoding for the direct luminance mode works.
|
|
||||||
TEST(EndpointCodecTest, LumaDirect) {
|
|
||||||
const auto mode = EndpointEncodingMode::kDirectLuma;
|
|
||||||
|
|
||||||
// With a 255 quantizer, all greys should be exact.
|
|
||||||
for (int i = 0; i < 255; ++i) {
|
|
||||||
for (int j = 0; j < 255; ++j) {
|
|
||||||
EXPECT_EQ(TestColors({{ i, i, i, 255 }}, {{ j, j, j, 255 }}, 255, mode),
|
|
||||||
MakeColors({{ i, i, i, 255 }}, {{ j, j, j, 255 }}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have almost grey, then they should encode to grey.
|
|
||||||
EXPECT_EQ(TestColors({{ 247, 248, 246, 255 }}, {{ 2, 3, 1, 255 }}, 255, mode),
|
|
||||||
MakeColors({{ 247, 247, 247, 255 }}, {{ 2, 2, 2, 255 }}));
|
|
||||||
|
|
||||||
EXPECT_EQ(TestColors({{ 80, 80, 50, 255 }}, {{ 99, 255, 6, 255 }}, 255, mode),
|
|
||||||
MakeColors({{ 70, 70, 70, 255 }}, {{ 120, 120, 120, 255 }}));
|
|
||||||
|
|
||||||
// If we have almost greys and a really small quantizer, it should be white
|
|
||||||
// and black (literally).
|
|
||||||
EXPECT_EQ(TestColors({{ 247, 248, 246, 255 }}, {{ 2, 3, 1, 255 }}, 15, mode),
|
|
||||||
MakeColors({{ 255, 255, 255, 255 }}, {{ 0, 0, 0, 255 }}));
|
|
||||||
|
|
||||||
// The average of 64, 127, and 192 is 127.666..., so it should round to
|
|
||||||
// 130 instead of 125.
|
|
||||||
EXPECT_EQ(TestColors({{ 64, 127, 192, 255 }}, {{ 0, 0, 0, 255 }}, 63, mode),
|
|
||||||
MakeColors({{ 130, 130, 130, 255 }}, {{ 0, 0, 0, 255 }}));
|
|
||||||
|
|
||||||
// If we have almost grey, then they should encode to grey -- similar to
|
|
||||||
// direct encoding since the encoded colors differ by < 63.
|
|
||||||
EXPECT_EQ(TestColors({{ 80, 80, 50, 255 }}, {{ 99, 255, 6, 255 }}, 255, mode),
|
|
||||||
MakeColors({{ 70, 70, 70, 255 }}, {{ 120, 120, 120, 255 }}));
|
|
||||||
|
|
||||||
// Low precision colors should still encode pretty well with base/offset.
|
|
||||||
EXPECT_EQ(TestColors({{ 35, 36, 38, 255 }}, {{ 42, 43, 40, 255 }}, 47, mode),
|
|
||||||
MakeColors({{ 38, 38, 38, 255 }}, {{ 43, 43, 43, 255 }}));
|
|
||||||
|
|
||||||
EXPECT_EQ(TestColors({{ 39, 42, 40, 255 }}, {{ 18, 20, 21, 255 }}, 39, mode),
|
|
||||||
MakeColors({{ 39, 39, 39, 255 }}, {{ 19, 19, 19, 255 }}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test encoding and decoding for the base-offset luminance mode.
|
|
||||||
TEST(EndpointCodecTest, LumaAlphaDirect) {
|
|
||||||
const auto mode = EndpointEncodingMode::kDirectLumaAlpha;
|
|
||||||
|
|
||||||
// With a 255 quantizer, all greys should be exact.
|
|
||||||
for (int i = 0; i < 255; ++i) {
|
|
||||||
for (int j = 0; j < 255; ++j) {
|
|
||||||
EXPECT_EQ(TestColors({{ i, i, i, j }}, {{ j, j, j, i }}, 255, mode),
|
|
||||||
MakeColors({{ i, i, i, j }}, {{ j, j, j, i }}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have almost grey, then they should encode to grey.
|
|
||||||
EXPECT_EQ(TestColors({{ 247, 248, 246, 250 }}, {{ 2, 3, 1, 172 }}, 255, mode),
|
|
||||||
MakeColors({{ 247, 247, 247, 250 }}, {{ 2, 2, 2, 172 }}));
|
|
||||||
|
|
||||||
EXPECT_EQ(TestColors({{ 80, 80, 50, 0 }}, {{ 99, 255, 6, 255 }}, 255, mode),
|
|
||||||
MakeColors({{ 70, 70, 70, 0 }}, {{ 120, 120, 120, 255 }}));
|
|
||||||
|
|
||||||
// If we have almost greys and a really small quantizer, it should be white
|
|
||||||
// and black (literally).
|
|
||||||
EXPECT_EQ(TestColors({{ 247, 248, 246, 253 }}, {{ 2, 3, 1, 3 }}, 15, mode),
|
|
||||||
MakeColors({{ 255, 255, 255, 255 }}, {{ 0, 0, 0, 0 }}));
|
|
||||||
|
|
||||||
// The average of 64, 127, and 192 is 127.666..., so it should round to
|
|
||||||
// 130 instead of 125. The alpha in this case is independent.
|
|
||||||
EXPECT_EQ(TestColors({{ 64, 127, 192, 127 }}, {{ 0, 0, 0, 20 }}, 63, mode),
|
|
||||||
MakeColors({{ 130, 130, 130, 125 }}, {{ 0, 0, 0, 20 }}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test encoding for the direct RGB mode.
|
|
||||||
TEST(EndpointCodecTest, RGBDirect) {
|
|
||||||
const auto mode = EndpointEncodingMode::kDirectRGB;
|
|
||||||
|
|
||||||
// Colors should be encoded exactly with a 255 quantizer.
|
|
||||||
std::mt19937 random(0xdeadbeef);
|
|
||||||
std::uniform_int_distribution<int> byte_distribution(0, 255);
|
|
||||||
|
|
||||||
for (int i = 0; i < 100; ++i) {
|
|
||||||
RgbaColor low, high;
|
|
||||||
for (auto& x : high) { x = byte_distribution(random); }
|
|
||||||
for (auto& x : low) { x = byte_distribution(random); }
|
|
||||||
high[3] = low[3] = 255; // RGB Direct mode has opaque alpha.
|
|
||||||
|
|
||||||
EXPECT_EQ(TestColors(low, high, 255, mode), std::make_pair(low, high))
|
|
||||||
<< "Random iter: " << i;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For each of the following tests, order of endpoints shouldn't have any
|
|
||||||
// bearing on the quantization properties, so we should be able to switch
|
|
||||||
// endpoints as we see fit and have them generate the same flipped encoded
|
|
||||||
// pairs.
|
|
||||||
|
|
||||||
EXPECT_EQ(TestColors({{ 64, 127, 192, 255 }}, {{ 0, 0, 0, 255 }}, 63, mode),
|
|
||||||
MakeColors({{ 65, 125, 190, 255 }}, {{ 0, 0, 0, 255 }}));
|
|
||||||
|
|
||||||
EXPECT_EQ(TestColors({{ 0, 0, 0, 255 }}, {{ 64, 127, 192, 255 }}, 63, mode),
|
|
||||||
MakeColors({{ 0, 0, 0, 255 }}, {{ 65, 125, 190, 255 }}));
|
|
||||||
|
|
||||||
EXPECT_EQ(TestColors({{ 1, 2, 94, 255 }}, {{ 168, 255, 13, 255 }}, 7, mode),
|
|
||||||
MakeColors({{ 0, 0, 109, 255 }}, {{ 182, 255, 0, 255 }}));
|
|
||||||
|
|
||||||
// Colors close to grey will likely need a blue contract.
|
|
||||||
EXPECT_EQ(TestColors(kBlueContractPairs[0].first,
|
|
||||||
kBlueContractPairs[0].second, 31, mode),
|
|
||||||
MakeColors({{ 24, 20, 33, 255 }}, {{ 160, 148, 156, 255 }}));
|
|
||||||
|
|
||||||
EXPECT_EQ(TestColors(kBlueContractPairs[0].second,
|
|
||||||
kBlueContractPairs[0].first, 31, mode),
|
|
||||||
MakeColors({{ 160, 148, 156, 255 }}, {{ 24, 20, 33, 255 }}));
|
|
||||||
|
|
||||||
EXPECT_EQ(TestColors(kBlueContractPairs[1].first,
|
|
||||||
kBlueContractPairs[1].second, 7, mode),
|
|
||||||
MakeColors({{ 18, 36, 36, 255 }}, {{ 237, 219, 219, 255 }}));
|
|
||||||
|
|
||||||
EXPECT_EQ(TestColors(kBlueContractPairs[1].second,
|
|
||||||
kBlueContractPairs[1].first, 7, mode),
|
|
||||||
MakeColors({{ 237, 219, 219, 255 }}, {{ 18, 36, 36, 255 }}));
|
|
||||||
|
|
||||||
// Colors close to grey (and each other) will likely need a blue contract AND
|
|
||||||
// use the offset mode for encoding
|
|
||||||
EXPECT_EQ(TestColors(kBlueContractPairs[2].first,
|
|
||||||
kBlueContractPairs[2].second, 31, mode),
|
|
||||||
MakeColors({{ 53, 59, 53, 255 }}, {{ 24, 30, 26, 255 }}));
|
|
||||||
|
|
||||||
EXPECT_EQ(TestColors(kBlueContractPairs[2].second,
|
|
||||||
kBlueContractPairs[2].first, 31, mode),
|
|
||||||
MakeColors({{ 24, 30, 26, 255 }}, {{ 53, 59, 53, 255 }}));
|
|
||||||
|
|
||||||
// Colors close to each other, but not to grey will likely only use the offset
|
|
||||||
// mode and not the blue-contract modes.
|
|
||||||
EXPECT_EQ(TestColors({{ 22, 148, 30, 59 }}, {{ 162, 18, 155, 59 }}, 31, mode),
|
|
||||||
MakeColors({{ 24, 148, 33, 255 }}, {{ 165, 16, 156, 255 }}));
|
|
||||||
|
|
||||||
EXPECT_EQ(TestColors({{ 162, 18, 155, 59 }}, {{ 22, 148, 30, 59 }}, 31, mode),
|
|
||||||
MakeColors({{ 165, 16, 156, 255 }}, {{ 24, 148, 33, 255 }}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that certain endpoint pairs result in the blue-contract path as
|
|
||||||
// we'd expect, such that we can make sure that we're hitting all of the encode
|
|
||||||
// paths.
|
|
||||||
TEST(EndpointCodecTest, RGBDirectMakesBlueContract) {
|
|
||||||
constexpr int kEndpointRange = 31;
|
|
||||||
for (const auto& endpoint_pair : kBlueContractPairs) {
|
|
||||||
ColorEndpointMode astc_mode;
|
|
||||||
std::vector<int> vals;
|
|
||||||
bool needs_swap = EncodeColorsForMode(
|
|
||||||
endpoint_pair.first, endpoint_pair.second,
|
|
||||||
kEndpointRange, EndpointEncodingMode::kDirectRGB, &astc_mode, &vals);
|
|
||||||
(void)(needs_swap); // Don't really care.
|
|
||||||
|
|
||||||
EXPECT_TRUE(UsesBlueContract(kEndpointRange, astc_mode, vals));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that encoding and decoding for the RGB base-scale mode works.
|
|
||||||
TEST(EndpointCodecTest, RGBBaseScale) {
|
|
||||||
const auto mode = EndpointEncodingMode::kBaseScaleRGB;
|
|
||||||
const auto close_to = [](RgbaColor c, int x) {
|
|
||||||
return Pointwise(IsCloseTo(x), c);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Identical colors should be encoded with a 255 scale factor. Since ASTC
|
|
||||||
// decodes the scaled color by doing (x * s) >> 8, the decoded color will be
|
|
||||||
// multiplied by 255/256. This might cause rounding errors sometimes, so we
|
|
||||||
// check that every channel only deviates by 1.
|
|
||||||
std::mt19937 random(0xdeadbeef);
|
|
||||||
std::uniform_int_distribution<int> byte_distribution(0, 255);
|
|
||||||
|
|
||||||
for (int i = 0; i < 100; ++i) {
|
|
||||||
RgbaColor color{{byte_distribution(random), byte_distribution(random),
|
|
||||||
byte_distribution(random), 255}};
|
|
||||||
const auto test_result = TestColors(color, color, 255, mode);
|
|
||||||
EXPECT_THAT(test_result, Pair(close_to(color, 1), close_to(color, 1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that if we want to scale by e.g. 1/4 then we can do that exactly:
|
|
||||||
const RgbaColor low = {{ 20, 4, 40, 255 }};
|
|
||||||
const RgbaColor high = {{ 80, 16, 160, 255 }};
|
|
||||||
EXPECT_THAT(TestColors(low, high, 255, mode),
|
|
||||||
Pair(close_to(low, 0), close_to(high, 0)));
|
|
||||||
|
|
||||||
// And if we quantize it, then we get roughly the same thing. The scale factor
|
|
||||||
// should be representable with most quantization levels. The problem is that
|
|
||||||
// if we're off on the 'high' color, then we will be off on the 'low' color.
|
|
||||||
EXPECT_THAT(TestColors(low, high, 127, mode),
|
|
||||||
Pair(close_to(low, 1), close_to(high, 1)));
|
|
||||||
|
|
||||||
EXPECT_THAT(TestColors(low, high, 63, mode),
|
|
||||||
Pair(close_to(low, 1), close_to(high, 2)));
|
|
||||||
|
|
||||||
EXPECT_THAT(TestColors(low, high, 31, mode),
|
|
||||||
Pair(close_to(low, 1), close_to(high, 4)));
|
|
||||||
|
|
||||||
EXPECT_THAT(TestColors(low, high, 15, mode),
|
|
||||||
Pair(close_to(low, 2), close_to(high, 8)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that encoding and decoding for the RGB base-offset mode works.
|
|
||||||
// Since we don't have a decoder, this is currently only a test that should work
|
|
||||||
// based on reasoning about what's written in the spec.
|
|
||||||
// TODO(krajcevski): Write an encoder.
|
|
||||||
TEST(EndpointCodecTest, RGBBaseOffset) {
|
|
||||||
const auto test_colors = [](const RgbaColor& low, const RgbaColor& high) {
|
|
||||||
const RgbaColor diff = {{ high[0] - low[0], high[1] - low[1],
|
|
||||||
high[2] - low[2], high[3] - low[3] }};
|
|
||||||
|
|
||||||
std::vector<int> vals;
|
|
||||||
for (int i = 0; i < 3; ++i) {
|
|
||||||
// If the base is "large", then it grabs it's most significant bit from
|
|
||||||
// the offset value. Hence, we need to save it here.
|
|
||||||
const bool is_large = low[i] >= 128;
|
|
||||||
vals.push_back((low[i] * 2) & 0xFF);
|
|
||||||
vals.push_back(diff[i] * 2);
|
|
||||||
|
|
||||||
// Give the "large" bases their bits back.
|
|
||||||
if (is_large) {
|
|
||||||
vals.back() |= 0x80;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RgbaColor dec_low, dec_high;
|
|
||||||
DecodeColorsForMode(vals, 255, ColorEndpointMode::kLDRRGBBaseOffset,
|
|
||||||
&dec_low, &dec_high);
|
|
||||||
|
|
||||||
EXPECT_THAT(std::make_pair(dec_low, dec_high), Pair(Eq(low), Eq(high)));
|
|
||||||
};
|
|
||||||
|
|
||||||
// Test the "direct encoding" path.
|
|
||||||
test_colors({{ 80, 16, 112, 255 }}, {{ 87, 18, 132, 255 }});
|
|
||||||
test_colors({{ 80, 74, 82, 255 }}, {{ 90, 92, 110, 255 }});
|
|
||||||
test_colors({{ 0, 0, 0, 255 }}, {{ 2, 2, 2, 255 }});
|
|
||||||
|
|
||||||
// Identical endpoints should always encode exactly, provided they satisfy the
|
|
||||||
// requirements for the base encoding.
|
|
||||||
std::mt19937 random(0xdeadbeef);
|
|
||||||
std::uniform_int_distribution<int> byte_distribution(0, 255);
|
|
||||||
for (int i = 0; i < 100; ++i) {
|
|
||||||
RgbaColor color{{byte_distribution(random), byte_distribution(random),
|
|
||||||
byte_distribution(random), 255}};
|
|
||||||
if ((color[0] | color[1] | color[2]) & 1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
test_colors(color, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(google): Test the "blue contract" path.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that we can decode colors that are given to us straight out of the
|
|
||||||
// ASTC codec.
|
|
||||||
TEST(EndpointCodecTest, DecodeCheckerboard) {
|
|
||||||
const RgbaColor kWhite {{ 255, 255, 255, 255 }};
|
|
||||||
const RgbaColor kBlack {{ 0, 0, 0, 255 }};
|
|
||||||
|
|
||||||
const std::string astc = LoadASTCFile("checkerboard");
|
|
||||||
for (int i = 0; i < astc.size(); i += 16) {
|
|
||||||
base::UInt128 block;
|
|
||||||
memcpy(&block, &astc[i], sizeof(block));
|
|
||||||
|
|
||||||
const auto intermediate = UnpackIntermediateBlock(PhysicalASTCBlock(block));
|
|
||||||
ASSERT_TRUE(intermediate) << "Block is void extent???";
|
|
||||||
|
|
||||||
const auto block_data = &intermediate.value();
|
|
||||||
ASSERT_THAT(block_data->endpoints, SizeIs(Eq(1)));
|
|
||||||
|
|
||||||
const int color_range = EndpointRangeForBlock(*block_data);
|
|
||||||
const auto& endpoints = block_data->endpoints[0];
|
|
||||||
|
|
||||||
RgbaColor low, high;
|
|
||||||
DecodeColorsForMode(endpoints.colors, color_range, endpoints.mode,
|
|
||||||
&low, &high);
|
|
||||||
|
|
||||||
// Expect that the endpoints are black and white, but either order.
|
|
||||||
EXPECT_THAT(std::make_pair(low, high),
|
|
||||||
AnyOf(
|
|
||||||
Pair(Eq(kWhite), Eq(kBlack)),
|
|
||||||
Pair(Eq(kBlack), Eq(kWhite))));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
} // namespace astc_codec
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
#include "src/decoder/footprint.h"
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <tuple>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
namespace astc_codec {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
TEST(FootprintTest, ParseAstcFootprintString) {
|
|
||||||
using ASTCTestPair = std::pair<std::string, Footprint>;
|
|
||||||
const std::array<ASTCTestPair, Footprint::NumValidFootprints()>
|
|
||||||
valid_footprints {{
|
|
||||||
std::make_pair("4x4", Footprint::Get4x4()),
|
|
||||||
std::make_pair("5x4", Footprint::Get5x4()),
|
|
||||||
std::make_pair("5x5", Footprint::Get5x5()),
|
|
||||||
std::make_pair("6x5", Footprint::Get6x5()),
|
|
||||||
std::make_pair("6x6", Footprint::Get6x6()),
|
|
||||||
std::make_pair("8x5", Footprint::Get8x5()),
|
|
||||||
std::make_pair("8x6", Footprint::Get8x6()),
|
|
||||||
std::make_pair("8x8", Footprint::Get8x8()),
|
|
||||||
std::make_pair("10x5", Footprint::Get10x5()),
|
|
||||||
std::make_pair("10x6", Footprint::Get10x6()),
|
|
||||||
std::make_pair("10x8", Footprint::Get10x8()),
|
|
||||||
std::make_pair("10x10", Footprint::Get10x10()),
|
|
||||||
std::make_pair("12x10", Footprint::Get12x10()),
|
|
||||||
std::make_pair("12x12", Footprint::Get12x12())
|
|
||||||
}};
|
|
||||||
|
|
||||||
for (const auto& test : valid_footprints) {
|
|
||||||
base::Optional<Footprint> footprint = Footprint::Parse(test.first.c_str());
|
|
||||||
EXPECT_TRUE(footprint);
|
|
||||||
EXPECT_EQ(test.second, footprint.value());
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPECT_DEBUG_DEATH(EXPECT_FALSE(Footprint::Parse("")), "");
|
|
||||||
EXPECT_DEBUG_DEATH(EXPECT_FALSE(Footprint::Parse("3")), "");
|
|
||||||
EXPECT_DEBUG_DEATH(EXPECT_FALSE(Footprint::Parse("x")), "");
|
|
||||||
// Validly formed but out-of-bounds dimensions do not assert, otherwise
|
|
||||||
// malformed ASTC files could crash the decoder in debug builds.
|
|
||||||
EXPECT_FALSE(Footprint::Parse("9999999999x10"));
|
|
||||||
EXPECT_DEBUG_DEATH(EXPECT_FALSE(Footprint::Parse("ax8")), "");
|
|
||||||
EXPECT_DEBUG_DEATH(EXPECT_FALSE(Footprint::Parse("2x3x4")), "");
|
|
||||||
EXPECT_DEBUG_DEATH(EXPECT_FALSE(Footprint::Parse("-3x4")), "");
|
|
||||||
EXPECT_FALSE(Footprint::Parse("10x4"));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(FootprintTest, Bitrates) {
|
|
||||||
EXPECT_NEAR(Footprint::Get4x4().Bitrate(), 8.f, 0.01f);
|
|
||||||
EXPECT_NEAR(Footprint::Get5x4().Bitrate(), 6.4f, 0.01f);
|
|
||||||
EXPECT_NEAR(Footprint::Get5x5().Bitrate(), 5.12f, 0.01f);
|
|
||||||
EXPECT_NEAR(Footprint::Get6x5().Bitrate(), 4.27f, 0.01f);
|
|
||||||
EXPECT_NEAR(Footprint::Get6x6().Bitrate(), 3.56f, 0.01f);
|
|
||||||
EXPECT_NEAR(Footprint::Get8x5().Bitrate(), 3.20f, 0.01f);
|
|
||||||
EXPECT_NEAR(Footprint::Get8x6().Bitrate(), 2.67f, 0.01f);
|
|
||||||
EXPECT_NEAR(Footprint::Get8x8().Bitrate(), 2.00f, 0.01f);
|
|
||||||
EXPECT_NEAR(Footprint::Get10x5().Bitrate(), 2.56f, 0.01f);
|
|
||||||
EXPECT_NEAR(Footprint::Get10x6().Bitrate(), 2.13f, 0.01f);
|
|
||||||
EXPECT_NEAR(Footprint::Get10x8().Bitrate(), 1.60f, 0.01f);
|
|
||||||
EXPECT_NEAR(Footprint::Get10x10().Bitrate(), 1.28f, 0.01f);
|
|
||||||
EXPECT_NEAR(Footprint::Get12x10().Bitrate(), 1.07f, 0.01f);
|
|
||||||
EXPECT_NEAR(Footprint::Get12x12().Bitrate(), 0.89f, 0.01f);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(FootprintTest, StorageRequirements) {
|
|
||||||
auto footprint = Footprint::Get10x8();
|
|
||||||
EXPECT_EQ(footprint.Width(), 10);
|
|
||||||
EXPECT_EQ(footprint.Height(), 8);
|
|
||||||
|
|
||||||
// If we have 8x8 blocks, then we have 64*16 = 1024 bytes.
|
|
||||||
EXPECT_EQ(footprint.StorageRequirements(80, 64), 1024);
|
|
||||||
|
|
||||||
// If our block is a little smaller this still counts because we need to
|
|
||||||
// cover a partial block with a fully encoded one.
|
|
||||||
EXPECT_EQ(footprint.StorageRequirements(79, 63), 1024);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
} // namespace astc_codec
|
|
||||||
217
3rdparty/astc-codec/src/decoder/test/image_utils.h
vendored
@@ -1,217 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include <fstream>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
static constexpr size_t kMaxVectorOutput = 128;
|
|
||||||
|
|
||||||
class ImageBuffer {
|
|
||||||
public:
|
|
||||||
static constexpr size_t Align = 4;
|
|
||||||
|
|
||||||
void Allocate(size_t width, size_t height, size_t bytes_per_pixel) {
|
|
||||||
width_ = width;
|
|
||||||
height_ = height;
|
|
||||||
bytes_per_pixel_ = bytes_per_pixel;
|
|
||||||
stride_ = AlignBytes(width * bytes_per_pixel);
|
|
||||||
data_.resize(stride_ * height);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t* operator()(size_t x, size_t y) {
|
|
||||||
assert(x < width_ && y < height_);
|
|
||||||
return &data_[y * Stride() + x * bytes_per_pixel_];
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Stride() const { return stride_; }
|
|
||||||
size_t BytesPerPixel() const { return bytes_per_pixel_; }
|
|
||||||
|
|
||||||
std::vector<uint8_t>& Data() { return data_; }
|
|
||||||
const std::vector<uint8_t>& Data() const { return data_; }
|
|
||||||
size_t DataSize() const { return data_.size(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
size_t AlignBytes(size_t bytes) const {
|
|
||||||
return (bytes + (Align - 1)) / Align * Align;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t width_ = 0;
|
|
||||||
size_t height_ = 0;
|
|
||||||
size_t stride_ = 0;
|
|
||||||
size_t bytes_per_pixel_ = 0;
|
|
||||||
std::vector<uint8_t> data_;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace std {
|
|
||||||
static void PrintTo(const vector<uint8_t>& vec, ostream* os) {
|
|
||||||
ios::fmtflags origFlags(os->flags());
|
|
||||||
|
|
||||||
*os << '{';
|
|
||||||
size_t count = 0;
|
|
||||||
for (vector<uint8_t>::const_iterator it = vec.begin(); it != vec.end();
|
|
||||||
++it, ++count) {
|
|
||||||
if (count > 0) {
|
|
||||||
*os << ", ";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count == kMaxVectorOutput) {
|
|
||||||
*os << "... ";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((count % 16) == 0) {
|
|
||||||
*os << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*it == 0) {
|
|
||||||
*os << " ";
|
|
||||||
} else {
|
|
||||||
*os << "0x" << std::hex << std::uppercase << std::setw(2)
|
|
||||||
<< std::setfill('0') << int(*it) << std::dec;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*os << '}';
|
|
||||||
|
|
||||||
os->flags(origFlags);
|
|
||||||
}
|
|
||||||
} // namespace std
|
|
||||||
|
|
||||||
static inline std::string LoadFile(const std::string& path) {
|
|
||||||
std::ifstream is(path, std::ios::binary);
|
|
||||||
EXPECT_TRUE(is) << "Failed to load file " << path;
|
|
||||||
if (!is) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostringstream ss;
|
|
||||||
ss << is.rdbuf();
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline std::string LoadASTCFile(const std::string& basename) {
|
|
||||||
const std::string filename =
|
|
||||||
std::string("src/decoder/testdata/") + basename + ".astc";
|
|
||||||
|
|
||||||
std::string result = LoadFile(filename);
|
|
||||||
// Don't parse the header here, we already know what kind of astc encoding it
|
|
||||||
// is.
|
|
||||||
if (result.size() < 16) {
|
|
||||||
return "";
|
|
||||||
} else {
|
|
||||||
return result.substr(16);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void LoadGoldenBmp(const std::string& path, ImageBuffer* result) {
|
|
||||||
constexpr size_t kBmpHeaderSize = 54;
|
|
||||||
|
|
||||||
SCOPED_TRACE(testing::Message() << "LoadGoldenBmp " << path);
|
|
||||||
|
|
||||||
const std::string data = LoadFile(path);
|
|
||||||
ASSERT_FALSE(data.empty()) << "Failed to open golden image: " << path;
|
|
||||||
|
|
||||||
ASSERT_GE(data.size(), kBmpHeaderSize);
|
|
||||||
ASSERT_EQ('B', data[0]);
|
|
||||||
ASSERT_EQ('M', data[1]);
|
|
||||||
|
|
||||||
uint32_t dataPos = *reinterpret_cast<const uint32_t*>(&data[0x0A]);
|
|
||||||
uint32_t imageSize = *reinterpret_cast<const uint32_t*>(&data[0x22]);
|
|
||||||
const uint16_t bitsPerPixel = *reinterpret_cast<const uint16_t*>(&data[0x1C]);
|
|
||||||
int width = *reinterpret_cast<const int*>(&data[0x12]);
|
|
||||||
int height = *reinterpret_cast<const int*>(&data[0x16]);
|
|
||||||
|
|
||||||
SCOPED_TRACE(testing::Message()
|
|
||||||
<< "dataPos=" << dataPos << ", imageSize=" << imageSize
|
|
||||||
<< ", bitsPerPixel=" << bitsPerPixel << ", width=" << width
|
|
||||||
<< ", height=" << height);
|
|
||||||
|
|
||||||
if (height < 0) {
|
|
||||||
height = -height;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (imageSize == 0) {
|
|
||||||
imageSize = width * height * 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dataPos < kBmpHeaderSize) {
|
|
||||||
dataPos = kBmpHeaderSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT_TRUE(bitsPerPixel == 24 || bitsPerPixel == 32)
|
|
||||||
<< "BMP bits per pixel mismatch, expected 24 or 32";
|
|
||||||
|
|
||||||
result->Allocate(width, height, bitsPerPixel == 24 ? 3 : 4);
|
|
||||||
ASSERT_LE(imageSize, result->DataSize());
|
|
||||||
|
|
||||||
std::vector<uint8_t>& resultData = result->Data();
|
|
||||||
const size_t stride = result->Stride();
|
|
||||||
|
|
||||||
// Copy the data row-by-row to make sure that stride is right.
|
|
||||||
for (size_t row = 0; row < static_cast<size_t>(height); ++row) {
|
|
||||||
memcpy(&resultData[row * stride], &data[dataPos + row * stride],
|
|
||||||
width * bitsPerPixel / 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bitsPerPixel == 32) {
|
|
||||||
// Swizzle the data from ABGR to ARGB.
|
|
||||||
for (size_t row = 0; row < static_cast<size_t>(height); ++row) {
|
|
||||||
uint8_t* rowData = resultData.data() + row * stride;
|
|
||||||
|
|
||||||
for (size_t i = 3; i < stride; i += 4) {
|
|
||||||
const uint8_t b = rowData[i - 3];
|
|
||||||
rowData[i - 3] = rowData[i - 1];
|
|
||||||
rowData[i - 1] = b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Swizzle the data from BGR to RGB.
|
|
||||||
for (size_t row = 0; row < static_cast<size_t>(height); ++row) {
|
|
||||||
uint8_t* rowData = resultData.data() + row * stride;
|
|
||||||
|
|
||||||
for (size_t i = 2; i < stride; i += 3) {
|
|
||||||
const uint8_t tmp = rowData[i - 2];
|
|
||||||
rowData[i - 2] = rowData[i];
|
|
||||||
rowData[i] = tmp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void CompareSumOfSquaredDifferences(const ImageBuffer& golden,
|
|
||||||
const ImageBuffer& image,
|
|
||||||
double threshold) {
|
|
||||||
ASSERT_EQ(golden.DataSize(), image.DataSize());
|
|
||||||
ASSERT_EQ(golden.Stride(), image.Stride());
|
|
||||||
ASSERT_EQ(golden.BytesPerPixel(), image.BytesPerPixel());
|
|
||||||
|
|
||||||
const std::vector<uint8_t>& image_data = image.Data();
|
|
||||||
const std::vector<uint8_t>& golden_data = golden.Data();
|
|
||||||
|
|
||||||
double sum = 0.0;
|
|
||||||
for (size_t i = 0; i < image_data.size(); ++i) {
|
|
||||||
const double diff = static_cast<double>(image_data[i]) - golden_data[i];
|
|
||||||
sum += diff * diff;
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPECT_LE(sum, threshold * image_data.size())
|
|
||||||
<< "Per pixel " << (sum / image_data.size())
|
|
||||||
<< ", expected <= " << threshold;
|
|
||||||
if (sum > threshold * image_data.size()) {
|
|
||||||
// Fall back to comparison which will dump first chunk of vector.
|
|
||||||
EXPECT_EQ(golden_data, image_data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,336 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
#include "src/decoder/integer_sequence_codec.h"
|
|
||||||
#include "src/base/uint128.h"
|
|
||||||
|
|
||||||
#include <random>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
using astc_codec::base::UInt128;
|
|
||||||
using astc_codec::base::BitStream;
|
|
||||||
using astc_codec::IntegerSequenceCodec;
|
|
||||||
using astc_codec::IntegerSequenceEncoder;
|
|
||||||
using astc_codec::IntegerSequenceDecoder;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
// Make sure that the counts returned for a specific range match what's
|
|
||||||
// expected. In particular, make sure that it fits with Table C.2.7
|
|
||||||
TEST(ASTCIntegerSequenceCodecTest, TestGetCountsForRange) {
|
|
||||||
std::array<int, 3> kExpectedCounts[31] = {
|
|
||||||
{{ 0, 0, 1 }}, // 1
|
|
||||||
{{ 1, 0, 0 }}, // 2
|
|
||||||
{{ 0, 0, 2 }}, // 3
|
|
||||||
{{ 0, 1, 0 }}, // 4
|
|
||||||
{{ 1, 0, 1 }}, // 5
|
|
||||||
{{ 0, 0, 3 }}, // 6
|
|
||||||
{{ 0, 0, 3 }}, // 7
|
|
||||||
{{ 0, 1, 1 }}, // 8
|
|
||||||
{{ 0, 1, 1 }}, // 9
|
|
||||||
{{ 1, 0, 2 }}, // 10
|
|
||||||
{{ 1, 0, 2 }}, // 11
|
|
||||||
{{ 0, 0, 4 }}, // 12
|
|
||||||
{{ 0, 0, 4 }}, // 13
|
|
||||||
{{ 0, 0, 4 }}, // 14
|
|
||||||
{{ 0, 0, 4 }}, // 15
|
|
||||||
{{ 0, 1, 2 }}, // 16
|
|
||||||
{{ 0, 1, 2 }}, // 17
|
|
||||||
{{ 0, 1, 2 }}, // 18
|
|
||||||
{{ 0, 1, 2 }}, // 19
|
|
||||||
{{ 1, 0, 3 }}, // 20
|
|
||||||
{{ 1, 0, 3 }}, // 21
|
|
||||||
{{ 1, 0, 3 }}, // 22
|
|
||||||
{{ 1, 0, 3 }}, // 23
|
|
||||||
{{ 0, 0, 5 }}, // 24
|
|
||||||
{{ 0, 0, 5 }}, // 25
|
|
||||||
{{ 0, 0, 5 }}, // 26
|
|
||||||
{{ 0, 0, 5 }}, // 27
|
|
||||||
{{ 0, 0, 5 }}, // 28
|
|
||||||
{{ 0, 0, 5 }}, // 29
|
|
||||||
{{ 0, 0, 5 }}, // 30
|
|
||||||
{{ 0, 0, 5 }}, // 31
|
|
||||||
};
|
|
||||||
|
|
||||||
int t, q, b;
|
|
||||||
for (int i = 1; i < 32; ++i) {
|
|
||||||
IntegerSequenceCodec::GetCountsForRange(i, &t, &q, &b);
|
|
||||||
EXPECT_EQ(t, kExpectedCounts[i - 1][0]);
|
|
||||||
EXPECT_EQ(q, kExpectedCounts[i - 1][1]);
|
|
||||||
EXPECT_EQ(b, kExpectedCounts[i - 1][2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT_DEATH(IntegerSequenceCodec::GetCountsForRange(0, &t, &q, &b), "");
|
|
||||||
ASSERT_DEATH(IntegerSequenceCodec::GetCountsForRange(256, &t, &q, &b), "");
|
|
||||||
|
|
||||||
IntegerSequenceCodec::GetCountsForRange(1, &t, &q, &b);
|
|
||||||
EXPECT_EQ(t, 0);
|
|
||||||
EXPECT_EQ(q, 0);
|
|
||||||
EXPECT_EQ(b, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test to make sure that we're calculating the number of bits needed to
|
|
||||||
// encode a given number of values based on the range of the values.
|
|
||||||
TEST(ASTCIntegerSequenceCodecTest, TestNumBitsForCounts) {
|
|
||||||
int trits = 0;
|
|
||||||
int quints = 0;
|
|
||||||
int bits = 0;
|
|
||||||
|
|
||||||
// A range of one should have single bits, so n 1-bit values should be n bits.
|
|
||||||
trits = 0;
|
|
||||||
quints = 0;
|
|
||||||
bits = 1;
|
|
||||||
for (int i = 0; i < 64; ++i) {
|
|
||||||
EXPECT_EQ(IntegerSequenceCodec::GetBitCount(i, trits, quints, bits), i);
|
|
||||||
EXPECT_EQ(IntegerSequenceCodec::GetBitCountForRange(i, 1), i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Similarly, N two-bit values should be 2n bits...
|
|
||||||
trits = 0;
|
|
||||||
quints = 0;
|
|
||||||
bits = 2;
|
|
||||||
for (int i = 0; i < 64; ++i) {
|
|
||||||
int bit_counts = IntegerSequenceCodec::GetBitCount(i, trits, quints, bits);
|
|
||||||
EXPECT_EQ(bit_counts, 2 * i);
|
|
||||||
EXPECT_EQ(IntegerSequenceCodec::GetBitCountForRange(i, 3), 2 * i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trits are a bit more complicated -- there are five trits in a block, so
|
|
||||||
// if we encode 15 values with 3 bits each in trits, we'd get three blocks,
|
|
||||||
// each with eight bits of trits.
|
|
||||||
trits = 1;
|
|
||||||
quints = 0;
|
|
||||||
bits = 3;
|
|
||||||
EXPECT_EQ(IntegerSequenceCodec::GetBitCount(15, trits, quints, bits),
|
|
||||||
8 * 3 + 15 * 3);
|
|
||||||
EXPECT_EQ(IntegerSequenceCodec::GetBitCountForRange(15, 23),
|
|
||||||
IntegerSequenceCodec::GetBitCount(15, trits, quints, bits));
|
|
||||||
|
|
||||||
// However, if instead we encode 13 values, we don't need to use the remaining
|
|
||||||
// two values, so we only need bits as they will be encoded. As it turns out,
|
|
||||||
// this means we can avoid three bits in the final block (one for the high
|
|
||||||
// order trit encoding and two for one of the values), resulting in 47 bits.
|
|
||||||
trits = 1;
|
|
||||||
quints = 0;
|
|
||||||
bits = 2;
|
|
||||||
EXPECT_EQ(IntegerSequenceCodec::GetBitCount(13, trits, quints, bits), 47);
|
|
||||||
EXPECT_EQ(IntegerSequenceCodec::GetBitCountForRange(13, 11),
|
|
||||||
IntegerSequenceCodec::GetBitCount(13, trits, quints, bits));
|
|
||||||
|
|
||||||
// Quints have a similar property -- if we encode six values using a quint and
|
|
||||||
// four bits, then we have two quint blocks each with three values and a seven
|
|
||||||
// bit encoded quint triplet...
|
|
||||||
trits = 0;
|
|
||||||
quints = 1;
|
|
||||||
bits = 4;
|
|
||||||
EXPECT_EQ(IntegerSequenceCodec::GetBitCount(6, trits, quints, bits),
|
|
||||||
7 * 2 + 6 * 4);
|
|
||||||
EXPECT_EQ(IntegerSequenceCodec::GetBitCountForRange(6, 79),
|
|
||||||
IntegerSequenceCodec::GetBitCount(6, trits, quints, bits));
|
|
||||||
|
|
||||||
// If we have fewer values than blocks we can again avoid about 2 + nbits
|
|
||||||
// bits...
|
|
||||||
trits = 0;
|
|
||||||
quints = 1;
|
|
||||||
bits = 3;
|
|
||||||
EXPECT_EQ(IntegerSequenceCodec::GetBitCount(7, trits, quints, bits),
|
|
||||||
/* first two quint blocks */ 7 * 2 +
|
|
||||||
/* first two blocks of bits */ 6 * 3 +
|
|
||||||
/* last quint block without the high order four bits */ 3 +
|
|
||||||
/* last block with one set of three bits */ 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tests that the encoder knows how to encode values of the form 5*2^k.
|
|
||||||
TEST(ASTCIntegerSequenceCodecTest, TestQuintCodec) {
|
|
||||||
// In this case, k = 4
|
|
||||||
|
|
||||||
// Setup bit src/sink
|
|
||||||
BitStream<UInt128> bit_sink;
|
|
||||||
|
|
||||||
const int kValueRange = 79;
|
|
||||||
IntegerSequenceEncoder enc(kValueRange);
|
|
||||||
enc.AddValue(3);
|
|
||||||
enc.AddValue(79);
|
|
||||||
enc.AddValue(37);
|
|
||||||
enc.Encode(&bit_sink);
|
|
||||||
|
|
||||||
// quint: 1000101 m0: 0011 m1: 1111 m2: 0101
|
|
||||||
// 100 0100 0111 1101 0010
|
|
||||||
// interleaved 10m200m1101m0
|
|
||||||
// should be 100 1010 0111 1101 0011 = 0x4A7D3
|
|
||||||
EXPECT_EQ(bit_sink.Bits(), 19);
|
|
||||||
|
|
||||||
uint64_t encoded = 0;
|
|
||||||
bit_sink.GetBits(19, &encoded);
|
|
||||||
EXPECT_EQ(encoded, 0x4A7D3);
|
|
||||||
|
|
||||||
// Now check that decoding it works as well
|
|
||||||
BitStream<UInt128> bit_src(encoded, 19);
|
|
||||||
|
|
||||||
IntegerSequenceDecoder dec(kValueRange);
|
|
||||||
auto decoded_vals = dec.Decode(3, &bit_src);
|
|
||||||
ASSERT_EQ(decoded_vals.size(), 3);
|
|
||||||
EXPECT_EQ(decoded_vals[0], 3);
|
|
||||||
EXPECT_EQ(decoded_vals[1], 79);
|
|
||||||
EXPECT_EQ(decoded_vals[2], 37);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tests that the encoder knows how to encode values of the form 3*2^k.
|
|
||||||
TEST(ASTCIntegerSequenceCodecTest, TestTritCodec) {
|
|
||||||
uint64_t encoded = 0;
|
|
||||||
|
|
||||||
// Setup bit src/sink
|
|
||||||
BitStream<UInt128> bit_sink(encoded, 0);
|
|
||||||
|
|
||||||
const int kValueRange = 11;
|
|
||||||
IntegerSequenceEncoder enc(kValueRange);
|
|
||||||
enc.AddValue(7);
|
|
||||||
enc.AddValue(5);
|
|
||||||
enc.AddValue(3);
|
|
||||||
enc.AddValue(6);
|
|
||||||
enc.AddValue(10);
|
|
||||||
enc.Encode(&bit_sink);
|
|
||||||
|
|
||||||
EXPECT_EQ(bit_sink.Bits(), 18);
|
|
||||||
|
|
||||||
bit_sink.GetBits(18, &encoded);
|
|
||||||
EXPECT_EQ(encoded, 0x37357);
|
|
||||||
|
|
||||||
// Now check that decoding it works as well
|
|
||||||
BitStream<UInt128> bit_src(encoded, 19);
|
|
||||||
|
|
||||||
IntegerSequenceDecoder dec(kValueRange);
|
|
||||||
auto decoded_vals = dec.Decode(5, &bit_src);
|
|
||||||
ASSERT_EQ(decoded_vals.size(), 5);
|
|
||||||
EXPECT_EQ(decoded_vals[0], 7);
|
|
||||||
EXPECT_EQ(decoded_vals[1], 5);
|
|
||||||
EXPECT_EQ(decoded_vals[2], 3);
|
|
||||||
EXPECT_EQ(decoded_vals[3], 6);
|
|
||||||
EXPECT_EQ(decoded_vals[4], 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test a specific quint encoding/decoding. This test makes sure that the way we
|
|
||||||
// encode and decode integer sequences matches what we should expect out of the
|
|
||||||
// reference ASTC encoder.
|
|
||||||
TEST(ASTCIntegerSequenceCodecTest, TestDecodeThenEncode) {
|
|
||||||
std::vector<int> vals = {{ 16, 18, 17, 4, 7, 14, 10, 0 }};
|
|
||||||
const uint64_t kValEncoding = 0x2b9c83dc;
|
|
||||||
|
|
||||||
BitStream<UInt128> bit_src(kValEncoding, 64);
|
|
||||||
IntegerSequenceDecoder dec(19);
|
|
||||||
auto decoded_vals = dec.Decode(8, &bit_src);
|
|
||||||
ASSERT_EQ(decoded_vals.size(), vals.size());
|
|
||||||
for (size_t i = 0; i < decoded_vals.size(); ++i) {
|
|
||||||
EXPECT_EQ(decoded_vals[i], vals[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup bit src/sink
|
|
||||||
BitStream<UInt128> bit_sink;
|
|
||||||
IntegerSequenceEncoder enc(19);
|
|
||||||
for (const auto& v : vals) {
|
|
||||||
enc.AddValue(v);
|
|
||||||
}
|
|
||||||
enc.Encode(&bit_sink);
|
|
||||||
EXPECT_EQ(bit_sink.Bits(), 35);
|
|
||||||
|
|
||||||
uint64_t encoded = 0;
|
|
||||||
EXPECT_TRUE(bit_sink.GetBits(35, &encoded));
|
|
||||||
EXPECT_EQ(encoded, kValEncoding)
|
|
||||||
<< std::hex << encoded << " -- " << kValEncoding;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Same as the previous test, except it uses a trit encoding rather than a
|
|
||||||
// quint encoding.
|
|
||||||
TEST(ASTCIntegerSequenceCodecTest, TestDecodeThenEncodeTrits) {
|
|
||||||
std::vector<int> vals = {{ 6, 0, 0, 2, 0, 0, 0, 0, 8, 0, 0, 0, 0, 8, 8, 0 }};
|
|
||||||
const uint64_t kValEncoding = 0x0004c0100001006ULL;
|
|
||||||
|
|
||||||
BitStream<UInt128> bit_src(kValEncoding, 64);
|
|
||||||
IntegerSequenceDecoder dec(11);
|
|
||||||
auto decoded_vals = dec.Decode(vals.size(), &bit_src);
|
|
||||||
ASSERT_EQ(decoded_vals.size(), vals.size());
|
|
||||||
for (size_t i = 0; i < decoded_vals.size(); ++i) {
|
|
||||||
EXPECT_EQ(decoded_vals[i], vals[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup bit src/sink
|
|
||||||
BitStream<UInt128> bit_sink;
|
|
||||||
IntegerSequenceEncoder enc(11);
|
|
||||||
for (const auto& v : vals) {
|
|
||||||
enc.AddValue(v);
|
|
||||||
}
|
|
||||||
enc.Encode(&bit_sink);
|
|
||||||
EXPECT_EQ(bit_sink.Bits(), 58);
|
|
||||||
|
|
||||||
uint64_t encoded = 0;
|
|
||||||
EXPECT_TRUE(bit_sink.GetBits(58, &encoded));
|
|
||||||
EXPECT_EQ(encoded, kValEncoding)
|
|
||||||
<< std::hex << encoded << " -- " << kValEncoding;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate a random sequence of integer codings with different ranges to test
|
|
||||||
// the reciprocability of our codec (encoded sequences should be able to
|
|
||||||
// decoded)
|
|
||||||
TEST(ASTCIntegerSequenceCodecTest, TestRandomReciprocation) {
|
|
||||||
std::mt19937 mt(0xbad7357);
|
|
||||||
std::uniform_int_distribution<int> rand(0, 255);
|
|
||||||
|
|
||||||
for (int test = 0; test < 1600; ++test) {
|
|
||||||
// Generate a random number of values and a random range
|
|
||||||
int num_vals = 4 + rand(mt) % 44; // Up to 48 weights in a grid
|
|
||||||
int range = 1 + rand(mt) % 63;
|
|
||||||
|
|
||||||
// If this produces a bit pattern larger than our buffer, then ignore
|
|
||||||
// it... we already know what our bounds are for the integer sequences
|
|
||||||
int num_bits = IntegerSequenceCodec::GetBitCountForRange(num_vals, range);
|
|
||||||
if (num_bits >= 64) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<int> generated_vals(num_vals);
|
|
||||||
for (auto& val : generated_vals) {
|
|
||||||
val = rand(mt) % (range + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode the values using the
|
|
||||||
BitStream<UInt128> bit_sink;
|
|
||||||
|
|
||||||
// Add them to the encoder
|
|
||||||
IntegerSequenceEncoder enc(range);
|
|
||||||
for (int v : generated_vals) {
|
|
||||||
enc.AddValue(v);
|
|
||||||
}
|
|
||||||
enc.Encode(&bit_sink);
|
|
||||||
|
|
||||||
uint64_t encoded = 0;
|
|
||||||
bit_sink.GetBits(bit_sink.Bits(), &encoded);
|
|
||||||
ASSERT_GE(encoded, 0);
|
|
||||||
EXPECT_LT(encoded, 1ULL << num_bits);
|
|
||||||
|
|
||||||
BitStream<UInt128> bit_src(encoded, 64);
|
|
||||||
|
|
||||||
IntegerSequenceDecoder dec(range);
|
|
||||||
auto decoded_vals = dec.Decode(num_vals, &bit_src);
|
|
||||||
|
|
||||||
ASSERT_EQ(decoded_vals.size(), generated_vals.size());
|
|
||||||
for (size_t i = 0; i < decoded_vals.size(); ++i) {
|
|
||||||
EXPECT_EQ(decoded_vals[i], generated_vals[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
@@ -1,454 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
#include "src/decoder/intermediate_astc_block.h"
|
|
||||||
#include "src/decoder/test/image_utils.h"
|
|
||||||
|
|
||||||
#include <gmock/gmock.h>
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace astc_codec {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
using ::testing::ElementsAre;
|
|
||||||
using ::testing::Eq;
|
|
||||||
using ::testing::HasSubstr;
|
|
||||||
using ::testing::SizeIs;
|
|
||||||
using ::testing::TestWithParam;
|
|
||||||
using ::testing::ValuesIn;
|
|
||||||
|
|
||||||
// Test to make sure that unpacking an error block returns false.
|
|
||||||
TEST(IntermediateASTCBlockTest, TestUnpackError) {
|
|
||||||
const PhysicalASTCBlock kErrorBlock(base::UInt128(0));
|
|
||||||
EXPECT_FALSE(UnpackVoidExtent(kErrorBlock));
|
|
||||||
EXPECT_FALSE(UnpackIntermediateBlock(kErrorBlock));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test to make sure that if we don't populate our weight data in the
|
|
||||||
// intermediate block than the resulting color range should error due to the
|
|
||||||
// mismatch.
|
|
||||||
TEST(IntermediateASTCBlockTest, TestEndpointRangeErrorOnNotSettingWeights) {
|
|
||||||
IntermediateBlockData data;
|
|
||||||
data.weight_range = 15;
|
|
||||||
for (auto& ep : data.endpoints) {
|
|
||||||
ep.mode = ColorEndpointMode::kLDRRGBDirect;
|
|
||||||
}
|
|
||||||
data.weight_grid_dim_x = 6;
|
|
||||||
data.weight_grid_dim_y = 6;
|
|
||||||
EXPECT_EQ(-1, EndpointRangeForBlock(data));
|
|
||||||
|
|
||||||
base::UInt128 dummy;
|
|
||||||
auto err_str = Pack(data, &dummy);
|
|
||||||
EXPECT_TRUE(err_str.hasValue());
|
|
||||||
EXPECT_THAT(err_str.value(), HasSubstr("Incorrect number of weights"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test to make sure that if we run out of bits, then we should say so.
|
|
||||||
TEST(IntermediateASTCBlockTest, TestEndpointRangeErrorOnNotEnoughBits) {
|
|
||||||
IntermediateBlockData data;
|
|
||||||
data.weight_range = 1;
|
|
||||||
data.partition_id = 0;
|
|
||||||
data.endpoints.resize(3);
|
|
||||||
for (auto& ep : data.endpoints) {
|
|
||||||
ep.mode = ColorEndpointMode::kLDRRGBDirect;
|
|
||||||
}
|
|
||||||
data.weight_grid_dim_x = 8;
|
|
||||||
data.weight_grid_dim_y = 8;
|
|
||||||
EXPECT_EQ(-2, EndpointRangeForBlock(data));
|
|
||||||
|
|
||||||
// Resize the weights to get past the error that they do not match the grid
|
|
||||||
// dimensions.
|
|
||||||
data.weights.resize(64);
|
|
||||||
|
|
||||||
base::UInt128 dummy;
|
|
||||||
auto err_str = Pack(data, &dummy);
|
|
||||||
EXPECT_TRUE(err_str.hasValue());
|
|
||||||
EXPECT_THAT(err_str.value(), HasSubstr("illegal color range"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test to make sure that as we increase the number of weights, we decrease the
|
|
||||||
// allowable range of colors
|
|
||||||
TEST(IntermediateASTCBlockTest, TestEndpointRangeForBlock) {
|
|
||||||
IntermediateBlockData data;
|
|
||||||
data.weight_range = 2;
|
|
||||||
data.endpoints.resize(2);
|
|
||||||
data.dual_plane_channel.clear();
|
|
||||||
for (auto& ep : data.endpoints) {
|
|
||||||
ep.mode = ColorEndpointMode::kLDRRGBDirect;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Weight params control how many weights are present in a block
|
|
||||||
struct WeightParams {
|
|
||||||
int width;
|
|
||||||
int height;
|
|
||||||
|
|
||||||
// We should sort based on number of weights for these params
|
|
||||||
int NumWeights() const { return width * height; }
|
|
||||||
bool operator<(const WeightParams& other) const {
|
|
||||||
return NumWeights() < other.NumWeights();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<WeightParams> weight_params;
|
|
||||||
for (int y = 2; y < 8; ++y) {
|
|
||||||
for (int x = 2; x < 8; ++x) {
|
|
||||||
weight_params.emplace_back(WeightParams{x, y});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort weights from fewest to largest such that the allowable color range
|
|
||||||
// should be monotonically decreasing
|
|
||||||
std::sort(weight_params.begin(), weight_params.end());
|
|
||||||
|
|
||||||
// Keep track of the largest available color range and measure that it
|
|
||||||
// decreases as we add more weights to our block
|
|
||||||
int last_color_range = 255;
|
|
||||||
for (const auto& params : weight_params) {
|
|
||||||
data.weight_grid_dim_x = params.width;
|
|
||||||
data.weight_grid_dim_y = params.height;
|
|
||||||
|
|
||||||
const int color_range = EndpointRangeForBlock(data);
|
|
||||||
EXPECT_LE(color_range, last_color_range);
|
|
||||||
last_color_range = std::min(color_range, last_color_range);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that we actually changed it at some point.
|
|
||||||
EXPECT_LT(last_color_range, 255);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test to make sure that unpacking an legitimate ASTC block returns the encoded
|
|
||||||
// values that we expect.
|
|
||||||
TEST(IntermediateASTCBlockTest, TestUnpackNonVoidExtentBlock) {
|
|
||||||
PhysicalASTCBlock blk(0x0000000001FE000173ULL);
|
|
||||||
auto b = UnpackIntermediateBlock(blk);
|
|
||||||
ASSERT_TRUE(b);
|
|
||||||
|
|
||||||
const auto& data = b.value();
|
|
||||||
EXPECT_EQ(data.weight_grid_dim_x, 6);
|
|
||||||
EXPECT_EQ(data.weight_grid_dim_y, 5);
|
|
||||||
EXPECT_EQ(data.weight_range, 7);
|
|
||||||
|
|
||||||
EXPECT_FALSE(data.partition_id);
|
|
||||||
EXPECT_FALSE(data.dual_plane_channel);
|
|
||||||
|
|
||||||
ASSERT_EQ(data.weights.size(), 30);
|
|
||||||
for (auto weight : data.weights) {
|
|
||||||
EXPECT_EQ(weight, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT_EQ(data.endpoints.size(), 1);
|
|
||||||
for (const auto& ep_data : data.endpoints) {
|
|
||||||
EXPECT_EQ(ep_data.mode, ColorEndpointMode::kLDRLumaDirect);
|
|
||||||
ASSERT_EQ(ep_data.colors.size(), 2);
|
|
||||||
EXPECT_EQ(ep_data.colors[0], 0);
|
|
||||||
EXPECT_EQ(ep_data.colors[1], 255);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that we can pack blocks that aren't void extent blocks. (In other
|
|
||||||
// words, can we actually deal with intermediate ASTC data).
|
|
||||||
TEST(IntermediateASTCBlockTest, TestPackNonVoidExtentBlock) {
|
|
||||||
IntermediateBlockData data;
|
|
||||||
|
|
||||||
data.weight_grid_dim_x = 6;
|
|
||||||
data.weight_grid_dim_y = 5;
|
|
||||||
data.weight_range = 7;
|
|
||||||
|
|
||||||
data.partition_id = {};
|
|
||||||
data.dual_plane_channel = {};
|
|
||||||
|
|
||||||
data.weights.resize(30);
|
|
||||||
for (auto& weight : data.weights) {
|
|
||||||
weight = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
data.endpoints.resize(1);
|
|
||||||
for (auto& ep_data : data.endpoints) {
|
|
||||||
ep_data.mode = ColorEndpointMode::kLDRLumaDirect;
|
|
||||||
ep_data.colors.resize(2);
|
|
||||||
ep_data.colors[0] = 0;
|
|
||||||
ep_data.colors[1] = 255;
|
|
||||||
}
|
|
||||||
|
|
||||||
base::UInt128 packed;
|
|
||||||
auto error_str = Pack(data, &packed);
|
|
||||||
ASSERT_FALSE(error_str) << (error_str ? error_str.value() : std::string(""));
|
|
||||||
EXPECT_EQ(packed, 0x0000000001FE000173ULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that we can unpack void extent blocks
|
|
||||||
TEST(IntermediateASTCBlockTest, TestUnpackVoidExtentBlock) {
|
|
||||||
PhysicalASTCBlock void_extent_block(0xFFFFFFFFFFFFFDFCULL);
|
|
||||||
|
|
||||||
auto b = UnpackVoidExtent(void_extent_block);
|
|
||||||
ASSERT_TRUE(b);
|
|
||||||
|
|
||||||
const auto& data = b.value();
|
|
||||||
EXPECT_EQ(data.r, 0);
|
|
||||||
EXPECT_EQ(data.g, 0);
|
|
||||||
EXPECT_EQ(data.b, 0);
|
|
||||||
EXPECT_EQ(data.a, 0);
|
|
||||||
for (const auto& coord : data.coords) {
|
|
||||||
EXPECT_EQ(coord, (1 << 13) - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
base::UInt128 more_interesting(0xdeadbeefdeadbeefULL, 0xFFF8003FFE000DFCULL);
|
|
||||||
b = UnpackVoidExtent(PhysicalASTCBlock(more_interesting));
|
|
||||||
ASSERT_TRUE(b);
|
|
||||||
|
|
||||||
const auto& other_data = b.value();
|
|
||||||
EXPECT_EQ(other_data.r, 0xbeef);
|
|
||||||
EXPECT_EQ(other_data.g, 0xdead);
|
|
||||||
EXPECT_EQ(other_data.b, 0xbeef);
|
|
||||||
EXPECT_EQ(other_data.a, 0xdead);
|
|
||||||
EXPECT_EQ(other_data.coords[0], 0);
|
|
||||||
EXPECT_EQ(other_data.coords[1], 8191);
|
|
||||||
EXPECT_EQ(other_data.coords[2], 0);
|
|
||||||
EXPECT_EQ(other_data.coords[3], 8191);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that we can pack void extent blocks and void extent data.
|
|
||||||
TEST(IntermediateASTCBlockTest, TestPackVoidExtentBlock) {
|
|
||||||
VoidExtentData data;
|
|
||||||
data.r = 0;
|
|
||||||
data.g = 0;
|
|
||||||
data.b = 0;
|
|
||||||
data.a = 0;
|
|
||||||
for (auto& coord : data.coords) {
|
|
||||||
coord = (1 << 13) - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
base::UInt128 packed;
|
|
||||||
auto error_str = Pack(data, &packed);
|
|
||||||
ASSERT_FALSE(error_str) << (error_str ? error_str.value() : std::string(""));
|
|
||||||
EXPECT_EQ(packed, 0xFFFFFFFFFFFFFDFCULL);
|
|
||||||
|
|
||||||
data.r = 0xbeef;
|
|
||||||
data.g = 0xdead;
|
|
||||||
data.b = 0xbeef;
|
|
||||||
data.a = 0xdead;
|
|
||||||
data.coords[0] = 0;
|
|
||||||
data.coords[1] = 8191;
|
|
||||||
data.coords[2] = 0;
|
|
||||||
data.coords[3] = 8191;
|
|
||||||
|
|
||||||
error_str = Pack(data, &packed);
|
|
||||||
ASSERT_FALSE(error_str) << (error_str ? error_str.value() : std::string(""));
|
|
||||||
EXPECT_EQ(packed,
|
|
||||||
base::UInt128(0xdeadbeefdeadbeefULL, 0xFFF8003FFE000DFCULL));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that the color endpoint mode is properly repacked. This test case
|
|
||||||
// was created as a bug during testing.
|
|
||||||
TEST(IntermediateASTCBlockTest, TestPackUnpackWithSameCEM) {
|
|
||||||
base::UInt128 orig(0xe8e8eaea20000980ULL, 0x20000200cb73f045ULL);
|
|
||||||
|
|
||||||
auto b = UnpackIntermediateBlock(PhysicalASTCBlock(orig));
|
|
||||||
ASSERT_TRUE(b);
|
|
||||||
|
|
||||||
base::UInt128 repacked;
|
|
||||||
auto err_str = Pack(b.value(), &repacked);
|
|
||||||
ASSERT_FALSE(err_str) << (err_str ? err_str.value() : std::string(""));
|
|
||||||
|
|
||||||
EXPECT_EQ(repacked, orig);
|
|
||||||
|
|
||||||
// Test case #2
|
|
||||||
orig = base::UInt128(0x3300c30700cb01c5ULL, 0x0573907b8c0f6879ULL);
|
|
||||||
b = UnpackIntermediateBlock(PhysicalASTCBlock(orig));
|
|
||||||
ASSERT_TRUE(b);
|
|
||||||
|
|
||||||
err_str = Pack(b.value(), &repacked);
|
|
||||||
ASSERT_FALSE(err_str) << (err_str ? err_str.value() : std::string(""));
|
|
||||||
EXPECT_EQ(repacked, orig);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that we can encode/decode a block that uses a very large gap
|
|
||||||
// between weight and endpoint data.
|
|
||||||
TEST(IntermediateASTCBlockTest, TestPackingWithLargeGap) {
|
|
||||||
// We can construct this block by doing the following:
|
|
||||||
// -- choose a block mode that only gives 24 weight bits
|
|
||||||
// -- choose the smallest endpoint mode: grayscale direct
|
|
||||||
// -- make sure there are no partitions
|
|
||||||
const base::UInt128 orig(0xBEDEAD0000000000ULL, 0x0000000001FE032EULL);
|
|
||||||
const auto b = UnpackIntermediateBlock(PhysicalASTCBlock(orig));
|
|
||||||
ASSERT_TRUE(b);
|
|
||||||
|
|
||||||
const auto& data = b.value();
|
|
||||||
EXPECT_EQ(data.weight_grid_dim_x, 2);
|
|
||||||
EXPECT_EQ(data.weight_grid_dim_y, 3);
|
|
||||||
EXPECT_EQ(data.weight_range, 15);
|
|
||||||
|
|
||||||
EXPECT_FALSE(data.partition_id);
|
|
||||||
EXPECT_FALSE(data.dual_plane_channel);
|
|
||||||
|
|
||||||
ASSERT_EQ(data.endpoints.size(), 1);
|
|
||||||
EXPECT_EQ(data.endpoints.at(0).mode, ColorEndpointMode::kLDRLumaDirect);
|
|
||||||
|
|
||||||
ASSERT_EQ(data.endpoints.at(0).colors.size(), 2);
|
|
||||||
EXPECT_EQ(data.endpoints.at(0).colors.at(0), 255);
|
|
||||||
EXPECT_EQ(data.endpoints.at(0).colors.at(1), 0);
|
|
||||||
|
|
||||||
// Now encode it again
|
|
||||||
base::UInt128 repacked;
|
|
||||||
const auto err_str = Pack(b.value(), &repacked);
|
|
||||||
EXPECT_EQ(orig, repacked) << (err_str ? err_str.value() : std::string(""));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take a block that is encoded using direct luma with full byte values and see
|
|
||||||
// if we properly set the endpoint range.
|
|
||||||
TEST(IntermediateASTCBlockTest, TestEndpointRange) {
|
|
||||||
PhysicalASTCBlock blk(0x0000000001FE000173ULL);
|
|
||||||
EXPECT_TRUE(blk.ColorValuesRange().hasValue());
|
|
||||||
EXPECT_EQ(blk.ColorValuesRange().valueOr(0), 255);
|
|
||||||
|
|
||||||
auto b = UnpackIntermediateBlock(blk);
|
|
||||||
ASSERT_TRUE(b);
|
|
||||||
|
|
||||||
const auto& data = b.value();
|
|
||||||
ASSERT_THAT(data.endpoints, SizeIs(1));
|
|
||||||
EXPECT_THAT(data.endpoints[0].mode, Eq(ColorEndpointMode::kLDRLumaDirect));
|
|
||||||
EXPECT_THAT(data.endpoints[0].colors, ElementsAre(0, 255));
|
|
||||||
EXPECT_TRUE(data.endpoint_range.hasValue());
|
|
||||||
EXPECT_EQ(data.endpoint_range.valueOr(0), 255);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ImageTestParams {
|
|
||||||
std::string image_name;
|
|
||||||
int checkered_dim;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void PrintTo(const ImageTestParams& params, std::ostream* os) {
|
|
||||||
*os << "ImageTestParams(" << params.image_name << ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
class IntermediateASTCBlockTest : public TestWithParam<ImageTestParams> { };
|
|
||||||
|
|
||||||
// Test whether or not a real-world ASTC implementation can be unpacked and
|
|
||||||
// then repacked into the same implementation. In conjunction with the other
|
|
||||||
// tests, we make sure that we can recreate ASTC blocks that we have previously
|
|
||||||
// unpacked.
|
|
||||||
TEST_P(IntermediateASTCBlockTest, TestPackUnpack) {
|
|
||||||
const auto& params = GetParam();
|
|
||||||
const int astc_dim = 8;
|
|
||||||
const int img_dim = params.checkered_dim * astc_dim;
|
|
||||||
const std::string astc = LoadASTCFile(params.image_name);
|
|
||||||
|
|
||||||
// Make sure that unpacking and repacking all of the blocks works...
|
|
||||||
const int kNumASTCBlocks = (img_dim / astc_dim) * (img_dim / astc_dim);
|
|
||||||
for (int i = 0; i < kNumASTCBlocks; ++i) {
|
|
||||||
base::UInt128 block_bits;
|
|
||||||
memcpy(&block_bits, astc.data() + PhysicalASTCBlock::kSizeInBytes * i,
|
|
||||||
PhysicalASTCBlock::kSizeInBytes);
|
|
||||||
|
|
||||||
const PhysicalASTCBlock block(block_bits);
|
|
||||||
|
|
||||||
base::UInt128 repacked;
|
|
||||||
if (block.IsVoidExtent()) {
|
|
||||||
auto b = UnpackVoidExtent(block);
|
|
||||||
ASSERT_TRUE(b);
|
|
||||||
|
|
||||||
auto err_str = Pack(b.value(), &repacked);
|
|
||||||
ASSERT_FALSE(err_str) << (err_str ? err_str.value() : std::string(""));
|
|
||||||
} else {
|
|
||||||
auto b = UnpackIntermediateBlock(block);
|
|
||||||
ASSERT_TRUE(b);
|
|
||||||
|
|
||||||
// Check to see that we properly set the endpoint range when we decoded
|
|
||||||
// the block.
|
|
||||||
auto& block_data = b.value();
|
|
||||||
EXPECT_EQ(block_data.endpoint_range, block.ColorValuesRange());
|
|
||||||
|
|
||||||
// Reset the endpoint range here to see if we correctly reconstruct it
|
|
||||||
// below
|
|
||||||
block_data.endpoint_range = {};
|
|
||||||
|
|
||||||
auto err_str = Pack(b.value(), &repacked);
|
|
||||||
ASSERT_FALSE(err_str) << (err_str ? err_str.value() : std::string(""));
|
|
||||||
}
|
|
||||||
|
|
||||||
// You would expect the following line to be enough:
|
|
||||||
// EXPECT_EQ(repacked, block.GetBlockBits())
|
|
||||||
// ... except that the ASTC encoder makes some interesting decisions
|
|
||||||
// about how to encode the same logical bits. One example is that
|
|
||||||
// sometimes if all partitions share an endpoint mode, the encoded
|
|
||||||
// block will not use the shared CEM mode, and rather list each
|
|
||||||
// partition's mode explicitly. For that reason, we just need to make as
|
|
||||||
// close of an approximation as possible that we decode to the same
|
|
||||||
// physical values.
|
|
||||||
|
|
||||||
PhysicalASTCBlock pb(repacked);
|
|
||||||
ASSERT_FALSE(pb.IsIllegalEncoding());
|
|
||||||
|
|
||||||
base::UInt128 pb_color_mask =
|
|
||||||
(base::UInt128(1) << pb.NumColorBits().value()) - 1;
|
|
||||||
base::UInt128 pb_color_bits =
|
|
||||||
pb.GetBlockBits() >> pb.ColorStartBit().value();
|
|
||||||
pb_color_bits &= pb_color_mask;
|
|
||||||
|
|
||||||
base::UInt128 b_color_mask =
|
|
||||||
(base::UInt128(1) << pb.NumColorBits().value()) - 1;
|
|
||||||
base::UInt128 b_color_bits =
|
|
||||||
block.GetBlockBits() >> block.ColorStartBit().value();
|
|
||||||
b_color_bits &= b_color_mask;
|
|
||||||
|
|
||||||
EXPECT_EQ(pb_color_mask, b_color_mask);
|
|
||||||
EXPECT_EQ(pb_color_bits, b_color_bits);
|
|
||||||
|
|
||||||
EXPECT_EQ(pb.IsVoidExtent(), block.IsVoidExtent());
|
|
||||||
EXPECT_EQ(pb.VoidExtentCoords(), block.VoidExtentCoords());
|
|
||||||
|
|
||||||
EXPECT_EQ(pb.WeightGridDims(), block.WeightGridDims());
|
|
||||||
EXPECT_EQ(pb.WeightRange(), block.WeightRange());
|
|
||||||
EXPECT_EQ(pb.NumWeightBits(), block.NumWeightBits());
|
|
||||||
EXPECT_EQ(pb.WeightStartBit(), block.WeightStartBit());
|
|
||||||
|
|
||||||
EXPECT_EQ(pb.IsDualPlane(), block.IsDualPlane());
|
|
||||||
EXPECT_EQ(pb.DualPlaneChannel(), block.DualPlaneChannel());
|
|
||||||
|
|
||||||
EXPECT_EQ(pb.NumPartitions(), block.NumPartitions());
|
|
||||||
EXPECT_EQ(pb.PartitionID(), block.PartitionID());
|
|
||||||
|
|
||||||
EXPECT_EQ(pb.NumColorValues(), block.NumColorValues());
|
|
||||||
EXPECT_EQ(pb.ColorValuesRange(), block.ColorValuesRange());
|
|
||||||
|
|
||||||
for (int j = 0; j < pb.NumPartitions().valueOr(0); ++j) {
|
|
||||||
EXPECT_EQ(pb.GetEndpointMode(j), block.GetEndpointMode(j));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<ImageTestParams> GetImageTestParams() {
|
|
||||||
return {
|
|
||||||
// image_name checkered_dim
|
|
||||||
{ "checkered_4", 4 },
|
|
||||||
{ "checkered_5", 5 },
|
|
||||||
{ "checkered_6", 6 },
|
|
||||||
{ "checkered_7", 7 },
|
|
||||||
{ "checkered_8", 8 },
|
|
||||||
{ "checkered_9", 9 },
|
|
||||||
{ "checkered_10", 10 },
|
|
||||||
{ "checkered_11", 11 },
|
|
||||||
{ "checkered_12", 12 },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(Checkered, IntermediateASTCBlockTest,
|
|
||||||
ValuesIn(GetImageTestParams()));
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
} // namespace astc_codec
|
|
||||||
@@ -1,273 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
#include "src/decoder/logical_astc_block.h"
|
|
||||||
#include "src/decoder/test/image_utils.h"
|
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
#include <gmock/gmock.h>
|
|
||||||
|
|
||||||
#include <fstream>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace astc_codec {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
using ::testing::Eq;
|
|
||||||
using ::testing::ElementsAre;
|
|
||||||
using ::testing::TestWithParam;
|
|
||||||
using ::testing::ValuesIn;
|
|
||||||
|
|
||||||
ImageBuffer LoadGoldenImageWithAlpha(std::string basename) {
|
|
||||||
const std::string filename = std::string("src/decoder/testdata/") + basename + ".bmp";
|
|
||||||
ImageBuffer result;
|
|
||||||
LoadGoldenBmp(filename, &result);
|
|
||||||
EXPECT_EQ(result.BytesPerPixel(), 4);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageBuffer LoadGoldenImage(std::string basename) {
|
|
||||||
const std::string filename = std::string("src/decoder/testdata/") + basename + ".bmp";
|
|
||||||
ImageBuffer result;
|
|
||||||
LoadGoldenBmp(filename, &result);
|
|
||||||
EXPECT_EQ(result.BytesPerPixel(), 3);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ImageTestParams {
|
|
||||||
std::string image_name;
|
|
||||||
bool has_alpha;
|
|
||||||
Footprint footprint;
|
|
||||||
int width;
|
|
||||||
int height;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void PrintTo(const ImageTestParams& params, std::ostream* os) {
|
|
||||||
*os << "ImageTestParams(" << params.image_name << ", "
|
|
||||||
<< params.width << "x" << params.height << ", "
|
|
||||||
<< (params.has_alpha ? "RGBA" : "RGB") << ", "
|
|
||||||
<< "footprint " << params.footprint.Width() << "x"
|
|
||||||
<< params.footprint.Height() << ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
class LogicalASTCBlockTest : public TestWithParam<ImageTestParams> { };
|
|
||||||
|
|
||||||
// Test to make sure that reading out color values from blocks is not
|
|
||||||
// terribly wrong. To do so, we compress an image and then decompress it
|
|
||||||
// using our logical blocks and the library. The difference between the
|
|
||||||
// decoded images should be minimal.
|
|
||||||
TEST_P(LogicalASTCBlockTest, ImageWithFootprint) {
|
|
||||||
const auto& params = GetParam();
|
|
||||||
const std::string astc = LoadASTCFile(params.image_name);
|
|
||||||
|
|
||||||
ImageBuffer our_decoded_image;
|
|
||||||
our_decoded_image.Allocate(params.width, params.height, params.has_alpha ? 4 : 3);
|
|
||||||
|
|
||||||
const int block_width = params.footprint.Width();
|
|
||||||
const int block_height = params.footprint.Height();
|
|
||||||
|
|
||||||
base::UInt128 block;
|
|
||||||
for (int i = 0; i < astc.size(); i += 16) {
|
|
||||||
const int block_index = i / 16;
|
|
||||||
const int blocks_wide =
|
|
||||||
(params.width + block_width - 1) / block_width;
|
|
||||||
const int block_x = block_index % blocks_wide;
|
|
||||||
const int block_y = block_index / blocks_wide;
|
|
||||||
memcpy(&block, astc.data() + i, sizeof(block));
|
|
||||||
|
|
||||||
PhysicalASTCBlock physical_block(block);
|
|
||||||
if (physical_block.IsVoidExtent()) {
|
|
||||||
auto ve = UnpackVoidExtent(physical_block);
|
|
||||||
ASSERT_TRUE(ve) << "ASTC encoder produced invalid block!";
|
|
||||||
} else {
|
|
||||||
auto ib = UnpackIntermediateBlock(physical_block);
|
|
||||||
ASSERT_TRUE(ib) << "ASTC encoder produced invalid block!";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that the library doesn't produce incorrect ASTC blocks.
|
|
||||||
// This is covered in more depth in other tests in
|
|
||||||
// intermediate_astc_block_test and physical_astc_block_test
|
|
||||||
auto lb = UnpackLogicalBlock(params.footprint, physical_block);
|
|
||||||
ASSERT_TRUE(lb) << "ASTC encoder produced invalid block!";
|
|
||||||
|
|
||||||
LogicalASTCBlock logical_block = lb.value();
|
|
||||||
const size_t color_size = params.has_alpha ? 4 : 3;
|
|
||||||
|
|
||||||
for (int y = 0; y < block_height; ++y) {
|
|
||||||
for (int x = 0; x < block_width; ++x) {
|
|
||||||
const int px = block_width * block_x + x;
|
|
||||||
const int py = block_height * block_y + y;
|
|
||||||
|
|
||||||
// Skip out of bounds.
|
|
||||||
if (px >= params.width || py >= params.height) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t* pixel = our_decoded_image(px, py);
|
|
||||||
const RgbaColor decoded_color = logical_block.ColorAt(x, y);
|
|
||||||
ASSERT_LE(color_size, decoded_color.size());
|
|
||||||
|
|
||||||
for (int c = 0; c < color_size; ++c) {
|
|
||||||
// All of the pixels should also be 8-bit values.
|
|
||||||
ASSERT_GE(decoded_color[c], 0);
|
|
||||||
ASSERT_LT(decoded_color[c], 256);
|
|
||||||
pixel[c] = decoded_color[c];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that the decoded image is *very* similar to the library decoding
|
|
||||||
// of an ASTC texture. They may not be exact due to differences in how we
|
|
||||||
// convert a 16-bit float to an 8-bit integer.
|
|
||||||
ImageBuffer decoded_image = params.has_alpha ? LoadGoldenImageWithAlpha(params.image_name) : LoadGoldenImage(params.image_name);
|
|
||||||
CompareSumOfSquaredDifferences(decoded_image, our_decoded_image, 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test to make sure that a simple gradient image can be compressed and decoded
|
|
||||||
// by our logical block representation. This should work with every footprint.
|
|
||||||
std::vector<ImageTestParams> GetSyntheticImageTestParams() {
|
|
||||||
return {
|
|
||||||
// image_name alpha astc footprint width height
|
|
||||||
{ "footprint_4x4", false, Footprint::Get4x4(), 32, 32 },
|
|
||||||
{ "footprint_5x4", false, Footprint::Get5x4(), 32, 32 },
|
|
||||||
{ "footprint_5x5", false, Footprint::Get5x5(), 32, 32 },
|
|
||||||
{ "footprint_6x5", false, Footprint::Get6x5(), 32, 32 },
|
|
||||||
{ "footprint_6x6", false, Footprint::Get6x6(), 32, 32 },
|
|
||||||
{ "footprint_8x5", false, Footprint::Get8x5(), 32, 32 },
|
|
||||||
{ "footprint_8x6", false, Footprint::Get8x6(), 32, 32 },
|
|
||||||
{ "footprint_10x5", false, Footprint::Get10x5(), 32, 32 },
|
|
||||||
{ "footprint_10x6", false, Footprint::Get10x6(), 32, 32 },
|
|
||||||
{ "footprint_8x8", false, Footprint::Get8x8(), 32, 32 },
|
|
||||||
{ "footprint_10x8", false, Footprint::Get10x8(), 32, 32 },
|
|
||||||
{ "footprint_10x10", false, Footprint::Get10x10(), 32, 32 },
|
|
||||||
{ "footprint_12x10", false, Footprint::Get12x10(), 32, 32 },
|
|
||||||
{ "footprint_12x12", false, Footprint::Get12x12(), 32, 32 },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(Synthetic, LogicalASTCBlockTest,
|
|
||||||
ValuesIn(GetSyntheticImageTestParams()));
|
|
||||||
|
|
||||||
// Test to make sure that reading out color values from blocks in a real-world
|
|
||||||
// image isn't terribly wrong, either.
|
|
||||||
std::vector<ImageTestParams> GetRealWorldImageTestParams() {
|
|
||||||
return {
|
|
||||||
// image_name alpha astc footprint width height
|
|
||||||
{ "rgb_4x4", false, Footprint::Get4x4(), 224, 288 },
|
|
||||||
{ "rgb_6x6", false, Footprint::Get6x6(), 224, 288 },
|
|
||||||
{ "rgb_8x8", false, Footprint::Get8x8(), 224, 288 },
|
|
||||||
{ "rgb_12x12", false, Footprint::Get12x12(), 224, 288 },
|
|
||||||
{ "rgb_5x4", false, Footprint::Get5x4(), 224, 288 }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(RealWorld, LogicalASTCBlockTest,
|
|
||||||
ValuesIn(GetRealWorldImageTestParams()));
|
|
||||||
|
|
||||||
// Test to make sure that reading out color values from blocks in a real-world
|
|
||||||
// image isn't terribly wrong, either.
|
|
||||||
std::vector<ImageTestParams> GetTransparentImageTestParams() {
|
|
||||||
return {
|
|
||||||
// image_name alpha astc footprint width height
|
|
||||||
{ "atlas_small_4x4", true, Footprint::Get4x4(), 256, 256 },
|
|
||||||
{ "atlas_small_5x5", true, Footprint::Get5x5(), 256, 256 },
|
|
||||||
{ "atlas_small_6x6", true, Footprint::Get6x6(), 256, 256 },
|
|
||||||
{ "atlas_small_8x8", true, Footprint::Get8x8(), 256, 256 },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(Transparent, LogicalASTCBlockTest,
|
|
||||||
ValuesIn(GetTransparentImageTestParams()));
|
|
||||||
|
|
||||||
// Test to make sure that if we set our endpoints then it's reflected in our
|
|
||||||
// color selection
|
|
||||||
TEST(LogicalASTCBlockTest, SetEndpoints) {
|
|
||||||
LogicalASTCBlock logical_block(Footprint::Get8x8());
|
|
||||||
|
|
||||||
// Setup a weight checkerboard
|
|
||||||
for (int j = 0; j < 8; ++j) {
|
|
||||||
for (int i = 0; i < 8; ++i) {
|
|
||||||
if (((i ^ j) & 1) == 1) {
|
|
||||||
logical_block.SetWeightAt(i, j, 0);
|
|
||||||
} else {
|
|
||||||
logical_block.SetWeightAt(i, j, 64);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now set the colors to something ridiculous
|
|
||||||
logical_block.SetEndpoints({{ 123, 45, 67, 89 }}, {{ 101, 121, 31, 41 }}, 0);
|
|
||||||
|
|
||||||
// For each pixel, we expect it to mirror the endpoints in a checkerboard
|
|
||||||
// pattern
|
|
||||||
for (int j = 0; j < 8; ++j) {
|
|
||||||
for (int i = 0; i < 8; ++i) {
|
|
||||||
if (((i ^ j) & 1) == 1) {
|
|
||||||
EXPECT_THAT(logical_block.ColorAt(i, j), ElementsAre(123, 45, 67, 89));
|
|
||||||
} else {
|
|
||||||
EXPECT_THAT(logical_block.ColorAt(i, j), ElementsAre(101, 121, 31, 41));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test whether or not setting weight values under different circumstances is
|
|
||||||
// supported and reflected in the query functions.
|
|
||||||
TEST(LogicalASTCBlockTest, SetWeightVals) {
|
|
||||||
LogicalASTCBlock logical_block(Footprint::Get4x4());
|
|
||||||
|
|
||||||
EXPECT_THAT(logical_block.GetFootprint(), Eq(Footprint::Get4x4()));
|
|
||||||
|
|
||||||
// Not a dual plane by default
|
|
||||||
EXPECT_FALSE(logical_block.IsDualPlane());
|
|
||||||
logical_block.SetWeightAt(2, 3, 2);
|
|
||||||
|
|
||||||
// Set the dual plane
|
|
||||||
logical_block.SetDualPlaneChannel(0);
|
|
||||||
EXPECT_TRUE(logical_block.IsDualPlane());
|
|
||||||
|
|
||||||
// This shouldn't have reset our weight
|
|
||||||
const LogicalASTCBlock other_block = logical_block;
|
|
||||||
EXPECT_THAT(other_block.WeightAt(2, 3), Eq(2));
|
|
||||||
EXPECT_THAT(other_block.DualPlaneWeightAt(0, 2, 3), Eq(2));
|
|
||||||
|
|
||||||
// If we set the dual plane weight, it shouldn't change the original weight
|
|
||||||
// value or the other channels
|
|
||||||
logical_block.SetDualPlaneWeightAt(0, 2, 3, 1);
|
|
||||||
EXPECT_THAT(logical_block.WeightAt(2, 3), Eq(2));
|
|
||||||
EXPECT_THAT(logical_block.DualPlaneWeightAt(0, 2, 3), Eq(1));
|
|
||||||
for (int i = 1; i < 4; ++i) {
|
|
||||||
EXPECT_THAT(logical_block.DualPlaneWeightAt(i, 2, 3), Eq(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the dual plane
|
|
||||||
logical_block.SetDualPlaneChannel(-1);
|
|
||||||
EXPECT_FALSE(logical_block.IsDualPlane());
|
|
||||||
|
|
||||||
// Now the original dual plane weight should be reset back to the others. Note
|
|
||||||
// that we have to call DualPlaneWeightAt from a const logical block since
|
|
||||||
// returning a reference to a weight that doesn't exist is illegal.
|
|
||||||
const LogicalASTCBlock other_block2 = logical_block;
|
|
||||||
EXPECT_THAT(logical_block.WeightAt(2, 3), Eq(2));
|
|
||||||
for (int i = 0; i < 4; ++i) {
|
|
||||||
EXPECT_EQ(logical_block.WeightAt(2, 3),
|
|
||||||
other_block2.DualPlaneWeightAt(i, 2, 3));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
} // namespace astc_codec
|
|
||||||
@@ -1,263 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
#include "src/decoder/partition.h"
|
|
||||||
|
|
||||||
#include <gmock/gmock.h>
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <random>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
using ::testing::ElementsAreArray;
|
|
||||||
using ::testing::Eq;
|
|
||||||
using ::testing::Le;
|
|
||||||
using ::testing::Not;
|
|
||||||
|
|
||||||
using astc_codec::Footprint;
|
|
||||||
using astc_codec::Partition;
|
|
||||||
using astc_codec::PartitionMetric;
|
|
||||||
using astc_codec::GetASTCPartition;
|
|
||||||
using astc_codec::FindClosestASTCPartition;
|
|
||||||
|
|
||||||
// Test to make sure that a simple difference between two partitions where
|
|
||||||
// most of the values are the same returns what we expect.
|
|
||||||
TEST(PartitionTest, TestSimplePartitionMetric) {
|
|
||||||
Partition a = {Footprint::Get6x6(), /* num_parts = */ 2,
|
|
||||||
/* partition_id = */ {}, /* assignment = */ {}};
|
|
||||||
Partition b = a;
|
|
||||||
|
|
||||||
a.assignment = {
|
|
||||||
0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
b.assignment = {
|
|
||||||
1, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
const int dist = PartitionMetric(a, b);
|
|
||||||
EXPECT_EQ(dist, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test to make sure that if one partition is a subset of another that we still
|
|
||||||
// return the proper difference against the subset of the larger one.
|
|
||||||
TEST(PartitionDeathTest, TestPartitionMetric) {
|
|
||||||
Partition a = {Footprint::Get4x4(), /* num_parts = */ 2,
|
|
||||||
/* partition_id = */ {}, /* assignment = */ {}};
|
|
||||||
Partition b = {Footprint::Get6x6(), /* num_parts = */ 2,
|
|
||||||
/* partition_id = */ {}, /* assignment = */ {}};
|
|
||||||
|
|
||||||
a.assignment = {{
|
|
||||||
1, 1, 1, 1,
|
|
||||||
0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0,
|
|
||||||
0, 0, 0, 1,
|
|
||||||
}};
|
|
||||||
|
|
||||||
b.assignment = {{
|
|
||||||
1, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 1,
|
|
||||||
0, 0, 0, 0, 0, 0,
|
|
||||||
0, 1, 0, 0, 1, 0,
|
|
||||||
0, 0, 1, 1, 0, 0,
|
|
||||||
}};
|
|
||||||
|
|
||||||
EXPECT_DEATH(PartitionMetric(a, b), "");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test to make sure that even if we have different numbers of subsets for each
|
|
||||||
// partition, that the returned value is what we'd expect.
|
|
||||||
TEST(PartitionTest, TestDiffPartsPartitionMetric) {
|
|
||||||
Partition a = {Footprint::Get4x4(), /* num_parts = */ 2,
|
|
||||||
/* partition_id = */ {}, /* assignment = */ {}};
|
|
||||||
Partition b = {Footprint::Get4x4(), /* num_parts = */ 3,
|
|
||||||
/* partition_id = */ {}, /* assignment = */ {}};
|
|
||||||
|
|
||||||
a.assignment = {{
|
|
||||||
2, 2, 2, 0,
|
|
||||||
0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0,
|
|
||||||
0, 0, 0, 1,
|
|
||||||
}};
|
|
||||||
|
|
||||||
b.assignment = {{
|
|
||||||
1, 0, 0, 0,
|
|
||||||
0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0
|
|
||||||
}};
|
|
||||||
|
|
||||||
const int dist = PartitionMetric(a, b);
|
|
||||||
EXPECT_EQ(dist, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
// An additional sanity check test that makes sure that we're not always mapping
|
|
||||||
// zero to zero in our tests.
|
|
||||||
TEST(PartitionTest, TestDiffMappingPartitionMetric) {
|
|
||||||
Partition a = {Footprint::Get4x4(), /* num_parts = */ 2,
|
|
||||||
/* partition_id = */ {}, /* assignment = */ {}};
|
|
||||||
Partition b = {Footprint::Get4x4(), /* num_parts = */ 3,
|
|
||||||
/* partition_id = */ {}, /* assignment = */ {}};
|
|
||||||
|
|
||||||
a.assignment = {{
|
|
||||||
0, 1, 2, 2,
|
|
||||||
2, 2, 2, 2,
|
|
||||||
2, 2, 2, 2,
|
|
||||||
2, 2, 2, 2,
|
|
||||||
}};
|
|
||||||
|
|
||||||
b.assignment = {{
|
|
||||||
1, 0, 0, 0,
|
|
||||||
0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0,
|
|
||||||
}};
|
|
||||||
|
|
||||||
const int dist = PartitionMetric(a, b);
|
|
||||||
EXPECT_EQ(dist, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally, if we grab an ASTC partition and modify it a tad, the closest
|
|
||||||
// partition should still be the same ASTC partition.
|
|
||||||
TEST(PartitionTest, TestFindingASTCPartition) {
|
|
||||||
const Partition astc = GetASTCPartition(Footprint::Get12x12(), 3, 0x3CB);
|
|
||||||
Partition almost_astc = astc;
|
|
||||||
almost_astc.assignment[0]++;
|
|
||||||
|
|
||||||
const Partition& closest_astc = FindClosestASTCPartition(almost_astc);
|
|
||||||
EXPECT_EQ(astc, closest_astc);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test a partition that was obtained from the reference ASTC encoder. We should
|
|
||||||
// be able to match it exactly
|
|
||||||
TEST(PartitionTest, TestSpecificPartition) {
|
|
||||||
const Partition astc = GetASTCPartition(Footprint::Get10x6(), 3, 557);
|
|
||||||
EXPECT_THAT(astc.assignment, ElementsAreArray(std::array<int, 60> {{
|
|
||||||
0, 0, 0, 0, 1, 1, 1, 2, 2, 2,
|
|
||||||
0, 0, 0, 0, 1, 1, 1, 2, 2, 2,
|
|
||||||
0, 0, 0, 0, 1, 1, 1, 2, 2, 2,
|
|
||||||
0, 0, 0, 0, 1, 1, 1, 2, 2, 2,
|
|
||||||
0, 0, 0, 0, 1, 1, 1, 2, 2, 2,
|
|
||||||
0, 0, 0, 0, 1, 1, 1, 2, 2, 2 }}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that when we match against this specific partition, it'll return a
|
|
||||||
// partition with the same number of subsets
|
|
||||||
TEST(PartitionTest, EstimatedPartitionSubsets) {
|
|
||||||
Partition partition = {
|
|
||||||
/* footprint = */ Footprint::Get6x6(),
|
|
||||||
/* num_parts = */ 2,
|
|
||||||
/* partition_id = */ {},
|
|
||||||
/* assignment = */ {
|
|
||||||
0, 0, 1, 1, 1, 0,
|
|
||||||
0, 0, 0, 0, 0, 0,
|
|
||||||
0, 0, 0, 0, 0, 0,
|
|
||||||
0, 1, 1, 1, 1, 1,
|
|
||||||
0, 0, 0, 0, 0, 0,
|
|
||||||
1, 1, 1, 1, 1, 1
|
|
||||||
}};
|
|
||||||
|
|
||||||
const Partition astc = FindClosestASTCPartition(partition);
|
|
||||||
EXPECT_THAT(astc.num_parts, Eq(partition.num_parts));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that regardless of what partition we match against, it'll return a
|
|
||||||
// partition with at most a fewer number of subsets
|
|
||||||
TEST(PartitionTest, EstimatedPartitionFewerSubsets) {
|
|
||||||
std::mt19937 random(0xdeadbeef);
|
|
||||||
auto randUniform = [&random](int max) {
|
|
||||||
std::uniform_int_distribution<> dist(0, max - 1);
|
|
||||||
return dist(random);
|
|
||||||
};
|
|
||||||
|
|
||||||
constexpr int kNumFootprints = Footprint::NumValidFootprints();
|
|
||||||
const auto kFootprints = std::array<Footprint, kNumFootprints> {{
|
|
||||||
Footprint::Get4x4(),
|
|
||||||
Footprint::Get5x4(),
|
|
||||||
Footprint::Get5x5(),
|
|
||||||
Footprint::Get6x5(),
|
|
||||||
Footprint::Get6x6(),
|
|
||||||
Footprint::Get8x5(),
|
|
||||||
Footprint::Get8x6(),
|
|
||||||
Footprint::Get8x8(),
|
|
||||||
Footprint::Get10x5(),
|
|
||||||
Footprint::Get10x6(),
|
|
||||||
Footprint::Get10x8(),
|
|
||||||
Footprint::Get10x10(),
|
|
||||||
Footprint::Get12x10(),
|
|
||||||
Footprint::Get12x12()
|
|
||||||
}};
|
|
||||||
|
|
||||||
constexpr int kNumTests = 200;
|
|
||||||
for (int i = 0; i < kNumTests; ++i) {
|
|
||||||
const auto& footprint = kFootprints[randUniform(kNumFootprints)];
|
|
||||||
const int num_parts = 2 + randUniform(3);
|
|
||||||
Partition partition = {
|
|
||||||
footprint,
|
|
||||||
num_parts,
|
|
||||||
/* partition_id = */ {},
|
|
||||||
/* assignment = */ std::vector<int>(footprint.NumPixels(), 0)};
|
|
||||||
|
|
||||||
for (auto& p : partition.assignment) {
|
|
||||||
p = randUniform(num_parts);
|
|
||||||
}
|
|
||||||
|
|
||||||
const Partition astc = FindClosestASTCPartition(partition);
|
|
||||||
EXPECT_THAT(astc.num_parts, Le(partition.num_parts))
|
|
||||||
<< "Test #" << i << ": "
|
|
||||||
<< "Selected partition with ID " << astc.partition_id.value();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that we generate unique partitions that are close to the
|
|
||||||
// candidates.
|
|
||||||
TEST(PartitionTest, UniquePartitionResults) {
|
|
||||||
Partition partition = {
|
|
||||||
/* footprint = */ Footprint::Get6x6(),
|
|
||||||
/* num_parts = */ 2,
|
|
||||||
/* partition_id = */ {},
|
|
||||||
/* assignment = */ {
|
|
||||||
0, 1, 1, 1, 1, 1,
|
|
||||||
0, 1, 1, 1, 1, 1,
|
|
||||||
0, 1, 1, 1, 1, 1,
|
|
||||||
0, 1, 1, 1, 1, 1,
|
|
||||||
0, 1, 1, 1, 1, 1,
|
|
||||||
0, 1, 1, 1, 1, 1
|
|
||||||
}};
|
|
||||||
|
|
||||||
const auto parts = FindKClosestASTCPartitions(partition, 2);
|
|
||||||
EXPECT_THAT(*parts[0], Not(Eq(*parts[1])));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(google): Verify somehow that the assignment generated from
|
|
||||||
// GetASTCPartition actually matches what's in the spec. The selection
|
|
||||||
// function was more or less copy/pasted though so it's unclear how to
|
|
||||||
// measure that against e.g. the ASTC encoder.
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
@@ -1,361 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
#include "src/decoder/physical_astc_block.h"
|
|
||||||
#include "src/base/uint128.h"
|
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
using astc_codec::PhysicalASTCBlock;
|
|
||||||
using astc_codec::ColorEndpointMode;
|
|
||||||
using astc_codec::base::UInt128;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
static const PhysicalASTCBlock kErrorBlock(UInt128(0));
|
|
||||||
|
|
||||||
// Test to make sure that each of the constructors work and that
|
|
||||||
// they produce the same block encodings, since the ASTC blocks
|
|
||||||
// are little-endian
|
|
||||||
TEST(PhysicalASTCBlockTest, TestConstructors) {
|
|
||||||
// Little-endian reading of bytes
|
|
||||||
PhysicalASTCBlock blk1(0x0000000001FE000173ULL);
|
|
||||||
PhysicalASTCBlock blk2(
|
|
||||||
std::string("\x73\x01\x00\xFE\x01\x00\x00\x00\x00"
|
|
||||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16));
|
|
||||||
EXPECT_EQ(blk1.GetBlockBits(), blk2.GetBlockBits());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test to see if we properly decode the maximum value that a weight
|
|
||||||
// can take in an ASTC block based on the block mode encoding. We test
|
|
||||||
// against a valid case and various error cases
|
|
||||||
TEST(PhysicalASTCBlockTest, TestWeightRange) {
|
|
||||||
PhysicalASTCBlock blk1(0x0000000001FE000173ULL);
|
|
||||||
auto weight_range = blk1.WeightRange();
|
|
||||||
ASSERT_TRUE(weight_range);
|
|
||||||
EXPECT_EQ(weight_range.value(), 7);
|
|
||||||
|
|
||||||
// If we flip the high bit then we should have a range of 31,
|
|
||||||
// although then we have too many bits and this should error.
|
|
||||||
PhysicalASTCBlock blk2(0x0000000001FE000373ULL);
|
|
||||||
EXPECT_FALSE(blk2.WeightRange());
|
|
||||||
|
|
||||||
// One bit per weight -- range of 1
|
|
||||||
PhysicalASTCBlock non_shared_cem(0x4000000000800D44ULL);
|
|
||||||
weight_range = non_shared_cem.WeightRange();
|
|
||||||
ASSERT_TRUE(weight_range);
|
|
||||||
EXPECT_EQ(weight_range.value(), 1);
|
|
||||||
|
|
||||||
// Error blocks have no weight range
|
|
||||||
EXPECT_FALSE(kErrorBlock.WeightRange());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test to see if we properly decode the weight grid width and height
|
|
||||||
// in an ASTC block based on the block mode encoding. We test against
|
|
||||||
// a valid case and various error cases
|
|
||||||
TEST(PhysicalASTCBlockTest, TestWeightDims) {
|
|
||||||
PhysicalASTCBlock blk1(0x0000000001FE000173ULL);
|
|
||||||
auto weight_dims = blk1.WeightGridDims();
|
|
||||||
EXPECT_TRUE(weight_dims);
|
|
||||||
EXPECT_EQ(weight_dims.value()[0], 6);
|
|
||||||
EXPECT_EQ(weight_dims.value()[1], 5);
|
|
||||||
|
|
||||||
// If we flip the high bit then we should have a range of 31,
|
|
||||||
// although then we have too many bits for the weight grid
|
|
||||||
// and this should error.
|
|
||||||
PhysicalASTCBlock blk2(0x0000000001FE000373ULL);
|
|
||||||
EXPECT_FALSE(blk2.WeightGridDims());
|
|
||||||
EXPECT_EQ(blk2.IsIllegalEncoding().value(),
|
|
||||||
"Too many bits required for weight grid");
|
|
||||||
|
|
||||||
// Dual plane block with 3x5 weight dims
|
|
||||||
PhysicalASTCBlock blk3(0x0000000001FE0005FFULL);
|
|
||||||
weight_dims = blk3.WeightGridDims();
|
|
||||||
ASSERT_TRUE(weight_dims);
|
|
||||||
EXPECT_EQ(weight_dims->at(0), 3);
|
|
||||||
EXPECT_EQ(weight_dims->at(1), 5);
|
|
||||||
|
|
||||||
// Error blocks shouldn't have any weight dims
|
|
||||||
EXPECT_FALSE(kErrorBlock.WeightGridDims());
|
|
||||||
|
|
||||||
PhysicalASTCBlock non_shared_cem(0x4000000000800D44ULL);
|
|
||||||
weight_dims = non_shared_cem.WeightGridDims();
|
|
||||||
ASSERT_TRUE(weight_dims);
|
|
||||||
EXPECT_EQ(weight_dims->at(0), 8);
|
|
||||||
EXPECT_EQ(weight_dims->at(1), 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test to see whether or not the presence of a dual-plane bit
|
|
||||||
// is decoded properly. Error encodings are tested to *not* return
|
|
||||||
// that they have dual planes.
|
|
||||||
TEST(PhysicalASTCBlockTest, TestDualPlane) {
|
|
||||||
PhysicalASTCBlock blk1(0x0000000001FE000173ULL);
|
|
||||||
EXPECT_FALSE(blk1.IsDualPlane());
|
|
||||||
EXPECT_FALSE(kErrorBlock.IsDualPlane());
|
|
||||||
|
|
||||||
// If we flip the dual plane bit, we will have too many bits
|
|
||||||
// for the weight grid and this should error
|
|
||||||
PhysicalASTCBlock blk2(0x0000000001FE000573ULL);
|
|
||||||
EXPECT_FALSE(blk2.IsDualPlane());
|
|
||||||
EXPECT_FALSE(blk2.WeightGridDims());
|
|
||||||
EXPECT_EQ(blk2.IsIllegalEncoding().value(),
|
|
||||||
"Too many bits required for weight grid");
|
|
||||||
|
|
||||||
// A dual plane with 3x5 weight grid should be supported
|
|
||||||
PhysicalASTCBlock blk3(0x0000000001FE0005FFULL);
|
|
||||||
EXPECT_TRUE(blk3.IsDualPlane());
|
|
||||||
|
|
||||||
// If we use the wrong block mode, then a valid block
|
|
||||||
// shouldn't have any dual plane
|
|
||||||
PhysicalASTCBlock blk4(0x0000000001FE000108ULL);
|
|
||||||
EXPECT_FALSE(blk4.IsDualPlane());
|
|
||||||
EXPECT_FALSE(blk4.IsIllegalEncoding());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that we properly calculate the number of bits used to encode
|
|
||||||
// the weight grid. Given error encodings or void extent blocks, this number
|
|
||||||
// should be zero
|
|
||||||
TEST(PhysicalASTCBlockTest, TestNumWeightBits) {
|
|
||||||
// 6x5 single-plane weight grid with 3-bit weights
|
|
||||||
// should have 90 bits for the weights.
|
|
||||||
PhysicalASTCBlock blk1(0x0000000001FE000173ULL);
|
|
||||||
EXPECT_EQ(90, blk1.NumWeightBits());
|
|
||||||
|
|
||||||
// Error block has no weight bits
|
|
||||||
EXPECT_FALSE(kErrorBlock.NumWeightBits());
|
|
||||||
|
|
||||||
// Void extent blocks have no weight bits
|
|
||||||
EXPECT_FALSE(PhysicalASTCBlock(0xFFF8003FFE000DFCULL).NumWeightBits());
|
|
||||||
|
|
||||||
// If we flip the dual plane bit, we will have too many bits
|
|
||||||
// for the weight grid and this should error and return no bits
|
|
||||||
PhysicalASTCBlock blk2(0x0000000001FE000573ULL);
|
|
||||||
EXPECT_FALSE(blk2.NumWeightBits());
|
|
||||||
|
|
||||||
// 3x5 dual-plane weight grid with 3-bit weights
|
|
||||||
// should have 90 bits for the weights.
|
|
||||||
PhysicalASTCBlock blk3(0x0000000001FE0005FFULL);
|
|
||||||
EXPECT_EQ(90, blk3.NumWeightBits());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test to make sure that our weight bits start where we expect them to.
|
|
||||||
// In other words, make sure that the calculation based on the block mode for
|
|
||||||
// where the weight bits start is accurate.
|
|
||||||
TEST(PhysicalASTCBlockTest, TestStartWeightBit) {
|
|
||||||
EXPECT_EQ(PhysicalASTCBlock(0x4000000000800D44ULL).WeightStartBit(), 64);
|
|
||||||
|
|
||||||
// Error blocks have no weight start bit
|
|
||||||
EXPECT_FALSE(kErrorBlock.WeightStartBit());
|
|
||||||
|
|
||||||
// Void extent blocks have no weight start bit
|
|
||||||
EXPECT_FALSE(PhysicalASTCBlock(0xFFF8003FFE000DFCULL).WeightStartBit());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test to make sure that we catch various different reasons for error encoding
|
|
||||||
// of ASTC blocks, but also that certain encodings aren't errors.
|
|
||||||
TEST(PhysicalASTCBlockTest, TestErrorBlocks) {
|
|
||||||
// Various valid block modes
|
|
||||||
EXPECT_FALSE(PhysicalASTCBlock(0x0000000001FE000173ULL).IsIllegalEncoding());
|
|
||||||
EXPECT_FALSE(PhysicalASTCBlock(0x0000000001FE0005FFULL).IsIllegalEncoding());
|
|
||||||
EXPECT_FALSE(PhysicalASTCBlock(0x0000000001FE000108ULL).IsIllegalEncoding());
|
|
||||||
|
|
||||||
// This is an error because it uses an invalid block mode
|
|
||||||
EXPECT_EQ(kErrorBlock.IsIllegalEncoding().value(), "Reserved block mode");
|
|
||||||
|
|
||||||
// This is an error because we have too many weight bits
|
|
||||||
PhysicalASTCBlock err_blk(0x0000000001FE000573ULL);
|
|
||||||
EXPECT_EQ(err_blk.IsIllegalEncoding().value(),
|
|
||||||
"Too many bits required for weight grid");
|
|
||||||
|
|
||||||
// This is an error because we have too many weights
|
|
||||||
PhysicalASTCBlock err_blk2 = PhysicalASTCBlock(0x0000000001FE0005A8ULL);
|
|
||||||
EXPECT_EQ(err_blk2.IsIllegalEncoding().value(), "Too many weights specified");
|
|
||||||
|
|
||||||
PhysicalASTCBlock err_blk3 = PhysicalASTCBlock(0x0000000001FE000588ULL);
|
|
||||||
EXPECT_EQ(err_blk3.IsIllegalEncoding().value(), "Too many weights specified");
|
|
||||||
|
|
||||||
// This is an error because we have too few weights
|
|
||||||
PhysicalASTCBlock err_blk4 = PhysicalASTCBlock(0x0000000001FE00002ULL);
|
|
||||||
EXPECT_EQ(err_blk4.IsIllegalEncoding().value(),
|
|
||||||
"Too few bits required for weight grid");
|
|
||||||
|
|
||||||
// Four partitions, dual plane -- should be error
|
|
||||||
// 2x2 weight grid, 3 bits per weight
|
|
||||||
PhysicalASTCBlock dual_plane_four_parts(0x000000000000001D1FULL);
|
|
||||||
EXPECT_FALSE(dual_plane_four_parts.NumPartitions());
|
|
||||||
EXPECT_EQ(dual_plane_four_parts.IsIllegalEncoding().value(),
|
|
||||||
"Both four partitions and dual plane specified");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test to make sure that we properly identify and can manipulate void-extent
|
|
||||||
// blocks. These are ASTC blocks that only define a single color for the entire
|
|
||||||
// block.
|
|
||||||
TEST(PhysicalASTCBlockTest, TestVoidExtentBlocks) {
|
|
||||||
// Various valid block modes that aren't void extent blocks
|
|
||||||
EXPECT_FALSE(PhysicalASTCBlock(0x0000000001FE000173ULL).IsVoidExtent());
|
|
||||||
EXPECT_FALSE(PhysicalASTCBlock(0x0000000001FE0005FFULL).IsVoidExtent());
|
|
||||||
EXPECT_FALSE(PhysicalASTCBlock(0x0000000001FE000108ULL).IsVoidExtent());
|
|
||||||
|
|
||||||
// Error block is not a void extent block
|
|
||||||
EXPECT_FALSE(kErrorBlock.IsVoidExtent());
|
|
||||||
|
|
||||||
// Void extent block is void extent block...
|
|
||||||
UInt128 void_extent_encoding(0, 0xFFF8003FFE000DFCULL);
|
|
||||||
EXPECT_FALSE(PhysicalASTCBlock(void_extent_encoding).IsIllegalEncoding());
|
|
||||||
EXPECT_TRUE(PhysicalASTCBlock(void_extent_encoding).IsVoidExtent());
|
|
||||||
|
|
||||||
// If we modify the high 64 bits it shouldn't change anything
|
|
||||||
void_extent_encoding |= UInt128(0xdeadbeefdeadbeef, 0);
|
|
||||||
EXPECT_FALSE(PhysicalASTCBlock(void_extent_encoding).IsIllegalEncoding());
|
|
||||||
EXPECT_TRUE(PhysicalASTCBlock(void_extent_encoding).IsVoidExtent());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(PhysicalASTCBlockTest, TestVoidExtentCoordinates) {
|
|
||||||
// The void extent block should have texture coordinates from 0-8191
|
|
||||||
auto coords = PhysicalASTCBlock(0xFFF8003FFE000DFCULL).VoidExtentCoords();
|
|
||||||
EXPECT_EQ(coords->at(0), 0);
|
|
||||||
EXPECT_EQ(coords->at(1), 8191);
|
|
||||||
EXPECT_EQ(coords->at(2), 0);
|
|
||||||
EXPECT_EQ(coords->at(3), 8191);
|
|
||||||
|
|
||||||
// If we set the coords to all 1's then it's still a void extent
|
|
||||||
// block, but there aren't any void extent coords.
|
|
||||||
EXPECT_FALSE(PhysicalASTCBlock(0xFFFFFFFFFFFFFDFCULL).IsIllegalEncoding());
|
|
||||||
EXPECT_TRUE(PhysicalASTCBlock(0xFFFFFFFFFFFFFDFCULL).IsVoidExtent());
|
|
||||||
EXPECT_FALSE(PhysicalASTCBlock(0xFFFFFFFFFFFFFDFCULL).VoidExtentCoords());
|
|
||||||
|
|
||||||
// If we set the void extent coords to something where the coords are
|
|
||||||
// >= each other, then the encoding is illegal.
|
|
||||||
EXPECT_TRUE(PhysicalASTCBlock(0x0008004002001DFCULL).IsIllegalEncoding());
|
|
||||||
EXPECT_TRUE(PhysicalASTCBlock(0x0007FFC001FFFDFCULL).IsIllegalEncoding());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test to see if we can properly identify the number of partitions in a block
|
|
||||||
// In particular -- we need to make sure we properly identify single and
|
|
||||||
// multi-partition blocks, but also that void extent and error blocks don't
|
|
||||||
// return valid numbers of partitions
|
|
||||||
TEST(PhysicalASTCBlockTest, TestNumPartitions) {
|
|
||||||
// Various valid block modes, but all single partition
|
|
||||||
EXPECT_EQ(PhysicalASTCBlock(0x0000000001FE000173ULL).NumPartitions(), 1);
|
|
||||||
EXPECT_EQ(PhysicalASTCBlock(0x0000000001FE0005FFULL).NumPartitions(), 1);
|
|
||||||
EXPECT_EQ(PhysicalASTCBlock(0x0000000001FE000108ULL).NumPartitions(), 1);
|
|
||||||
|
|
||||||
// Two to four partitions don't have enough bits for color.
|
|
||||||
EXPECT_FALSE(PhysicalASTCBlock(0x000000000000000973ULL).NumPartitions());
|
|
||||||
EXPECT_FALSE(PhysicalASTCBlock(0x000000000000001173ULL).NumPartitions());
|
|
||||||
EXPECT_FALSE(PhysicalASTCBlock(0x000000000000001973ULL).NumPartitions());
|
|
||||||
|
|
||||||
// Test against having more than one partition
|
|
||||||
PhysicalASTCBlock non_shared_cem(0x4000000000800D44ULL);
|
|
||||||
EXPECT_EQ(non_shared_cem.NumPartitions(), 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test the color endpoint modes specified for how the endpoints are encoded.
|
|
||||||
// In particular, test that shared color endpoint modes work for multi-partition
|
|
||||||
// blocks and that non-shared color endpoint modes also work.
|
|
||||||
TEST(PhysicalASTCBlockTest, TestColorEndpointModes) {
|
|
||||||
// Four partitions -- one shared CEM
|
|
||||||
const auto blk1 = PhysicalASTCBlock(0x000000000000001961ULL);
|
|
||||||
for (int i = 0; i < 4; ++i) {
|
|
||||||
EXPECT_EQ(blk1.GetEndpointMode(i), ColorEndpointMode::kLDRLumaDirect);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Void extent blocks have no endpoint modes
|
|
||||||
EXPECT_FALSE(PhysicalASTCBlock(0xFFF8003FFE000DFCULL).GetEndpointMode(0));
|
|
||||||
|
|
||||||
// Test out of range partitions
|
|
||||||
EXPECT_FALSE(PhysicalASTCBlock(0x0000000001FE000173ULL).GetEndpointMode(1));
|
|
||||||
EXPECT_FALSE(PhysicalASTCBlock(0x0000000001FE000173ULL).GetEndpointMode(-1));
|
|
||||||
EXPECT_FALSE(PhysicalASTCBlock(0x0000000001FE000173ULL).GetEndpointMode(100));
|
|
||||||
|
|
||||||
// Error blocks have no endpoint modes
|
|
||||||
EXPECT_FALSE(kErrorBlock.GetEndpointMode(0));
|
|
||||||
|
|
||||||
// Test non-shared CEMs
|
|
||||||
PhysicalASTCBlock non_shared_cem(0x4000000000800D44ULL);
|
|
||||||
EXPECT_EQ(non_shared_cem.GetEndpointMode(0),
|
|
||||||
ColorEndpointMode::kLDRLumaDirect);
|
|
||||||
EXPECT_EQ(non_shared_cem.GetEndpointMode(1),
|
|
||||||
ColorEndpointMode::kLDRLumaBaseOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that if we have more than one partition then we have proper
|
|
||||||
// partition IDs (these determine which pixels correspond to which partition)
|
|
||||||
TEST(PhysicalASTCBlockTest, TestPartitionID) {
|
|
||||||
// Valid partitions
|
|
||||||
EXPECT_EQ(PhysicalASTCBlock(0x4000000000FFED44ULL).PartitionID(), 0x3FF);
|
|
||||||
EXPECT_EQ(PhysicalASTCBlock(0x4000000000AAAD44ULL).PartitionID(), 0x155);
|
|
||||||
|
|
||||||
// Error blocks have no partition IDs
|
|
||||||
EXPECT_FALSE(kErrorBlock.PartitionID());
|
|
||||||
|
|
||||||
// Void extent blocks have no endpoint modes
|
|
||||||
EXPECT_FALSE(PhysicalASTCBlock(0xFFF8003FFE000DFCULL).PartitionID());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that we're properly attributing the number of bits associated with
|
|
||||||
// the encoded color values.
|
|
||||||
TEST(PhysicalASTCBlockTest, TestNumColorBits) {
|
|
||||||
// If we're using a direct luma channel, then the number of color bits is 16
|
|
||||||
EXPECT_EQ(PhysicalASTCBlock(0x0000000001FE000173ULL).NumColorValues(), 2);
|
|
||||||
EXPECT_EQ(PhysicalASTCBlock(0x0000000001FE000173ULL).NumColorBits(), 16);
|
|
||||||
|
|
||||||
// Error blocks have nothing
|
|
||||||
EXPECT_FALSE(kErrorBlock.NumColorValues());
|
|
||||||
EXPECT_FALSE(kErrorBlock.NumColorBits());
|
|
||||||
|
|
||||||
// Void extent blocks have four color values and 64 bits of color
|
|
||||||
EXPECT_EQ(PhysicalASTCBlock(0xFFF8003FFE000DFCULL).NumColorValues(), 4);
|
|
||||||
EXPECT_EQ(PhysicalASTCBlock(0xFFF8003FFE000DFCULL).NumColorBits(), 64);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that we're properly decoding the range of values that each of the
|
|
||||||
// encoded color values can take
|
|
||||||
TEST(PhysicalASTCBlockTest, TestColorValuesRange) {
|
|
||||||
// If we're using a direct luma channel, then we use two color values up to
|
|
||||||
// a full byte each.
|
|
||||||
EXPECT_EQ(PhysicalASTCBlock(0x0000000001FE000173ULL).ColorValuesRange(), 255);
|
|
||||||
|
|
||||||
// Error blocks have nothing
|
|
||||||
EXPECT_FALSE(kErrorBlock.ColorValuesRange());
|
|
||||||
|
|
||||||
// Void extent blocks have four color values and 64 bits of color, so the
|
|
||||||
// color range for each is sixteen bits.
|
|
||||||
EXPECT_EQ(PhysicalASTCBlock(0xFFF8003FFE000DFCULL).ColorValuesRange(),
|
|
||||||
(1 << 16) - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that we know where the color data starts. This is different mostly
|
|
||||||
// depending on whether or not the block is single-partition or void extent.
|
|
||||||
TEST(PhysicalASTCBlockTest, TestColorStartBits) {
|
|
||||||
// Void extent blocks start at bit 64
|
|
||||||
EXPECT_EQ(PhysicalASTCBlock(0xFFF8003FFE000DFCULL).ColorStartBit(), 64);
|
|
||||||
|
|
||||||
// Error blocks don't start anywhere
|
|
||||||
EXPECT_FALSE(kErrorBlock.ColorStartBit());
|
|
||||||
|
|
||||||
// Single partition blocks start at bit 17
|
|
||||||
EXPECT_EQ(PhysicalASTCBlock(0x0000000001FE000173ULL).ColorStartBit(), 17);
|
|
||||||
EXPECT_EQ(PhysicalASTCBlock(0x0000000001FE0005FFULL).ColorStartBit(), 17);
|
|
||||||
EXPECT_EQ(PhysicalASTCBlock(0x0000000001FE000108ULL).ColorStartBit(), 17);
|
|
||||||
|
|
||||||
// Multi-partition blocks start at bit 29
|
|
||||||
EXPECT_EQ(PhysicalASTCBlock(0x4000000000FFED44ULL).ColorStartBit(), 29);
|
|
||||||
EXPECT_EQ(PhysicalASTCBlock(0x4000000000AAAD44ULL).ColorStartBit(), 29);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
@@ -1,288 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
#include "src/decoder/quantization.h"
|
|
||||||
#include "src/decoder/integer_sequence_codec.h"
|
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace astc_codec {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
// Make sure that we never exceed the maximum range that we pass in.
|
|
||||||
TEST(QuantizationTest, TestQuantizeMaxRange) {
|
|
||||||
for (int i = kEndpointRangeMinValue; i < 256; ++i) {
|
|
||||||
EXPECT_LE(QuantizeCEValueToRange(255, i), i);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 1; i < kWeightRangeMaxValue; ++i) {
|
|
||||||
EXPECT_LE(QuantizeWeightToRange(64, i), i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that whenever we unquantize and requantize a value we get back
|
|
||||||
// what we started with.
|
|
||||||
TEST(QuantizationTest, TestReversibility) {
|
|
||||||
for (auto itr = ISERangeBegin(); itr != ISERangeEnd(); itr++) {
|
|
||||||
const int range = *itr;
|
|
||||||
if (range <= kWeightRangeMaxValue) {
|
|
||||||
for (int j = 0; j <= range; ++j) {
|
|
||||||
const int q = UnquantizeWeightFromRange(j, range);
|
|
||||||
EXPECT_EQ(QuantizeWeightToRange(q, range), j);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (range >= kEndpointRangeMinValue) {
|
|
||||||
for (int j = 0; j <= range; ++j) {
|
|
||||||
const int q = UnquantizeCEValueFromRange(j, range);
|
|
||||||
EXPECT_EQ(QuantizeCEValueToRange(q, range), j);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that whenever we quantize a non-maximal value it gets sent to the
|
|
||||||
// proper range
|
|
||||||
TEST(QuantizationTest, TestQuantizationRange) {
|
|
||||||
for (auto itr = ISERangeBegin(); itr != ISERangeEnd(); itr++) {
|
|
||||||
const int range = *itr;
|
|
||||||
if (range >= kEndpointRangeMinValue) {
|
|
||||||
EXPECT_LE(QuantizeCEValueToRange(0, range), range);
|
|
||||||
EXPECT_LE(QuantizeCEValueToRange(4, range), range);
|
|
||||||
EXPECT_LE(QuantizeCEValueToRange(15, range), range);
|
|
||||||
EXPECT_LE(QuantizeCEValueToRange(22, range), range);
|
|
||||||
EXPECT_LE(QuantizeCEValueToRange(66, range), range);
|
|
||||||
EXPECT_LE(QuantizeCEValueToRange(91, range), range);
|
|
||||||
EXPECT_LE(QuantizeCEValueToRange(126, range), range);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (range <= kWeightRangeMaxValue) {
|
|
||||||
EXPECT_LE(QuantizeWeightToRange(0, range), range);
|
|
||||||
EXPECT_LE(QuantizeWeightToRange(4, range), range);
|
|
||||||
EXPECT_LE(QuantizeWeightToRange(15, range), range);
|
|
||||||
EXPECT_LE(QuantizeWeightToRange(22, range), range);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that whenever we unquantize a value it remains within [0, 255]
|
|
||||||
TEST(QuantizationTest, TestUnquantizationRange) {
|
|
||||||
EXPECT_LT(UnquantizeCEValueFromRange(2, 7), 256);
|
|
||||||
EXPECT_LT(UnquantizeCEValueFromRange(7, 7), 256);
|
|
||||||
EXPECT_LT(UnquantizeCEValueFromRange(39, 63), 256);
|
|
||||||
EXPECT_LT(UnquantizeCEValueFromRange(66, 79), 256);
|
|
||||||
EXPECT_LT(UnquantizeCEValueFromRange(91, 191), 256);
|
|
||||||
EXPECT_LT(UnquantizeCEValueFromRange(126, 255), 256);
|
|
||||||
EXPECT_LT(UnquantizeCEValueFromRange(255, 255), 256);
|
|
||||||
|
|
||||||
EXPECT_LE(UnquantizeWeightFromRange(0, 1), 64);
|
|
||||||
EXPECT_LE(UnquantizeWeightFromRange(2, 7), 64);
|
|
||||||
EXPECT_LE(UnquantizeWeightFromRange(7, 7), 64);
|
|
||||||
EXPECT_LE(UnquantizeWeightFromRange(29, 31), 64);
|
|
||||||
}
|
|
||||||
|
|
||||||
// When we quantize a value, it should use the largest quantization range that
|
|
||||||
// does not exceed the desired range.
|
|
||||||
TEST(QuantizationTest, TestUpperBoundRanges) {
|
|
||||||
auto expected_range_itr = ISERangeBegin();
|
|
||||||
for (int desired_range = 1; desired_range < 256; ++desired_range) {
|
|
||||||
if (desired_range == *(expected_range_itr + 1)) {
|
|
||||||
++expected_range_itr;
|
|
||||||
}
|
|
||||||
const int expected_range = *expected_range_itr;
|
|
||||||
ASSERT_LE(expected_range, desired_range);
|
|
||||||
|
|
||||||
if (desired_range >= kEndpointRangeMinValue) {
|
|
||||||
EXPECT_EQ(QuantizeCEValueToRange(0, desired_range),
|
|
||||||
QuantizeCEValueToRange(0, expected_range));
|
|
||||||
|
|
||||||
EXPECT_EQ(QuantizeCEValueToRange(208, desired_range),
|
|
||||||
QuantizeCEValueToRange(208, expected_range));
|
|
||||||
|
|
||||||
EXPECT_EQ(QuantizeCEValueToRange(173, desired_range),
|
|
||||||
QuantizeCEValueToRange(173, expected_range));
|
|
||||||
|
|
||||||
EXPECT_EQ(QuantizeCEValueToRange(13, desired_range),
|
|
||||||
QuantizeCEValueToRange(13, expected_range));
|
|
||||||
|
|
||||||
EXPECT_EQ(QuantizeCEValueToRange(255, desired_range),
|
|
||||||
QuantizeCEValueToRange(255, expected_range));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (desired_range <= kWeightRangeMaxValue) {
|
|
||||||
EXPECT_EQ(QuantizeWeightToRange(0, desired_range),
|
|
||||||
QuantizeWeightToRange(0, expected_range));
|
|
||||||
|
|
||||||
EXPECT_EQ(QuantizeWeightToRange(63, desired_range),
|
|
||||||
QuantizeWeightToRange(63, expected_range));
|
|
||||||
|
|
||||||
EXPECT_EQ(QuantizeWeightToRange(12, desired_range),
|
|
||||||
QuantizeWeightToRange(12, expected_range));
|
|
||||||
|
|
||||||
EXPECT_EQ(QuantizeWeightToRange(23, desired_range),
|
|
||||||
QuantizeWeightToRange(23, expected_range));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that we covered all the possible ranges
|
|
||||||
ASSERT_EQ(std::next(expected_range_itr), ISERangeEnd());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that quantizing to the largest range is the identity function.
|
|
||||||
TEST(QuantizationTest, TestIdentity) {
|
|
||||||
for (int i = 0; i < 256; ++i) {
|
|
||||||
EXPECT_EQ(QuantizeCEValueToRange(i, 255), i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: This doesn't apply to weights since there's a weird hack to convert
|
|
||||||
// values from [0, 31] to [0, 64].
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that bit quantization is monotonic with respect to the input,
|
|
||||||
// since quantizing and dequantizing bits is a matter of truncation and bit
|
|
||||||
// replication
|
|
||||||
TEST(QuantizationTest, TestMonotonicBitPacking) {
|
|
||||||
for (int num_bits = 3; num_bits < 8; ++num_bits) {
|
|
||||||
const int range = (1 << num_bits) - 1;
|
|
||||||
int last_quant_val = -1;
|
|
||||||
for (int i = 0; i < 256; ++i) {
|
|
||||||
const int quant_val = QuantizeCEValueToRange(i, range);
|
|
||||||
EXPECT_LE(last_quant_val, quant_val);
|
|
||||||
last_quant_val = quant_val;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Also expect the last quantization val to be equal to the range
|
|
||||||
EXPECT_EQ(last_quant_val, range);
|
|
||||||
|
|
||||||
if (range <= kWeightRangeMaxValue) {
|
|
||||||
last_quant_val = -1;
|
|
||||||
for (int i = 0; i <= 64; ++i) {
|
|
||||||
const int quant_val = QuantizeWeightToRange(i, range);
|
|
||||||
EXPECT_LE(last_quant_val, quant_val);
|
|
||||||
last_quant_val = quant_val;
|
|
||||||
}
|
|
||||||
EXPECT_EQ(last_quant_val, range);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that bit quantization reflects that quantized values below the bit
|
|
||||||
// replication threshold get mapped to zero
|
|
||||||
TEST(QuantizationTest, TestSmallBitPacking) {
|
|
||||||
for (int num_bits = 1; num_bits <= 8; ++num_bits) {
|
|
||||||
const int range = (1 << num_bits) - 1;
|
|
||||||
|
|
||||||
// The largest number that should map to zero is one less than half of the
|
|
||||||
// smallest representation w.r.t. range. For example: if we have a range
|
|
||||||
// of 7, it means that we have 3 total bits abc for quantized values. If we
|
|
||||||
// unquantize to 8 bits, it means that our resulting value will be abcabcab.
|
|
||||||
// Hence, we map 000 to 0 and 001 to 0b00100100 = 36. The earliest value
|
|
||||||
// that should not map to zero with three bits is therefore 0b00001111 = 15.
|
|
||||||
// This ends up being (1 << (8 - 3 - 1)) - 1. We don't use 0b00011111 = 31
|
|
||||||
// because this would "round up" to 1 during quantization. This value is not
|
|
||||||
// necessarily the largest, but it is the largest that we can *guarantee*
|
|
||||||
// should map to zero.
|
|
||||||
|
|
||||||
if (range >= kEndpointRangeMinValue) {
|
|
||||||
constexpr int cev_bits = 8;
|
|
||||||
const int half_max_quant_bits = std::max(0, cev_bits - num_bits - 1);
|
|
||||||
const int largest_cev_to_zero = (1 << half_max_quant_bits) - 1;
|
|
||||||
EXPECT_EQ(QuantizeCEValueToRange(largest_cev_to_zero, range), 0)
|
|
||||||
<< " Largest CEV to zero: " << largest_cev_to_zero
|
|
||||||
<< " Range: " << range;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (range <= kWeightRangeMaxValue) {
|
|
||||||
constexpr int weight_bits = 6;
|
|
||||||
const int half_max_quant_bits = std::max(0, weight_bits - num_bits - 1);
|
|
||||||
const int largest_weight_to_zero = (1 << half_max_quant_bits) - 1;
|
|
||||||
EXPECT_EQ(QuantizeWeightToRange(largest_weight_to_zero, range), 0)
|
|
||||||
<< " Largest weight to zero: " << largest_weight_to_zero
|
|
||||||
<< " Range: " << range;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test specific quint and trit weight encodings with values that were obtained
|
|
||||||
// using the reference ASTC codec.
|
|
||||||
TEST(QuantizationTest, TestSpecificQuintTritPackings) {
|
|
||||||
std::vector<int> vals = { 4, 6, 4, 6, 7, 5, 7, 5 };
|
|
||||||
std::vector<int> quantized;
|
|
||||||
|
|
||||||
// Test a quint packing
|
|
||||||
std::transform(
|
|
||||||
vals.begin(), vals.end(), std::back_inserter(quantized),
|
|
||||||
std::bind(UnquantizeWeightFromRange, std::placeholders::_1, 9));
|
|
||||||
const std::vector<int> quintExpected = {14, 21, 14, 21, 43, 50, 43, 50 };
|
|
||||||
EXPECT_EQ(quantized, quintExpected);
|
|
||||||
|
|
||||||
// Test a trit packing
|
|
||||||
std::transform(
|
|
||||||
vals.begin(), vals.end(), quantized.begin(),
|
|
||||||
std::bind(UnquantizeWeightFromRange, std::placeholders::_1, 11));
|
|
||||||
const std::vector<int> tritExpected = { 5, 23, 5, 23, 41, 59, 41, 59 };
|
|
||||||
EXPECT_EQ(quantized, tritExpected);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that we properly die when we pass in values below the minimum
|
|
||||||
// allowed ranges for our quantization intervals.
|
|
||||||
TEST(QuantizationDeathTest, TestInvalidMinRange) {
|
|
||||||
for (int i = 0; i < kEndpointRangeMinValue; ++i) {
|
|
||||||
EXPECT_DEBUG_DEATH(QuantizeCEValueToRange(0, i), "");
|
|
||||||
EXPECT_DEBUG_DEATH(UnquantizeCEValueFromRange(0, i), "");
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPECT_DEBUG_DEATH(QuantizeWeightToRange(0, 0), "");
|
|
||||||
EXPECT_DEBUG_DEATH(UnquantizeWeightFromRange(0, 0), "");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that we properly die when we pass in bogus values.
|
|
||||||
TEST(QuantizationDeathTest, TestOutOfRange) {
|
|
||||||
EXPECT_DEBUG_DEATH(QuantizeCEValueToRange(-1, 10), "");
|
|
||||||
EXPECT_DEBUG_DEATH(QuantizeCEValueToRange(256, 7), "");
|
|
||||||
EXPECT_DEBUG_DEATH(QuantizeCEValueToRange(10000, 17), "");
|
|
||||||
|
|
||||||
EXPECT_DEBUG_DEATH(UnquantizeCEValueFromRange(-1, 10), "");
|
|
||||||
EXPECT_DEBUG_DEATH(UnquantizeCEValueFromRange(8, 7), "");
|
|
||||||
EXPECT_DEBUG_DEATH(UnquantizeCEValueFromRange(-1000, 17), "");
|
|
||||||
|
|
||||||
EXPECT_DEBUG_DEATH(QuantizeCEValueToRange(0, -7), "");
|
|
||||||
EXPECT_DEBUG_DEATH(UnquantizeCEValueFromRange(0, -17), "");
|
|
||||||
|
|
||||||
EXPECT_DEBUG_DEATH(QuantizeCEValueToRange(0, 257), "");
|
|
||||||
EXPECT_DEBUG_DEATH(UnquantizeCEValueFromRange(0, 256), "");
|
|
||||||
|
|
||||||
EXPECT_DEBUG_DEATH(QuantizeWeightToRange(-1, 10), "");
|
|
||||||
EXPECT_DEBUG_DEATH(QuantizeWeightToRange(256, 7), "");
|
|
||||||
EXPECT_DEBUG_DEATH(QuantizeWeightToRange(10000, 17), "");
|
|
||||||
|
|
||||||
EXPECT_DEBUG_DEATH(UnquantizeWeightFromRange(-1, 10), "");
|
|
||||||
EXPECT_DEBUG_DEATH(UnquantizeWeightFromRange(8, 7), "");
|
|
||||||
EXPECT_DEBUG_DEATH(UnquantizeWeightFromRange(-1000, 17), "");
|
|
||||||
|
|
||||||
EXPECT_DEBUG_DEATH(QuantizeWeightToRange(0, -7), "");
|
|
||||||
EXPECT_DEBUG_DEATH(UnquantizeWeightFromRange(0, -17), "");
|
|
||||||
|
|
||||||
EXPECT_DEBUG_DEATH(QuantizeWeightToRange(0, 32), "");
|
|
||||||
EXPECT_DEBUG_DEATH(UnquantizeWeightFromRange(0, 64), "");
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
} // namespace astc_codec
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
// 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.
|
|
||||||
|
|
||||||
#include "src/decoder/weight_infill.h"
|
|
||||||
#include "src/decoder/footprint.h"
|
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace astc_codec {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
// Make sure that the physical size of the bit representations for certain
|
|
||||||
// dimensions of weight grids matches our expectations
|
|
||||||
TEST(ASTCWeightInfillTest, TestGetBitCount) {
|
|
||||||
// Bit encodings
|
|
||||||
EXPECT_EQ(32, CountBitsForWeights(4, 4, 3));
|
|
||||||
EXPECT_EQ(48, CountBitsForWeights(4, 4, 7));
|
|
||||||
EXPECT_EQ(24, CountBitsForWeights(2, 4, 7));
|
|
||||||
EXPECT_EQ(8, CountBitsForWeights(2, 4, 1));
|
|
||||||
|
|
||||||
// Trit encodings
|
|
||||||
EXPECT_EQ(32, CountBitsForWeights(4, 5, 2));
|
|
||||||
EXPECT_EQ(26, CountBitsForWeights(4, 4, 2));
|
|
||||||
EXPECT_EQ(52, CountBitsForWeights(4, 5, 5));
|
|
||||||
EXPECT_EQ(42, CountBitsForWeights(4, 4, 5));
|
|
||||||
|
|
||||||
// Quint encodings
|
|
||||||
EXPECT_EQ(21, CountBitsForWeights(3, 3, 4));
|
|
||||||
EXPECT_EQ(38, CountBitsForWeights(4, 4, 4));
|
|
||||||
EXPECT_EQ(49, CountBitsForWeights(3, 7, 4));
|
|
||||||
EXPECT_EQ(52, CountBitsForWeights(4, 3, 19));
|
|
||||||
EXPECT_EQ(70, CountBitsForWeights(4, 4, 19));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that we bilerp our weights properly
|
|
||||||
TEST(ASTCWeightInfillTest, TestInfillBilerp) {
|
|
||||||
std::vector<int> weights = InfillWeights(
|
|
||||||
{{ 1, 3, 5, 3, 5, 7, 5, 7, 9 }}, Footprint::Get5x5(), 3, 3);
|
|
||||||
|
|
||||||
std::vector<int> expected_weights = {
|
|
||||||
1, 2, 3, 4, 5,
|
|
||||||
2, 3, 4, 5, 6,
|
|
||||||
3, 4, 5, 6, 7,
|
|
||||||
4, 5, 6, 7, 8,
|
|
||||||
5, 6, 7, 8, 9 };
|
|
||||||
|
|
||||||
ASSERT_EQ(weights.size(), expected_weights.size());
|
|
||||||
for (int i = 0; i < weights.size(); ++i) {
|
|
||||||
EXPECT_EQ(weights[i], expected_weights[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
} // namespace astc_codec
|
|
||||||
|
Before Width: | Height: | Size: 256 KiB |
|
Before Width: | Height: | Size: 256 KiB |
|
Before Width: | Height: | Size: 256 KiB |
|
Before Width: | Height: | Size: 256 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 189 KiB |
BIN
3rdparty/astc-codec/src/decoder/testdata/rgb_4x4.bmp
vendored
|
Before Width: | Height: | Size: 189 KiB |
BIN
3rdparty/astc-codec/src/decoder/testdata/rgb_5x4.bmp
vendored
|
Before Width: | Height: | Size: 189 KiB |
BIN
3rdparty/astc-codec/src/decoder/testdata/rgb_6x6.bmp
vendored
|
Before Width: | Height: | Size: 189 KiB |
BIN
3rdparty/astc-codec/src/decoder/testdata/rgb_8x8.bmp
vendored
|
Before Width: | Height: | Size: 189 KiB |
@@ -1,785 +0,0 @@
|
|||||||
// 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;
|
|
||||||
}
|
|
||||||
0
3rdparty/astc-codec/third_party/BUILD
vendored
39
3rdparty/astc-codec/third_party/honggfuzz.BUILD
vendored
@@ -1,39 +0,0 @@
|
|||||||
config_setting(
|
|
||||||
name = "opt",
|
|
||||||
values = {"compilation_mode": "opt"}
|
|
||||||
)
|
|
||||||
|
|
||||||
cc_library(
|
|
||||||
name = "honggfuzz",
|
|
||||||
srcs = glob([
|
|
||||||
"libhfuzz/*.c",
|
|
||||||
"libhfcommon/*.c",
|
|
||||||
],
|
|
||||||
exclude = ["libhfuzz/linux.c"],
|
|
||||||
) + select({
|
|
||||||
"@bazel_tools//src/conditions:darwin_x86_64": [],
|
|
||||||
"@bazel_tools//src/conditions:darwin": [],
|
|
||||||
"//conditions:default": ["libhfuzz/linux.c"],
|
|
||||||
}),
|
|
||||||
hdrs = glob([
|
|
||||||
"libhfuzz/*.h",
|
|
||||||
"libhfcommon/*.h",
|
|
||||||
"honggfuzz.h",
|
|
||||||
]),
|
|
||||||
copts = [
|
|
||||||
"-std=c11",
|
|
||||||
],
|
|
||||||
defines = select({
|
|
||||||
"@bazel_tools//src/conditions:darwin_x86_64": ["_HF_ARCH_DARWIN"],
|
|
||||||
"@bazel_tools//src/conditions:darwin": ["_HF_ARCH_DARWIN"],
|
|
||||||
"//conditions:default": ["_HF_ARCH_LINUX"],
|
|
||||||
}) + select({
|
|
||||||
":opt": [],
|
|
||||||
"//conditions:default": ["DEBUG=DEBUG"],
|
|
||||||
}) + [
|
|
||||||
"_GNU_SOURCE",
|
|
||||||
],
|
|
||||||
includes = ["."],
|
|
||||||
visibility = ["//visibility:public"],
|
|
||||||
linkstatic = 1
|
|
||||||
)
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# Configures builds for our CI environment.
|
|
||||||
|
|
||||||
# Print commands and exit on error.
|
|
||||||
set -ex
|
|
||||||
|
|
||||||
if [ "$KOKORO_BUILD_ID" ]; then
|
|
||||||
echo "Running job $KOKORO_JOB_NAME"
|
|
||||||
TARGET=`echo "$KOKORO_JOB_NAME" | awk -F "/" '{print $NF}'`
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! "$TARGET" ]; then
|
|
||||||
if [ "$1" ]; then
|
|
||||||
TARGET=$1
|
|
||||||
else
|
|
||||||
TARGET=release
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Building $TARGET target"
|
|
||||||
|
|
||||||
pushd `dirname $0`/../../.. > /dev/null
|
|
||||||
|
|
||||||
BUILD_RELEASE=
|
|
||||||
BUILD_DEBUG=
|
|
||||||
BUILD_CMAKE=
|
|
||||||
RUN_TESTS=
|
|
||||||
|
|
||||||
if [ "$TARGET" == "presubmit" ]; then
|
|
||||||
BUILD_DEBUG=1
|
|
||||||
BUILD_RELEASE=1
|
|
||||||
BUILD_CMAKE=1
|
|
||||||
RUN_TESTS=1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$TARGET" == "debug" ]; then
|
|
||||||
BUILD_DEBUG=1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$TARGET" == "release" ]; then
|
|
||||||
BUILD_RELEASE=1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$TARGET" == "continuous" ]; then
|
|
||||||
BUILD_DEBUG=1
|
|
||||||
BUILD_RELEASE=1
|
|
||||||
BUILD_CMAKE=1
|
|
||||||
RUN_TESTS=1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$BUILD_DEBUG" == "1" ]; then
|
|
||||||
echo "Starting debug build"
|
|
||||||
bazel build -c dbg //...
|
|
||||||
|
|
||||||
if [ "$RUN_TESTS" == "1" ]; then
|
|
||||||
bazel test -c dbg //...
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$BUILD_RELEASE" == "1" ]; then
|
|
||||||
echo "Starting release build"
|
|
||||||
bazel build -c opt //...
|
|
||||||
|
|
||||||
if [ "$RUN_TESTS" == "1" ]; then
|
|
||||||
bazel test -c opt //...
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$BUILD_CMAKE" == "1" ]; then
|
|
||||||
echo "Starting cmake build"
|
|
||||||
mkdir build
|
|
||||||
pushd build
|
|
||||||
cmake ..
|
|
||||||
make -j$((`nproc`+1))
|
|
||||||
popd
|
|
||||||
fi
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Format: //devtools/kokoro/config/proto/build.proto
|
|
||||||
|
|
||||||
build_file: "astc-codec/tools/build-ci/linux/build.sh"
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Format: //devtools/kokoro/config/proto/build.proto
|
|
||||||
|
|
||||||
build_file: "astc-codec/tools/build-ci/linux/build.sh"
|
|
||||||