blob: c4eb311a4ee3d0a2b7899d4bb1bf851ed2151baa [file] [log] [blame]
// Copyright 2021 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/service/transitions/transferable_resource_tracker.h"
#include <GLES2/gl2.h>
#include <limits>
#include <memory>
#include <utility>
#include "base/containers/contains.h"
#include "components/viz/common/resources/release_callback.h"
#include "components/viz/common/resources/resource_id.h"
#include "components/viz/common/resources/shared_bitmap.h"
#include "components/viz/common/resources/transferable_resource.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "gpu/command_buffer/common/sync_token.h"
#include "ui/gfx/geometry/size.h"
namespace viz {
TransferableResourceTracker::TransferableResourceTracker(
SharedBitmapManager* shared_bitmap_manager)
: starting_id_(kVizReservedRangeStartId.GetUnsafeValue()),
next_id_(kVizReservedRangeStartId.GetUnsafeValue()),
shared_bitmap_manager_(shared_bitmap_manager) {}
TransferableResourceTracker::~TransferableResourceTracker() = default;
TransferableResourceTracker::ResourceFrame
TransferableResourceTracker::ImportResources(
std::unique_ptr<SurfaceSavedFrame> saved_frame) {
DCHECK(saved_frame);
// Since we will be dereferencing this blindly, CHECK that the frame is indeed
// valid.
CHECK(saved_frame->IsValid());
absl::optional<SurfaceSavedFrame::FrameResult> frame_copy =
saved_frame->TakeResult();
const auto& directive = saved_frame->directive();
ResourceFrame resource_frame;
resource_frame.shared.resize(frame_copy->shared_results.size());
for (size_t i = 0; i < frame_copy->shared_results.size(); ++i) {
auto& shared_result = frame_copy->shared_results[i];
if (shared_result.has_value()) {
resource_frame.shared[i].emplace(
ImportResource(std::move(*shared_result)));
auto view_transition_element_resource_id =
directive.shared_elements()[i].view_transition_element_resource_id;
if (view_transition_element_resource_id.IsValid()) {
resource_frame
.element_id_to_resource[view_transition_element_resource_id] =
resource_frame.shared[i]->resource;
}
}
}
for (auto resource_id : frame_copy->empty_resource_ids) {
DCHECK(!resource_frame.element_id_to_resource.contains(resource_id));
resource_frame.element_id_to_resource[resource_id] = TransferableResource();
}
return resource_frame;
}
TransferableResourceTracker::PositionedResource
TransferableResourceTracker::ImportResource(
SurfaceSavedFrame::OutputCopyResult output_copy) {
TransferableResource resource;
TransferableResourceHolder::ResourceReleaseCallback release_callback;
if (output_copy.is_software) {
DCHECK(output_copy.mailbox.IsZero());
DCHECK(!output_copy.release_callback);
SharedBitmapId id = SharedBitmap::GenerateId();
shared_bitmap_manager_->LocalAllocatedSharedBitmap(
std::move(output_copy.bitmap), id);
resource = TransferableResource::MakeSoftware(
id, output_copy.draw_data.size, SinglePlaneFormat::kRGBA_8888);
// Remove the bitmap from shared bitmap manager when no longer in use.
release_callback = base::BindOnce(
[](SharedBitmapManager* manager, const TransferableResource& resource) {
const SharedBitmapId& id = resource.mailbox_holder.mailbox;
manager->ChildDeletedSharedBitmap(id);
},
shared_bitmap_manager_);
} else {
DCHECK(output_copy.bitmap.drawsNothing());
resource = TransferableResource::MakeGpu(
output_copy.mailbox, GL_TEXTURE_2D, output_copy.sync_token,
output_copy.draw_data.size, SinglePlaneFormat::kRGBA_8888,
/*is_overlay_candidate=*/false);
resource.color_space = output_copy.color_space;
// Run the SingleReleaseCallback when no longer in use.
if (output_copy.release_callback) {
release_callback = base::BindOnce(
[](ReleaseCallback callback, const TransferableResource& resource) {
std::move(callback).Run(resource.mailbox_holder.sync_token,
/*is_lost=*/false);
},
std::move(output_copy.release_callback));
}
}
resource.id = GetNextAvailableResourceId();
DCHECK(!base::Contains(managed_resources_, resource.id));
managed_resources_.emplace(
resource.id,
TransferableResourceHolder(resource, std::move(release_callback)));
PositionedResource result;
result.resource = resource;
result.draw_data = output_copy.draw_data;
return result;
}
void TransferableResourceTracker::ReturnFrame(const ResourceFrame& frame) {
for (const auto& shared : frame.shared) {
if (shared.has_value())
UnrefResource(shared->resource.id, /*count=*/1);
}
}
void TransferableResourceTracker::RefResource(ResourceId id) {
DCHECK(base::Contains(managed_resources_, id));
++managed_resources_[id].ref_count;
}
void TransferableResourceTracker::UnrefResource(ResourceId id, int count) {
DCHECK(base::Contains(managed_resources_, id));
DCHECK_LE(count, managed_resources_[id].ref_count);
managed_resources_[id].ref_count -= count;
if (managed_resources_[id].ref_count == 0)
managed_resources_.erase(id);
}
ResourceId TransferableResourceTracker::GetNextAvailableResourceId() {
uint32_t result = next_id_;
// Since we're working with a limit range of resources, it is a lot more
// likely that we will loop back to the starting id after running out of
// resource ids. This loop ensures that `next_id_` is set to a value that is
// not `result` and is also available in that it's currently tracked by
// `managed_resources_`. Note that if we end up looping twice, we fail with a
// CHECK since we don't have any available resources for this request.
bool looped = false;
while (next_id_ == result ||
base::Contains(managed_resources_, ResourceId(next_id_))) {
if (next_id_ == std::numeric_limits<uint32_t>::max()) {
CHECK(!looped);
next_id_ = starting_id_;
looped = true;
} else {
++next_id_;
}
}
DCHECK_GE(result, kVizReservedRangeStartId.GetUnsafeValue());
return ResourceId(result);
}
TransferableResourceTracker::TransferableResourceHolder::
TransferableResourceHolder() = default;
TransferableResourceTracker::TransferableResourceHolder::
TransferableResourceHolder(TransferableResourceHolder&& other) = default;
TransferableResourceTracker::TransferableResourceHolder::
TransferableResourceHolder(const TransferableResource& resource,
ResourceReleaseCallback release_callback)
: resource(resource),
release_callback(std::move(release_callback)),
ref_count(1u) {}
TransferableResourceTracker::TransferableResourceHolder::
~TransferableResourceHolder() {
if (release_callback)
std::move(release_callback).Run(resource);
}
TransferableResourceTracker::TransferableResourceHolder&
TransferableResourceTracker::TransferableResourceHolder::operator=(
TransferableResourceHolder&& other) = default;
TransferableResourceTracker::ResourceFrame::ResourceFrame() = default;
TransferableResourceTracker::ResourceFrame::ResourceFrame(
ResourceFrame&& other) = default;
TransferableResourceTracker::ResourceFrame::~ResourceFrame() = default;
TransferableResourceTracker::ResourceFrame&
TransferableResourceTracker::ResourceFrame::operator=(ResourceFrame&& other) =
default;
} // namespace viz