Commit db6ea3c0 authored by Khushal's avatar Khushal Committed by Commit Bot

gpu: Fix oop raster font cache memory leak.

The code for tracking locked font handles on the service currently
assumes that only one DoRasterCHROMIUM command is interleaved between a
Begin/EndRaster sequence, which is an incorrect assumption. As a result
since we clear the vector of locked handles in each DoRasterCHROMIUM,
this leaks ref-counts if multiple DoRasterCHROMIUM commands are present
between a single Begin/EndRaster sequence.

Fix this by appending ref-counts on the service instead of over-writing
the current list.

R=piman@chromium.org

Bug: 829622
Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;luci.chromium.try:linux_optional_gpu_tests_rel;luci.chromium.try:mac_optional_gpu_tests_rel;luci.chromium.try:win_optional_gpu_tests_rel;master.tryserver.blink:linux_trusty_blink_rel
Change-Id: I15025153fe887e712e0879cbeb32944c3e258e92
Reviewed-on: https://chromium-review.googlesource.com/1077819
Commit-Queue: Khushal <khushalsagar@chromium.org>
Reviewed-by: default avatarAntoine Labour <piman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#563092}
parent 700e9213
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "base/command_line.h" #include "base/command_line.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/threading/thread_task_runner_handle.h" #include "base/threading/thread_task_runner_handle.h"
#include "cc/base/completion_event.h"
#include "cc/base/region.h" #include "cc/base/region.h"
#include "cc/layers/recording_source.h" #include "cc/layers/recording_source.h"
#include "cc/paint/display_item_list.h" #include "cc/paint/display_item_list.h"
...@@ -28,6 +29,7 @@ ...@@ -28,6 +29,7 @@
#include "gpu/ipc/gl_in_process_context.h" #include "gpu/ipc/gl_in_process_context.h"
#include "gpu/skia_bindings/grcontext_for_gles2_interface.h" #include "gpu/skia_bindings/grcontext_for_gles2_interface.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkGraphics.h"
#include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/gpu/GrBackendSurface.h" #include "third_party/skia/include/gpu/GrBackendSurface.h"
#include "third_party/skia/include/gpu/GrContext.h" #include "third_party/skia/include/gpu/GrContext.h"
...@@ -99,6 +101,7 @@ class OopPixelTest : public testing::Test { ...@@ -99,6 +101,7 @@ class OopPixelTest : public testing::Test {
bool preclear = false; bool preclear = false;
SkColor preclear_color; SkColor preclear_color;
ImageDecodeCache* image_cache = nullptr; ImageDecodeCache* image_cache = nullptr;
std::vector<scoped_refptr<DisplayItemList>> additional_lists;
}; };
SkBitmap Raster(scoped_refptr<DisplayItemList> display_item_list, SkBitmap Raster(scoped_refptr<DisplayItemList> display_item_list,
...@@ -151,6 +154,12 @@ class OopPixelTest : public testing::Test { ...@@ -151,6 +154,12 @@ class OopPixelTest : public testing::Test {
display_item_list.get(), &image_provider, options.content_size, display_item_list.get(), &image_provider, options.content_size,
options.full_raster_rect, options.playback_rect, options.post_translate, options.full_raster_rect, options.playback_rect, options.post_translate,
options.post_scale, options.requires_clear); options.post_scale, options.requires_clear);
for (const auto& list : options.additional_lists) {
raster_implementation->RasterCHROMIUM(
list.get(), &image_provider, options.content_size,
options.full_raster_rect, options.playback_rect,
options.post_translate, options.post_scale, options.requires_clear);
}
raster_implementation->EndRasterCHROMIUM(); raster_implementation->EndRasterCHROMIUM();
// Produce a mailbox and insert an ordering barrier (assumes the raster // Produce a mailbox and insert an ordering barrier (assumes the raster
...@@ -1200,10 +1209,12 @@ TEST_F(OopPixelTest, DrawRectColorSpace) { ...@@ -1200,10 +1209,12 @@ TEST_F(OopPixelTest, DrawRectColorSpace) {
ExpectEquals(actual, expected); ExpectEquals(actual, expected);
} }
scoped_refptr<PaintTextBlob> buildTextBlob() { scoped_refptr<PaintTextBlob> BuildTextBlob(
PaintTypeface typeface = PaintTypeface()) {
SkFontStyle style; SkFontStyle style;
PaintTypeface typeface = if (!typeface) {
PaintTypeface::FromFamilyNameAndFontStyle("monospace", style); typeface = PaintTypeface::FromFamilyNameAndFontStyle("monospace", style);
}
PaintFont font; PaintFont font;
font.SetTypeface(typeface); font.SetTypeface(typeface);
...@@ -1235,7 +1246,7 @@ TEST_F(OopPixelTest, DrawTextBlob) { ...@@ -1235,7 +1246,7 @@ TEST_F(OopPixelTest, DrawTextBlob) {
PaintFlags flags; PaintFlags flags;
flags.setStyle(PaintFlags::kFill_Style); flags.setStyle(PaintFlags::kFill_Style);
flags.setColor(SK_ColorGREEN); flags.setColor(SK_ColorGREEN);
display_item_list->push<DrawTextBlobOp>(buildTextBlob(), 0u, 0u, flags); display_item_list->push<DrawTextBlobOp>(BuildTextBlob(), 0u, 0u, flags);
display_item_list->EndPaintOfUnpaired(options.full_raster_rect); display_item_list->EndPaintOfUnpaired(options.full_raster_rect);
display_item_list->Finalize(); display_item_list->Finalize();
...@@ -1256,7 +1267,7 @@ TEST_F(OopPixelTest, DrawRecordShaderWithTextScaled) { ...@@ -1256,7 +1267,7 @@ TEST_F(OopPixelTest, DrawRecordShaderWithTextScaled) {
PaintFlags flags; PaintFlags flags;
flags.setStyle(PaintFlags::kFill_Style); flags.setStyle(PaintFlags::kFill_Style);
flags.setColor(SK_ColorGREEN); flags.setColor(SK_ColorGREEN);
paint_record->push<DrawTextBlobOp>(buildTextBlob(), 0u, 0u, flags); paint_record->push<DrawTextBlobOp>(BuildTextBlob(), 0u, 0u, flags);
auto paint_record_shader = PaintShader::MakePaintRecord( auto paint_record_shader = PaintShader::MakePaintRecord(
paint_record, SkRect::MakeWH(25, 25), SkShader::kRepeat_TileMode, paint_record, SkRect::MakeWH(25, 25), SkShader::kRepeat_TileMode,
SkShader::kRepeat_TileMode, nullptr); SkShader::kRepeat_TileMode, nullptr);
...@@ -1287,7 +1298,7 @@ TEST_F(OopPixelTest, DrawRecordFilterWithTextScaled) { ...@@ -1287,7 +1298,7 @@ TEST_F(OopPixelTest, DrawRecordFilterWithTextScaled) {
PaintFlags flags; PaintFlags flags;
flags.setStyle(PaintFlags::kFill_Style); flags.setStyle(PaintFlags::kFill_Style);
flags.setColor(SK_ColorGREEN); flags.setColor(SK_ColorGREEN);
paint_record->push<DrawTextBlobOp>(buildTextBlob(), 0u, 0u, flags); paint_record->push<DrawTextBlobOp>(BuildTextBlob(), 0u, 0u, flags);
auto paint_record_filter = auto paint_record_filter =
sk_make_sp<RecordPaintFilter>(paint_record, SkRect::MakeWH(100, 100)); sk_make_sp<RecordPaintFilter>(paint_record, SkRect::MakeWH(100, 100));
...@@ -1305,6 +1316,57 @@ TEST_F(OopPixelTest, DrawRecordFilterWithTextScaled) { ...@@ -1305,6 +1316,57 @@ TEST_F(OopPixelTest, DrawRecordFilterWithTextScaled) {
ExpectEquals(actual, expected); ExpectEquals(actual, expected);
} }
void ClearFontCache(CompletionEvent* event) {
SkGraphics::PurgeFontCache();
event->Signal();
}
TEST_F(OopPixelTest, DrawTextMultipleRasterCHROMIUM) {
RasterOptions options;
options.resource_size = gfx::Size(100, 100);
options.content_size = options.resource_size;
options.full_raster_rect = gfx::Rect(options.content_size);
options.playback_rect = options.full_raster_rect;
options.color_space = gfx::ColorSpace::CreateSRGB();
auto sk_typeface_1 = SkTypeface::MakeFromName("monospace", SkFontStyle());
auto sk_typeface_2 = SkTypeface::MakeFromName("roboto", SkFontStyle());
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
display_item_list->StartPaint();
PaintFlags flags;
flags.setStyle(PaintFlags::kFill_Style);
flags.setColor(SK_ColorGREEN);
display_item_list->push<DrawTextBlobOp>(
BuildTextBlob(PaintTypeface::FromSkTypeface(sk_typeface_1)), 0u, 0u,
flags);
display_item_list->EndPaintOfUnpaired(options.full_raster_rect);
display_item_list->Finalize();
// Create another list with a different typeface.
auto display_item_list_2 = base::MakeRefCounted<DisplayItemList>();
display_item_list_2->StartPaint();
display_item_list_2->push<DrawTextBlobOp>(
BuildTextBlob(PaintTypeface::FromSkTypeface(sk_typeface_2)), 0u, 0u,
flags);
display_item_list_2->EndPaintOfUnpaired(options.full_raster_rect);
display_item_list_2->Finalize();
// Raster both these lists with 2 RasterCHROMIUM commands between a single
// Begin/EndRaster sequence.
options.additional_lists = {display_item_list_2};
Raster(display_item_list, options);
// Clear skia's font cache. No entries should remain since the service
// should unpin everything.
EXPECT_GT(SkGraphics::GetFontCacheUsed(), 0u);
CompletionEvent event;
raster_context_provider_->ExecuteOnGpuThread(
base::BindOnce(&ClearFontCache, &event));
event.Wait();
EXPECT_EQ(SkGraphics::GetFontCacheUsed(), 0u);
}
INSTANTIATE_TEST_CASE_P(P, OopImagePixelTest, ::testing::Bool()); INSTANTIATE_TEST_CASE_P(P, OopImagePixelTest, ::testing::Bool());
} // namespace } // namespace
......
...@@ -172,4 +172,11 @@ const gpu::GpuFeatureInfo& TestInProcessContextProvider::GetGpuFeatureInfo() ...@@ -172,4 +172,11 @@ const gpu::GpuFeatureInfo& TestInProcessContextProvider::GetGpuFeatureInfo()
return gpu_feature_info_; return gpu_feature_info_;
} }
void TestInProcessContextProvider::ExecuteOnGpuThread(base::OnceClosure task) {
DCHECK(raster_context_);
raster_context_->GetCommandBufferForTest()
->service_for_testing()
->ScheduleTask(std::move(task));
}
} // namespace cc } // namespace cc
...@@ -54,6 +54,8 @@ class TestInProcessContextProvider ...@@ -54,6 +54,8 @@ class TestInProcessContextProvider
void AddObserver(viz::ContextLostObserver* obs) override {} void AddObserver(viz::ContextLostObserver* obs) override {}
void RemoveObserver(viz::ContextLostObserver* obs) override {} void RemoveObserver(viz::ContextLostObserver* obs) override {}
void ExecuteOnGpuThread(base::OnceClosure task);
protected: protected:
friend class base::RefCountedThreadSafe<TestInProcessContextProvider>; friend class base::RefCountedThreadSafe<TestInProcessContextProvider>;
~TestInProcessContextProvider() override; ~TestInProcessContextProvider() override;
......
...@@ -3058,12 +3058,15 @@ void RasterDecoderImpl::DoRasterCHROMIUM(GLuint raster_shm_id, ...@@ -3058,12 +3058,15 @@ void RasterDecoderImpl::DoRasterCHROMIUM(GLuint raster_shm_id,
return; return;
} }
std::vector<SkDiscardableHandleId> new_locked_handles;
if (!font_manager_.Deserialize(font_buffer_memory, font_shm_size, if (!font_manager_.Deserialize(font_buffer_memory, font_shm_size,
&locked_handles_)) { &new_locked_handles)) {
LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glRasterCHROMIUM", LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glRasterCHROMIUM",
"Invalid font buffer."); "Invalid font buffer.");
return; return;
} }
locked_handles_.insert(locked_handles_.end(), new_locked_handles.begin(),
new_locked_handles.end());
} }
char* paint_buffer_memory = GetSharedMemoryAs<char*>( char* paint_buffer_memory = GetSharedMemoryAs<char*>(
......
...@@ -86,16 +86,22 @@ class ServiceFontManager::SkiaDiscardableManager ...@@ -86,16 +86,22 @@ class ServiceFontManager::SkiaDiscardableManager
ServiceFontManager::ServiceFontManager(Client* client) ServiceFontManager::ServiceFontManager(Client* client)
: client_(client), weak_factory_(this) { : client_(client), weak_factory_(this) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
strike_client_ = std::make_unique<SkStrikeClient>( strike_client_ = std::make_unique<SkStrikeClient>(
sk_make_sp<SkiaDiscardableManager>(weak_factory_.GetWeakPtr())); sk_make_sp<SkiaDiscardableManager>(weak_factory_.GetWeakPtr()));
} }
ServiceFontManager::~ServiceFontManager() = default; ServiceFontManager::~ServiceFontManager() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
}
bool ServiceFontManager::Deserialize( bool ServiceFontManager::Deserialize(
const volatile char* memory, const volatile char* memory,
size_t memory_size, size_t memory_size,
std::vector<SkDiscardableHandleId>* locked_handles) { std::vector<SkDiscardableHandleId>* locked_handles) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(locked_handles->empty());
// All new handles. // All new handles.
Deserializer deserializer(memory, memory_size); Deserializer deserializer(memory, memory_size);
size_t new_handles_created; size_t new_handles_created;
...@@ -143,6 +149,8 @@ bool ServiceFontManager::Deserialize( ...@@ -143,6 +149,8 @@ bool ServiceFontManager::Deserialize(
bool ServiceFontManager::AddHandle(SkDiscardableHandleId handle_id, bool ServiceFontManager::AddHandle(SkDiscardableHandleId handle_id,
ServiceDiscardableHandle handle) { ServiceDiscardableHandle handle) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (discardable_handle_map_.find(handle_id) != discardable_handle_map_.end()) if (discardable_handle_map_.find(handle_id) != discardable_handle_map_.end())
return false; return false;
discardable_handle_map_[handle_id] = std::move(handle); discardable_handle_map_[handle_id] = std::move(handle);
...@@ -151,6 +159,8 @@ bool ServiceFontManager::AddHandle(SkDiscardableHandleId handle_id, ...@@ -151,6 +159,8 @@ bool ServiceFontManager::AddHandle(SkDiscardableHandleId handle_id,
bool ServiceFontManager::Unlock( bool ServiceFontManager::Unlock(
const std::vector<SkDiscardableHandleId>& handles) { const std::vector<SkDiscardableHandleId>& handles) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
for (auto handle_id : handles) { for (auto handle_id : handles) {
auto it = discardable_handle_map_.find(handle_id); auto it = discardable_handle_map_.find(handle_id);
if (it == discardable_handle_map_.end()) if (it == discardable_handle_map_.end())
...@@ -161,6 +171,8 @@ bool ServiceFontManager::Unlock( ...@@ -161,6 +171,8 @@ bool ServiceFontManager::Unlock(
} }
bool ServiceFontManager::DeleteHandle(SkDiscardableHandleId handle_id) { bool ServiceFontManager::DeleteHandle(SkDiscardableHandleId handle_id) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
auto it = discardable_handle_map_.find(handle_id); auto it = discardable_handle_map_.find(handle_id);
if (it == discardable_handle_map_.end()) { if (it == discardable_handle_map_.end()) {
LOG(ERROR) << "Tried to delete invalid SkDiscardableHandleId: " LOG(ERROR) << "Tried to delete invalid SkDiscardableHandleId: "
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "base/containers/flat_map.h" #include "base/containers/flat_map.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/threading/thread_checker.h"
#include "gpu/command_buffer/common/discardable_handle.h" #include "gpu/command_buffer/common/discardable_handle.h"
#include "gpu/gpu_gles2_export.h" #include "gpu/gpu_gles2_export.h"
#include "third_party/skia/src/core/SkRemoteGlyphCache.h" #include "third_party/skia/src/core/SkRemoteGlyphCache.h"
...@@ -42,6 +43,7 @@ class GPU_GLES2_EXPORT ServiceFontManager { ...@@ -42,6 +43,7 @@ class GPU_GLES2_EXPORT ServiceFontManager {
std::unique_ptr<SkStrikeClient> strike_client_; std::unique_ptr<SkStrikeClient> strike_client_;
base::flat_map<SkDiscardableHandleId, ServiceDiscardableHandle> base::flat_map<SkDiscardableHandleId, ServiceDiscardableHandle>
discardable_handle_map_; discardable_handle_map_;
THREAD_CHECKER(thread_checker_);
base::WeakPtrFactory<ServiceFontManager> weak_factory_; base::WeakPtrFactory<ServiceFontManager> weak_factory_;
}; };
......
...@@ -261,6 +261,8 @@ class GL_IN_PROCESS_CONTEXT_EXPORT InProcessCommandBuffer ...@@ -261,6 +261,8 @@ class GL_IN_PROCESS_CONTEXT_EXPORT InProcessCommandBuffer
gles2::FramebufferCompletenessCache framebuffer_completeness_cache_; gles2::FramebufferCompletenessCache framebuffer_completeness_cache_;
}; };
Service* service_for_testing() const { return service_.get(); }
private: private:
struct InitializeOnGpuThreadParams { struct InitializeOnGpuThreadParams {
bool is_offscreen; bool is_offscreen;
......
...@@ -125,4 +125,9 @@ ServiceTransferCache* RasterInProcessContext::GetTransferCacheForTest() const { ...@@ -125,4 +125,9 @@ ServiceTransferCache* RasterInProcessContext::GetTransferCacheForTest() const {
return command_buffer_->GetTransferCacheForTest(); return command_buffer_->GetTransferCacheForTest();
} }
InProcessCommandBuffer* RasterInProcessContext::GetCommandBufferForTest()
const {
return command_buffer_.get();
}
} // namespace gpu } // namespace gpu
...@@ -55,6 +55,7 @@ class RasterInProcessContext { ...@@ -55,6 +55,7 @@ class RasterInProcessContext {
// Test only functions. // Test only functions.
ServiceTransferCache* GetTransferCacheForTest() const; ServiceTransferCache* GetTransferCacheForTest() const;
InProcessCommandBuffer* GetCommandBufferForTest() const;
private: private:
std::unique_ptr<CommandBufferHelper> helper_; std::unique_ptr<CommandBufferHelper> helper_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment