blob: 307e4ddf731989afd07c61c0757c557135fcf50b [file] [log] [blame]
// Copyright 2013 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/frame_sinks/copy_output_request.h"
#include <algorithm>
#include <utility>
#include "base/check_op.h"
#include "base/functional/bind.h"
#include "base/no_destructor.h"
#include "base/strings/stringprintf.h"
#include "base/synchronization/lock.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/trace_event/trace_event.h"
#include "components/viz/common/frame_sinks/copy_output_result.h"
#include "third_party/perfetto/include/perfetto/tracing/track.h"
#include "third_party/skia/include/core/SkBitmap.h"
namespace {
constexpr int kMaxPendingSendResult = 4;
const char* ResultFormatToShortString(
viz::CopyOutputRequest::ResultFormat result_format) {
switch (result_format) {
case viz::CopyOutputRequest::ResultFormat::RGBA:
return "RGBA";
case viz::CopyOutputRequest::ResultFormat::I420_PLANES:
return "I420";
case viz::CopyOutputRequest::ResultFormat::NV12:
return "NV12";
case viz::CopyOutputRequest::ResultFormat::RGBAF16:
return "RGBAF16";
}
}
const char* ResultDestinationToShortString(
viz::CopyOutputRequest::ResultDestination result_destination) {
switch (result_destination) {
case viz::CopyOutputRequest::ResultDestination::kSystemMemory:
return "CPU";
case viz::CopyOutputRequest::ResultDestination::kSharedImage:
return "GPU";
}
}
int g_pending_send_result_count = 0;
base::Lock& GetPendingSendResultLock() {
static base::NoDestructor<base::Lock> lock;
return *lock;
}
} // namespace
namespace viz {
CopyOutputRequest::CopyOutputRequest(ResultFormat result_format,
ResultDestination result_destination,
CopyOutputRequestCallback result_callback)
: result_format_(result_format),
result_destination_(result_destination),
result_callback_(std::move(result_callback)),
result_task_runner_(base::SequencedTaskRunner::GetCurrentDefault()),
scale_from_(1, 1),
scale_to_(1, 1) {
// If format is I420_PLANES, the result must be in system memory. Returning
// I420_PLANES via textures is not yet supported.
DCHECK(result_format_ != ResultFormat::I420_PLANES ||
result_destination_ == ResultDestination::kSystemMemory);
DCHECK(!result_callback_.is_null());
TRACE_EVENT_BEGIN("viz", "CopyOutputRequest",
perfetto::Track::FromPointer(this));
}
CopyOutputRequest::~CopyOutputRequest() {
if (!result_callback_.is_null()) {
// Send an empty result to indicate the request was never satisfied.
SendResult(std::make_unique<CopyOutputResult>(
result_format_, result_destination_, gfx::Rect(), false));
}
}
std::string CopyOutputRequest::ToString() const {
return base::StringPrintf(
"[%s] -%s→%s-> [%s] @ [%s, %s] %s",
has_area() ? area().ToString().c_str() : "noclip",
scale_from().ToString().c_str(), scale_to().ToString().c_str(),
has_result_selection() ? result_selection().ToString().c_str()
: "noclamp",
ResultFormatToShortString(result_format()),
ResultDestinationToShortString(result_destination()),
has_blit_request() ? blit_request_->ToString().c_str() : "noblit");
}
void CopyOutputRequest::SetScaleRatio(const gfx::Vector2d& scale_from,
const gfx::Vector2d& scale_to) {
// These are CHECKs, and not DCHECKs, because it's critical that crash report
// bugs be tied to the client callpoint rather than the later mojo or service-
// side processing of the CopyOutputRequest.
CHECK_GT(scale_from.x(), 0);
CHECK_GT(scale_from.y(), 0);
CHECK_GT(scale_to.x(), 0);
CHECK_GT(scale_to.y(), 0);
scale_from_ = scale_from;
scale_to_ = scale_to;
}
void CopyOutputRequest::SetUniformScaleRatio(int scale_from, int scale_to) {
// See note in SetScaleRatio() as to why these are CHECKs and not DCHECKs.
CHECK_GT(scale_from, 0);
CHECK_GT(scale_to, 0);
scale_from_ = gfx::Vector2d(scale_from, scale_from);
scale_to_ = gfx::Vector2d(scale_to, scale_to);
}
void CopyOutputRequest::set_blit_request(BlitRequest blit_request) {
DCHECK(!blit_request_);
DCHECK_EQ(result_destination(), ResultDestination::kSharedImage);
DCHECK(result_format() == ResultFormat::NV12 ||
result_format() == ResultFormat::RGBA ||
result_format() == ResultFormat::RGBAF16);
DCHECK(has_result_selection());
if (result_format() == ResultFormat::NV12) {
// Destination region must start at an even offset for NV12 results:
DCHECK_EQ(blit_request.destination_region_offset().x() % 2, 0);
DCHECK_EQ(blit_request.destination_region_offset().y() % 2, 0);
}
CHECK(blit_request.shared_image());
blit_request_ = std::move(blit_request);
}
void CopyOutputRequest::SendResult(std::unique_ptr<CopyOutputResult> result) {
TRACE_EVENT_END("viz",
/* CopyOutputRequest */ perfetto::Track::FromPointer(this),
"success", !result->IsEmpty(), "has_provided_task_runner",
!!result_task_runner_);
CHECK(result_task_runner_);
auto task = base::BindOnce(std::move(result_callback_), std::move(result));
if (send_result_delay_.is_zero()) {
result_task_runner_->PostTask(FROM_HERE, std::move(task));
} else {
base::AutoLock locked_counter(GetPendingSendResultLock());
if (g_pending_send_result_count >= kMaxPendingSendResult) {
result_task_runner_->PostTask(FROM_HERE, std::move(task));
} else {
g_pending_send_result_count++;
result_task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(
[](base::OnceClosure callback) {
std::move(callback).Run();
base::AutoLock locked_counter(GetPendingSendResultLock());
g_pending_send_result_count--;
},
std::move(task)),
send_result_delay_);
}
}
// Remove the reference to the task runner (no-op if we didn't have one).
result_task_runner_ = nullptr;
}
bool CopyOutputRequest::SendsResultsInCurrentSequence() const {
return result_task_runner_ &&
result_task_runner_->RunsTasksInCurrentSequence();
}
// static
std::unique_ptr<CopyOutputRequest> CopyOutputRequest::CreateStubForTesting() {
return std::make_unique<CopyOutputRequest>(
ResultFormat::RGBA, ResultDestination::kSystemMemory,
base::BindOnce([](std::unique_ptr<CopyOutputResult>) {}));
}
} // namespace viz