blob: 9682b123e7bf6c74de236944606503185dc3f221 [file] [log] [blame]
// 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.
// -----------------------------------------------------------------------------
//
// Functions related to tile decoding.
//
// Author: Skal (pascal.massimino@gmail.com)
#include "src/dec/tile_dec.h"
#include <algorithm>
#include <cstddef>
#include "src/common/global_params.h"
#include "src/common/lossy/block.h"
#include "src/common/lossy/block_size.h"
#include "src/common/vdebug.h"
#include "src/dec/wp2_dec_i.h"
#include "src/dsp/math.h"
#include "src/wp2/base.h"
#include "src/wp2/format_constants.h"
#include "src/utils/utils.h"
namespace WP2 {
//------------------------------------------------------------------------------
void Tile::Clear() {
chunk_size_is_known = false;
chunk_size = 0;
input = nullptr;
private_input.Reset();
data_handle = DataSource::DataHandle();
rect = {};
output_is_yuv = false;
// Output is a view, no memory dealloc/realloc happens.
assert(rgb_output.IsEmpty() || rgb_output.IsView());
rgb_output.Deallocate();
assert(yuv_output.IsEmpty() || yuv_output.IsView());
yuv_output.Clear();
num_decoded_rows = 0;
block_map.Clear();
row_progress = ProgressScale();
}
//------------------------------------------------------------------------------
const Tile& TilesLayout::GetTileAt(uint32_t x, uint32_t y) const {
return tiles[(y / tile_height) * num_tiles_x + x / tile_width];
}
//------------------------------------------------------------------------------
// TileDecoder definitions.
WP2Status TileDecoder::TryAssignNextTile() {
tile_ = nullptr;
if (self_assigning_) {
// Get first unassigned tile, and increment the counter if valid.
WP2_CHECK_STATUS(tiles_layout_->assignment_lock.Acquire());
uint32_t tile_index = tiles_layout_->first_unassigned_tile_index;
if (tile_index < tiles_layout_->num_assignable_tiles) {
tile_ = &tiles_layout_->tiles[tile_index];
++tiles_layout_->first_unassigned_tile_index;
}
tiles_layout_->assignment_lock.Release();
}
return WP2_STATUS_OK;
}
WP2Status TileDecoder::Execute() {
while (tile_ != nullptr) {
const uint32_t max_num_bytes =
GetTileMaxNumBytes(features_->rgb_bit_depth, *gparams_, tile_->rect);
if (tile_->chunk_size == max_num_bytes) {
// Raw samples are expected instead of the regular ANS decoding.
WP2_CHECK_STATUS(BypassTileDec());
} else if (gparams_->type_ == GlobalParams::GP_AV1) {
DataView chunk = {nullptr, tile_->chunk_size};
WP2_CHECK_OK(tile_->input->TryReadNext(tile_->chunk_size, &chunk.bytes),
WP2_STATUS_NOT_ENOUGH_DATA);
WP2_CHECK_STATUS(Av1Decode(*features_, *config_, chunk, tile_));
} else {
ANSDec dec(tile_->input);
bool is_lossless;
{
ANSDebugPrefix prefix(&dec, "GlobalHeader");
is_lossless = gparams_->type_ == GlobalParams::GP_LOSSLESS ||
(gparams_->type_ == GlobalParams::GP_BOTH &&
dec.ReadBool("use_lossless"));
}
WP2_CHECK_STATUS(dec.GetStatus());
WP2_CHECK_STATUS(
is_lossless
? LosslessDecode(*features_, *config_, *gparams_, &dec, tile_)
: LossyDecode(*features_, *config_, tiles_layout_, &dec, tile_));
#if defined(WP2_BITTRACE)
if (config_->info != nullptr) {
WP2_CHECK_STATUS(tiles_layout_->assignment_lock.Acquire());
for (const auto& it : dec.GetBitTraces()) {
config_->info->bit_traces[it.first].bits += it.second.bits;
config_->info->bit_traces[it.first].num_occurrences +=
it.second.num_occurrences;
config_->info->bit_traces[it.first].type = it.second.type;
for (const auto& p : it.second.histo) {
config_->info->bit_traces[it.first].histo[p.first] += p.second;
}
}
tiles_layout_->assignment_lock.Release();
}
#endif
}
#if !defined(WP2_REDUCE_BINARY_SIZE)
if (VDMatch(*config_, "blocks/partition")) tile_->Draw(*config_);
#endif // WP2_REDUCE_BINARY_SIZE
WP2_CHECK_STATUS(TryAssignNextTile());
}
return WP2_STATUS_OK;
}
//------------------------------------------------------------------------------
WP2Status SetupWorkers(const BitstreamFeatures& features,
const DecoderConfig& config,
TilesLayout* const tiles_layout,
Vector<TileDecoder>* const workers) {
WP2_CHECK_OK(config.thread_level <= 2147483647u,
WP2_STATUS_INVALID_CONFIGURATION);
// The number of workers is limited by the number of threads and tiles.
const uint32_t num_workers =
std::min(config.thread_level + 1, (uint32_t)tiles_layout->tiles.size());
WP2_CHECK_ALLOC_OK(workers->resize(num_workers));
for (TileDecoder& worker : *workers) {
worker.features_ = &features;
worker.config_ = &config;
worker.gparams_ = tiles_layout->gparams;
worker.tiles_layout_ = tiles_layout;
worker.self_assigning_ = true;
WP2_CHECK_STATUS(worker.TryAssignNextTile());
}
return WP2_STATUS_OK;
}
//------------------------------------------------------------------------------
uint32_t GetNumTiles(uint32_t frame_width, uint32_t frame_height,
uint32_t tile_width, uint32_t tile_height,
uint32_t* const num_tiles_x, uint32_t* const num_tiles_y) {
const uint32_t num_x = DivCeil(frame_width, tile_width);
const uint32_t num_y = DivCeil(frame_height, tile_height);
if (num_tiles_x != nullptr) *num_tiles_x = num_x;
if (num_tiles_y != nullptr) *num_tiles_y = num_y;
return num_x * num_y;
}
Rectangle GetTileRect(uint32_t frame_width, uint32_t frame_height,
uint32_t tile_width, uint32_t tile_height,
uint32_t tile_index) {
uint32_t num_tiles_x;
if (tile_index >= GetNumTiles(frame_width, frame_height, tile_width,
tile_height, &num_tiles_x)) {
assert(false);
}
const uint32_t tile_x = (tile_index % num_tiles_x) * tile_width;
const uint32_t tile_y = (tile_index / num_tiles_x) * tile_height;
return {tile_x, tile_y, std::min(frame_width - tile_x, tile_width),
std::min(frame_height - tile_y, tile_height)};
}
WP2Status GetTilesLayout(uint32_t frame_width, uint32_t frame_height,
uint32_t tile_width, uint32_t tile_height,
const ProgressRange& progress,
ArgbBuffer* const rgb_output,
YUVPlane* const yuv_output,
TilesLayout* const tiles_layout) {
WP2_CHECK_OK(frame_width > 0 && frame_height > 0, WP2_STATUS_BITSTREAM_ERROR);
WP2_CHECK_OK(frame_width <= kImageDimMax && frame_height <= kImageDimMax,
WP2_STATUS_BITSTREAM_ERROR);
tiles_layout->tile_width = tile_width;
tiles_layout->tile_height = tile_height;
const uint32_t num_tiles =
GetNumTiles(frame_width, frame_height, tile_width, tile_height,
&tiles_layout->num_tiles_x, &tiles_layout->num_tiles_y);
WP2_CHECK_ALLOC_OK(tiles_layout->tiles.resize(num_tiles));
const uint32_t num_pixels = frame_width * frame_height;
for (uint32_t tile_index = 0; tile_index < num_tiles; ++tile_index) {
Tile& tile = tiles_layout->tiles[tile_index];
tile.Clear();
tile.rect = GetTileRect(frame_width, frame_height, tile_width, tile_height,
tile_index);
const double per_tile = 1. * tile.rect.GetArea() / num_pixels;
const double per_row = 1. / tile.rect.height;
tile.row_progress = ProgressScale(progress, per_tile * per_row);
// Set views on RGB and YUV output buffers, both can be used.
if (!rgb_output->IsEmpty()) {
assert(rgb_output->width() == frame_width &&
rgb_output->height() == frame_height);
WP2_CHECK_STATUS(tile.rgb_output.SetFormat(rgb_output->format()));
WP2_CHECK_STATUS(tile.rgb_output.SetView(*rgb_output, tile.rect));
tile.output_is_yuv = false;
}
if (!yuv_output->IsEmpty()) {
assert(yuv_output->Y.w_ == Pad(frame_width, kPredWidth));
assert(yuv_output->Y.h_ == Pad(frame_height, kPredWidth));
WP2_CHECK_STATUS(tile.yuv_output.SetView(
*yuv_output,
{tile.rect.x, tile.rect.y, Pad(tile.rect.width, kPredWidth),
Pad(tile.rect.height, kPredWidth)}));
tile.output_is_yuv = true; // Overridden in Lossy/LosslessDecode() anyway
}
}
tiles_layout->num_assignable_tiles = 0;
tiles_layout->first_unassigned_tile_index = 0;
tiles_layout->num_decoded_tiles = 0;
return WP2_STATUS_OK;
}
uint32_t GetTileMaxNumBytes(uint32_t rgb_bit_depth, const GlobalParams& gparams,
const Rectangle& tile_rect) {
#if defined(WP2_ENC_DEC_MATCH)
(void)rgb_bit_depth, (void)gparams, (void)tile_rect;
// Make sure it does not fall back to raw pixel coding in CodeTiles().
return kMaxChunkSize;
#else
const bool is_rgb = (gparams.type_ == GlobalParams::GP_LOSSLESS ||
gparams.type_ == GlobalParams::GP_AV1);
const uint32_t channel_bits =
is_rgb ? rgb_bit_depth : gparams.transf_.GetYuvDepth().num_bits;
const uint32_t num_pixels = tile_rect.width * tile_rect.height;
const uint32_t alpha_bits = gparams.has_alpha_ ? kAlphaBits : 0;
const uint32_t max_num_bits = (alpha_bits + 3 * channel_bits) * num_pixels;
return DivCeil(max_num_bits, 8);
#endif // defined(WP2_ENC_DEC_MATCH)
}
//------------------------------------------------------------------------------
// Reads the global params and then reads each tile and skips them.
WP2Status SkipGlobalParamsAndTiles(DataSource* const data_source,
const BitstreamFeatures& features,
const Rectangle& window) {
GlobalParams gparams;
WP2_CHECK_STATUS(
DecodeGLBL(data_source, DecoderConfig::kDefault, features, &gparams));
WP2_CHECK_STATUS(SkipTiles(gparams, data_source, features, window));
return WP2_STATUS_OK;
}
// Reads the size of each tile and skips them.
WP2Status SkipTiles(const GlobalParams& gparams, DataSource* const data_source,
const BitstreamFeatures& features,
const Rectangle& window) {
const uint32_t num_tiles = GetNumTiles(
window.width, window.height, features.tile_width, features.tile_height);
for (uint32_t tile_index = 0; tile_index < num_tiles; ++tile_index) {
const Rectangle& tile_rect =
GetTileRect(window.width, window.height, features.tile_width,
features.tile_height, tile_index);
uint32_t tile_chunk_size;
WP2_CHECK_STATUS(DecodeTileChunkSize(features.rgb_bit_depth, gparams,
tile_rect, data_source,
&tile_chunk_size));
data_source->MarkNumBytesAsRead(tile_chunk_size);
}
return WP2_STATUS_OK;
}
//------------------------------------------------------------------------------
} // namespace WP2