blob: 4c9e1dc1c6499cd08d21b9dfdb6390c1fb3bc8e9 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/gpu/windows/d3d11_av1_accelerator.h"
#include <windows.h>
#include <numeric>
#include <string>
#include <utility>
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_functions.h"
#include "media/gpu/av1_picture.h"
#include "media/gpu/codec_picture.h"
#include "media/gpu/windows/d3d11_picture_buffer.h"
// These are from <dxva.h> in a newer SDK than the one Chrome ships with. They
// should be deleted once Chrome switches to the updated SDK; they have been
// copied from: https://www.microsoft.com/en-us/download/details.aspx?id=101577
#pragma pack(push, 1)
typedef struct _DXVA_PicEntry_AV1 {
UINT width;
UINT height;
// Global motion parameters
INT wmmat[6];
union {
struct {
UCHAR wminvalid : 1;
UCHAR wmtype : 2;
UCHAR Reserved : 5;
};
UCHAR wGlobalMotionFlags;
};
UCHAR Index;
USHORT Reserved16Bits;
} DXVA_PicEntry_AV1, *LPDXVA_PicEntry_AV1;
/* AV1 picture parameters structure */
typedef struct _DXVA_PicParams_AV1 {
UINT width;
UINT height;
UINT max_width;
UINT max_height;
UCHAR CurrPicTextureIndex;
UCHAR superres_denom;
UCHAR bitdepth;
UCHAR seq_profile;
// Tiles:
struct {
UCHAR cols;
UCHAR rows;
USHORT context_update_id;
USHORT widths[64];
USHORT heights[64];
} tiles;
// Coding Tools
union {
struct {
UINT use_128x128_superblock : 1;
UINT intra_edge_filter : 1;
UINT interintra_compound : 1;
UINT masked_compound : 1;
UINT warped_motion : 1;
UINT dual_filter : 1;
UINT jnt_comp : 1;
UINT screen_content_tools : 1;
UINT integer_mv : 1;
UINT cdef : 1;
UINT restoration : 1;
UINT film_grain : 1;
UINT intrabc : 1;
UINT high_precision_mv : 1;
UINT switchable_motion_mode : 1;
UINT filter_intra : 1;
UINT disable_frame_end_update_cdf : 1;
UINT disable_cdf_update : 1;
UINT reference_mode : 1;
UINT skip_mode : 1;
UINT reduced_tx_set : 1;
UINT superres : 1;
UINT tx_mode : 2;
UINT use_ref_frame_mvs : 1;
UINT enable_ref_frame_mvs : 1;
UINT reference_frame_update : 1;
UINT Reserved : 5;
};
UINT32 CodingParamToolFlags;
} coding;
// Format & Picture Info flags
union {
struct {
UCHAR frame_type : 2;
UCHAR show_frame : 1;
UCHAR showable_frame : 1;
UCHAR subsampling_x : 1;
UCHAR subsampling_y : 1;
UCHAR mono_chrome : 1;
UCHAR Reserved : 1;
};
UCHAR FormatAndPictureInfoFlags;
} format;
// References
UCHAR primary_ref_frame;
UCHAR order_hint;
UCHAR order_hint_bits;
DXVA_PicEntry_AV1 frame_refs[7];
UCHAR RefFrameMapTextureIndex[8];
// Loop filter parameters
struct {
UCHAR filter_level[2];
UCHAR filter_level_u;
UCHAR filter_level_v;
UCHAR sharpness_level;
union {
struct {
UCHAR mode_ref_delta_enabled : 1;
UCHAR mode_ref_delta_update : 1;
UCHAR delta_lf_multi : 1;
UCHAR delta_lf_present : 1;
UCHAR Reserved : 4;
};
UCHAR ControlFlags;
} DUMMYUNIONNAME;
CHAR ref_deltas[8];
CHAR mode_deltas[2];
UCHAR delta_lf_res;
UCHAR frame_restoration_type[3];
USHORT log2_restoration_unit_size[3];
UINT16 Reserved16Bits;
} loop_filter;
// Quantization
struct {
union {
struct {
UCHAR delta_q_present : 1;
UCHAR delta_q_res : 2;
UCHAR Reserved : 5;
};
UCHAR ControlFlags;
} DUMMYUNIONNAME;
UCHAR base_qindex;
CHAR y_dc_delta_q;
CHAR u_dc_delta_q;
CHAR v_dc_delta_q;
CHAR u_ac_delta_q;
CHAR v_ac_delta_q;
// using_qmatrix:
UCHAR qm_y;
UCHAR qm_u;
UCHAR qm_v;
UINT16 Reserved16Bits;
} quantization;
// Cdef parameters
struct {
union {
struct {
UCHAR damping : 2;
UCHAR bits : 2;
UCHAR Reserved : 4;
};
UCHAR ControlFlags;
} DUMMYUNIONNAME;
union {
struct {
UCHAR primary : 6;
UCHAR secondary : 2;
};
UCHAR combined;
} y_strengths[8];
union {
struct {
UCHAR primary : 6;
UCHAR secondary : 2;
};
UCHAR combined;
} uv_strengths[8];
} cdef;
UCHAR interp_filter;
// Segmentation
struct {
union {
struct {
UCHAR enabled : 1;
UCHAR update_map : 1;
UCHAR update_data : 1;
UCHAR temporal_update : 1;
UCHAR Reserved : 4;
};
UCHAR ControlFlags;
} DUMMYUNIONNAME;
UCHAR Reserved24Bits[3];
union {
struct {
UCHAR alt_q : 1;
UCHAR alt_lf_y_v : 1;
UCHAR alt_lf_y_h : 1;
UCHAR alt_lf_u : 1;
UCHAR alt_lf_v : 1;
UCHAR ref_frame : 1;
UCHAR skip : 1;
UCHAR globalmv : 1;
};
UCHAR mask;
} feature_mask[8];
SHORT feature_data[8][8];
} segmentation;
struct {
union {
struct {
USHORT apply_grain : 1;
USHORT scaling_shift_minus8 : 2;
USHORT chroma_scaling_from_luma : 1;
USHORT ar_coeff_lag : 2;
USHORT ar_coeff_shift_minus6 : 2;
USHORT grain_scale_shift : 2;
USHORT overlap_flag : 1;
USHORT clip_to_restricted_range : 1;
USHORT matrix_coeff_is_identity : 1;
USHORT Reserved : 3;
};
USHORT ControlFlags;
} DUMMYUNIONNAME;
USHORT grain_seed;
UCHAR scaling_points_y[14][2];
UCHAR num_y_points;
UCHAR scaling_points_cb[10][2];
UCHAR num_cb_points;
UCHAR scaling_points_cr[10][2];
UCHAR num_cr_points;
UCHAR ar_coeffs_y[24];
UCHAR ar_coeffs_cb[25];
UCHAR ar_coeffs_cr[25];
UCHAR cb_mult;
UCHAR cb_luma_mult;
UCHAR cr_mult;
UCHAR cr_luma_mult;
UCHAR Reserved8Bits;
SHORT cb_offset;
SHORT cr_offset;
} film_grain;
UINT Reserved32Bits;
UINT StatusReportFeedbackNumber;
} DXVA_PicParams_AV1, *LPDXVA_PicParams_AV1;
typedef struct _DXVA_Tile_AV1 {
UINT DataOffset;
UINT DataSize;
USHORT row;
USHORT column;
USHORT Reserved16Bits;
UCHAR anchor_frame;
UCHAR Reserved8Bits;
} DXVA_Tile_AV1, *LPDXVA_Tile_AV1;
#pragma pack(pop)
namespace media {
using DecodeStatus = AV1Decoder::AV1Accelerator::Status;
class D3D11AV1Picture : public AV1Picture {
public:
explicit D3D11AV1Picture(D3D11PictureBuffer* d3d11_picture,
D3D11VideoDecoderClient* client,
bool apply_grain)
: picture_buffer_(d3d11_picture),
client_(client),
apply_grain_(apply_grain),
picture_index_(d3d11_picture->picture_index()) {
picture_buffer_->set_in_picture_use(true);
}
bool apply_grain() const { return apply_grain_; }
D3D11PictureBuffer* picture_buffer() const { return picture_buffer_; }
protected:
~D3D11AV1Picture() override { picture_buffer_->set_in_picture_use(false); }
private:
scoped_refptr<AV1Picture> CreateDuplicate() override {
// We've already sent off the base frame for rendering, so we can just stamp
// |picture_buffer_| with the updated timestamp.
client_->UpdateTimestamp(picture_buffer_);
return this;
}
D3D11PictureBuffer* const picture_buffer_;
D3D11VideoDecoderClient* const client_;
const bool apply_grain_;
const size_t picture_index_;
};
class D3D11AV1Accelerator::ScopedDecoderBuffer {
public:
ScopedDecoderBuffer(MediaLog* media_log,
VideoContextWrapper* context,
ID3D11VideoDecoder* decoder,
D3D11_VIDEO_DECODER_BUFFER_TYPE type)
: media_log_(media_log),
context_(context),
decoder_(decoder),
type_(type) {
UINT size;
uint8_t* buffer;
driver_call_result_ = context_->GetDecoderBuffer(
decoder_, type_, &size, reinterpret_cast<void**>(&buffer));
if (FAILED(driver_call_result_)) {
MEDIA_LOG(ERROR, media_log_)
<< "ScopedDecoderBuffer(" << type_
<< ")=" << logging::SystemErrorCodeToString(driver_call_result_);
return;
}
buffer_ = base::span<uint8_t>(buffer, size);
}
ScopedDecoderBuffer(ScopedDecoderBuffer&& o)
: media_log_(o.media_log_),
context_(o.context_),
decoder_(o.decoder_),
type_(o.type_),
buffer_(std::move(o.buffer_)) {
DCHECK(o.buffer_.empty());
}
~ScopedDecoderBuffer() { Commit(); }
ScopedDecoderBuffer(const ScopedDecoderBuffer&) = delete;
ScopedDecoderBuffer& operator=(const ScopedDecoderBuffer&) = delete;
void Commit() {
if (buffer_.empty())
return;
driver_call_result_ = context_->ReleaseDecoderBuffer(decoder_, type_);
if (FAILED(driver_call_result_)) {
MEDIA_LOG(ERROR, media_log_)
<< "~ScopedDecoderBuffer(" << type_
<< ")=" << logging::SystemErrorCodeToString(driver_call_result_);
}
buffer_ = base::span<uint8_t>();
}
bool empty() const { return buffer_.empty(); }
uint8_t* data() const { return buffer_.data(); }
size_t size() const { return buffer_.size(); }
HRESULT error() const { return driver_call_result_; }
private:
MediaLog* const media_log_;
VideoContextWrapper* const context_;
ID3D11VideoDecoder* const decoder_;
const D3D11_VIDEO_DECODER_BUFFER_TYPE type_;
base::span<uint8_t> buffer_;
HRESULT driver_call_result_ = S_OK;
};
D3D11AV1Accelerator::D3D11AV1Accelerator(
D3D11VideoDecoderClient* client,
MediaLog* media_log,
ComD3D11VideoDevice video_device,
std::unique_ptr<VideoContextWrapper> video_context)
: client_(client),
media_log_(media_log->Clone()),
video_device_(std::move(video_device)),
video_context_(std::move(video_context)) {
DCHECK(client);
DCHECK(media_log_);
client->SetDecoderCB(base::BindRepeating(
&D3D11AV1Accelerator::SetVideoDecoder, base::Unretained(this)));
}
D3D11AV1Accelerator::~D3D11AV1Accelerator() {}
void D3D11AV1Accelerator::RecordFailure(const std::string& fail_type,
media::Status error) {
RecordFailure(fail_type, error.message(), error.code());
}
void D3D11AV1Accelerator::RecordFailure(const std::string& fail_type,
const std::string& message,
StatusCode reason) {
MEDIA_LOG(ERROR, media_log_)
<< "DX11AV1Failure(" << fail_type << ")=" << message;
base::UmaHistogramSparse("Media.D3D11.AV1Status", static_cast<int>(reason));
}
scoped_refptr<AV1Picture> D3D11AV1Accelerator::CreateAV1Picture(
bool apply_grain) {
D3D11PictureBuffer* picture_buffer = client_->GetPicture();
return picture_buffer ? base::MakeRefCounted<D3D11AV1Picture>(
picture_buffer, client_, apply_grain)
: nullptr;
}
D3D11AV1Accelerator::ScopedDecoderBuffer D3D11AV1Accelerator::GetBuffer(
D3D11_VIDEO_DECODER_BUFFER_TYPE type) {
return ScopedDecoderBuffer(media_log_.get(), video_context_.get(),
video_decoder_.Get(), type);
}
bool D3D11AV1Accelerator::SubmitDecoderBuffer(
const DXVA_PicParams_AV1& pic_params,
const libgav1::Vector<libgav1::TileBuffer>& tile_buffers) {
// Buffer #1 - AV1 specific picture parameters.
auto params_buffer = GetBuffer(D3D11_VIDEO_DECODER_BUFFER_PICTURE_PARAMETERS);
if (params_buffer.empty() || params_buffer.size() < sizeof(pic_params)) {
RecordFailure("SubmitDecoderBuffers",
logging::SystemErrorCodeToString(params_buffer.error()),
StatusCode::kGetPicParamBufferFailed);
return false;
}
memcpy(params_buffer.data(), &pic_params, sizeof(pic_params));
// Buffer #2 - Slice control data.
const auto tile_size = sizeof(DXVA_Tile_AV1) * tile_buffers.size();
auto tile_buffer = GetBuffer(D3D11_VIDEO_DECODER_BUFFER_SLICE_CONTROL);
if (tile_buffer.empty() || tile_buffer.size() < tile_size) {
RecordFailure("SubmitDecoderBuffers",
logging::SystemErrorCodeToString(tile_buffer.error()),
StatusCode::kGetSliceControlBufferFailed);
return false;
}
auto* tiles = reinterpret_cast<DXVA_Tile_AV1*>(tile_buffer.data());
// Buffer #3 - Tile buffer bitstream data.
const size_t bitstream_size = std::accumulate(
tile_buffers.begin(), tile_buffers.end(), 0,
[](size_t acc, const auto& buffer) { return acc + buffer.size; });
auto bitstream_buffer = GetBuffer(D3D11_VIDEO_DECODER_BUFFER_BITSTREAM);
if (bitstream_buffer.empty() || bitstream_buffer.size() < bitstream_size) {
RecordFailure("SubmitDecoderBuffers",
logging::SystemErrorCodeToString(bitstream_buffer.error()),
StatusCode::kGetBitstreamBufferFailed);
return false;
}
size_t tile_offset = 0;
for (size_t i = 0; i < tile_buffers.size(); ++i) {
const auto& tile = tile_buffers[i];
tiles[i].DataOffset = tile_offset;
tiles[i].DataSize = tile.size;
tiles[i].row = i / pic_params.tiles.cols;
tiles[i].column = i % pic_params.tiles.cols;
tiles[i].anchor_frame = 0xFF;
memcpy(bitstream_buffer.data() + tile_offset, tile.data, tile.size);
tile_offset += tile.size;
}
// Commit the buffers we prepared above.
params_buffer.Commit();
tile_buffer.Commit();
bitstream_buffer.Commit();
constexpr int kBuffersCount = 3;
VideoContextWrapper::VideoBufferWrapper buffers[kBuffersCount] = {};
buffers[0].BufferType = D3D11_VIDEO_DECODER_BUFFER_PICTURE_PARAMETERS;
buffers[0].DataSize = sizeof(pic_params);
buffers[1].BufferType = D3D11_VIDEO_DECODER_BUFFER_SLICE_CONTROL;
buffers[1].DataSize = tile_size;
buffers[2].BufferType = D3D11_VIDEO_DECODER_BUFFER_BITSTREAM;
buffers[2].DataSize = bitstream_size;
const auto hr = video_context_->SubmitDecoderBuffers(video_decoder_.Get(),
kBuffersCount, buffers);
if (FAILED(hr)) {
RecordFailure("SubmitDecoderBuffers", logging::SystemErrorCodeToString(hr),
StatusCode::kSubmitDecoderBuffersFailed);
return false;
}
return true;
}
DecodeStatus D3D11AV1Accelerator::SubmitDecode(
const AV1Picture& pic,
const libgav1::ObuSequenceHeader& seq_header,
const AV1ReferenceFrameVector& ref_frames,
const libgav1::Vector<libgav1::TileBuffer>& tile_buffers,
base::span<const uint8_t> data) {
const D3D11AV1Picture* pic_ptr = static_cast<const D3D11AV1Picture*>(&pic);
do {
ID3D11VideoDecoderOutputView* output_view = nullptr;
auto result = pic_ptr->picture_buffer()->AcquireOutputView();
if (result.has_value()) {
output_view = std::move(result).value();
} else {
RecordFailure("AcquireOutputView", std::move(result).error());
return DecodeStatus::kFail;
}
const auto hr = video_context_->DecoderBeginFrame(video_decoder_.Get(),
output_view, 0, nullptr);
if (SUCCEEDED(hr)) {
break;
} else if (hr == E_PENDING || hr == D3DERR_WASSTILLDRAWING) {
base::PlatformThread::YieldCurrentThread();
} else if (FAILED(hr)) {
RecordFailure("DecoderBeginFrame", logging::SystemErrorCodeToString(hr),
StatusCode::kDecoderBeginFrameFailed);
return DecodeStatus::kFail;
}
} while (true);
DXVA_PicParams_AV1 pic_params = {0};
FillPicParams(pic_ptr->picture_buffer()->picture_index(),
pic_ptr->apply_grain(), pic.frame_header, seq_header,
ref_frames, &pic_params);
if (!SubmitDecoderBuffer(pic_params, tile_buffers))
return DecodeStatus::kFail;
const auto hr = video_context_->DecoderEndFrame(video_decoder_.Get());
if (FAILED(hr)) {
RecordFailure("DecoderEndFrame", logging::SystemErrorCodeToString(hr),
StatusCode::kDecoderEndFrameFailed);
return DecodeStatus::kFail;
}
return DecodeStatus::kOk;
}
bool D3D11AV1Accelerator::OutputPicture(const AV1Picture& pic) {
const auto* pic_ptr = static_cast<const D3D11AV1Picture*>(&pic);
return client_->OutputResult(pic_ptr, pic_ptr->picture_buffer());
}
void D3D11AV1Accelerator::SetVideoDecoder(ComD3D11VideoDecoder video_decoder) {
video_decoder_ = std::move(video_decoder);
}
void D3D11AV1Accelerator::FillPicParams(
size_t picture_index,
bool apply_grain,
const libgav1::ObuFrameHeader& frame_header,
const libgav1::ObuSequenceHeader& seq_header,
const AV1ReferenceFrameVector& ref_frames,
DXVA_PicParams_AV1* pp) {
// Note: Unclear from documentation if DXVA wants these values -1. The docs
// say they correspond to the "minus_1" variants... Microsoft's dav1d
// implementation uses the full values.
pp->width = frame_header.width;
pp->height = frame_header.height;
pp->max_width = seq_header.max_frame_width;
pp->max_height = seq_header.max_frame_height;
pp->CurrPicTextureIndex = picture_index;
pp->superres_denom = frame_header.use_superres
? frame_header.superres_scale_denominator
: libgav1::kSuperResScaleNumerator;
pp->bitdepth = seq_header.color_config.bitdepth;
pp->seq_profile = seq_header.profile;
const auto& tile_info = frame_header.tile_info;
pp->tiles.cols = tile_info.tile_columns;
pp->tiles.rows = tile_info.tile_rows;
pp->tiles.context_update_id = tile_info.context_update_id;
if (tile_info.uniform_spacing) {
// TODO(b/174802667): Just use tile_column_width_in_superblocks and
// tile_row_height_in_superblocks once they're always populated by libgav1.
const auto tile_width_sb =
(tile_info.sb_columns + (1 << tile_info.tile_columns_log2) - 1) >>
tile_info.tile_columns_log2;
const int last_width_idx = tile_info.tile_columns - 1;
for (int i = 0; i < last_width_idx; ++i)
pp->tiles.widths[i] = tile_width_sb;
pp->tiles.widths[last_width_idx] =
tile_info.sb_columns - last_width_idx * tile_width_sb;
const auto tile_height_sb =
(tile_info.sb_rows + (1 << tile_info.tile_rows_log2) - 1) >>
tile_info.tile_rows_log2;
const int last_height_idx = tile_info.tile_rows - 1;
for (int i = 0; i < last_height_idx; ++i)
pp->tiles.heights[i] = tile_height_sb;
pp->tiles.heights[last_height_idx] =
tile_info.sb_rows - last_height_idx * tile_height_sb;
} else {
for (int i = 0; i < pp->tiles.cols; ++i) {
pp->tiles.widths[i] =
frame_header.tile_info.tile_column_width_in_superblocks[i];
}
for (int i = 0; i < pp->tiles.rows; ++i) {
pp->tiles.heights[i] =
frame_header.tile_info.tile_row_height_in_superblocks[i];
}
}
pp->coding.use_128x128_superblock = seq_header.use_128x128_superblock;
pp->coding.intra_edge_filter = seq_header.enable_intra_edge_filter;
pp->coding.interintra_compound = seq_header.enable_interintra_compound;
pp->coding.masked_compound = seq_header.enable_masked_compound;
// Note: The ObuSequenceHeader has a |enable_warped_motion| field and the
// ObuFrameHeader has a |allow_warped_motion|. Per the DXVA spec,
// "[warped_motion] corresponds to the syntax element named
// allow_warped_motion from the specification."
pp->coding.warped_motion = frame_header.allow_warped_motion;
pp->coding.dual_filter = seq_header.enable_dual_filter;
pp->coding.jnt_comp = seq_header.enable_jnt_comp;
// Another field in both the sequence and frame header, per the DXVA spec:
// "[screen_content_tools] corresponds to the syntax element named
// allow_screen_content_tools from the specification."
pp->coding.screen_content_tools = frame_header.allow_screen_content_tools;
// Another field in both the sequence and frame header, per the DXVA spec:
// "[integer_mv] corresponds to the syntax element named force_integer_mv
// from the specification."
pp->coding.integer_mv = frame_header.force_integer_mv;
pp->coding.cdef = seq_header.enable_cdef;
pp->coding.restoration = seq_header.enable_restoration;
pp->coding.film_grain = seq_header.film_grain_params_present;
pp->coding.intrabc = frame_header.allow_intrabc;
pp->coding.high_precision_mv = frame_header.allow_high_precision_mv;
pp->coding.switchable_motion_mode = frame_header.is_motion_mode_switchable;
pp->coding.filter_intra = seq_header.enable_filter_intra;
pp->coding.disable_frame_end_update_cdf =
!frame_header.enable_frame_end_update_cdf;
pp->coding.disable_cdf_update = !frame_header.enable_cdf_update;
pp->coding.reference_mode = frame_header.reference_mode_select;
pp->coding.skip_mode = frame_header.skip_mode_present;
pp->coding.reduced_tx_set = frame_header.reduced_tx_set;
pp->coding.superres = frame_header.use_superres;
pp->coding.tx_mode = frame_header.tx_mode;
pp->coding.use_ref_frame_mvs = frame_header.use_ref_frame_mvs;
pp->coding.enable_ref_frame_mvs = seq_header.enable_ref_frame_mvs;
pp->coding.reference_frame_update =
!(frame_header.show_existing_frame &&
frame_header.frame_type == libgav1::kFrameKey);
pp->format.frame_type = frame_header.frame_type;
pp->format.show_frame = frame_header.show_frame;
pp->format.showable_frame = frame_header.showable_frame;
pp->format.subsampling_x = seq_header.color_config.subsampling_x;
pp->format.subsampling_y = seq_header.color_config.subsampling_y;
pp->format.mono_chrome = seq_header.color_config.is_monochrome;
pp->primary_ref_frame = frame_header.primary_reference_frame;
pp->order_hint = frame_header.order_hint;
pp->order_hint_bits = seq_header.order_hint_bits;
for (size_t i = 0; i < libgav1::kNumReferenceFrameTypes - 1; ++i) {
if (libgav1::IsIntraFrame(frame_header.frame_type)) {
pp->frame_refs[i].Index = 0xFF;
continue;
}
const auto ref_idx = frame_header.reference_frame_index[i];
const auto* rp =
static_cast<const D3D11AV1Picture*>(ref_frames[ref_idx].get());
if (!rp) {
pp->frame_refs[i].Index = 0xFF;
continue;
}
pp->frame_refs[i].width = rp->frame_header.width;
pp->frame_refs[i].height = rp->frame_header.height;
const auto& gm =
frame_header.global_motion[libgav1::kReferenceFrameLast + i];
for (size_t j = 0; j < 6; ++j)
pp->frame_refs[i].wmmat[j] = gm.params[j];
pp->frame_refs[i].wminvalid =
gm.type == libgav1::kGlobalMotionTransformationTypeIdentity;
pp->frame_refs[i].wmtype = gm.type;
pp->frame_refs[i].Index = ref_idx;
}
for (size_t i = 0; i < libgav1::kNumReferenceFrameTypes; ++i) {
const auto* rp = static_cast<const D3D11AV1Picture*>(ref_frames[i].get());
pp->RefFrameMapTextureIndex[i] =
rp ? rp->picture_buffer()->picture_index() : 0xFF;
}
pp->loop_filter.filter_level[0] = frame_header.loop_filter.level[0];
pp->loop_filter.filter_level[1] = frame_header.loop_filter.level[1];
pp->loop_filter.filter_level_u = frame_header.loop_filter.level[2];
pp->loop_filter.filter_level_v = frame_header.loop_filter.level[3];
pp->loop_filter.sharpness_level = frame_header.loop_filter.sharpness;
pp->loop_filter.mode_ref_delta_enabled =
frame_header.loop_filter.delta_enabled;
pp->loop_filter.mode_ref_delta_update = frame_header.loop_filter.delta_update;
pp->loop_filter.delta_lf_multi = frame_header.delta_lf.multi;
pp->loop_filter.delta_lf_present = frame_header.delta_lf.present;
for (size_t i = 0; i < libgav1::kNumReferenceFrameTypes; ++i)
pp->loop_filter.ref_deltas[i] = frame_header.loop_filter.ref_deltas[i];
pp->loop_filter.mode_deltas[0] = frame_header.loop_filter.mode_deltas[0];
pp->loop_filter.mode_deltas[1] = frame_header.loop_filter.mode_deltas[1];
pp->loop_filter.delta_lf_res = frame_header.delta_lf.scale;
for (size_t i = 0; i < libgav1::kMaxPlanes; ++i) {
constexpr uint8_t kD3D11LoopRestorationMapping[4] = {
0, // libgav1::kLoopRestorationTypeNone,
3, // libgav1::kLoopRestorationTypeSwitchable,
1, // libgav1::kLoopRestorationTypeWiener,
2, // libgav1::kLoopRestorationTypeSgrProj
};
pp->loop_filter.frame_restoration_type[i] =
kD3D11LoopRestorationMapping[frame_header.loop_restoration.type[i]];
pp->loop_filter.log2_restoration_unit_size[i] =
frame_header.loop_restoration.unit_size_log2[i];
}
pp->quantization.delta_q_present = frame_header.delta_q.present;
pp->quantization.delta_q_res = frame_header.delta_q.scale;
pp->quantization.base_qindex = frame_header.quantizer.base_index;
pp->quantization.y_dc_delta_q = frame_header.quantizer.delta_dc[0];
pp->quantization.u_dc_delta_q = frame_header.quantizer.delta_dc[1];
pp->quantization.v_dc_delta_q = frame_header.quantizer.delta_dc[2];
pp->quantization.u_ac_delta_q = frame_header.quantizer.delta_ac[1];
pp->quantization.v_ac_delta_q = frame_header.quantizer.delta_ac[2];
pp->quantization.qm_y = frame_header.quantizer.use_matrix
? frame_header.quantizer.matrix_level[0]
: 0xFF;
pp->quantization.qm_u = frame_header.quantizer.use_matrix
? frame_header.quantizer.matrix_level[1]
: 0xFF;
pp->quantization.qm_v = frame_header.quantizer.use_matrix
? frame_header.quantizer.matrix_level[2]
: 0xFF;
// libgav1 stores the computed versions of the cdef values, so we must undo
// the computation for DXVA. See ObuParser::ParseCdefParameters().
const uint8_t coeff_shift = pp->bitdepth - 8;
pp->cdef.damping = frame_header.cdef.damping - coeff_shift - 3u;
pp->cdef.bits = frame_header.cdef.bits;
for (size_t i = 0; i < libgav1::kMaxCdefStrengths; ++i) {
pp->cdef.y_strengths[i].primary =
frame_header.cdef.y_primary_strength[i] >> coeff_shift;
pp->cdef.y_strengths[i].secondary =
frame_header.cdef.y_secondary_strength[i] >> coeff_shift;
pp->cdef.uv_strengths[i].primary =
frame_header.cdef.uv_primary_strength[i] >> coeff_shift;
pp->cdef.uv_strengths[i].secondary =
frame_header.cdef.uv_secondary_strength[i] >> coeff_shift;
}
pp->interp_filter = frame_header.interpolation_filter;
pp->segmentation.enabled = frame_header.segmentation.enabled;
pp->segmentation.update_map = frame_header.segmentation.update_map;
pp->segmentation.update_data = frame_header.segmentation.update_data;
pp->segmentation.temporal_update = frame_header.segmentation.temporal_update;
for (size_t i = 0; i < libgav1::kMaxSegments; ++i) {
for (size_t j = 0; j < libgav1::kSegmentFeatureMax; ++j) {
pp->segmentation.feature_mask[i].mask |=
frame_header.segmentation.feature_enabled[i][j] << j;
pp->segmentation.feature_data[i][j] =
frame_header.segmentation.feature_data[i][j];
}
}
if (apply_grain) {
const auto& fg = frame_header.film_grain_params;
pp->film_grain.apply_grain = fg.apply_grain;
pp->film_grain.scaling_shift_minus8 = fg.chroma_scaling - 8;
pp->film_grain.chroma_scaling_from_luma = fg.chroma_scaling_from_luma;
pp->film_grain.ar_coeff_lag = fg.auto_regression_coeff_lag;
pp->film_grain.ar_coeff_shift_minus6 = fg.auto_regression_shift - 6;
pp->film_grain.grain_scale_shift = fg.grain_scale_shift;
pp->film_grain.overlap_flag = fg.overlap_flag;
pp->film_grain.clip_to_restricted_range = fg.clip_to_restricted_range;
pp->film_grain.matrix_coeff_is_identity =
seq_header.color_config.matrix_coefficients ==
libgav1::kMatrixCoefficientsIdentity;
pp->film_grain.grain_seed = fg.grain_seed;
pp->film_grain.num_y_points = fg.num_y_points;
for (uint8_t i = 0; i < fg.num_y_points; ++i) {
pp->film_grain.scaling_points_y[i][0] = fg.point_y_value[i];
pp->film_grain.scaling_points_y[i][1] = fg.point_y_scaling[i];
}
pp->film_grain.num_cb_points = fg.num_u_points;
for (uint8_t i = 0; i < fg.num_u_points; ++i) {
pp->film_grain.scaling_points_cb[i][0] = fg.point_u_value[i];
pp->film_grain.scaling_points_cb[i][1] = fg.point_u_scaling[i];
}
pp->film_grain.num_cr_points = fg.num_v_points;
for (uint8_t i = 0; i < fg.num_v_points; ++i) {
pp->film_grain.scaling_points_cr[i][0] = fg.point_v_value[i];
pp->film_grain.scaling_points_cr[i][1] = fg.point_v_scaling[i];
}
for (size_t i = 0; i < base::size(fg.auto_regression_coeff_y); ++i) {
pp->film_grain.ar_coeffs_y[i] = fg.auto_regression_coeff_y[i] + 128;
}
for (size_t i = 0; i < base::size(fg.auto_regression_coeff_u); ++i) {
pp->film_grain.ar_coeffs_cb[i] = fg.auto_regression_coeff_u[i] + 128;
pp->film_grain.ar_coeffs_cr[i] = fg.auto_regression_coeff_v[i] + 128;
}
pp->film_grain.cb_mult = fg.u_multiplier;
pp->film_grain.cb_luma_mult = fg.u_luma_multiplier;
pp->film_grain.cb_offset = fg.u_offset;
pp->film_grain.cr_mult = fg.v_multiplier;
pp->film_grain.cr_luma_mult = fg.v_luma_multiplier;
pp->film_grain.cr_offset = fg.v_offset;
}
// StatusReportFeedbackNumber "should not be equal to 0"... but it crashes :|
// pp->StatusReportFeedbackNumber = ++status_feedback_;
}
} // namespace media