#include "gpu/ipc/service/dcomp_texture_win.h"
#include <string.h>
#include "base/functional/bind.h"
#include "base/memory/scoped_refptr.h"
#include "base/notreached.h"
#include "base/power_monitor/power_monitor.h"
#include "base/win/windows_types.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "gpu/command_buffer/service/scheduler.h"
#include "gpu/command_buffer/service/scheduler_task_runner.h"
#include "gpu/command_buffer/service/shared_image/shared_image_backing.h"
#include "gpu/command_buffer/service/shared_image/shared_image_factory.h"
#include "gpu/command_buffer/service/shared_image/shared_image_representation.h"
#include "gpu/ipc/common/gpu_channel.mojom.h"
#include "gpu/ipc/service/gpu_channel.h"
#include "gpu/ipc/service/gpu_channel_manager.h"
#include "ipc/ipc_mojo_bootstrap.h"
#include "ui/gfx/color_space.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/video_types.h"
#include "ui/gl/dcomp_surface_registry.h"
#include "ui/gl/scoped_make_current.h"
namespace gpu {
namespace {
constexpr base::TimeDelta kParentWindowPosPollingPeriod = base::Seconds(1);
constexpr base::TimeDelta kPowerChangeDetectionGracePeriod = base::Seconds(2);
class DCOMPTextureRepresentation : public OverlayImageRepresentation {
public:
DCOMPTextureRepresentation(
SharedImageManager* manager,
SharedImageBacking* backing,
MemoryTypeTracker* tracker,
scoped_refptr<gl::DCOMPSurfaceProxy> dcomp_surface_proxy)
: OverlayImageRepresentation(manager, backing, tracker),
dcomp_surface_proxy_(std::move(dcomp_surface_proxy)) {}
std::optional<gl::DCLayerOverlayImage> GetDCLayerOverlayImage() override {
return std::make_optional<gl::DCLayerOverlayImage>(size(),
dcomp_surface_proxy_);
}
bool BeginReadAccess(gfx::GpuFenceHandle& acquire_fence) override {
return true;
}
void EndReadAccess(gfx::GpuFenceHandle release_fence) override {}
private:
scoped_refptr<gl::DCOMPSurfaceProxy> dcomp_surface_proxy_;
};
class DCOMPTextureBacking : public ClearTrackingSharedImageBacking {
public:
DCOMPTextureBacking(scoped_refptr<gl::DCOMPSurfaceProxy> dcomp_surface_proxy,
const Mailbox& mailbox,
const gfx::Size& size)
: ClearTrackingSharedImageBacking(
mailbox,
viz::SinglePlaneFormat::kBGRA_8888,
size,
gfx::ColorSpace(gfx::ColorSpace::PrimaryID::BT709,
gfx::ColorSpace::TransferID::BT709),
kTopLeft_GrSurfaceOrigin,
kPremul_SkAlphaType,
gpu::SHARED_IMAGE_USAGE_SCANOUT,
{},
0,
false),
dcomp_surface_proxy_(std::move(dcomp_surface_proxy)) {
SetCleared();
}
SharedImageBackingType GetType() const override {
return SharedImageBackingType::kDCOMPSurfaceProxy;
}
std::unique_ptr<OverlayImageRepresentation> ProduceOverlay(
SharedImageManager* manager,
MemoryTypeTracker* tracker) override {
return std::make_unique<DCOMPTextureRepresentation>(manager, this, tracker,
dcomp_surface_proxy_);
}
private:
scoped_refptr<gl::DCOMPSurfaceProxy> dcomp_surface_proxy_;
};
}
scoped_refptr<DCOMPTexture> DCOMPTexture::Create(
GpuChannel* channel,
int route_id,
mojo::PendingAssociatedReceiver<mojom::DCOMPTexture> receiver) {
ContextResult result;
auto context_state =
channel->gpu_channel_manager()->GetSharedContextState(&result);
if (result != ContextResult::kSuccess) {
DLOG(ERROR) << "GetSharedContextState() failed.";
return nullptr;
}
return base::WrapRefCounted(new DCOMPTexture(
channel, route_id, std::move(receiver), std::move(context_state)));
}
DCOMPTexture::DCOMPTexture(
GpuChannel* channel,
int32_t route_id,
mojo::PendingAssociatedReceiver<mojom::DCOMPTexture> receiver,
scoped_refptr<SharedContextState> context_state)
: channel_(channel),
route_id_(route_id),
context_state_(std::move(context_state)),
sequence_(channel_->scheduler()->CreateSequence(SchedulingPriority::kLow,
channel_->task_runner())),
receiver_(this) {
auto runner = base::MakeRefCounted<SchedulerTaskRunner>(
*channel_->scheduler(), sequence_);
IPC::ScopedAllowOffSequenceChannelAssociatedBindings allow_binding;
receiver_.Bind(std::move(receiver), runner);
context_state_->AddContextLostObserver(this);
base::PowerMonitor::GetInstance()->AddPowerSuspendObserver(this);
channel_->AddRoute(route_id, sequence_);
}
DCOMPTexture::~DCOMPTexture() {
DVLOG(1) << __func__;
DCHECK(!channel_);
context_state_->RemoveContextLostObserver(this);
base::PowerMonitor::GetInstance()->RemovePowerSuspendObserver(this);
if (window_pos_timer_.IsRunning()) {
window_pos_timer_.Stop();
}
}
void DCOMPTexture::ReleaseChannel() {
DVLOG(1) << __func__;
DCHECK(channel_);
receiver_.ResetFromAnotherSequenceUnsafe();
channel_->RemoveRoute(route_id_);
channel_->scheduler()->DestroySequence(sequence_);
sequence_ = SequenceId();
channel_ = nullptr;
ResetSizeIfNeeded();
}
void DCOMPTexture::OnContextLost() {
DVLOG(1) << __func__;
}
void DCOMPTexture::OnResume() {
DVLOG(1) << __func__;
last_power_change_time_ = base::TimeTicks::Now();
ResetSizeIfNeeded();
}
void DCOMPTexture::ResetSizeIfNeeded() {
DVLOG(2) << __func__;
if (!channel_ &&
base::TimeTicks::Now() - last_power_change_time_ <
kPowerChangeDetectionGracePeriod) {
DVLOG(1) << __func__
<< ": Resetting size to {1,1} to release dcomp surface resources "
"and prevent stale content from being displayed";
size_ = gfx::Size(1, 1);
}
}
void DCOMPTexture::StartListening(
mojo::PendingAssociatedRemote<mojom::DCOMPTextureClient> client) {
client_.Bind(std::move(client));
}
void DCOMPTexture::SetTextureSize(const gfx::Size& size) {
size_ = size;
if (!shared_image_mailbox_created_) {
if (client_) {
shared_image_mailbox_created_ = true;
gpu::Mailbox mailbox = CreateSharedImage();
client_->OnSharedImageMailboxBound(mailbox);
} else
DLOG(ERROR) << "Unable to call client_->OnSharedImageMailboxBound";
}
}
const gfx::Size& DCOMPTexture::GetSize() const {
return size_;
}
HANDLE DCOMPTexture::GetSurfaceHandle() {
return surface_handle_.get();
}
void DCOMPTexture::SetDCOMPSurfaceHandle(
const base::UnguessableToken& token,
SetDCOMPSurfaceHandleCallback callback) {
DVLOG(1) << __func__;
base::win::ScopedHandle surface_handle =
gl::DCOMPSurfaceRegistry::GetInstance()->TakeDCOMPSurfaceHandle(token);
if (!surface_handle.is_valid()) {
DLOG(ERROR) << __func__ << ": No surface registered for token " << token;
std::move(callback).Run(false);
return;
}
surface_handle_.Set(surface_handle.Take());
std::move(callback).Run(true);
}
gpu::Mailbox DCOMPTexture::CreateSharedImage() {
DCHECK(channel_);
auto mailbox = gpu::Mailbox::Generate();
auto shared_image =
std::make_unique<DCOMPTextureBacking>(this, mailbox, size_);
channel_->shared_image_stub()->factory()->RegisterBacking(
std::move(shared_image));
return mailbox;
}
gfx::Rect DCOMPTexture::GetParentWindowRect() {
RECT parent_window_rect = {};
::GetWindowRect(last_parent_, &parent_window_rect);
return gfx::Rect(parent_window_rect);
}
void DCOMPTexture::OnUpdateParentWindowRect() {
gfx::Rect parent_window_rect = GetParentWindowRect();
if (parent_window_rect_ != parent_window_rect) {
parent_window_rect_ = parent_window_rect;
SendOutputRect();
}
}
void DCOMPTexture::SetParentWindow(HWND parent) {
if (last_parent_ != parent) {
last_parent_ = parent;
OnUpdateParentWindowRect();
if (!window_pos_timer_.IsRunning()) {
window_pos_timer_.Start(FROM_HERE, kParentWindowPosPollingPeriod, this,
&DCOMPTexture::OnUpdateParentWindowRect);
}
}
}
void DCOMPTexture::SetRect(const gfx::Rect& window_relative_rect) {
bool should_send_output_rect = false;
if (window_relative_rect != window_relative_rect_) {
window_relative_rect_ = window_relative_rect;
should_send_output_rect = true;
}
gfx::Rect parent_window_rect = GetParentWindowRect();
if (parent_window_rect_ != parent_window_rect) {
parent_window_rect_ = parent_window_rect;
should_send_output_rect = true;
}
if (should_send_output_rect)
SendOutputRect();
}
void DCOMPTexture::SendOutputRect() {
if (!client_)
return;
gfx::Rect output_rect = window_relative_rect_;
output_rect.set_x(window_relative_rect_.x() + parent_window_rect_.x());
output_rect.set_y(window_relative_rect_.y() + parent_window_rect_.y());
if (last_output_rect_ != output_rect) {
if (!output_rect.IsEmpty()) {
client_->OnOutputRectChange(output_rect);
}
last_output_rect_ = output_rect;
}
}
}