#include "gpu/ipc/service/gpu_memory_ablation_experiment.h"
#include <algorithm>
#include "base/functional/bind.h"
#include "base/metrics/field_trial_params.h"
#include "base/strings/string_number_conversions.h"
#include "base/task/sequenced_task_runner.h"
#include "base/time/time.h"
#include "base/trace_event/common/trace_event_common.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "gpu/command_buffer/common/shared_image_usage.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/surface_handle.h"
#include "gpu/ipc/service/gpu_channel_manager.h"
#include "gpu/ipc/service/gpu_memory_buffer_factory.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/skia/include/gpu/GrDirectContext.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_implementation.h"
#include "ui/gl/gl_surface.h"
namespace gpu {
BASE_FEATURE(kGPUMemoryAblationFeature,
"GPUMemoryAblation",
base::FEATURE_DISABLED_BY_DEFAULT);
const char kGPUMemoryAblationFeatureSizeParam[] = "Size";
constexpr viz::SharedImageFormat kFormat = viz::SinglePlaneFormat::kRGBA_8888;
constexpr uint32_t kUsage = SHARED_IMAGE_USAGE_DISPLAY_READ;
bool GpuMemoryAblationExperiment::ExperimentSupported() {
if (!base::FeatureList::IsEnabled(kGPUMemoryAblationFeature))
return false;
gl::GLImplementation gl_impl = gl::GetGLImplementation();
if (gl_impl == gl::kGLImplementationNone ||
gl_impl == gl::kGLImplementationMockGL ||
gl_impl == gl::kGLImplementationStubGL ||
gl_impl == gl::kGLImplementationDisabled) {
return false;
}
return true;
}
GpuMemoryAblationExperiment::GpuMemoryAblationExperiment(
GpuChannelManager* channel_manager,
scoped_refptr<base::SequencedTaskRunner> task_runner)
: channel_manager_(channel_manager), task_runner_(task_runner) {
if (!GpuMemoryAblationExperiment::ExperimentSupported())
init_status_ = Status::DISABLED;
}
GpuMemoryAblationExperiment::~GpuMemoryAblationExperiment() {
if (mailboxes_.empty())
return;
bool have_context = context_state_->MakeCurrent(context_state_->surface());
factory_->DestroyAllSharedImages(have_context);
}
void GpuMemoryAblationExperiment::OnMemoryAllocated(uint64_t old_size,
uint64_t new_size) {
if (init_status_ == Status::DISABLED) {
return;
} else if (init_status_ == Status::UNINITIALIZED) {
if (InitGpu(channel_manager_)) {
init_status_ = Status::ENABLED;
} else {
init_status_ = Status::DISABLED;
context_state_ = nullptr;
return;
}
}
if (new_size > old_size) {
AllocateGpuMemory();
} else if (old_size > new_size) {
if (!mailboxes_.empty()) {
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&GpuMemoryAblationExperiment::DeleteGpuMemory,
weak_factory_.GetWeakPtr()));
}
}
}
uint64_t GpuMemoryAblationExperiment::GetPeakMemory(
uint32_t sequence_num) const {
auto it = sequences_.find(sequence_num);
if (it == sequences_.end())
return 0u;
return it->second;
}
void GpuMemoryAblationExperiment::StartSequence(uint32_t sequence_num) {
sequences_.emplace(sequence_num, 0u);
}
void GpuMemoryAblationExperiment::StopSequence(uint32_t sequence_num) {
auto it = sequences_.find(sequence_num);
if (it == sequences_.end())
return;
sequences_.erase(it);
}
void GpuMemoryAblationExperiment::AllocateGpuMemory() {
std::unique_ptr<ui::ScopedMakeCurrent> scoped_current =
ScopedMakeContextCurrent();
if (!scoped_current)
return;
auto mailbox = Mailbox::GenerateForSharedImage();
auto color_space = gfx::ColorSpace::CreateSRGB();
if (!factory_->CreateSharedImage(mailbox, kFormat, size_, color_space,
kTopLeft_GrSurfaceOrigin,
kPremul_SkAlphaType, gpu::kNullSurfaceHandle,
kUsage, "GpuMemoryAblation")) {
return;
}
auto skia_rep = rep_factory_->ProduceSkia(mailbox, context_state_);
if (!skia_rep)
return;
auto write_access = skia_rep->BeginScopedWriteAccess(
{}, {},
SharedImageRepresentation::AllowUnclearedAccess::kYes);
if (!write_access)
return;
auto* canvas = write_access->surface()->getCanvas();
canvas->clear(SkColors::kWhite);
mailboxes_.push_back(mailbox);
}
void GpuMemoryAblationExperiment::DeleteGpuMemory() {
if (mailboxes_.empty())
return;
auto mailbox = mailboxes_.front();
if (context_state_->MakeCurrent(nullptr))
factory_->DestroySharedImage(mailbox);
mailboxes_.erase(mailboxes_.begin());
}
bool GpuMemoryAblationExperiment::InitGpu(GpuChannelManager* channel_manager) {
ContextResult result;
context_state_ = channel_manager->GetSharedContextState(&result);
if (result != ContextResult::kSuccess)
return false;
const int default_value = 0;
int arg_value = base::GetFieldTrialParamByFeatureAsInt(
kGPUMemoryAblationFeature, kGPUMemoryAblationFeatureSizeParam,
default_value);
if (arg_value == default_value)
return false;
int texture_size =
std::min(256 * arg_value, context_state_->gr_context()->maxTextureSize());
size_ = gfx::Size(texture_size, texture_size);
std::unique_ptr<ui::ScopedMakeCurrent> scoped_current =
ScopedMakeContextCurrent();
if (!scoped_current)
return false;
factory_ = std::make_unique<SharedImageFactory>(
channel_manager->gpu_preferences(),
channel_manager->gpu_driver_bug_workarounds(),
channel_manager->gpu_feature_info(), context_state_.get(),
channel_manager->shared_image_manager(), this,
false);
rep_factory_ = std::make_unique<SharedImageRepresentationFactory>(
channel_manager->shared_image_manager(), this);
return true;
}
std::unique_ptr<ui::ScopedMakeCurrent>
GpuMemoryAblationExperiment::ScopedMakeContextCurrent() {
std::unique_ptr<ui::ScopedMakeCurrent> scoped_current =
std::make_unique<ui::ScopedMakeCurrent>(context_state_->context(),
context_state_->surface());
if (!context_state_->IsCurrent(context_state_->surface()))
return nullptr;
return scoped_current;
}
void GpuMemoryAblationExperiment::TrackMemoryAllocatedChange(int64_t delta) {
DCHECK(delta >= 0 || gpu_allocation_size_ >= static_cast<uint64_t>(-delta));
gpu_allocation_size_ += delta;
for (auto& it : sequences_) {
if (gpu_allocation_size_ > it.second)
it.second = gpu_allocation_size_;
}
}
uint64_t GpuMemoryAblationExperiment::GetSize() const {
return 0u;
}
uint64_t GpuMemoryAblationExperiment::ClientTracingId() const {
return 0u;
}
int GpuMemoryAblationExperiment::ClientId() const {
return 0;
}
uint64_t GpuMemoryAblationExperiment::ContextGroupTracingId() const {
return 0u;
}
}