diff --git a/content/browser/gpu/gpu_data_manager_impl_private.cc b/content/browser/gpu/gpu_data_manager_impl_private.cc index a9c81d71e1842..b808445055715 100644 --- a/content/browser/gpu/gpu_data_manager_impl_private.cc +++ b/content/browser/gpu/gpu_data_manager_impl_private.cc @@ -42,6 +42,7 @@ #include "gpu/config/gpu_util.h" #include "gpu/config/software_rendering_list_autogen.h" #include "gpu/ipc/common/memory_stats.h" +#include "gpu/ipc/host/shader_disk_cache.h" #include "gpu/ipc/service/switches.h" #include "media/media_features.h" #include "ui/base/ui_base_switches.h" @@ -925,6 +926,9 @@ void GpuDataManagerImplPrivate::UpdateGpuPreferences( (command_line->HasSwitch(switches::kEnableES3APIs) || !IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_WEBGL2)) && !command_line->HasSwitch(switches::kDisableES3APIs); + + gpu_preferences->gpu_program_cache_size = + gpu::ShaderDiskCache::CacheSizeBytes(); } void GpuDataManagerImplPrivate::DisableHardwareAcceleration() { diff --git a/gpu/DEPS b/gpu/DEPS index a395bb7ce2ff5..0273fe34a2f98 100644 --- a/gpu/DEPS +++ b/gpu/DEPS @@ -5,6 +5,7 @@ include_rules = [ "+third_party/smhasher", "+third_party/swiftshader", "+third_party/protbuf", + "+third_party/zlib", "+crypto", "+ui/gfx", "+ui/gl", diff --git a/gpu/command_buffer/service/BUILD.gn b/gpu/command_buffer/service/BUILD.gn index 743b21e603683..b4b40b989a266 100644 --- a/gpu/command_buffer/service/BUILD.gn +++ b/gpu/command_buffer/service/BUILD.gn @@ -179,6 +179,7 @@ target(link_target_type, "service_sources") { "//third_party/protobuf:protobuf_lite", "//third_party/re2", "//third_party/smhasher:cityhash", + "//third_party/zlib", "//ui/gfx", "//ui/gfx/geometry", "//ui/gfx/ipc/color", diff --git a/gpu/command_buffer/service/disk_cache_proto.proto b/gpu/command_buffer/service/disk_cache_proto.proto index b1f47fe5238ae..784bf4ee63aa6 100644 --- a/gpu/command_buffer/service/disk_cache_proto.proto +++ b/gpu/command_buffer/service/disk_cache_proto.proto @@ -61,6 +61,8 @@ message GpuProgramProto { optional bytes sha = 1; optional uint32 format = 2; optional bytes program = 3; + optional bool program_is_compressed = 6; + optional uint32 program_decompressed_length = 7; optional ShaderProto vertex_shader = 4; optional ShaderProto fragment_shader = 5; diff --git a/gpu/command_buffer/service/gpu_preferences.cc b/gpu/command_buffer/service/gpu_preferences.cc index 1b7983d64206c..3de69ee8e46ea 100644 --- a/gpu/command_buffer/service/gpu_preferences.cc +++ b/gpu/command_buffer/service/gpu_preferences.cc @@ -8,13 +8,7 @@ namespace gpu { -GpuPreferences::GpuPreferences() { - gpu_program_cache_size = kDefaultMaxProgramCacheMemoryBytes; -#if defined(OS_ANDROID) - if (base::SysInfo::IsLowEndDevice()) - gpu_program_cache_size = kLowEndMaxProgramCacheMemoryBytes; -#endif -} +GpuPreferences::GpuPreferences() = default; GpuPreferences::GpuPreferences(const GpuPreferences& other) = default; diff --git a/gpu/command_buffer/service/memory_program_cache.cc b/gpu/command_buffer/service/memory_program_cache.cc index 2601dea81e472..912a2dcf1eeda 100644 --- a/gpu/command_buffer/service/memory_program_cache.cc +++ b/gpu/command_buffer/service/memory_program_cache.cc @@ -12,8 +12,11 @@ #include "base/command_line.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" +#include "base/numerics/checked_math.h" #include "base/sha1.h" #include "base/strings/string_number_conversions.h" +#include "base/sys_info.h" +#include "build/build_config.h" #include "gpu/command_buffer/common/activity_flags.h" #include "gpu/command_buffer/common/constants.h" #include "gpu/command_buffer/service/disk_cache_proto.pb.h" @@ -21,8 +24,19 @@ #include "gpu/command_buffer/service/gles2_cmd_decoder.h" #include "gpu/command_buffer/service/gpu_preferences.h" #include "gpu/command_buffer/service/shader_manager.h" +#include "third_party/zlib/zlib.h" #include "ui/gl/gl_bindings.h" +// Macro to help with logging times under 10ms. +#define UMA_HISTOGRAM_VERY_SHORT_TIMES(name, time_delta) \ + UMA_HISTOGRAM_CUSTOM_COUNTS( \ + name, \ + static_cast<base::HistogramBase::Sample>((time_delta).InMicroseconds()), \ + 1, \ + static_cast<base::HistogramBase::Sample>( \ + base::TimeDelta::FromMilliseconds(10).InMicroseconds()), \ + 50); + namespace gpu { namespace gles2 { @@ -209,6 +223,64 @@ bool ProgramBinaryExtensionsAvailable() { gl::g_current_gl_driver->ext.b_GL_OES_get_program_binary); } +// Returns an empty vector if compression fails. +std::vector<uint8_t> CompressData(const std::vector<uint8_t>& data) { + auto start_time = base::TimeTicks::Now(); + Cr_z_uLongf compressed_size = compressBound(data.size()); + std::vector<uint8_t> compressed_data(compressed_size); + // Level indicates a trade-off between compression and speed. Level 1 + // indicates fastest speed (with worst compression). + auto result = compress2(compressed_data.data(), &compressed_size, data.data(), + data.size(), 1 /* level */); + // It should be impossible for compression to fail with the provided + // parameters. + bool success = Z_OK == result; + UMA_HISTOGRAM_BOOLEAN("GPU.ProgramCache.CompressDataSuccess", success); + if (!success) + return std::vector<uint8_t>(); + + compressed_data.resize(compressed_size); + compressed_data.shrink_to_fit(); + + UMA_HISTOGRAM_VERY_SHORT_TIMES("GPU.ProgramCache.CompressDataTime", + base::TimeTicks::Now() - start_time); + UMA_HISTOGRAM_PERCENTAGE("GPU.ProgramCache.CompressionPercentage", + (100 * compressed_size) / data.size()); + + return compressed_data; +} + +// Returns an empty vector if decompression fails. +std::vector<uint8_t> DecompressData(const std::vector<uint8_t>& data, + size_t decompressed_size, + size_t max_size_bytes) { + auto start_time = base::TimeTicks::Now(); + std::vector<uint8_t> decompressed_data(decompressed_size); + Cr_z_uLongf decompressed_size_out = + static_cast<Cr_z_uLongf>(decompressed_size); + auto result = uncompress(decompressed_data.data(), &decompressed_size_out, + data.data(), data.size()); + + bool success = + result == Z_OK && decompressed_data.size() == decompressed_size_out; + UMA_HISTOGRAM_BOOLEAN("GPU.ProgramCache.DecompressDataSuccess", success); + if (!success) + return std::vector<uint8_t>(); + + UMA_HISTOGRAM_VERY_SHORT_TIMES("GPU.ProgramCache.DecompressDataTime", + base::TimeTicks::Now() - start_time); + + return decompressed_data; +} + +bool CompressProgramBinaries() { +#if !defined(OS_ANDROID) + return false; +#else // !defined(OS_ANDROID) + return base::SysInfo::IsLowEndDevice(); +#endif // !defined(OS_ANDROID) +} + } // namespace MemoryProgramCache::MemoryProgramCache( @@ -220,6 +292,7 @@ MemoryProgramCache::MemoryProgramCache( disable_gpu_shader_disk_cache_(disable_gpu_shader_disk_cache), disable_program_caching_for_transform_feedback_( disable_program_caching_for_transform_feedback), + compress_program_binaries_(CompressProgramBinaries()), curr_size_bytes_(0), store_(ProgramMRUCache::NO_AUTO_EVICT), activity_flags_(activity_flags) {} @@ -267,12 +340,22 @@ ProgramCache::ProgramLoadResult MemoryProgramCache::LoadLinkedProgram( return PROGRAM_LOAD_FAILURE; } const scoped_refptr<ProgramCacheValue> value = found->second; + const std::vector<uint8_t>& decoded = + value->is_compressed() + ? DecompressData(value->data(), value->decompressed_length(), + max_size_bytes_) + : value->data(); + if (decoded.empty()) { + // Decompression failure. + DCHECK(value->is_compressed()); + return PROGRAM_LOAD_FAILURE; + } { GpuProcessActivityFlags::ScopedSetFlag scoped_set_flag( activity_flags_, ActivityFlagsBase::FLAG_LOADING_PROGRAM_BINARY); glProgramBinary(program, value->format(), - static_cast<const GLvoid*>(value->data()), value->length()); + static_cast<const GLvoid*>(decoded.data()), decoded.size()); } GLint success = 0; @@ -296,7 +379,9 @@ ProgramCache::ProgramLoadResult MemoryProgramCache::LoadLinkedProgram( GpuProgramProto::default_instance().New()); proto->set_sha(sha, kHashLength); proto->set_format(value->format()); - proto->set_program(value->data(), value->length()); + proto->set_program(value->data().data(), value->data().size()); + proto->set_program_is_compressed(value->is_compressed()); + proto->set_program_decompressed_length(value->decompressed_length()); FillShaderProto(proto->mutable_vertex_shader(), a_sha, shader_a); FillShaderProto(proto->mutable_fragment_shader(), b_sha, shader_b); @@ -324,17 +409,23 @@ void MemoryProgramCache::SaveLinkedProgram( } GLenum format; GLsizei length = 0; - glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH_OES, &length); + glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &length); if (length == 0 || static_cast<unsigned int>(length) > max_size_bytes_) { return; } - std::unique_ptr<char[]> binary(new char[length]); - glGetProgramBinary(program, - length, - NULL, - &format, - binary.get()); - UMA_HISTOGRAM_COUNTS("GPU.ProgramCache.ProgramBinarySizeBytes", length); + std::vector<uint8_t> binary(length); + glGetProgramBinary(program, length, NULL, &format, + reinterpret_cast<char*>(binary.data())); + + if (compress_program_binaries_) { + binary = CompressData(binary); + if (binary.empty()) { + // Zero size indicates failure. + return; + } + } + UMA_HISTOGRAM_COUNTS("GPU.ProgramCache.ProgramBinarySizeBytes", + binary.size()); char a_sha[kHashLength]; char b_sha[kHashLength]; @@ -363,7 +454,7 @@ void MemoryProgramCache::SaveLinkedProgram( if(existing != store_.end()) store_.Erase(existing); - while (curr_size_bytes_ + length > max_size_bytes_) { + while (curr_size_bytes_ + binary.size() > max_size_bytes_) { DCHECK(!store_.empty()); store_.Erase(store_.rbegin()); } @@ -373,7 +464,9 @@ void MemoryProgramCache::SaveLinkedProgram( GpuProgramProto::default_instance().New()); proto->set_sha(sha, kHashLength); proto->set_format(format); - proto->set_program(binary.get(), length); + proto->set_program(binary.data(), binary.size()); + proto->set_program_decompressed_length(length); + proto->set_program_is_compressed(compress_program_binaries_); FillShaderProto(proto->mutable_vertex_shader(), a_sha, shader_a); FillShaderProto(proto->mutable_fragment_shader(), b_sha, shader_b); @@ -383,13 +476,13 @@ void MemoryProgramCache::SaveLinkedProgram( store_.Put( sha_string, new ProgramCacheValue( - length, format, binary.release(), sha_string, a_sha, - shader_a->attrib_map(), shader_a->uniform_map(), + format, std::move(binary), compress_program_binaries_, length, + sha_string, a_sha, shader_a->attrib_map(), shader_a->uniform_map(), shader_a->varying_map(), shader_a->output_variable_list(), - shader_a->interface_block_map(), b_sha, - shader_b->attrib_map(), shader_b->uniform_map(), - shader_b->varying_map(), shader_b->output_variable_list(), - shader_b->interface_block_map(), this)); + shader_a->interface_block_map(), b_sha, shader_b->attrib_map(), + shader_b->uniform_map(), shader_b->varying_map(), + shader_b->output_variable_list(), shader_b->interface_block_map(), + this)); UMA_HISTOGRAM_COUNTS("GPU.ProgramCache.MemorySizeAfterKb", curr_size_bytes_ / 1024); @@ -454,14 +547,17 @@ void MemoryProgramCache::LoadProgram(const std::string& key, &fragment_interface_blocks); } - std::unique_ptr<char[]> binary(new char[proto->program().length()]); - memcpy(binary.get(), proto->program().c_str(), proto->program().length()); + std::vector<uint8_t> binary(proto->program().length()); + memcpy(binary.data(), proto->program().c_str(), proto->program().length()); store_.Put( proto->sha(), new ProgramCacheValue( - proto->program().length(), proto->format(), binary.release(), - proto->sha(), proto->vertex_shader().sha().c_str(), vertex_attribs, + proto->format(), std::move(binary), + proto->has_program_is_compressed() && + proto->program_is_compressed(), + proto->program_decompressed_length(), proto->sha(), + proto->vertex_shader().sha().c_str(), vertex_attribs, vertex_uniforms, vertex_varyings, vertex_output_variables, vertex_interface_blocks, proto->fragment_shader().sha().c_str(), fragment_attribs, fragment_uniforms, fragment_varyings, @@ -484,9 +580,10 @@ size_t MemoryProgramCache::Trim(size_t limit) { } MemoryProgramCache::ProgramCacheValue::ProgramCacheValue( - GLsizei length, GLenum format, - const char* data, + std::vector<uint8_t> data, + bool is_compressed, + GLsizei decompressed_length, const std::string& program_hash, const char* shader_0_hash, const AttributeMap& attrib_map_0, @@ -501,9 +598,10 @@ MemoryProgramCache::ProgramCacheValue::ProgramCacheValue( const OutputVariableList& output_variable_list_1, const InterfaceBlockMap& interface_block_map_1, MemoryProgramCache* program_cache) - : length_(length), - format_(format), - data_(data), + : format_(format), + data_(std::move(data)), + is_compressed_(is_compressed), + decompressed_length_(decompressed_length), program_hash_(program_hash), shader_0_hash_(shader_0_hash, kHashLength), attrib_map_0_(attrib_map_0), @@ -518,12 +616,12 @@ MemoryProgramCache::ProgramCacheValue::ProgramCacheValue( output_variable_list_1_(output_variable_list_1), interface_block_map_1_(interface_block_map_1), program_cache_(program_cache) { - program_cache_->curr_size_bytes_ += length_; + program_cache_->curr_size_bytes_ += data_.size(); program_cache_->LinkedProgramCacheSuccess(program_hash); } MemoryProgramCache::ProgramCacheValue::~ProgramCacheValue() { - program_cache_->curr_size_bytes_ -= length_; + program_cache_->curr_size_bytes_ -= data_.size(); program_cache_->Evict(program_hash_); } diff --git a/gpu/command_buffer/service/memory_program_cache.h b/gpu/command_buffer/service/memory_program_cache.h index 03881f2c888d7..b854fd26e72ae 100644 --- a/gpu/command_buffer/service/memory_program_cache.h +++ b/gpu/command_buffer/service/memory_program_cache.h @@ -59,9 +59,10 @@ class GPU_EXPORT MemoryProgramCache : public ProgramCache { class ProgramCacheValue : public base::RefCounted<ProgramCacheValue> { public: - ProgramCacheValue(GLsizei length, - GLenum format, - const char* data, + ProgramCacheValue(GLenum format, + std::vector<uint8_t> data, + bool is_compressed, + GLsizei decompressed_length, const std::string& program_hash, const char* shader_0_hash, const AttributeMap& attrib_map_0, @@ -77,17 +78,15 @@ class GPU_EXPORT MemoryProgramCache : public ProgramCache { const InterfaceBlockMap& interface_block_map_1, MemoryProgramCache* program_cache); - GLsizei length() const { - return length_; - } - GLenum format() const { return format_; } - const char* data() const { - return data_.get(); - } + const std::vector<uint8_t>& data() const { return data_; } + + bool is_compressed() const { return is_compressed_; } + + GLsizei decompressed_length() const { return decompressed_length_; } const std::string& shader_0_hash() const { return shader_0_hash_; @@ -142,9 +141,10 @@ class GPU_EXPORT MemoryProgramCache : public ProgramCache { ~ProgramCacheValue(); - const GLsizei length_; const GLenum format_; - const std::unique_ptr<const char[]> data_; + const std::vector<uint8_t> data_; + const bool is_compressed_; + const GLsizei decompressed_length_; const std::string program_hash_; const std::string shader_0_hash_; const AttributeMap attrib_map_0_; @@ -171,6 +171,7 @@ class GPU_EXPORT MemoryProgramCache : public ProgramCache { const size_t max_size_bytes_; const bool disable_gpu_shader_disk_cache_; const bool disable_program_caching_for_transform_feedback_; + const bool compress_program_binaries_; size_t curr_size_bytes_; ProgramMRUCache store_; GpuProcessActivityFlags* activity_flags_; diff --git a/gpu/command_buffer/service/program_manager.cc b/gpu/command_buffer/service/program_manager.cc index c1ffc0a753e0b..46df984df45ba 100644 --- a/gpu/command_buffer/service/program_manager.cc +++ b/gpu/command_buffer/service/program_manager.cc @@ -1413,7 +1413,6 @@ bool Program::Link(ShaderManager* manager, ExecuteProgramOutputBindCalls(); - before_time = TimeTicks::Now(); if (cache && gl::g_current_gl_driver->ext.b_GL_ARB_get_program_binary) { glProgramParameteri(service_id(), PROGRAM_BINARY_RETRIEVABLE_HINT, diff --git a/gpu/ipc/host/shader_disk_cache.cc b/gpu/ipc/host/shader_disk_cache.cc index 144f9af47832d..b03e2d5cb403f 100644 --- a/gpu/ipc/host/shader_disk_cache.cc +++ b/gpu/ipc/host/shader_disk_cache.cc @@ -7,7 +7,9 @@ #include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/single_thread_task_runner.h" +#include "base/sys_info.h" #include "base/threading/thread_checker.h" +#include "build/build_config.h" #include "gpu/command_buffer/common/constants.h" #include "net/base/cache_type.h" #include "net/base/io_buffer.h" @@ -554,9 +556,9 @@ void ShaderDiskCache::Init( int rv = disk_cache::CreateCacheBackend( net::SHADER_CACHE, net::CACHE_BACKEND_DEFAULT, - cache_path_.Append(kGpuCachePath), - gpu::kDefaultMaxProgramCacheMemoryBytes, true, cache_task_runner, NULL, - &backend_, base::Bind(&ShaderDiskCache::CacheCreatedCallback, this)); + cache_path_.Append(kGpuCachePath), CacheSizeBytes(), true, + cache_task_runner, NULL, &backend_, + base::Bind(&ShaderDiskCache::CacheCreatedCallback, this)); if (rv == net::OK) cache_available_ = true; @@ -637,4 +639,16 @@ int ShaderDiskCache::SetCacheCompleteCallback( return net::ERR_IO_PENDING; } +// static +size_t ShaderDiskCache::CacheSizeBytes() { +#if !defined(OS_ANDROID) + return kDefaultMaxProgramCacheMemoryBytes; +#else // !defined(OS_ANDROID) + if (!base::SysInfo::IsLowEndDevice()) + return kDefaultMaxProgramCacheMemoryBytes; + else + return kLowEndMaxProgramCacheMemoryBytes; +#endif // !defined(OS_ANDROID) +} + } // namespace gpu diff --git a/gpu/ipc/host/shader_disk_cache.h b/gpu/ipc/host/shader_disk_cache.h index 32decebf1b1e7..7d6e8acca47d4 100644 --- a/gpu/ipc/host/shader_disk_cache.h +++ b/gpu/ipc/host/shader_disk_cache.h @@ -65,6 +65,9 @@ class ShaderDiskCache : public base::RefCounted<ShaderDiskCache> { // been written to the cache. int SetCacheCompleteCallback(const net::CompletionCallback& callback); + // Returns the size which should be used for the shader disk cache. + static size_t CacheSizeBytes(); + private: friend class base::RefCounted<ShaderDiskCache>; friend class ShaderDiskCacheEntry; diff --git a/gpu/ipc/service/gpu_channel_manager.cc b/gpu/ipc/service/gpu_channel_manager.cc index 3a481b86fbe46..d6c3aa80a89eb 100644 --- a/gpu/ipc/service/gpu_channel_manager.cc +++ b/gpu/ipc/service/gpu_channel_manager.cc @@ -104,8 +104,7 @@ GpuChannelManager::~GpuChannelManager() { } gles2::ProgramCache* GpuChannelManager::program_cache() { - if (!program_cache_.get() && - !gpu_preferences_.disable_gpu_program_cache) { + if (!program_cache_.get()) { const GpuDriverBugWorkarounds& workarounds = gpu_driver_bug_workarounds_; bool disable_disk_cache = gpu_preferences_.disable_gpu_shader_disk_cache || diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 1f85804bf99a8..bab2275eae373 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml @@ -24606,6 +24606,45 @@ http://cs/file:chrome/histograms.xml - but prefer this file for new entries. <summary>The time to compile a shader.</summary> </histogram> +<histogram name="GPU.ProgramCache.CompressDataSuccess" units="BooleanSuccess"> + <owner>ericrk@chromium.org</owner> + <summary> + Whether we succeeded in compressing program data. Expected to always be + true. + </summary> +</histogram> + +<histogram name="GPU.ProgramCache.CompressDataTime" units="microseconds"> + <owner>ericrk@chromium.org</owner> + <summary> + The time to compress a program's binary data during insertion into the + program cache. + </summary> +</histogram> + +<histogram name="GPU.ProgramCache.CompressionPercentage" units="%"> + <owner>ericrk@chromium.org</owner> + <summary> + The percentage of raw size that a program binary takes after compression. + </summary> +</histogram> + +<histogram name="GPU.ProgramCache.DecompressDataSuccess" units="BooleanSuccess"> + <owner>ericrk@chromium.org</owner> + <summary> + Whether we succeeded in decompressing program data. Failure indicates disk + or memory corruption. + </summary> +</histogram> + +<histogram name="GPU.ProgramCache.DecompressDataTime" units="microseconds"> + <owner>ericrk@chromium.org</owner> + <summary> + The time to decompress a program's binary data during retrieval from the + program cache. + </summary> +</histogram> + <histogram name="GPU.ProgramCache.LoadBinarySuccess" enum="BooleanSuccess"> <owner>vmiura@chromium.org</owner> <summary>