blob: 2938d076a7141c0f309c75c01834a39b871cca43 [file]
// Copyright 2019 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.
// -----------------------------------------------------------------------------
//
// Tool for finding the best block layout.
//
// Author: Yannis Guyon (yguyon@google.com)
#include "src/enc/partitioning/partitioner_top_left.h"
#include <algorithm>
#include <cassert>
#include <cstdint>
#include "src/common/lossy/block_size.h"
#include "src/common/lossy/block_size_io.h"
#include "src/common/progress_watcher.h"
#include "src/utils/front_mgr.h"
#include "src/utils/utils.h"
#include "src/utils/vector.h"
#include "src/wp2/base.h"
namespace WP2 {
//------------------------------------------------------------------------------
WP2Status TopLeftBestPartitioner::GetBestPartition(
const ProgressRange& progress, VectorNoCtor<Block>* const blocks,
Vector_u32* const) {
std::fill(occupancy_.begin(), occupancy_.end(), false);
const uint32_t max_num_blocks = num_block_cols_ * num_block_rows_;
uint32_t max_num_blocks_left = max_num_blocks;
const ProgressScale block_progress(progress, 1. / max_num_blocks);
// Begin with the user-defined blocks, if any (debug).
WP2_CHECK_STATUS(RegisterForcedBlocks(*blocks, &max_num_blocks_left));
// Sort forced blocks by position for binary search below.
VectorNoCtor<Block> forced_blocks;
WP2_CHECK_ALLOC_OK(forced_blocks.resize(blocks->size()));
std::copy(blocks->begin(), blocks->end(), forced_blocks.begin());
std::sort(forced_blocks.begin(), forced_blocks.end());
// Use a front manager to easily browse the blocks.
FrontMgrLexico front_mgr;
WP2_CHECK_STATUS(front_mgr.Init(config_->partition_set,
config_->partition_snapping, src_->Y.w_,
src_->Y.h_));
while (!front_mgr.Done()) {
// TODO(yguyon): This is using a top-most first order. Find an order that
// maximizes top and left context instead.
const Block max_block = front_mgr.GetMaxFittingBlock();
// Binary search to find if 'max_block' is already one of the forced blocks.
const auto forced_block_it =
std::lower_bound(forced_blocks.begin(), forced_blocks.end(), max_block);
Block best_block;
if (forced_block_it != forced_blocks.end() &&
forced_block_it->x() == max_block.x() &&
forced_block_it->y() == max_block.y()) {
best_block = *forced_block_it;
WP2_CHECK_STATUS(block_progress.AdvanceBy(1.));
} else {
assert(max_num_blocks_left > 0);
WP2_CHECK_STATUS(GetBestSize(max_block, forced_blocks.size(),
block_progress, &best_block));
WP2_CHECK_REDUCED_STATUS(RegisterOrderForVDebug(
0, 0, best_block, blocks->size(), max_num_blocks));
WP2_CHECK_ALLOC_OK(blocks->push_back(best_block));
// The following instructions are done only for assertions/debug
// because the front manager should handle everything, except for
// the forced blocks but these are checked above.
assert((Occupy(best_block), true));
assert(max_num_blocks_left >= best_block.w() * best_block.h());
max_num_blocks_left -= best_block.w() * best_block.h();
}
assert(best_block.dim() != BLK_LAST);
WP2_CHECK_ALLOC_OK(front_mgr.UseSize(best_block.dim(), nullptr,
/*use_block=*/true));
WP2_CHECK_STATUS(score_func_->Use(best_block));
// Cover the whole block.
WP2_CHECK_STATUS(
block_progress.AdvanceBy(best_block.rect().GetArea() - 1.));
}
assert(max_num_blocks_left == 0);
return WP2_STATUS_OK;
}
WP2Status TopLeftBestPartitioner::GetBestSize(const Block& max_block,
uint32_t num_forced_blocks,
const ProgressRange& progress,
Block* const best_block) {
const ProgressScale size_progress(
progress, 1. / GetNumBlockSizes(config_->partition_set));
const BlockSize* const sizes = GetBlockSizes(config_->partition_set);
float best_score = 0.f;
// Test each size and keep the best score.
for (const BlockSize* size = sizes; *size != BLK_LAST; ++size) {
const Block block = Block(max_block.x(), max_block.y(), *size);
if (IsBlockValid(block, max_block, num_forced_blocks)) {
float score;
WP2_CHECK_STATUS(score_func_->ComputeScore(block, size_progress, &score));
const bool first_score = (size == sizes);
// Increasing sizes: keep the largest one if same score (rare).
if (first_score || score >= best_score) {
best_score = score;
*best_block = block;
}
} else {
WP2_CHECK_STATUS(size_progress.AdvanceBy(1.));
}
}
return WP2_STATUS_OK;
}
//------------------------------------------------------------------------------
} // namespace WP2