blob: 71a457099b8fea4128b4497d3ad4ce25f8631170 [file] [log] [blame]
/*
* Copyright 2017 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "cobalt/browser/memory_settings/auto_mem.h"
#include <algorithm>
#include <string>
#include <vector>
#include "base/optional.h"
#include "base/string_number_conversions.h"
#include "base/string_split.h"
#include "base/stringprintf.h"
#include "cobalt/browser/memory_settings/build_settings.h"
#include "cobalt/browser/memory_settings/calculations.h"
#include "cobalt/browser/memory_settings/constants.h"
#include "cobalt/browser/memory_settings/memory_settings.h"
#include "cobalt/browser/memory_settings/pretty_print.h"
#include "cobalt/browser/switches.h"
#include "nb/lexical_cast.h"
namespace cobalt {
namespace browser {
namespace memory_settings {
namespace {
// Determines if the string value signals "autoset".
bool StringValueSignalsAutoset(const std::string& value) {
std::string value_lower_case = value;
std::transform(value_lower_case.begin(), value_lower_case.end(),
value_lower_case.begin(), ::tolower);
bool is_autoset = (value_lower_case == "auto") ||
(value_lower_case == "autoset") ||
(value_lower_case == "-1");
return is_autoset;
}
// Creates the specified memory setting type and binds it to (1) command line
// or else (2) build setting or else (3) an auto_set value.
template <typename MemorySettingType, typename ValueType>
scoped_ptr<MemorySettingType> CreateMemorySetting(
const char* setting_name,
const CommandLine& cmd_line, // Optional.
const base::optional<ValueType>& build_setting,
const ValueType& autoset_value) {
scoped_ptr<MemorySettingType> output(new MemorySettingType(setting_name));
// True when the command line explicitly requests the variable to be autoset.
bool force_autoset = false;
// The value is set according to importance:
// 1) Command line switches are the most important, so set those if they
// exist.
if (cmd_line.HasSwitch(setting_name)) {
std::string value = cmd_line.GetSwitchValueNative(setting_name);
if (StringValueSignalsAutoset(value)) {
force_autoset = true;
} else if (output->TryParseValue(MemorySetting::kCmdLine, value)) {
return output.Pass();
}
}
// 2) Is there a build setting? Then set to build_setting, unless the command
// line specifies that it should be autoset.
if (build_setting && !force_autoset) {
output->set_value(MemorySetting::kBuildSetting, *build_setting);
} else {
// 3) Otherwise bind to the autoset_value.
output->set_value(MemorySetting::kAutoSet, autoset_value);
}
return output.Pass();
}
scoped_ptr<IntSetting> CreateSystemMemorySetting(
const char* setting_name,
MemorySetting::MemoryType memory_type,
const CommandLine& command_line,
const base::optional<int64_t>& build_setting,
const base::optional<int64_t>& starboard_value) {
scoped_ptr<IntSetting> setting(new IntSetting(setting_name));
setting->set_memory_type(memory_type);
if (command_line.HasSwitch(setting_name)) {
const std::string value = command_line.GetSwitchValueNative(setting_name);
if (setting->TryParseValue(MemorySetting::kCmdLine, value)) {
return setting.Pass();
}
}
if (build_setting) {
setting->set_value(MemorySetting::kBuildSetting, *build_setting);
return setting.Pass();
}
if (starboard_value) {
setting->set_value(MemorySetting::kStarboardAPI, *starboard_value);
return setting.Pass();
}
setting->set_value(MemorySetting::kStarboardAPI, -1);
return setting.Pass();
}
void EnsureValuePositive(IntSetting* setting) {
if (setting->value() < 0) {
setting->set_value(setting->source_type(), 0);
}
}
void EnsureValuePositive(DimensionSetting* setting) {
const TextureDimensions value = setting->value();
if (value.width() < 0 || value.height() < 0 || value.bytes_per_pixel() < 0) {
setting->set_value(setting->source_type(), TextureDimensions());
}
}
void EnsureTwoBytesPerPixel(DimensionSetting* setting) {
TextureDimensions value = setting->value();
if (value.bytes_per_pixel() != 2) {
LOG(ERROR) << "Only two bytes per pixel are allowed for setting: "
<< setting->name();
value.set_bytes_per_pixel(2);
setting->set_value(setting->source_type(), value);
}
}
// Sums up the memory consumption of all memory settings with of the input
// memory type.
int64_t SumMemoryConsumption(
MemorySetting::MemoryType memory_type_filter,
const std::vector<const MemorySetting*>& memory_settings) {
int64_t sum = 0;
for (size_t i = 0; i < memory_settings.size(); ++i) {
const MemorySetting* setting = memory_settings[i];
if (memory_type_filter == setting->memory_type()) {
sum += setting->MemoryConsumption();
}
}
return sum;
}
// Creates the GPU setting.
// This setting is unique because it may not be defined by command line, or
// build. In this was, it can be unset.
scoped_ptr<IntSetting> CreateGpuSetting(const CommandLine& command_line,
const BuildSettings& build_settings) {
// Bind to the starboard api, if applicable.
base::optional<int64_t> starboard_setting;
if (SbSystemHasCapability(kSbSystemCapabilityCanQueryGPUMemoryStats)) {
starboard_setting = SbSystemGetTotalGPUMemory();
}
scoped_ptr<IntSetting> gpu_setting =
CreateSystemMemorySetting(
switches::kMaxCobaltGpuUsage,
MemorySetting::kGPU,
command_line,
build_settings.max_gpu_in_bytes,
starboard_setting);
EnsureValuePositive(gpu_setting.get());
return gpu_setting.Pass();
}
scoped_ptr<IntSetting> CreateCpuSetting(const CommandLine& command_line,
const BuildSettings& build_settings) {
scoped_ptr<IntSetting> cpu_setting =
CreateSystemMemorySetting(
switches::kMaxCobaltGpuUsage,
MemorySetting::kCPU,
command_line,
build_settings.max_cpu_in_bytes,
SbSystemGetTotalCPUMemory());
EnsureValuePositive(cpu_setting.get());
return cpu_setting.Pass();
}
} // namespace
AutoMem::AutoMem(const math::Size& ui_resolution,
const CommandLine& command_line,
const BuildSettings& build_settings) {
max_cpu_bytes_ = CreateCpuSetting(command_line, build_settings);
max_gpu_bytes_ = CreateGpuSetting(command_line, build_settings);
// Set the ImageCache
image_cache_size_in_bytes_ = CreateMemorySetting<IntSetting, int64_t>(
switches::kImageCacheSizeInBytes,
command_line,
build_settings.cobalt_image_cache_size_in_bytes,
CalculateImageCacheSize(ui_resolution));
EnsureValuePositive(image_cache_size_in_bytes_.get());
image_cache_size_in_bytes_->set_memory_type(MemorySetting::kGPU);
// Set javascript gc threshold
javascript_gc_threshold_in_bytes_ = CreateMemorySetting<IntSetting, int64_t>(
switches::kJavaScriptGcThresholdInBytes,
command_line,
build_settings.javascript_garbage_collection_threshold_in_bytes,
kDefaultJsGarbageCollectionThresholdSize);
EnsureValuePositive(javascript_gc_threshold_in_bytes_.get());
// Set the misc cobalt size to a specific size.
misc_cobalt_cpu_size_in_bytes_.reset(
new IntSetting("misc_cobalt_cpu_size_in_bytes"));
misc_cobalt_cpu_size_in_bytes_->set_value(
MemorySetting::kAutoSet, kMiscCobaltSizeInBytes);
// Set remote_type_face_cache size.
remote_typeface_cache_size_in_bytes_ =
CreateMemorySetting<IntSetting, int64_t>(
switches::kRemoteTypefaceCacheSizeInBytes,
command_line,
build_settings.remote_typeface_cache_capacity_in_bytes,
kDefaultRemoteTypeFaceCacheSize);
EnsureValuePositive(remote_typeface_cache_size_in_bytes_.get());
// Set skia_atlas_texture_dimensions
skia_atlas_texture_dimensions_ =
CreateMemorySetting<DimensionSetting, TextureDimensions>(
switches::kSkiaTextureAtlasDimensions,
command_line,
build_settings.skia_texture_atlas_dimensions,
CalculateSkiaGlyphAtlasTextureSize(ui_resolution));
// Not available for non-blitter platforms.
if (build_settings.has_blitter) {
skia_atlas_texture_dimensions_->set_memory_type(
MemorySetting::kNotApplicable);
} else {
// Skia always uses gpu memory, when enabled.
skia_atlas_texture_dimensions_->set_memory_type(MemorySetting::kGPU);
}
EnsureValuePositive(skia_atlas_texture_dimensions_.get());
EnsureTwoBytesPerPixel(skia_atlas_texture_dimensions_.get());
// Set skia_cache_size_in_bytes
skia_cache_size_in_bytes_ = CreateMemorySetting<IntSetting, int64_t>(
switches::kSkiaCacheSizeInBytes,
command_line,
build_settings.skia_cache_size_in_bytes,
CalculateSkiaCacheSize(ui_resolution));
// Not available for blitter platforms.
if (build_settings.has_blitter) {
skia_cache_size_in_bytes_->set_memory_type(MemorySetting::kNotApplicable);
} else {
// Skia always uses gpu memory, when enabled.
skia_cache_size_in_bytes_->set_memory_type(MemorySetting::kGPU);
}
EnsureValuePositive(skia_cache_size_in_bytes_.get());
// Set software_surface_cache_size_in_bytes
software_surface_cache_size_in_bytes_ =
CreateMemorySetting<IntSetting, int64_t>(
switches::kSoftwareSurfaceCacheSizeInBytes,
command_line,
build_settings.software_surface_cache_size_in_bytes,
CalculateSoftwareSurfaceCacheSizeInBytes(ui_resolution));
// Blitter only feature.
if (!build_settings.has_blitter) {
software_surface_cache_size_in_bytes_->set_memory_type(
MemorySetting::kNotApplicable);
}
EnsureValuePositive(software_surface_cache_size_in_bytes_.get());
}
AutoMem::~AutoMem() {}
const IntSetting* AutoMem::misc_engine_cpu_size_in_bytes() const {
return misc_cobalt_cpu_size_in_bytes_.get();
}
const IntSetting* AutoMem::remote_typeface_cache_size_in_bytes() const {
return remote_typeface_cache_size_in_bytes_.get();
}
const IntSetting* AutoMem::image_cache_size_in_bytes() const {
return image_cache_size_in_bytes_.get();
}
const IntSetting* AutoMem::javascript_gc_threshold_in_bytes() const {
return javascript_gc_threshold_in_bytes_.get();
}
const DimensionSetting* AutoMem::skia_atlas_texture_dimensions() const {
return skia_atlas_texture_dimensions_.get();
}
const IntSetting* AutoMem::skia_cache_size_in_bytes() const {
return skia_cache_size_in_bytes_.get();
}
const IntSetting* AutoMem::software_surface_cache_size_in_bytes() const {
return software_surface_cache_size_in_bytes_.get();
}
std::vector<const MemorySetting*> AutoMem::AllMemorySettings() const {
AutoMem* this_unconst = const_cast<AutoMem*>(this);
std::vector<MemorySetting*> all_settings_mutable =
this_unconst->AllMemorySettingsMutable();
std::vector<const MemorySetting*> all_settings;
all_settings.assign(all_settings_mutable.begin(),
all_settings_mutable.end());
return all_settings;
}
// Make sure that this is the same as AllMemorySettings().
std::vector<MemorySetting*> AutoMem::AllMemorySettingsMutable() {
std::vector<MemorySetting*> all_settings;
// Keep these in alphabetical order.
all_settings.push_back(image_cache_size_in_bytes_.get());
all_settings.push_back(javascript_gc_threshold_in_bytes_.get());
all_settings.push_back(misc_cobalt_cpu_size_in_bytes_.get());
all_settings.push_back(remote_typeface_cache_size_in_bytes_.get());
all_settings.push_back(skia_atlas_texture_dimensions_.get());
all_settings.push_back(skia_cache_size_in_bytes_.get());
all_settings.push_back(software_surface_cache_size_in_bytes_.get());
return all_settings;
}
std::string AutoMem::ToPrettyPrintString() const {
std::stringstream ss;
std::vector<const MemorySetting*> all_settings = AllMemorySettings();
ss << GeneratePrettyPrintTable(all_settings) << "\n";
int64_t cpu_consumption =
SumMemoryConsumption(MemorySetting::kCPU, all_settings);
int64_t gpu_consumption =
SumMemoryConsumption(MemorySetting::kGPU, all_settings);
ss << GenerateMemoryTable(*max_cpu_bytes_, *max_gpu_bytes_,
cpu_consumption, gpu_consumption);
std::string output_str = ss.str();
return output_str;
}
} // namespace memory_settings
} // namespace browser
} // namespace cobalt