blob: a671b1bafe28e86d54c1545a8b35e46296894244 [file] [log] [blame]
// Copyright 2020 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
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// testing FrontMgr class
#include <algorithm>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <set>
#include <vector>
#include "include/helpers.h"
#include "src/utils/front_mgr.h"
#include "src/wp2/format_constants.h"
namespace WP2 {
namespace {
//------------------------------------------------------------------------------
void CreateData(uint32_t w, uint32_t h, bool snapped,
UniformIntDistribution* const gen,
VectorNoCtor<Block>* const blocks, bool dump = false) {
testutil::CreatePartition(w, h, snapped, gen, blocks);
std::vector<uint8_t> occupancy(w * h, 0x00);
uint8_t color = 0xff;
for (const Block& block : *blocks) {
const uint32_t px = block.x_pix();
const uint32_t py = block.y_pix();
const uint32_t dx = block.w_pix();
const uint32_t dy = block.h_pix();
// draw a rectangle, check we're not overlapping
for (uint32_t y = py; y < std::min(py + dy, h); ++y) {
for (uint32_t x = px; x < std::min(px + dx, w); ++x) {
EXPECT_EQ(occupancy[y * w + x], 0x00) << "Overlap check failed.";
// Draw top and left borders with a different color for each block.
occupancy[y * w + x] = (x == px || y == py) ? color : 0x01;
}
}
color += 17;
color |= 0x80; // Make sure it is not zero and bright enough.
}
// check we've covered all the area
ASSERT_TRUE(std::find(occupancy.begin(), occupancy.end(), 0x00) ==
occupancy.end())
<< "All the area should have been covered!";
if (dump) {
FILE* const f = fopen("/tmp/dump.ppm", "wb");
fprintf(f, "P5\n%d %d\n255\n", w, h);
for (uint8_t v : occupancy) fprintf(f, "%c", v);
fclose(f);
}
}
//------------------------------------------------------------------------------
enum FrontMgrType { kLexico, kMax, kArea };
FrontMgrDoubleOrderBase* GetMgr(FrontMgrType front_mgr_type,
FrontMgrLexico* const mgr_lex,
FrontMgrMax* const mgr_max,
FrontMgrArea* const mgr_area) {
if (front_mgr_type == kLexico) return mgr_lex;
if (front_mgr_type == kMax) return mgr_max;
if (front_mgr_type == kArea) return mgr_area;
return nullptr;
}
bool GetSnapped(FrontMgrType type) { return (type == kArea); }
constexpr uint32_t kAreaSize = kMaxBlockSizePix;
class TestFrontMgr : public testing::TestWithParam<
std::tuple<uint32_t, uint32_t, FrontMgrType>> {};
TEST_P(TestFrontMgr, LeftContext) {
const uint32_t seed = std::get<0>(GetParam());
const uint32_t num_iterations = std::get<1>(GetParam());
const FrontMgrType front_mgr_type = std::get<2>(GetParam());
UniformIntDistribution gen(seed);
const uint32_t kRangeMax = 100;
for (size_t n = 0; n < num_iterations; ++n) {
const uint32_t w = gen.Get(1u, kRangeMax);
const uint32_t h = gen.Get(1u, kRangeMax);
FrontMgrLexico mgr_lex;
FrontMgrMax mgr_max;
FrontMgrArea mgr_area(kAreaSize, kAreaSize);
VectorNoCtor<Block> blocks;
Vector_u16 size_order_indices;
FrontMgrDoubleOrderBase* const mgr =
GetMgr(front_mgr_type, &mgr_lex, &mgr_max, &mgr_area);
CreateData(w, h, GetSnapped(front_mgr_type), &gen, &blocks);
ASSERT_WP2_OK(mgr->Init(ALL_RECTS, GetSnapped(front_mgr_type), w, h));
ASSERT_WP2_OK(mgr->Sort(blocks, size_order_indices));
ASSERT_EQ(blocks.size(), size_order_indices.size());
// Check all indices are used only once.
ASSERT_EQ(blocks.size(), std::set<uint16_t>(size_order_indices.begin(),
size_order_indices.end())
.size());
// Go over all indices and make sure it works.
mgr->Clear();
for (uint16_t i : size_order_indices) {
const Block& block = blocks[i];
const Block max_block = mgr->GetMaxPossibleBlock();
ASSERT_GE(block.x(), max_block.x());
ASSERT_GE(block.y(), max_block.y());
ASSERT_LE(block.x() + block.w(), max_block.x() + max_block.w());
ASSERT_LE(block.y() + block.h(), max_block.y() + max_block.h());
Block block_tmp;
ASSERT_TRUE(mgr->TryGetNextBlock(block.dim(), &block_tmp));
ASSERT_EQ(block, block_tmp);
ASSERT_FALSE(mgr->Done());
ASSERT_TRUE(mgr->UseSize(block.dim(), &block_tmp));
ASSERT_EQ(block, block_tmp);
while (mgr->UseReady()) {
}
}
ASSERT_TRUE(mgr->Done());
}
}
// Make sure the deep copy is perfect.
TEST_P(TestFrontMgr, CopyFrom) {
const uint32_t seed = std::get<0>(GetParam());
const uint32_t num_iterations = std::get<1>(GetParam());
const FrontMgrType front_mgr_type = std::get<2>(GetParam());
UniformIntDistribution gen(seed);
const uint32_t kRangeMax = 100;
FrontMgrLexico mgr_lex_clone;
FrontMgrMax mgr_max_clone;
FrontMgrArea mgr_area_clone(kAreaSize, kAreaSize);
FrontMgrDoubleOrderBase* const mgr_clone =
GetMgr(front_mgr_type, &mgr_lex_clone, &mgr_max_clone, &mgr_area_clone);
for (size_t n = 0; n < num_iterations; ++n) {
const uint32_t w = gen.Get(1u, kRangeMax), h = gen.Get(1u, kRangeMax);
FrontMgrLexico mgr_lex;
FrontMgrMax mgr_max;
FrontMgrArea mgr_area(kAreaSize, kAreaSize);
FrontMgrDoubleOrderBase* const mgr =
GetMgr(front_mgr_type, &mgr_lex, &mgr_max, &mgr_area);
ASSERT_WP2_OK(mgr->Init(ALL_RECTS, GetSnapped(front_mgr_type), w, h));
VectorNoCtor<Block> blocks;
Vector_u16 size_order_indices;
CreateData(w, h, GetSnapped(front_mgr_type), &gen, &blocks);
ASSERT_WP2_OK(mgr->Sort(blocks, size_order_indices));
mgr->Clear(); // Sort() messes up the FrontMgr state so clear it.
int32_t num_blocks_before_cloning = gen.Get<int32_t>(1, blocks.size());
for (uint16_t i : size_order_indices) {
if (num_blocks_before_cloning == 0) {
ASSERT_WP2_OK((front_mgr_type == kLexico)
? mgr_lex_clone.CopyFrom(mgr_lex)
: (front_mgr_type == kMax)
? mgr_max_clone.CopyFrom(mgr_max)
: mgr_area_clone.CopyFrom(mgr_area));
}
const Block& block = blocks[i];
Block block_tmp;
ASSERT_TRUE(mgr->TryGetNextBlock(block.dim(), &block_tmp));
if (num_blocks_before_cloning <= 0) {
// The clone was copied. Verify that it returns the same values as the
// original 'map' and apply the same modifications to it from now on.
Block block_tmp_clone;
ASSERT_TRUE(mgr_clone->TryGetNextBlock(block.dim(), &block_tmp_clone));
ASSERT_EQ(block_tmp, block_tmp_clone);
ASSERT_EQ(mgr->Done(), mgr_clone->Done());
ASSERT_TRUE(mgr_clone->UseSize(block.dim(), &block_tmp_clone));
while (mgr_clone->UseReady()) {
}
}
ASSERT_TRUE(mgr->UseSize(block.dim(), &block_tmp));
while (mgr->UseReady()) {
}
--num_blocks_before_cloning;
}
ASSERT_TRUE(mgr->Done());
ASSERT_TRUE(mgr_clone->Done());
}
}
INSTANTIATE_TEST_SUITE_P(
TestFrontMgrInstanciation, TestFrontMgr,
testing::Combine(
/*seed=*/testing::Values(37, 42),
/*num_iterations=*/testing::Values(7),
/*front_mgr_type=*/testing::Values(kLexico, kMax, kArea)));
//------------------------------------------------------------------------------
TEST(TestFrontMgr, Area) {
FrontMgrArea mgr(kAreaSize, kAreaSize);
ASSERT_WP2_OK(mgr.Init(ALL_RECTS, /*snapped=*/true, 39, 64));
const Block blocks[] = {{0, 0, BLK_16x16}, {4, 0, BLK_16x16}, // First area
{0, 4, BLK_16x16}, {4, 4, BLK_16x16},
{8, 0, BLK_8x4}, {8, 1, BLK_8x4}, // Second area
{8, 2, BLK_8x8}, {8, 4, BLK_8x16},
{0, 8, BLK_32x32}, // Third area
{8, 8, BLK_8x32}}; // Fourth area
for (const Block& block : blocks) {
ASSERT_FALSE(mgr.Done());
Block next_block;
ASSERT_TRUE(mgr.UseSize(block.dim(), &next_block, /*use_block=*/true));
ASSERT_EQ(block, next_block);
}
ASSERT_TRUE(mgr.Done());
}
TEST(TestFrontMgr, Comp) {
const FrontMgrArea::Comp comp(kMaxTileSize / kMinBlockSizePix, kMaxBlockSize,
kMaxBlockSize);
std::vector<Block> blocks = {{4, 0, BLK_16x8}, {8, 0, BLK_16x8},
{12, 0, BLK_16x4}, {0, 4, BLK_16x4},
{0, 0, BLK_16x4}, {12, 4, BLK_16x4}};
std::sort(blocks.begin(), blocks.end(), comp);
// Expected block order: aaaabbbbddddeeee
// ccccbbbbddddffff
EXPECT_THAT(blocks, testing::ElementsAre(
Block{0, 0, BLK_16x4}, Block{4, 0, BLK_16x8},
Block{0, 4, BLK_16x4}, Block{8, 0, BLK_16x8},
Block{12, 0, BLK_16x4}, Block{12, 4, BLK_16x4}));
// Expects e then f.
auto it = std::lower_bound(blocks.begin(), blocks.end(),
Block{12, 0, BLK_32x32}, comp);
EXPECT_EQ(*it, Block(12, 0, BLK_16x4));
EXPECT_EQ(*(++it), Block(12, 4, BLK_16x4));
}
//------------------------------------------------------------------------------
} // namespace
} // namespace WP2