blob: bc963d246c860e3f3a63e7897b65bec47f9213a1 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/viz/common/quads/render_pass_io.h"
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "base/base64.h"
#include "base/bit_cast.h"
#include "base/compiler_specific.h"
#include "base/containers/span.h"
#include "base/json/values_util.h"
#include "base/memory/raw_ptr_exclusion.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_tokenizer.h"
#include "base/values.h"
#include "cc/paint/paint_op_reader.h"
#include "cc/paint/paint_op_writer.h"
#include "components/viz/common/quads/compositor_frame.h"
#include "components/viz/common/quads/compositor_render_pass.h"
#include "components/viz/common/quads/compositor_render_pass_draw_quad.h"
#include "components/viz/common/quads/picture_draw_quad.h"
#include "components/viz/common/quads/solid_color_draw_quad.h"
#include "components/viz/common/quads/surface_draw_quad.h"
#include "components/viz/common/quads/texture_draw_quad.h"
#include "components/viz/common/quads/tile_draw_quad.h"
#include "components/viz/common/quads/video_hole_draw_quad.h"
#include "third_party/skia/modules/skcms/skcms.h"
#include "ui/gfx/geometry/rect_conversions.h"
namespace gfx {
struct HDRMetadata;
}
namespace viz {
namespace {
enum RenderPassField {
kRenderPassID = 1 << 0,
kRenderPassOutputRect = 1 << 1,
kRenderPassDamageRect = 1 << 2,
kRenderPassTransformToRootTarget = 1 << 3,
kRenderPassFilters = 1 << 4,
kRenderPassBackdropFilters = 1 << 5,
kRenderPassBackdropFilterBounds = 1 << 6,
kRenderPassColorSpace = 1 << 7,
kRenderPassHasTransparentBackground = 1 << 8,
kRenderPassCacheRenderPass = 1 << 9,
kRenderPassHasDamageFromContributingContent = 1 << 10,
kRenderPassGenerateMipmap = 1 << 11,
kRenderPassCopyRequests = 1 << 12,
kRenderPassQuadList = 1 << 13,
kRenderPassSharedQuadStateList = 1 << 14,
kRenderPassHasPreQuadDamage = 1 << 15,
kRenderPassAllFields = 0xFFFFFFFF,
};
// This controls which fields are processed in CompositorRenderPassToDict() and
// CompositorRenderPassFromDict().
// Values other than kAllFields should never be checked in. This is only for
// local debugging convenience.
RenderPassField g_render_pass_fields = kRenderPassAllFields;
bool ProcessRenderPassField(RenderPassField field) {
return (g_render_pass_fields & field) == field;
}
base::Value::Dict RectToDict(const gfx::Rect& rect) {
return base::Value::Dict()
.Set("x", rect.x())
.Set("y", rect.y())
.Set("width", rect.width())
.Set("height", rect.height());
}
bool RectFromDict(const base::Value::Dict& dict, gfx::Rect* rect) {
DCHECK(rect);
std::optional<int> x = dict.FindInt("x");
std::optional<int> y = dict.FindInt("y");
std::optional<int> width = dict.FindInt("width");
std::optional<int> height = dict.FindInt("height");
if (!x || !y || !width || !height) {
return false;
}
rect->SetRect(x.value(), y.value(), width.value(), height.value());
return true;
}
base::Value::Dict RectFToDict(const gfx::RectF& rect) {
return base::Value::Dict()
.Set("x", rect.x())
.Set("y", rect.y())
.Set("width", rect.width())
.Set("height", rect.height());
}
bool RectFFromDict(const base::Value::Dict& dict, gfx::RectF* rect) {
DCHECK(rect);
std::optional<double> x = dict.FindDouble("x");
std::optional<double> y = dict.FindDouble("y");
std::optional<double> width = dict.FindDouble("width");
std::optional<double> height = dict.FindDouble("height");
if (!x || !y || !width || !height) {
return false;
}
rect->SetRect(static_cast<float>(x.value()), static_cast<float>(y.value()),
static_cast<float>(width.value()),
static_cast<float>(height.value()));
return true;
}
base::Value::Dict SizeToDict(const gfx::Size& size) {
return base::Value::Dict()
.Set("width", size.width())
.Set("height", size.height());
}
bool SizeFromDict(const base::Value::Dict& dict, gfx::Size* size) {
DCHECK(size);
std::optional<int> width = dict.FindInt("width");
std::optional<int> height = dict.FindInt("height");
if (!width || !height) {
return false;
}
size->set_width(width.value());
size->set_height(height.value());
return true;
}
base::Value::Dict PointToDict(const gfx::Point& point) {
return base::Value::Dict().Set("x", point.x()).Set("y", point.y());
}
bool PointFromDict(const base::Value::Dict& dict, gfx::Point* point) {
DCHECK(point);
std::optional<int> x = dict.FindInt("x");
std::optional<int> y = dict.FindInt("y");
if (!x || !y) {
return false;
}
point->set_x(x.value());
point->set_y(y.value());
return true;
}
base::Value::Dict SkColor4fToDict(const SkColor4f color) {
return base::Value::Dict()
.Set("red", color.fR)
.Set("green", color.fG)
.Set("blue", color.fB)
.Set("alpha", color.fA);
}
bool SkColor4fFromDict(const base::Value::Dict& dict, SkColor4f* color) {
DCHECK(color);
std::optional<double> red = dict.FindDouble("red");
std::optional<double> green = dict.FindDouble("green");
std::optional<double> blue = dict.FindDouble("blue");
std::optional<double> alpha = dict.FindDouble("alpha");
if (!red || !green || !blue || !alpha)
return false;
color->fR = static_cast<float>(red.value());
color->fG = static_cast<float>(green.value());
color->fB = static_cast<float>(blue.value());
color->fA = static_cast<float>(alpha.value());
return true;
}
// Many quads now store color as an SkColor4f, but older logs will still store
// SkColors (which are ints). For backward compatibility's sake, read either.
bool ColorFromDict(const base::Value::Dict& dict,
std::string_view key,
SkColor4f* output_color) {
const base::Value::Dict* color_key = dict.FindDict(key);
SkColor4f color_4f;
if (!color_key || !SkColor4fFromDict(*color_key, &color_4f)) {
std::optional<int> color_int = dict.FindInt(key);
if (!color_int)
return false;
color_4f = SkColor4f::FromColor(static_cast<SkColor>(color_int.value()));
}
output_color->fR = color_4f.fR;
output_color->fG = color_4f.fG;
output_color->fB = color_4f.fB;
output_color->fA = color_4f.fA;
return true;
}
base::Value::Dict PointFToDict(const gfx::PointF& point) {
return base::Value::Dict().Set("x", point.x()).Set("y", point.y());
}
bool PointFFromDict(const base::Value::Dict& dict, gfx::PointF* point) {
DCHECK(point);
std::optional<double> x = dict.FindDouble("x");
std::optional<double> y = dict.FindDouble("y");
if (!x || !y) {
return false;
}
point->set_x(static_cast<float>(x.value()));
point->set_y(static_cast<float>(y.value()));
return true;
}
base::Value::Dict Vector2dFToDict(const gfx::Vector2dF& v) {
return PointFToDict(gfx::PointF(v.x(), v.y()));
}
bool Vector2dFFromDict(const base::Value::Dict& dict, gfx::Vector2dF* v) {
DCHECK(v);
gfx::PointF point;
if (!PointFFromDict(dict, &point))
return false;
v->set_x(point.x());
v->set_y(point.y());
return true;
}
base::Value::List FloatArrayToList(base::span<const float> data) {
base::Value::List list;
for (float num : data)
list.Append(num);
return list;
}
bool FloatArrayFromList(const base::Value::List& list,
size_t expected_count,
float* data) {
DCHECK(data);
DCHECK_LT(0u, expected_count);
size_t count = list.size();
if (count != expected_count)
return false;
std::vector<double> double_data(count);
for (size_t ii = 0; ii < count; ++ii) {
if (!list[ii].is_double())
return false;
double_data[ii] = list[ii].GetDouble();
}
for (size_t ii = 0; ii < count; ++ii)
UNSAFE_TODO(data[ii]) = static_cast<float>(double_data[ii]);
return true;
}
#define MAP_RRECTF_TYPE_TO_STRING(NAME) \
case gfx::RRectF::Type::NAME: \
return #NAME;
const char* RRectFTypeToString(gfx::RRectF::Type type) {
switch (type) {
MAP_RRECTF_TYPE_TO_STRING(kEmpty)
MAP_RRECTF_TYPE_TO_STRING(kRect)
MAP_RRECTF_TYPE_TO_STRING(kSingle)
MAP_RRECTF_TYPE_TO_STRING(kSimple)
MAP_RRECTF_TYPE_TO_STRING(kOval)
MAP_RRECTF_TYPE_TO_STRING(kComplex)
default:
NOTREACHED();
}
}
#undef MAP_RRECTF_TYPE_TO_STRING
#define MAP_STRING_TO_RRECTF_TYPE(NAME) \
if (str == #NAME) \
return static_cast<int>(gfx::RRectF::Type::NAME);
int StringToRRectFType(const std::string& str) {
MAP_STRING_TO_RRECTF_TYPE(kEmpty)
MAP_STRING_TO_RRECTF_TYPE(kRect)
MAP_STRING_TO_RRECTF_TYPE(kSingle)
MAP_STRING_TO_RRECTF_TYPE(kSimple)
MAP_STRING_TO_RRECTF_TYPE(kOval)
MAP_STRING_TO_RRECTF_TYPE(kComplex)
return -1;
}
#undef MAP_STRING_TO_RRECTF_TYPE
base::Value::Dict RRectFToDict(const gfx::RRectF& rect) {
base::Value::Dict dict;
dict.Set("type", RRectFTypeToString(rect.GetType()));
if (rect.GetType() != gfx::RRectF::Type::kEmpty) {
dict.Set("rect", RectFToDict(rect.rect()));
dict.Set("upper_left.x",
rect.GetCornerRadii(gfx::RRectF::Corner::kUpperLeft).x());
dict.Set("upper_left.y",
rect.GetCornerRadii(gfx::RRectF::Corner::kUpperLeft).y());
dict.Set("upper_right.x",
rect.GetCornerRadii(gfx::RRectF::Corner::kUpperRight).x());
dict.Set("upper_right.y",
rect.GetCornerRadii(gfx::RRectF::Corner::kUpperRight).y());
dict.Set("lower_right.x",
rect.GetCornerRadii(gfx::RRectF::Corner::kLowerRight).x());
dict.Set("lower_right.y",
rect.GetCornerRadii(gfx::RRectF::Corner::kLowerRight).y());
dict.Set("lower_left.x",
rect.GetCornerRadii(gfx::RRectF::Corner::kLowerLeft).x());
dict.Set("lower_left.y",
rect.GetCornerRadii(gfx::RRectF::Corner::kLowerLeft).y());
}
return dict;
}
bool RRectFFromDict(const base::Value::Dict& dict, gfx::RRectF* out) {
DCHECK(out);
const std::string* type = dict.FindString("type");
if (!type)
return false;
int type_index = StringToRRectFType(*type);
if (type_index < 0)
return false;
gfx::RRectF::Type t_type = static_cast<gfx::RRectF::Type>(type_index);
if (t_type == gfx::RRectF::Type::kEmpty) {
*out = gfx::RRectF();
DCHECK_EQ(gfx::RRectF::Type::kEmpty, out->GetType());
return true;
}
const base::Value::Dict* rect = dict.FindDict("rect");
std::optional<double> upper_left_x = dict.FindDouble("upper_left.x");
std::optional<double> upper_left_y = dict.FindDouble("upper_left.y");
std::optional<double> upper_right_x = dict.FindDouble("upper_right.x");
std::optional<double> upper_right_y = dict.FindDouble("upper_right.y");
std::optional<double> lower_right_x = dict.FindDouble("lower_right.x");
std::optional<double> lower_right_y = dict.FindDouble("lower_right.y");
std::optional<double> lower_left_x = dict.FindDouble("lower_left.x");
std::optional<double> lower_left_y = dict.FindDouble("lower_left.y");
if (!rect || !upper_left_x || !upper_left_y || !upper_right_x ||
!upper_right_y || !lower_right_x || !lower_right_y || !lower_left_x ||
!lower_left_y) {
return false;
}
gfx::RectF rect_f;
if (!RectFFromDict(*rect, &rect_f))
return false;
gfx::RRectF rrectf(rect_f, static_cast<float>(upper_left_x.value()),
static_cast<float>(upper_left_y.value()),
static_cast<float>(upper_right_x.value()),
static_cast<float>(upper_right_y.value()),
static_cast<float>(lower_right_x.value()),
static_cast<float>(lower_right_y.value()),
static_cast<float>(lower_left_x.value()),
static_cast<float>(lower_left_y.value()));
if (rrectf.GetType() != t_type)
return false;
*out = rrectf;
return true;
}
base::Value::BlobStorage SkPathToBlob(const SkPath& path) {
base::Value::BlobStorage blob(path.writeToMemory(nullptr));
CHECK(path.writeToMemory(blob.data()));
return blob;
}
bool SkPathFromBlob(const base::Value::BlobStorage& blob, SkPath* out) {
return out->readFromMemory(blob.data(), blob.size());
}
base::Value::Dict LinearGradientToDict(
const gfx::LinearGradient& gradient_mask) {
base::Value::List steps;
for (size_t i = 0; i < gradient_mask.step_count(); ++i) {
steps.Append(
base::Value::Dict()
.Set("fraction",
static_cast<double>(gradient_mask.steps()[i].fraction))
.Set("alpha", static_cast<int>(gradient_mask.steps()[i].alpha)));
}
return base::Value::Dict()
.Set("angle", static_cast<double>(gradient_mask.angle()))
.Set("step_count", static_cast<int>(gradient_mask.step_count()))
.Set("steps", std::move(steps));
}
bool LinearGradientFromDict(const base::Value::Dict& dict,
gfx::LinearGradient* out) {
std::optional<double> angle = dict.FindDouble("angle");
std::optional<int> step_count = dict.FindInt("step_count");
if (!angle || !step_count)
return false;
gfx::LinearGradient gradient_mask = gfx::LinearGradient(*angle);
const base::Value::List* steps = dict.FindList("steps");
if (!steps)
return false;
for (const base::Value& v : *steps) {
const base::Value::Dict* step = v.GetIfDict();
if (!step)
return false;
std::optional<double> fraction = step->FindDouble("fraction");
std::optional<int> alpha = step->FindInt("alpha");
if (!fraction || !alpha)
return false;
gradient_mask.AddStep(*fraction, *alpha);
}
*out = gradient_mask;
return true;
}
base::Value::Dict MaskFilterInfoToDict(
const gfx::MaskFilterInfo& mask_filter_info) {
auto dict = base::Value::Dict().Set(
"rounded_corner_bounds",
RRectFToDict(mask_filter_info.rounded_corner_bounds()));
if (mask_filter_info.HasGradientMask()) {
dict.Set("gradient_mask",
LinearGradientToDict(*mask_filter_info.gradient_mask()));
}
return dict;
}
bool MaskFilterInfoFromDict(const base::Value::Dict& dict,
gfx::MaskFilterInfo* out) {
DCHECK(out);
const base::Value::Dict* rounded_corner_bounds =
dict.FindDict("rounded_corner_bounds");
if (!rounded_corner_bounds)
return false;
gfx::RRectF t_rounded_corner_bounds;
if (!RRectFFromDict(*rounded_corner_bounds, &t_rounded_corner_bounds))
return false;
const base::Value::Dict* gradient_mask = dict.FindDict("gradient_mask");
if (!gradient_mask) {
*out = gfx::MaskFilterInfo(t_rounded_corner_bounds);
return true;
}
gfx::LinearGradient t_gradient_mask;
if (!LinearGradientFromDict(*gradient_mask, &t_gradient_mask))
return false;
*out = gfx::MaskFilterInfo(t_rounded_corner_bounds, t_gradient_mask);
return true;
}
base::Value::List TransformToList(const gfx::Transform& transform) {
base::Value::List list;
float data[16];
transform.GetColMajorF(data);
for (float value : data)
list.Append(value);
return list;
}
bool TransformFromList(const base::Value::List& list,
gfx::Transform* transform) {
DCHECK(transform);
if (list.size() != 16)
return false;
float data[16];
for (size_t ii = 0; ii < 16; ++ii) {
if (!list[ii].is_double())
return false;
UNSAFE_TODO(data[ii]) = list[ii].GetDouble();
}
*transform = gfx::Transform::ColMajorF(data);
return true;
}
base::Value::List ShapeRectsToList(
const cc::FilterOperation::ShapeRects& shape) {
base::Value::List list;
for (const auto& ii : shape) {
list.Append(RectToDict(ii));
}
return list;
}
bool ShapeRectsFromList(const base::Value::List& list,
cc::FilterOperation::ShapeRects* shape) {
DCHECK(shape);
size_t size = list.size();
cc::FilterOperation::ShapeRects data;
data.resize(size);
for (size_t ii = 0; ii < size; ++ii) {
const base::Value& dict_value = list[ii];
if (!dict_value.is_dict())
return false;
if (!RectFromDict(dict_value.GetDict(), &data[ii]))
return false;
}
*shape = data;
return true;
}
std::string PaintFilterToString(const sk_sp<cc::PaintFilter>& filter) {
// TODO(zmo): Expand to readable fields. Such recorded data becomes invalid
// when we update any data structure.
std::vector<uint8_t> buffer(cc::PaintOpWriter::SerializedSize(filter.get()));
// No need to populate the SerializeOptions here since the security
// constraints explicitly disable serializing images using the transfer cache
// and serialization of PaintRecords.
cc::PaintOp::SerializeOptions options;
cc::PaintOpWriter writer(buffer.data(), buffer.size(), options,
true /* enable_security_constraints */);
writer.Write(filter.get(), SkM44());
if (writer.size() == 0)
return "";
buffer.resize(writer.size());
return base::Base64Encode(buffer);
}
sk_sp<cc::PaintFilter> PaintFilterFromString(const std::string& encoded) {
if (encoded.empty()) {
return nullptr;
}
std::string buffer;
if (!base::Base64Decode(encoded, &buffer))
return nullptr;
// We don't need to populate the DeserializeOptions here since the security
// constraints explicitly disable serializing images using the transfer cache
// and serialization of PaintRecords.
std::vector<uint8_t> scratch_buffer;
cc::PaintOp::DeserializeOptions options{.scratch_buffer = scratch_buffer};
cc::PaintOpReader reader(buffer.data(), buffer.size(), options,
/*enable_security_constraints=*/true);
sk_sp<cc::PaintFilter> filter;
reader.Read(&filter);
if (!reader.valid())
return nullptr;
// We must have consumed all bytes written when reading this filter.
if (reader.remaining_bytes() != 0u)
return nullptr;
return filter;
}
base::Value::Dict FilterOperationToDict(const cc::FilterOperation& filter) {
base::Value::Dict dict;
cc::FilterOperation::FilterType type = filter.type();
dict.Set("type", type);
if (type != cc::FilterOperation::COLOR_MATRIX &&
type != cc::FilterOperation::REFERENCE &&
type != cc::FilterOperation::OFFSET) {
dict.Set("amount", filter.amount());
}
switch (type) {
case cc::FilterOperation::ALPHA_THRESHOLD:
dict.Set("shape", ShapeRectsToList(filter.shape()));
break;
case cc::FilterOperation::DROP_SHADOW:
dict.Set("offset", PointToDict(filter.offset()));
dict.Set("drop_shadow_color",
SkColor4fToDict(filter.drop_shadow_color()));
break;
case cc::FilterOperation::REFERENCE:
dict.Set("image_filter", PaintFilterToString(filter.image_filter()));
break;
case cc::FilterOperation::COLOR_MATRIX:
dict.Set("matrix", FloatArrayToList(filter.matrix()));
break;
case cc::FilterOperation::ZOOM:
dict.Set("zoom_inset", filter.zoom_inset());
break;
case cc::FilterOperation::BLUR:
dict.Set("blur_tile_mode", static_cast<int>(filter.blur_tile_mode()));
break;
case cc::FilterOperation::OFFSET:
dict.Set("offset", PointToDict(filter.offset()));
break;
default:
break;
}
return dict;
}
bool FilterOperationFromDict(const base::Value& dict_value,
cc::FilterOperation* out) {
DCHECK(out);
if (!dict_value.is_dict()) {
return false;
}
const base::Value::Dict& dict = dict_value.GetDict();
std::optional<int> type = dict.FindInt("type");
std::optional<double> amount = dict.FindDouble("amount");
const base::Value::Dict* offset = dict.FindDict("offset");
const std::string* image_filter = dict.FindString("image_filter");
const base::Value::List* matrix = dict.FindList("matrix");
std::optional<int> zoom_inset = dict.FindInt("zoom_inset");
const base::Value::List* shape = dict.FindList("shape");
std::optional<int> blur_tile_mode = dict.FindInt("blur_tile_mode");
cc::FilterOperation filter;
if (!type)
return false;
cc::FilterOperation::FilterType filter_type =
static_cast<cc::FilterOperation::FilterType>(type.value());
filter.set_type(filter_type);
if (filter_type != cc::FilterOperation::COLOR_MATRIX &&
filter_type != cc::FilterOperation::REFERENCE &&
filter_type != cc::FilterOperation::OFFSET) {
if (!amount)
return false;
filter.set_amount(static_cast<float>(amount.value()));
}
switch (filter_type) {
case cc::FilterOperation::ALPHA_THRESHOLD: {
cc::FilterOperation::ShapeRects shape_rects;
if (!shape || !ShapeRectsFromList(*shape, &shape_rects)) {
return false;
}
filter.set_shape(shape_rects);
} break;
case cc::FilterOperation::DROP_SHADOW: {
gfx::Point drop_shadow_offset;
if (!offset || !PointFromDict(*offset, &drop_shadow_offset)) {
return false;
}
filter.set_offset(drop_shadow_offset);
SkColor4f t_drop_shadow_color;
if (!ColorFromDict(dict, "drop_shadow_color", &t_drop_shadow_color)) {
return false;
}
filter.set_drop_shadow_color(t_drop_shadow_color);
} break;
case cc::FilterOperation::REFERENCE:
if (!image_filter)
return false;
filter.set_image_filter(PaintFilterFromString(*image_filter));
break;
case cc::FilterOperation::COLOR_MATRIX: {
cc::FilterOperation::Matrix mat;
if (!matrix || !FloatArrayFromList(*matrix, 20u, &mat[0]))
return false;
filter.set_matrix(mat);
} break;
case cc::FilterOperation::ZOOM:
if (!zoom_inset)
return false;
filter.set_zoom_inset(zoom_inset.value());
break;
case cc::FilterOperation::BLUR:
if (!blur_tile_mode)
return false;
filter.set_blur_tile_mode(
static_cast<SkTileMode>(blur_tile_mode.value()));
break;
case cc::FilterOperation::OFFSET: {
gfx::Point filter_offset;
if (!offset || !PointFromDict(*offset, &filter_offset)) {
return false;
}
filter.set_offset(filter_offset);
} break;
default:
break;
}
*out = filter;
return true;
}
base::Value::List FilterOperationsToList(const cc::FilterOperations& filters) {
base::Value::List list;
for (size_t ii = 0; ii < filters.size(); ++ii) {
base::Value::Dict filter_dict = FilterOperationToDict(filters.at(ii));
list.Append(std::move(filter_dict));
}
return list;
}
bool FilterOperationsFromList(const base::Value::List& list,
cc::FilterOperations* filters) {
DCHECK(filters);
cc::FilterOperations data;
for (const auto& entry : list) {
cc::FilterOperation filter;
if (!FilterOperationFromDict(entry, &filter))
return false;
data.Append(filter);
}
*filters = data;
return true;
}
#define MATCH_ENUM_CASE(TYPE, NAME) \
case gfx::ColorSpace::TYPE::NAME: \
return #NAME;
const char* ColorSpacePrimaryIdToString(gfx::ColorSpace::PrimaryID id) {
switch (id) {
MATCH_ENUM_CASE(PrimaryID, INVALID)
MATCH_ENUM_CASE(PrimaryID, BT709)
MATCH_ENUM_CASE(PrimaryID, BT470M)
MATCH_ENUM_CASE(PrimaryID, BT470BG)
MATCH_ENUM_CASE(PrimaryID, SMPTE170M)
MATCH_ENUM_CASE(PrimaryID, SMPTE240M)
MATCH_ENUM_CASE(PrimaryID, FILM)
MATCH_ENUM_CASE(PrimaryID, BT2020)
MATCH_ENUM_CASE(PrimaryID, SMPTEST428_1)
MATCH_ENUM_CASE(PrimaryID, SMPTEST431_2)
MATCH_ENUM_CASE(PrimaryID, P3)
MATCH_ENUM_CASE(PrimaryID, XYZ_D50)
MATCH_ENUM_CASE(PrimaryID, ADOBE_RGB)
MATCH_ENUM_CASE(PrimaryID, APPLE_GENERIC_RGB)
MATCH_ENUM_CASE(PrimaryID, WIDE_GAMUT_COLOR_SPIN)
MATCH_ENUM_CASE(PrimaryID, CUSTOM)
MATCH_ENUM_CASE(PrimaryID, EBU_3213_E)
}
}
const char* ColorSpaceTransferIdToString(gfx::ColorSpace::TransferID id) {
switch (id) {
MATCH_ENUM_CASE(TransferID, INVALID)
MATCH_ENUM_CASE(TransferID, BT709)
MATCH_ENUM_CASE(TransferID, BT709_APPLE)
MATCH_ENUM_CASE(TransferID, GAMMA18)
MATCH_ENUM_CASE(TransferID, GAMMA22)
MATCH_ENUM_CASE(TransferID, GAMMA24)
MATCH_ENUM_CASE(TransferID, GAMMA28)
MATCH_ENUM_CASE(TransferID, SMPTE170M)
MATCH_ENUM_CASE(TransferID, SMPTE240M)
MATCH_ENUM_CASE(TransferID, LINEAR)
MATCH_ENUM_CASE(TransferID, LOG)
MATCH_ENUM_CASE(TransferID, LOG_SQRT)
MATCH_ENUM_CASE(TransferID, IEC61966_2_4)
MATCH_ENUM_CASE(TransferID, BT1361_ECG)
MATCH_ENUM_CASE(TransferID, SRGB)
MATCH_ENUM_CASE(TransferID, BT2020_10)
MATCH_ENUM_CASE(TransferID, BT2020_12)
MATCH_ENUM_CASE(TransferID, PQ)
MATCH_ENUM_CASE(TransferID, SMPTEST428_1)
MATCH_ENUM_CASE(TransferID, HLG)
MATCH_ENUM_CASE(TransferID, SRGB_HDR)
MATCH_ENUM_CASE(TransferID, LINEAR_HDR)
MATCH_ENUM_CASE(TransferID, CUSTOM)
MATCH_ENUM_CASE(TransferID, CUSTOM_HDR)
MATCH_ENUM_CASE(TransferID, SCRGB_LINEAR_80_NITS)
}
}
const char* ColorSpaceMatrixIdToString(gfx::ColorSpace::MatrixID id) {
switch (id) {
MATCH_ENUM_CASE(MatrixID, INVALID)
MATCH_ENUM_CASE(MatrixID, RGB)
MATCH_ENUM_CASE(MatrixID, BT709)
MATCH_ENUM_CASE(MatrixID, FCC)
MATCH_ENUM_CASE(MatrixID, BT470BG)
MATCH_ENUM_CASE(MatrixID, SMPTE170M)
MATCH_ENUM_CASE(MatrixID, SMPTE240M)
MATCH_ENUM_CASE(MatrixID, YCOCG)
MATCH_ENUM_CASE(MatrixID, BT2020_NCL)
MATCH_ENUM_CASE(MatrixID, YDZDX)
MATCH_ENUM_CASE(MatrixID, GBR)
}
}
const char* ColorSpaceRangeIdToString(gfx::ColorSpace::RangeID id) {
switch (id) {
MATCH_ENUM_CASE(RangeID, INVALID)
MATCH_ENUM_CASE(RangeID, LIMITED)
MATCH_ENUM_CASE(RangeID, FULL)
MATCH_ENUM_CASE(RangeID, DERIVED)
}
}
#undef MATCH_ENUM_CASE
#define MATCH_ENUM_CASE(TYPE, NAME) \
if (token == #NAME) \
return static_cast<uint8_t>(gfx::ColorSpace::TYPE::NAME);
uint8_t StringToColorSpacePrimaryId(const std::string& token) {
MATCH_ENUM_CASE(PrimaryID, INVALID)
MATCH_ENUM_CASE(PrimaryID, BT709)
MATCH_ENUM_CASE(PrimaryID, BT470M)
MATCH_ENUM_CASE(PrimaryID, BT470BG)
MATCH_ENUM_CASE(PrimaryID, SMPTE170M)
MATCH_ENUM_CASE(PrimaryID, SMPTE240M)
MATCH_ENUM_CASE(PrimaryID, FILM)
MATCH_ENUM_CASE(PrimaryID, BT2020)
MATCH_ENUM_CASE(PrimaryID, SMPTEST428_1)
MATCH_ENUM_CASE(PrimaryID, SMPTEST431_2)
MATCH_ENUM_CASE(PrimaryID, P3)
MATCH_ENUM_CASE(PrimaryID, XYZ_D50)
MATCH_ENUM_CASE(PrimaryID, ADOBE_RGB)
MATCH_ENUM_CASE(PrimaryID, APPLE_GENERIC_RGB)
MATCH_ENUM_CASE(PrimaryID, WIDE_GAMUT_COLOR_SPIN)
MATCH_ENUM_CASE(PrimaryID, CUSTOM)
MATCH_ENUM_CASE(PrimaryID, EBU_3213_E)
return -1;
}
uint8_t StringToColorSpaceTransferId(const std::string& token) {
MATCH_ENUM_CASE(TransferID, INVALID)
MATCH_ENUM_CASE(TransferID, BT709)
MATCH_ENUM_CASE(TransferID, BT709_APPLE)
MATCH_ENUM_CASE(TransferID, GAMMA18)
MATCH_ENUM_CASE(TransferID, GAMMA22)
MATCH_ENUM_CASE(TransferID, GAMMA24)
MATCH_ENUM_CASE(TransferID, GAMMA28)
MATCH_ENUM_CASE(TransferID, SMPTE170M)
MATCH_ENUM_CASE(TransferID, SMPTE240M)
MATCH_ENUM_CASE(TransferID, LINEAR)
MATCH_ENUM_CASE(TransferID, LOG)
MATCH_ENUM_CASE(TransferID, LOG_SQRT)
MATCH_ENUM_CASE(TransferID, IEC61966_2_4)
MATCH_ENUM_CASE(TransferID, BT1361_ECG)
MATCH_ENUM_CASE(TransferID, SRGB)
MATCH_ENUM_CASE(TransferID, BT2020_10)
MATCH_ENUM_CASE(TransferID, BT2020_12)
MATCH_ENUM_CASE(TransferID, PQ)
MATCH_ENUM_CASE(TransferID, SMPTEST428_1)
MATCH_ENUM_CASE(TransferID, HLG)
MATCH_ENUM_CASE(TransferID, SRGB_HDR)
MATCH_ENUM_CASE(TransferID, LINEAR_HDR)
MATCH_ENUM_CASE(TransferID, CUSTOM)
MATCH_ENUM_CASE(TransferID, CUSTOM_HDR)
MATCH_ENUM_CASE(TransferID, SCRGB_LINEAR_80_NITS)
return -1;
}
uint8_t StringToColorSpaceMatrixId(const std::string& token) {
MATCH_ENUM_CASE(MatrixID, INVALID)
MATCH_ENUM_CASE(MatrixID, RGB)
MATCH_ENUM_CASE(MatrixID, BT709)
MATCH_ENUM_CASE(MatrixID, FCC)
MATCH_ENUM_CASE(MatrixID, BT470BG)
MATCH_ENUM_CASE(MatrixID, SMPTE170M)
MATCH_ENUM_CASE(MatrixID, SMPTE240M)
MATCH_ENUM_CASE(MatrixID, YCOCG)
MATCH_ENUM_CASE(MatrixID, BT2020_NCL)
MATCH_ENUM_CASE(MatrixID, YDZDX)
MATCH_ENUM_CASE(MatrixID, GBR)
return -1;
}
uint8_t StringToColorSpaceRangeId(const std::string& token) {
MATCH_ENUM_CASE(RangeID, INVALID)
MATCH_ENUM_CASE(RangeID, LIMITED)
MATCH_ENUM_CASE(RangeID, FULL)
MATCH_ENUM_CASE(RangeID, DERIVED)
return -1;
}
#undef MATCH_ENUM_CASE
base::Value::List Matrix3x3ToList(const skcms_Matrix3x3& mat) {
float data[9];
UNSAFE_TODO(memcpy(data, mat.vals, sizeof(mat)));
return FloatArrayToList(data);
}
bool Matrix3x3FromList(const base::Value::List& list, skcms_Matrix3x3* mat) {
DCHECK(mat);
return FloatArrayFromList(list, 9u, reinterpret_cast<float*>(mat->vals));
}
base::Value::List TransferFunctionToList(const skcms_TransferFunction& fn) {
float data[7];
data[0] = fn.a;
data[1] = fn.b;
data[2] = fn.c;
data[3] = fn.d;
data[4] = fn.e;
data[5] = fn.f;
data[6] = fn.g;
return FloatArrayToList(data);
}
bool TransferFunctionFromList(const base::Value::List& list,
skcms_TransferFunction* fn) {
DCHECK(fn);
float data[7];
if (!FloatArrayFromList(list, 7u, data))
return false;
fn->a = data[0];
fn->b = data[1];
fn->c = data[2];
fn->d = data[3];
fn->e = data[4];
fn->f = data[5];
fn->g = data[6];
return true;
}
base::Value::Dict ColorSpaceToDict(const gfx::ColorSpace& color_space) {
auto dict =
base::Value::Dict()
.Set("primaries",
ColorSpacePrimaryIdToString(color_space.GetPrimaryID()))
.Set("transfer",
ColorSpaceTransferIdToString(color_space.GetTransferID()))
.Set("matrix", ColorSpaceMatrixIdToString(color_space.GetMatrixID()))
.Set("range", ColorSpaceRangeIdToString(color_space.GetRangeID()));
if (color_space.GetPrimaryID() == gfx::ColorSpace::PrimaryID::CUSTOM) {
skcms_Matrix3x3 mat;
color_space.GetPrimaryMatrix(&mat);
dict.Set("custom_primary_matrix", Matrix3x3ToList(mat));
}
if (color_space.GetTransferID() == gfx::ColorSpace::TransferID::CUSTOM ||
color_space.GetTransferID() == gfx::ColorSpace::TransferID::CUSTOM_HDR) {
skcms_TransferFunction fn;
color_space.GetTransferFunction(&fn);
dict.Set("custom_transfer_params", TransferFunctionToList(fn));
}
return dict;
}
bool ColorSpaceFromDict(const base::Value::Dict& dict,
gfx::ColorSpace* color_space) {
DCHECK(color_space);
const std::string* primaries = dict.FindString("primaries");
const std::string* transfer = dict.FindString("transfer");
const std::string* matrix = dict.FindString("matrix");
const std::string* range = dict.FindString("range");
if (!primaries || !transfer || !matrix || !range)
return false;
uint8_t primary_id = StringToColorSpacePrimaryId(*primaries);
uint8_t transfer_id = StringToColorSpaceTransferId(*transfer);
uint8_t matrix_id = StringToColorSpaceMatrixId(*matrix);
uint8_t range_id = StringToColorSpaceRangeId(*range);
if (primary_id < 0 || transfer_id < 0 || matrix_id < 0 || range_id < 0)
return false;
skcms_Matrix3x3 t_custom_primary_matrix;
bool uses_custom_primary_matrix =
primary_id == static_cast<uint8_t>(gfx::ColorSpace::PrimaryID::CUSTOM);
if (uses_custom_primary_matrix) {
const base::Value::List* custom_primary_matrix =
dict.FindList("custom_primary_matrix");
if (!custom_primary_matrix ||
!Matrix3x3FromList(*custom_primary_matrix, &t_custom_primary_matrix)) {
return false;
}
}
skcms_TransferFunction t_custom_transfer_params;
bool uses_custom_transfer_params =
transfer_id ==
static_cast<uint8_t>(gfx::ColorSpace::TransferID::CUSTOM) ||
transfer_id ==
static_cast<uint8_t>(gfx::ColorSpace::TransferID::CUSTOM_HDR);
if (uses_custom_transfer_params) {
const base::Value::List* custom_transfer_params =
dict.FindList("custom_transfer_params");
if (!custom_transfer_params ||
!TransferFunctionFromList(*custom_transfer_params,
&t_custom_transfer_params)) {
return false;
}
}
*color_space = gfx::ColorSpace(
static_cast<gfx::ColorSpace::PrimaryID>(primary_id),
static_cast<gfx::ColorSpace::TransferID>(transfer_id),
static_cast<gfx::ColorSpace::MatrixID>(matrix_id),
static_cast<gfx::ColorSpace::RangeID>(range_id),
uses_custom_primary_matrix ? &t_custom_primary_matrix : nullptr,
uses_custom_transfer_params ? &t_custom_transfer_params : nullptr);
return true;
}
base::Value::List DrawQuadResourceToList(ResourceId resource_id) {
base::Value::List list;
if (resource_id != kInvalidResourceId) {
list.Append(static_cast<int>(resource_id.GetUnsafeValue()));
}
return list;
}
bool DrawQuadResourceFromList(const base::Value::List& list,
ResourceId& resource_id) {
size_t size = list.size();
if (size == 0u) {
return true;
}
// DrawQuad resources are stored as a list as quads used to have multiple
// resources. Now they should all have at most a single resource.
if (size > 1) {
return false;
}
if (!list[0].is_int()) {
return false;
}
resource_id = ResourceId(list[0].GetInt());
return true;
}
base::Value::Dict SurfaceIdToDict(const SurfaceId& id) {
return base::Value::Dict()
.Set("client_id", static_cast<int>(id.frame_sink_id().client_id()))
.Set("sink_id", static_cast<int>(id.frame_sink_id().sink_id()))
.Set("parent_seq",
static_cast<int>(id.local_surface_id().parent_sequence_number()))
.Set("child_seq",
static_cast<int>(id.local_surface_id().child_sequence_number()))
.Set("embed_token",
base::UnguessableTokenToValue(id.local_surface_id().embed_token()));
}
std::optional<SurfaceId> SurfaceIdFromDict(const base::Value::Dict& dict) {
std::optional<int> client_id = dict.FindInt("client_id");
std::optional<int> sink_id = dict.FindInt("sink_id");
std::optional<int> parent_seq = dict.FindInt("parent_seq");
std::optional<int> child_seq = dict.FindInt("child_seq");
const base::Value* embed_token_value = dict.Find("embed_token");
if (!client_id || !sink_id || !parent_seq || !child_seq || !embed_token_value)
return std::nullopt;
auto token = base::ValueToUnguessableToken(*embed_token_value);
if (!token) {
return std::nullopt;
}
return SurfaceId(FrameSinkId(*client_id, *sink_id),
LocalSurfaceId(*parent_seq, *child_seq, *token));
}
base::Value::Dict SurfaceRangeToDict(const SurfaceRange& range) {
base::Value::Dict dict;
if (range.start().has_value())
dict.Set("start", SurfaceIdToDict(*(range.start())));
dict.Set("end", SurfaceIdToDict(range.end()));
return dict;
}
std::optional<SurfaceRange> SurfaceRangeFromDict(
const base::Value::Dict& dict) {
const base::Value::Dict* start_dict = dict.FindDict("start");
const base::Value::Dict* end_dict = dict.FindDict("end");
if (!end_dict)
return std::nullopt;
std::optional<SurfaceId> start =
start_dict ? SurfaceIdFromDict(*start_dict) : std::nullopt;
std::optional<SurfaceId> end = SurfaceIdFromDict(*end_dict);
if (!end || (start_dict && !start))
return std::nullopt;
return SurfaceRange(start, *end);
}
int GetSharedQuadStateIndex(const SharedQuadStateList& shared_quad_state_list,
const SharedQuadState* shared_quad_state) {
for (auto iter = shared_quad_state_list.begin();
iter != shared_quad_state_list.end(); ++iter) {
if (*iter == shared_quad_state)
return static_cast<int>(iter.index());
}
return -1;
}
#define MAP_STRING_TO_MATERIAL(NAME) \
if (str == #NAME) \
return static_cast<int>(DrawQuad::Material::NAME);
int StringToDrawQuadMaterial(const std::string& str) {
MAP_STRING_TO_MATERIAL(kInvalid)
MAP_STRING_TO_MATERIAL(kDebugBorder)
MAP_STRING_TO_MATERIAL(kPictureContent)
MAP_STRING_TO_MATERIAL(kCompositorRenderPass)
MAP_STRING_TO_MATERIAL(kSharedElement)
MAP_STRING_TO_MATERIAL(kSolidColor)
MAP_STRING_TO_MATERIAL(kSurfaceContent)
MAP_STRING_TO_MATERIAL(kTextureContent)
MAP_STRING_TO_MATERIAL(kTiledContent)
MAP_STRING_TO_MATERIAL(kVideoHole)
return -1;
}
#undef MAP_STRING_TO_MATERIAL
void DrawQuadCommonToDict(const DrawQuad* draw_quad,
base::Value::Dict* dict,
const SharedQuadStateList& shared_quad_state_list) {
DCHECK(draw_quad);
DCHECK(dict);
dict->Set("material", DrawQuadMaterialToString(draw_quad->material));
dict->Set("rect", RectToDict(draw_quad->rect));
dict->Set("visible_rect", RectToDict(draw_quad->visible_rect));
dict->Set("needs_blending", draw_quad->needs_blending);
int shared_quad_state_index = GetSharedQuadStateIndex(
shared_quad_state_list, draw_quad->shared_quad_state);
DCHECK_LE(0, shared_quad_state_index);
dict->Set("shared_quad_state_index", shared_quad_state_index);
dict->Set("resources", DrawQuadResourceToList(draw_quad->resource_id));
}
void ContentDrawQuadCommonToDict(const ContentDrawQuadBase* draw_quad,
base::Value::Dict* dict) {
DCHECK(draw_quad);
DCHECK(dict);
dict->Set("tex_coord_rect", RectFToDict(draw_quad->tex_coord_rect));
dict->Set("nearest_neighbor", draw_quad->nearest_neighbor);
dict->Set("force_anti_aliasing_off", draw_quad->force_anti_aliasing_off);
}
struct DrawQuadCommon {
DrawQuad::Material material = DrawQuad::Material::kInvalid;
gfx::Rect rect;
gfx::Rect visible_rect;
bool needs_blending = false;
// RAW_PTR_EXCLUSION: Performance reasons (based on analysis of speedometer3).
RAW_PTR_EXCLUSION const SharedQuadState* shared_quad_state = nullptr;
ResourceId resource_id;
};
std::optional<DrawQuadCommon> GetDrawQuadCommonFromDict(
const base::Value::Dict& dict,
const SharedQuadStateList& shared_quad_state_list) {
const std::string* material = dict.FindString("material");
const base::Value::Dict* rect = dict.FindDict("rect");
const base::Value::Dict* visible_rect = dict.FindDict("visible_rect");
std::optional<bool> needs_blending = dict.FindBool("needs_blending");
std::optional<int> shared_quad_state_index =
dict.FindInt("shared_quad_state_index");
const base::Value::List* resources = dict.FindList("resources");
if (!material || !rect || !visible_rect || !needs_blending ||
!shared_quad_state_index || !resources) {
return std::nullopt;
}
int material_index = StringToDrawQuadMaterial(*material);
if (material_index < 0)
return std::nullopt;
int sqs_index = shared_quad_state_index.value();
if (sqs_index < 0 ||
static_cast<size_t>(sqs_index) >= shared_quad_state_list.size()) {
return std::nullopt;
}
gfx::Rect t_rect, t_visible_rect;
if (!RectFromDict(*rect, &t_rect) ||
!RectFromDict(*visible_rect, &t_visible_rect)) {
return std::nullopt;
}
ResourceId t_resource;
if (!DrawQuadResourceFromList(*resources, t_resource)) {
return std::nullopt;
}
return DrawQuadCommon{static_cast<DrawQuad::Material>(material_index),
t_rect,
t_visible_rect,
needs_blending.value(),
shared_quad_state_list.ElementAt(sqs_index),
t_resource};
}
struct ContentDrawQuadCommon {
gfx::RectF tex_coord_rect;
bool is_premultiplied;
bool nearest_neighbor;
bool force_anti_aliasing_off;
};
std::optional<ContentDrawQuadCommon> GetContentDrawQuadCommonFromDict(
const base::Value::Dict& dict) {
const base::Value::Dict* tex_coord_rect = dict.FindDict("tex_coord_rect");
std::optional<bool> is_premultiplied = dict.FindBool("is_premultiplied");
std::optional<bool> nearest_neighbor = dict.FindBool("nearest_neighbor");
std::optional<bool> force_anti_aliasing_off =
dict.FindBool("force_anti_aliasing_off");
if (!tex_coord_rect || !is_premultiplied || !nearest_neighbor ||
!force_anti_aliasing_off) {
return std::nullopt;
}
gfx::RectF t_tex_coord_rect;
if (!RectFFromDict(*tex_coord_rect, &t_tex_coord_rect)) {
return std::nullopt;
}
return ContentDrawQuadCommon{t_tex_coord_rect, is_premultiplied.value(),
nearest_neighbor.value(),
force_anti_aliasing_off.value()};
}
void CompositorRenderPassDrawQuadToDict(
const CompositorRenderPassDrawQuad* draw_quad,
base::Value::Dict* dict) {
DCHECK(draw_quad);
DCHECK(dict);
dict->Set(
"render_pass_id",
base::NumberToString(static_cast<uint64_t>(draw_quad->render_pass_id)));
dict->Set("mask_uv_rect", RectFToDict(draw_quad->mask_uv_rect));
dict->Set("mask_texture_size", SizeToDict(draw_quad->mask_texture_size));
dict->Set("filters_scale", Vector2dFToDict(draw_quad->filters_scale));
dict->Set("filters_origin", PointFToDict(draw_quad->filters_origin));
dict->Set("tex_coord_rect", RectFToDict(draw_quad->tex_coord_rect));
dict->Set("backdrop_filter_quality", draw_quad->backdrop_filter_quality);
dict->Set("force_anti_aliasing_off", draw_quad->force_anti_aliasing_off);
dict->Set("intersects_damage_under", draw_quad->intersects_damage_under);
}
void SolidColorDrawQuadToDict(const SolidColorDrawQuad* draw_quad,
base::Value::Dict* dict) {
DCHECK(draw_quad);
DCHECK(dict);
dict->Set("color", SkColor4fToDict(draw_quad->color));
dict->Set("force_anti_aliasing_off", draw_quad->force_anti_aliasing_off);
}
#define MAP_VIDEO_TYPE_TO_STRING(NAME) \
case gfx::ProtectedVideoType::NAME: \
return #NAME;
const char* ProtectedVideoTypeToString(gfx::ProtectedVideoType type) {
switch (type) {
MAP_VIDEO_TYPE_TO_STRING(kClear)
MAP_VIDEO_TYPE_TO_STRING(kSoftwareProtected)
MAP_VIDEO_TYPE_TO_STRING(kHardwareProtected)
default:
NOTREACHED();
}
}
#undef MAP_VIDEO_TYPE_TO_STRING
#define MAP_STRING_TO_VIDEO_TYPE(NAME) \
if (str == #NAME) \
return static_cast<int>(gfx::ProtectedVideoType::NAME);
int StringToProtectedVideoType(const std::string& str) {
MAP_STRING_TO_VIDEO_TYPE(kClear)
MAP_STRING_TO_VIDEO_TYPE(kSoftwareProtected)
MAP_STRING_TO_VIDEO_TYPE(kHardwareProtected)
return -1;
}
#undef MAP_STRING_TO_VIDEO_TYPE
void SurfaceDrawQuadToDict(const SurfaceDrawQuad* draw_quad,
base::Value::Dict* dict) {
DCHECK(draw_quad);
DCHECK(dict);
dict->Set("surface_range", SurfaceRangeToDict(draw_quad->surface_range));
dict->Set("default_background_color",
SkColor4fToDict(draw_quad->default_background_color));
dict->Set("stretch_content", draw_quad->stretch_content_to_fill_bounds);
dict->Set("is_reflection", draw_quad->is_reflection);
dict->Set("allow_merge", draw_quad->allow_merge);
}
void TextureDrawQuadToDict(const TextureDrawQuad* draw_quad,
base::Value::Dict* dict) {
DCHECK(draw_quad);
DCHECK(dict);
// Set premultiplied_alpha to not break backwards-compatibility with unit test
// data.
dict->Set("premultiplied_alpha", true);
dict->Set("uv_top_left", PointFToDict(draw_quad->uv_top_left));
dict->Set("uv_bottom_right", PointFToDict(draw_quad->uv_bottom_right));
dict->Set("background_color", SkColor4fToDict(draw_quad->background_color));
// TODO(crbug.com/40942150): Update
// "components/test/data/viz/render_pass_data/" to reflect the deprecation of
// vertex opacity.
float vertex_opacity[4] = {1.f, 1.0f, 1.0f, 1.f};
dict->Set("vertex_opacity", FloatArrayToList(vertex_opacity));
dict->Set("nearest_neighbor", draw_quad->nearest_neighbor);
dict->Set("secure_output_only", draw_quad->secure_output_only);
dict->Set("protected_video_type",
ProtectedVideoTypeToString(draw_quad->protected_video_type));
if (draw_quad->damage_rect.has_value()) {
dict->Set("damage_rect", RectToDict(draw_quad->damage_rect.value()));
}
}
void TileDrawQuadToDict(const TileDrawQuad* draw_quad,
base::Value::Dict* dict) {
DCHECK(draw_quad);
DCHECK(dict);
// Set is_premultiplied to not break backwards-compatibility with unit test
// data.
dict->Set("is_premultiplied", true);
ContentDrawQuadCommonToDict(draw_quad, dict);
}
void VideoHoleDrawQuadToDict(const VideoHoleDrawQuad* draw_quad,
base::Value::Dict* dict) {
DCHECK(draw_quad);
DCHECK(dict);
dict->Set("overlay_plane_id.empty", draw_quad->overlay_plane_id.is_empty());
if (!draw_quad->overlay_plane_id.is_empty()) {
dict->Set("overlay_plane_id.unguessable_token",
base::UnguessableTokenToValue(draw_quad->overlay_plane_id));
}
}
#define UNEXPECTED_DRAW_QUAD_TYPE(NAME) \
case DrawQuad::Material::NAME: \
NOTREACHED() << "Unexpected " << #NAME;
#define WRITE_DRAW_QUAD_TYPE_FIELDS(NAME, TYPE) \
case DrawQuad::Material::NAME: \
TYPE##ToDict(reinterpret_cast<const TYPE*>(draw_quad), &dict); \
break;
base::Value::Dict DrawQuadToDict(
const DrawQuad* draw_quad,
const SharedQuadStateList& shared_quad_state_list) {
DCHECK(draw_quad);
base::Value::Dict dict;
DrawQuadCommonToDict(draw_quad, &dict, shared_quad_state_list);
switch (draw_quad->material) {
WRITE_DRAW_QUAD_TYPE_FIELDS(kCompositorRenderPass,
CompositorRenderPassDrawQuad)
WRITE_DRAW_QUAD_TYPE_FIELDS(kSolidColor, SolidColorDrawQuad)
WRITE_DRAW_QUAD_TYPE_FIELDS(kSurfaceContent, SurfaceDrawQuad)
WRITE_DRAW_QUAD_TYPE_FIELDS(kTextureContent, TextureDrawQuad)
WRITE_DRAW_QUAD_TYPE_FIELDS(kTiledContent, TileDrawQuad)
WRITE_DRAW_QUAD_TYPE_FIELDS(kVideoHole, VideoHoleDrawQuad)
UNEXPECTED_DRAW_QUAD_TYPE(kPictureContent)
default:
break;
}
return dict;
}
#undef WRITE_DRAW_QUAD_TYPE_FIELDS
#undef UNEXPECTED_DRAW_QUAD_TYPE
base::Value::List QuadListToList(
const QuadList& quad_list,
const SharedQuadStateList& shared_quad_state_list) {
base::Value::List list;
for (size_t ii = 0; ii < quad_list.size(); ++ii) {
list.Append(
DrawQuadToDict(quad_list.ElementAt(ii), shared_quad_state_list));
}
return list;
}
bool CompositorRenderPassDrawQuadFromDict(
const base::Value::Dict& dict,
const DrawQuadCommon& common,
CompositorRenderPassDrawQuad* draw_quad) {
DCHECK(draw_quad);
const std::string* render_pass_id = dict.FindString("render_pass_id");
const base::Value::Dict* mask_uv_rect = dict.FindDict("mask_uv_rect");
const base::Value::Dict* mask_texture_size =
dict.FindDict("mask_texture_size");
const base::Value::Dict* filters_scale = dict.FindDict("filters_scale");
const base::Value::Dict* filters_origin = dict.FindDict("filters_origin");
const base::Value::Dict* tex_coord_rect = dict.FindDict("tex_coord_rect");
std::optional<double> backdrop_filter_quality =
dict.FindDouble("backdrop_filter_quality");
std::optional<bool> force_anti_aliasing_off =
dict.FindBool("force_anti_aliasing_off");
std::optional<bool> intersects_damage_under =
dict.FindBool("intersects_damage_under");
if (!render_pass_id || !mask_uv_rect || !mask_texture_size ||
!filters_scale || !filters_origin || !tex_coord_rect ||
!backdrop_filter_quality || !force_anti_aliasing_off) {
return false;
}
uint64_t render_pass_id_as_int;
gfx::RectF t_mask_uv_rect, t_tex_coord_rect;
gfx::Size t_mask_texture_size;
gfx::Vector2dF t_filters_scale;
gfx::PointF t_filters_origin;
if (!base::StringToUint64(*render_pass_id, &render_pass_id_as_int) ||
!RectFFromDict(*mask_uv_rect, &t_mask_uv_rect) ||
!SizeFromDict(*mask_texture_size, &t_mask_texture_size) ||
!Vector2dFFromDict(*filters_scale, &t_filters_scale) ||
!PointFFromDict(*filters_origin, &t_filters_origin) ||
!RectFFromDict(*tex_coord_rect, &t_tex_coord_rect)) {
return false;
}
CompositorRenderPassId t_render_pass_id{render_pass_id_as_int};
ResourceId mask_resource_id = common.resource_id;
draw_quad->SetAll(
common.shared_quad_state, common.rect, common.visible_rect,
common.needs_blending, t_render_pass_id, mask_resource_id, t_mask_uv_rect,
t_mask_texture_size, t_filters_scale, t_filters_origin, t_tex_coord_rect,
force_anti_aliasing_off.value(), backdrop_filter_quality.value(),
intersects_damage_under && intersects_damage_under.value());
return true;
}
bool SolidColorDrawQuadFromDict(const base::Value::Dict& dict,
const DrawQuadCommon& common,
SolidColorDrawQuad* draw_quad) {
DCHECK(draw_quad);
std::optional<bool> force_anti_aliasing_off =
dict.FindBool("force_anti_aliasing_off");
if (!force_anti_aliasing_off)
return false;
SkColor4f t_color;
if (!ColorFromDict(dict, "color", &t_color))
return false;
draw_quad->SetAll(common.shared_quad_state, common.rect, common.visible_rect,
common.needs_blending, t_color,
force_anti_aliasing_off.value());
return true;
}
bool SurfaceDrawQuadFromDict(const base::Value::Dict& dict,
const DrawQuadCommon& common,
SurfaceDrawQuad* draw_quad) {
DCHECK(draw_quad);
const base::Value::Dict* surface_range_dict = dict.FindDict("surface_range");
if (!surface_range_dict)
return false;
std::optional<SurfaceRange> surface_range =
SurfaceRangeFromDict(*surface_range_dict);
std::optional<bool> stretch_content = dict.FindBool("stretch_content");
std::optional<bool> is_reflection = dict.FindBool("is_reflection");
std::optional<bool> allow_merge = dict.FindBool("allow_merge");
if (!surface_range || !stretch_content || !is_reflection || !allow_merge)
return false;
SkColor4f t_default_background_color;
if (!ColorFromDict(dict, "default_background_color",
&t_default_background_color)) {
return false;
}
draw_quad->SetAll(common.shared_quad_state, common.rect, common.visible_rect,
common.needs_blending, *surface_range,
t_default_background_color, *stretch_content,
*is_reflection, *allow_merge);
return true;
}
bool TextureDrawQuadFromDict(const base::Value::Dict& dict,
const DrawQuadCommon& common,
TextureDrawQuad* draw_quad) {
DCHECK(draw_quad);
const base::Value::Dict* uv_top_left = dict.FindDict("uv_top_left");
const base::Value::Dict* uv_bottom_right = dict.FindDict("uv_bottom_right");
// TODO(crbug.com/40942150): Update
// "components/test/data/viz/render_pass_data/" to reflect the deprecation of
// vertex opacity.
const base::Value::List* vertex_opacity = dict.FindList("vertex_opacity");
const base::Value::Dict* damage_rect = dict.FindDict("damage_rect");
std::optional<bool> nearest_neighbor = dict.FindBool("nearest_neighbor");
std::optional<bool> secure_output_only = dict.FindBool("secure_output_only");
const std::string* protected_video_type =
dict.FindString("protected_video_type");
if (!uv_top_left || !uv_bottom_right || !vertex_opacity ||
!nearest_neighbor || !secure_output_only || !protected_video_type) {
return false;
}
int protected_video_type_index =
StringToProtectedVideoType(*protected_video_type);
if (protected_video_type_index < 0)
return false;
gfx::PointF t_uv_top_left, t_uv_bottom_right;
SkColor4f t_background_color;
if (!PointFFromDict(*uv_top_left, &t_uv_top_left) ||
!PointFFromDict(*uv_bottom_right, &t_uv_bottom_right) ||
!ColorFromDict(dict, "background_color", &t_background_color)) {
return false;
}
ResourceId resource_id = common.resource_id;
draw_quad->SetAll(
common.shared_quad_state, common.rect, common.visible_rect,
common.needs_blending, resource_id, t_uv_top_left, t_uv_bottom_right,
t_background_color, nearest_neighbor.value(), secure_output_only.value(),
static_cast<gfx::ProtectedVideoType>(protected_video_type_index));
gfx::Rect t_damage_rect;
if (damage_rect && RectFromDict(*damage_rect, &t_damage_rect)) {
draw_quad->damage_rect = t_damage_rect;
}
return true;
}
bool TileDrawQuadFromDict(const base::Value::Dict& dict,
const DrawQuadCommon& common,
TileDrawQuad* draw_quad) {
DCHECK(draw_quad);
std::optional<ContentDrawQuadCommon> content_common =
GetContentDrawQuadCommonFromDict(dict);
if (!content_common)
return false;
ResourceId resource_id = common.resource_id;
draw_quad->SetAll(common.shared_quad_state, common.rect, common.visible_rect,
common.needs_blending, resource_id,
content_common->tex_coord_rect,
content_common->nearest_neighbor,
content_common->force_anti_aliasing_off);
return true;
}
bool VideoHoleDrawQuadFromDict(const base::Value::Dict& dict,
const DrawQuadCommon& common,
VideoHoleDrawQuad* draw_quad) {
DCHECK(draw_quad);
std::optional<bool> overlay_plane_id_empty =
dict.FindBool("overlay_plane_id.empty");
if (!overlay_plane_id_empty)
return false;
base::UnguessableToken overlay_plane_id;
DCHECK(overlay_plane_id.is_empty());
if (!overlay_plane_id_empty.value()) {
std::optional<base::UnguessableToken> deserialized_overlay_plane_id =
base::ValueToUnguessableToken(
dict.Find("overlay_plane_id.unguessable_token"));
if (!deserialized_overlay_plane_id) {
return false;
}
overlay_plane_id = deserialized_overlay_plane_id.value();
}
draw_quad->SetAll(common.shared_quad_state, common.rect, common.visible_rect,
common.needs_blending, overlay_plane_id);
return true;
}
#define UNEXPECTED_DRAW_QUAD_TYPE(NAME) \
case DrawQuad::Material::NAME: \
NOTREACHED() << "Unexpected " << #NAME;
#define GET_QUAD_FROM_DICT(NAME, TYPE) \
case DrawQuad::Material::NAME: { \
TYPE* quad = quads.AllocateAndConstruct<TYPE>(); \
if (!list[ii].is_dict()) \
return false; \
if (!TYPE##FromDict(list[ii].GetDict(), common.value(), quad)) \
return false; \
} break;
bool QuadListFromList(const base::Value::List& list,
QuadList* quad_list,
const SharedQuadStateList& shared_quad_state_list) {
DCHECK(quad_list);
size_t size = list.size();
if (size == 0) {
quad_list->clear();
return true;
}
QuadList quads(size);
for (size_t ii = 0; ii < size; ++ii) {
if (!list[ii].is_dict())
return false;
std::optional<DrawQuadCommon> common =
GetDrawQuadCommonFromDict(list[ii].GetDict(), shared_quad_state_list);
if (!common)
return false;
switch (common->material) {
GET_QUAD_FROM_DICT(kCompositorRenderPass, CompositorRenderPassDrawQuad)
GET_QUAD_FROM_DICT(kSolidColor, SolidColorDrawQuad)
GET_QUAD_FROM_DICT(kSurfaceContent, SurfaceDrawQuad)
GET_QUAD_FROM_DICT(kTextureContent, TextureDrawQuad)
GET_QUAD_FROM_DICT(kTiledContent, TileDrawQuad)
GET_QUAD_FROM_DICT(kVideoHole, VideoHoleDrawQuad)
UNEXPECTED_DRAW_QUAD_TYPE(kPictureContent)
default:
break;
}
}
quad_list->swap(quads);
return true;
}
#undef GET_QUAD_FROM_DICT
#undef UNEXPECTED_DRAW_QUAD_TYPE
base::Value::Dict SharedQuadStateToDict(const SharedQuadState& sqs) {
auto dict =
base::Value::Dict()
.Set("quad_to_target_transform",
TransformToList(sqs.quad_to_target_transform))
.Set("quad_layer_rect", RectToDict(sqs.quad_layer_rect))
.Set("visible_quad_layer_rect",
RectToDict(sqs.visible_quad_layer_rect))
.Set("mask_filter_info", MaskFilterInfoToDict(sqs.mask_filter_info))
.Set("are_contents_opaque", sqs.are_contents_opaque)
.Set("opacity", sqs.opacity)
.Set("blend_mode", BlendModeToString(sqs.blend_mode))
.Set("sorting_context_id", sqs.sorting_context_id)
.Set("is_fast_rounded_corner", sqs.is_fast_rounded_corner);
if (sqs.clip_rect) {
dict.Set("clip_rect", RectToDict(*sqs.clip_rect));
}
return dict;
}
#define MAP_STRING_TO_BLEND_MODE(NAME) \
if (str == #NAME) \
return static_cast<int>(SkBlendMode::NAME);
int StringToBlendMode(const std::string& str) {
MAP_STRING_TO_BLEND_MODE(kClear)
MAP_STRING_TO_BLEND_MODE(kSrc)
MAP_STRING_TO_BLEND_MODE(kDst)
MAP_STRING_TO_BLEND_MODE(kSrcOver)
MAP_STRING_TO_BLEND_MODE(kDstOver)
MAP_STRING_TO_BLEND_MODE(kSrcIn)
MAP_STRING_TO_BLEND_MODE(kDstIn)
MAP_STRING_TO_BLEND_MODE(kSrcOut)
MAP_STRING_TO_BLEND_MODE(kDstOut)
MAP_STRING_TO_BLEND_MODE(kSrcATop)
MAP_STRING_TO_BLEND_MODE(kDstATop)
MAP_STRING_TO_BLEND_MODE(kXor)
MAP_STRING_TO_BLEND_MODE(kPlus)
MAP_STRING_TO_BLEND_MODE(kModulate)
MAP_STRING_TO_BLEND_MODE(kScreen)
MAP_STRING_TO_BLEND_MODE(kOverlay)
MAP_STRING_TO_BLEND_MODE(kDarken)
MAP_STRING_TO_BLEND_MODE(kLighten)
MAP_STRING_TO_BLEND_MODE(kColorDodge)
MAP_STRING_TO_BLEND_MODE(kColorBurn)
MAP_STRING_TO_BLEND_MODE(kHardLight)
MAP_STRING_TO_BLEND_MODE(kSoftLight)
MAP_STRING_TO_BLEND_MODE(kDifference)
MAP_STRING_TO_BLEND_MODE(kExclusion)
MAP_STRING_TO_BLEND_MODE(kMultiply)
MAP_STRING_TO_BLEND_MODE(kHue)
MAP_STRING_TO_BLEND_MODE(kSaturation)
MAP_STRING_TO_BLEND_MODE(kColor)
MAP_STRING_TO_BLEND_MODE(kLuminosity)
return -1;
}
#undef MAP_STRING_TO_BLEND_MODE
bool SharedQuadStateFromDict(const base::Value::Dict& dict,
SharedQuadState* sqs) {
DCHECK(sqs);
const base::Value::List* quad_to_target_transform =
dict.FindList("quad_to_target_transform");
const base::Value::Dict* quad_layer_rect = dict.FindDict("quad_layer_rect");
const base::Value::Dict* visible_quad_layer_rect =
dict.FindDict("visible_quad_layer_rect");
const base::Value::Dict* mask_filter_info = dict.FindDict("mask_filter_info");
const base::Value::Dict* clip_rect = dict.FindDict("clip_rect");
std::optional<bool> is_clipped = dict.FindBool("is_clipped");
std::optional<bool> are_contents_opaque =
dict.FindBool("are_contents_opaque");
std::optional<double> opacity = dict.FindDouble("opacity");
const std::string* blend_mode = dict.FindString("blend_mode");
std::optional<int> sorting_context_id = dict.FindInt("sorting_context_id");
std::optional<bool> is_fast_rounded_corner =
dict.FindBool("is_fast_rounded_corner");
if (!quad_to_target_transform || !quad_layer_rect ||
!visible_quad_layer_rect || !are_contents_opaque || !opacity ||
!blend_mode || !sorting_context_id || !is_fast_rounded_corner) {
return false;
}
gfx::Transform t_quad_to_target_transform;
gfx::Rect t_quad_layer_rect, t_visible_quad_layer_rect, t_clip_rect;
if (!TransformFromList(*quad_to_target_transform,
&t_quad_to_target_transform) ||
!RectFromDict(*quad_layer_rect, &t_quad_layer_rect) ||
!RectFromDict(*visible_quad_layer_rect, &t_visible_quad_layer_rect) ||
(clip_rect && !RectFromDict(*clip_rect, &t_clip_rect))) {
return false;
}
gfx::MaskFilterInfo t_mask_filter_info;
if (mask_filter_info &&
!MaskFilterInfoFromDict(*mask_filter_info, &t_mask_filter_info)) {
return false;
}
std::optional<gfx::Rect> clip_rect_opt;
// Some older files still use the is_clipped field. If it's present, we'll
// respect it, and ignore clip_rect if it's false.
if (is_clipped.has_value()) {
if (is_clipped.value()) {
clip_rect_opt = t_clip_rect;
}
} else if (clip_rect) {
clip_rect_opt = t_clip_rect;
}
int blend_mode_index = StringToBlendMode(*blend_mode);
DCHECK_GE(static_cast<int>(SkBlendMode::kLastMode), blend_mode_index);
if (blend_mode_index < 0)
return false;
SkBlendMode t_blend_mode = static_cast<SkBlendMode>(blend_mode_index);
sqs->SetAll(t_quad_to_target_transform, t_quad_layer_rect,
t_visible_quad_layer_rect, t_mask_filter_info, clip_rect_opt,
are_contents_opaque.value(), static_cast<float>(opacity.value()),
t_blend_mode, sorting_context_id.value(), /*layer_id=*/0u,
is_fast_rounded_corner.value());
return true;
}
base::Value::List SharedQuadStateListToList(
const SharedQuadStateList& shared_quad_state_list) {
base::Value::List list;
for (size_t ii = 0; ii < shared_quad_state_list.size(); ++ii)
list.Append(SharedQuadStateToDict(*(shared_quad_state_list.ElementAt(ii))));
return list;
}
bool SharedQuadStateListFromList(const base::Value::List& list,
SharedQuadStateList* shared_quad_state_list) {
DCHECK(shared_quad_state_list);
size_t size = list.size();
SharedQuadStateList states(alignof(SharedQuadState), sizeof(SharedQuadState),
size);
for (size_t ii = 0; ii < size; ++ii) {
if (!list[ii].is_dict())
return false;
SharedQuadState* sqs = states.AllocateAndConstruct<SharedQuadState>();
if (!SharedQuadStateFromDict(list[ii].GetDict(), sqs))
return false;
}
shared_quad_state_list->swap(states);
return true;
}
base::Value::Dict GetRenderPassMetadata(
const CompositorRenderPass& render_pass) {
return base::Value::Dict()
.Set("render_pass_id",
base::NumberToString(static_cast<uint64_t>(render_pass.id)))
.Set("quad_count", static_cast<int>(render_pass.quad_list.size()))
.Set("shared_quad_state_count",
static_cast<int>(render_pass.shared_quad_state_list.size()));
}
base::Value::List GetRenderPassListMetadata(
const CompositorRenderPassList& render_pass_list) {
base::Value::List metadata;
for (const auto& render_pass : render_pass_list) {
metadata.Append(GetRenderPassMetadata(*(render_pass.get())));
}
return metadata;
}
} // namespace
#define MAP_BLEND_MODE_TO_STRING(NAME) \
case SkBlendMode::NAME: \
return #NAME;
const char* BlendModeToString(SkBlendMode blend_mode) {
switch (blend_mode) {
MAP_BLEND_MODE_TO_STRING(kClear)
MAP_BLEND_MODE_TO_STRING(kSrc)
MAP_BLEND_MODE_TO_STRING(kDst)
MAP_BLEND_MODE_TO_STRING(kSrcOver)
MAP_BLEND_MODE_TO_STRING(kDstOver)
MAP_BLEND_MODE_TO_STRING(kSrcIn)
MAP_BLEND_MODE_TO_STRING(kDstIn)
MAP_BLEND_MODE_TO_STRING(kSrcOut)
MAP_BLEND_MODE_TO_STRING(kDstOut)
MAP_BLEND_MODE_TO_STRING(kSrcATop)
MAP_BLEND_MODE_TO_STRING(kDstATop)
MAP_BLEND_MODE_TO_STRING(kXor)
MAP_BLEND_MODE_TO_STRING(kPlus)
MAP_BLEND_MODE_TO_STRING(kModulate)
MAP_BLEND_MODE_TO_STRING(kScreen)
MAP_BLEND_MODE_TO_STRING(kOverlay)
MAP_BLEND_MODE_TO_STRING(kDarken)
MAP_BLEND_MODE_TO_STRING(kLighten)
MAP_BLEND_MODE_TO_STRING(kColorDodge)
MAP_BLEND_MODE_TO_STRING(kColorBurn)
MAP_BLEND_MODE_TO_STRING(kHardLight)
MAP_BLEND_MODE_TO_STRING(kSoftLight)
MAP_BLEND_MODE_TO_STRING(kDifference)
MAP_BLEND_MODE_TO_STRING(kExclusion)
MAP_BLEND_MODE_TO_STRING(kMultiply)
MAP_BLEND_MODE_TO_STRING(kHue)
MAP_BLEND_MODE_TO_STRING(kSaturation)
MAP_BLEND_MODE_TO_STRING(kColor)
MAP_BLEND_MODE_TO_STRING(kLuminosity)
default:
NOTREACHED();
}
}
#undef MAP_BLEND_MODE_TO_STRING
#define MAP_MATERIAL_TO_STRING(NAME) \
case DrawQuad::Material::NAME: \
return #NAME;
const char* DrawQuadMaterialToString(DrawQuad::Material material) {
switch (material) {
MAP_MATERIAL_TO_STRING(kInvalid)
MAP_MATERIAL_TO_STRING(kDebugBorder)
MAP_MATERIAL_TO_STRING(kPictureContent)
MAP_MATERIAL_TO_STRING(kCompositorRenderPass)
MAP_MATERIAL_TO_STRING(kSharedElement)
MAP_MATERIAL_TO_STRING(kSolidColor)
MAP_MATERIAL_TO_STRING(kSurfaceContent)
MAP_MATERIAL_TO_STRING(kTextureContent)
MAP_MATERIAL_TO_STRING(kTiledContent)
MAP_MATERIAL_TO_STRING(kVideoHole)
default:
NOTREACHED();
}
}
#undef MAP_MATERIAL_TO_STRING
base::Value::Dict CompositorRenderPassToDict(
const CompositorRenderPass& render_pass) {
base::Value::Dict dict;
if (ProcessRenderPassField(kRenderPassID))
dict.Set("id", base::NumberToString(static_cast<uint64_t>(render_pass.id)));
if (ProcessRenderPassField(kRenderPassOutputRect))
dict.Set("output_rect", RectToDict(render_pass.output_rect));
if (ProcessRenderPassField(kRenderPassDamageRect))
dict.Set("damage_rect", RectToDict(render_pass.damage_rect));
if (ProcessRenderPassField(kRenderPassTransformToRootTarget)) {
dict.Set("transform_to_root_target",
TransformToList(render_pass.transform_to_root_target));
}
if (ProcessRenderPassField(kRenderPassFilters))
dict.Set("filters", FilterOperationsToList(render_pass.filters));
if (ProcessRenderPassField(kRenderPassBackdropFilters)) {
dict.Set("backdrop_filters",
FilterOperationsToList(render_pass.backdrop_filters));
}
if (ProcessRenderPassField(kRenderPassBackdropFilterBounds) &&
render_pass.backdrop_filter_bounds) {
dict.Set("backdrop_filter_bounds",
SkPathToBlob(render_pass.backdrop_filter_bounds.value()));
}
if (ProcessRenderPassField(kRenderPassColorSpace)) {
// CompositorRenderPasses used to have a color space field, but this was
// removed in favor of color usage. https://crbug.com/1049334
gfx::ColorSpace render_pass_color_space = gfx::ColorSpace::CreateSRGB();
dict.Set("color_space", ColorSpaceToDict(render_pass_color_space));
}
if (ProcessRenderPassField(kRenderPassHasTransparentBackground)) {
dict.Set("has_transparent_background",
render_pass.has_transparent_background);
}
if (ProcessRenderPassField(kRenderPassCacheRenderPass))
dict.Set("cache_render_pass", render_pass.cache_render_pass);
if (ProcessRenderPassField(kRenderPassHasPreQuadDamage)) {
// Set the dict value only if it is not the non default value.
if (render_pass.has_per_quad_damage)
dict.Set("has_per_quad_damage", render_pass.has_per_quad_damage);
}
if (ProcessRenderPassField(kRenderPassHasDamageFromContributingContent)) {
dict.Set("has_damage_from_contributing_content",
render_pass.has_damage_from_contributing_content);
}
if (ProcessRenderPassField(kRenderPassGenerateMipmap))
dict.Set("generate_mipmap", render_pass.generate_mipmap);
if (ProcessRenderPassField(kRenderPassCopyRequests)) {
// TODO(zmo): Write copy_requests.
}
if (ProcessRenderPassField(kRenderPassQuadList)) {
dict.Set("quad_list", QuadListToList(render_pass.quad_list,
render_pass.shared_quad_state_list));
}
if (ProcessRenderPassField(kRenderPassSharedQuadStateList)) {
dict.Set("shared_quad_state_list",
SharedQuadStateListToList(render_pass.shared_quad_state_list));
}
return dict;
}
std::unique_ptr<CompositorRenderPass> CompositorRenderPassFromDict(
const base::Value::Dict& dict) {
auto pass = CompositorRenderPass::Create();
if (ProcessRenderPassField(kRenderPassID)) {
const std::string* id = dict.FindString("id");
if (!id)
return nullptr;
uint64_t pass_id_as_int = 0;
if (!base::StringToUint64(*id, &pass_id_as_int))
return nullptr;
pass->id = CompositorRenderPassId{pass_id_as_int};
}
if (ProcessRenderPassField(kRenderPassOutputRect)) {
const base::Value::Dict* output_rect = dict.FindDict("output_rect");
if (!output_rect)
return nullptr;
if (!RectFromDict(*output_rect, &(pass->output_rect)))
return nullptr;
}
if (ProcessRenderPassField(kRenderPassDamageRect)) {
const base::Value::Dict* damage_rect = dict.FindDict("damage_rect");
if (!damage_rect)
return nullptr;
if (!RectFromDict(*damage_rect, &(pass->damage_rect)))
return nullptr;
}
if (ProcessRenderPassField(kRenderPassTransformToRootTarget)) {
const base::Value::List* transform_to_root_target =
dict.FindList("transform_to_root_target");
if (!transform_to_root_target)
return nullptr;
if (!TransformFromList(*transform_to_root_target,
&(pass->transform_to_root_target))) {
return nullptr;
}
}
if (ProcessRenderPassField(kRenderPassFilters)) {
const base::Value::List* filters = dict.FindList("filters");
if (!filters)
return nullptr;
if (!FilterOperationsFromList(*filters, &(pass->filters)))
return nullptr;
}
if (ProcessRenderPassField(kRenderPassBackdropFilters)) {
const base::Value::List* backdrop_filters =
dict.FindList("backdrop_filters");
if (!backdrop_filters)
return nullptr;
if (!FilterOperationsFromList(*backdrop_filters,
&(pass->backdrop_filters))) {
return nullptr;
}
}
if (ProcessRenderPassField(kRenderPassBackdropFilterBounds)) {
const base::Value::BlobStorage* backdrop_filter_bounds =
dict.FindBlob("backdrop_filter_bounds");
if (backdrop_filter_bounds) {
SkPath bounds;
if (!SkPathFromBlob(*backdrop_filter_bounds, &bounds)) {
return nullptr;
}
pass->backdrop_filter_bounds = bounds;
}
}
if (ProcessRenderPassField(kRenderPassColorSpace)) {
const base::Value::Dict* color_space = dict.FindDict("color_space");
if (!color_space)
return nullptr;
// CompositorRenderPasses used to have a color space field, but this was
// removed in favor of color usage. https://crbug.com/1049334
gfx::ColorSpace pass_color_space = gfx::ColorSpace::CreateSRGB();
if (!ColorSpaceFromDict(*color_space, &pass_color_space))
return nullptr;
}
if (ProcessRenderPassField(kRenderPassHasTransparentBackground)) {
const std::optional<bool> has_transparent_background =
dict.FindBool("has_transparent_background");
if (!has_transparent_background)
return nullptr;
pass->has_transparent_background = has_transparent_background.value();
}
if (ProcessRenderPassField(kRenderPassCacheRenderPass)) {
const std::optional<bool> cache_render_pass =
dict.FindBool("cache_render_pass");
if (!cache_render_pass)
return nullptr;
pass->cache_render_pass = cache_render_pass.value();
}
if (ProcessRenderPassField(kRenderPassHasPreQuadDamage)) {
const std::optional<bool> has_per_quad_damage =
dict.FindBool("has_per_quad_damage");
if (has_per_quad_damage)
pass->has_per_quad_damage = has_per_quad_damage.value();
}
if (ProcessRenderPassField(kRenderPassHasDamageFromContributingContent)) {
const std::optional<bool> has_damage_from_contributing_content =
dict.FindBool("has_damage_from_contributing_content");
if (!has_damage_from_contributing_content)
return nullptr;
pass->has_damage_from_contributing_content =
has_damage_from_contributing_content.value();
}
if (ProcessRenderPassField(kRenderPassGenerateMipmap)) {
const std::optional<bool> generate_mipmap =
dict.FindBool("generate_mipmap");
if (!generate_mipmap)
return nullptr;
pass->generate_mipmap = generate_mipmap.value();
}
if (ProcessRenderPassField(kRenderPassCopyRequests)) {
// TODO(zmo): Read copy_requests.
}
// shared_quad_state_list has to be processed before quad_list.
if (ProcessRenderPassField(kRenderPassSharedQuadStateList)) {
const base::Value::List* shared_quad_state_list =
dict.FindList("shared_quad_state_list");
if (!shared_quad_state_list)
return nullptr;
if (!SharedQuadStateListFromList(*shared_quad_state_list,
&(pass->shared_quad_state_list))) {
return nullptr;
}
}
if (ProcessRenderPassField(kRenderPassQuadList)) {
const base::Value::List* quad_list = dict.FindList("quad_list");
if (!quad_list)
return nullptr;
if (!QuadListFromList(*quad_list, &(pass->quad_list),
pass->shared_quad_state_list)) {
return nullptr;
}
}
return pass;
}
base::Value::Dict CompositorRenderPassListToDict(
const CompositorRenderPassList& render_pass_list) {
base::Value::List list;
for (const auto& pass : render_pass_list) {
list.Append(CompositorRenderPassToDict(*pass));
}
return base::Value::Dict()
.Set("render_pass_count", static_cast<int>(render_pass_list.size()))
.Set("metadata", GetRenderPassListMetadata(render_pass_list))
.Set("render_pass_list", std::move(list));
}
bool CompositorRenderPassListFromDict(
const base::Value::Dict& dict,
CompositorRenderPassList* render_pass_list) {
DCHECK(render_pass_list);
DCHECK(render_pass_list->empty());
const base::Value::List* list = dict.FindList("render_pass_list");
if (!list) {
return false;
}
for (const auto& item : *list) {
const base::Value::Dict* item_dict = item.GetIfDict();
if (!item_dict) {
render_pass_list->clear();
return false;
}
std::unique_ptr<CompositorRenderPass> render_pass =
CompositorRenderPassFromDict(*item_dict);
if (!render_pass) {
render_pass_list->clear();
return false;
}
render_pass_list->push_back(std::move(render_pass));
}
return true;
}
base::Value::Dict CompositorFrameToDict(
const CompositorFrame& compositor_frame) {
base::Value::List referenced_surfaces;
for (auto& surface_range : compositor_frame.metadata.referenced_surfaces) {
referenced_surfaces.Append(SurfaceRangeToDict(surface_range));
}
return base::Value::Dict()
.Set("render_pass_list",
CompositorRenderPassListToDict(compositor_frame.render_pass_list))
.Set("metadata", base::Value::Dict().Set("referenced_surfaces",
std::move(referenced_surfaces)));
}
bool CompositorFrameFromDict(const base::Value::Dict& dict,
CompositorFrame* compositor_frame) {
DCHECK(compositor_frame);
const base::Value::Dict* render_pass_list = dict.FindDict("render_pass_list");
if (!render_pass_list) {
return false;
}
if (!CompositorRenderPassListFromDict(*render_pass_list,
&compositor_frame->render_pass_list)) {
return false;
}
const base::Value::Dict* metadata = dict.FindDict("metadata");
if (!metadata) {
return false;
}
const base::Value::List* referenced_surfaces =
metadata->FindList("referenced_surfaces");
if (!referenced_surfaces) {
return false;
}
for (auto& referenced_surface_dict : *referenced_surfaces) {
auto referenced_surface =
SurfaceRangeFromDict(referenced_surface_dict.GetDict());
if (!referenced_surface) {
return false;
}
compositor_frame->metadata.referenced_surfaces.push_back(
*referenced_surface);
}
return true;
}
base::Value::List FrameDataToList(
const std::vector<FrameData>& frame_data_list) {
base::Value::List list;
for (auto& frame_data : frame_data_list) {
list.Append(
base::Value::Dict()
.Set("surface_id", SurfaceIdToDict(frame_data.surface_id))
// This cast will be safe because we should never have more than
// |INT_MAX| frames in recorded data.
.Set("frame_index", static_cast<int>(frame_data.frame_index))
.Set("compositor_frame",
CompositorFrameToDict(frame_data.compositor_frame)));
}
return list;
}
bool FrameDataFromList(const base::Value::List& list,
std::vector<FrameData>* frame_data_list) {
DCHECK(frame_data_list);
DCHECK(frame_data_list->empty());
for (const auto& frame_data_value : list) {
const base::Value::Dict* frame_data_dict = frame_data_value.GetIfDict();
if (!frame_data_dict) {
return false;
}
FrameData frame_data;
const base::Value::Dict* surface_id_dict =
frame_data_dict->FindDict("surface_id");
if (!surface_id_dict) {
return false;
}
std::optional<SurfaceId> surface_id = SurfaceIdFromDict(*surface_id_dict);
if (!surface_id) {
return false;
}
frame_data.surface_id = *surface_id;
std::optional<int> frame_index = frame_data_dict->FindInt("frame_index");
if (!frame_index) {
return false;
}
frame_data.frame_index = *frame_index;
const base::Value::Dict* compositor_frame_dict =
frame_data_dict->FindDict("compositor_frame");
if (!compositor_frame_dict) {
return false;
}
if (!CompositorFrameFromDict(*compositor_frame_dict,
&frame_data.compositor_frame)) {
return false;
}
frame_data_list->push_back(std::move(frame_data));
}
return true;
}
FrameData::FrameData() = default;
FrameData::FrameData(FrameData&& other) = default;
FrameData& FrameData::operator=(FrameData&& other) = default;
FrameData::FrameData(const SurfaceId& surface_id,
const uint64_t frame_index,
const CompositorFrame& compositor_frame)
: surface_id(surface_id), frame_index(frame_index) {
this->compositor_frame.metadata = compositor_frame.metadata.Clone();
this->compositor_frame.resource_list = compositor_frame.resource_list;
for (const auto& render_pass : compositor_frame.render_pass_list) {
this->compositor_frame.render_pass_list.push_back(render_pass->DeepCopy());
}
}
} // namespace viz