| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "gpu/command_buffer/service/passthrough_program_cache.h" |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <memory> |
| |
| #include "base/base64.h" |
| #include "base/bind.h" |
| #include "gpu/command_buffer/common/activity_flags.h" |
| #include "gpu/command_buffer/common/gles2_cmd_format.h" |
| #include "gpu/command_buffer/service/decoder_client.h" |
| #include "gpu/command_buffer/service/gl_utils.h" |
| #include "gpu/command_buffer/service/gpu_service_test.h" |
| #include "gpu/command_buffer/service/test_helper.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/gl/gl_bindings.h" |
| #include "ui/gl/gl_mock.h" |
| |
| using ::testing::_; |
| using ::testing::Invoke; |
| using ::testing::SetArgPointee; |
| |
| namespace gpu { |
| namespace gles2 { |
| |
| class PassthroughProgramCacheTest : public GpuServiceTest, |
| public DecoderClient { |
| public: |
| static const size_t kCacheSizeBytes = 1024; |
| static const bool kDisableGpuDiskCache = false; |
| |
| PassthroughProgramCacheTest() |
| : cache_( |
| new PassthroughProgramCache(kCacheSizeBytes, kDisableGpuDiskCache)), |
| blob_count_(0) {} |
| ~PassthroughProgramCacheTest() override {} |
| |
| void OnConsoleMessage(int32_t id, const std::string& message) override {} |
| void CacheShader(const std::string& key, const std::string& shader) override { |
| } |
| void OnFenceSyncRelease(uint64_t release) override {} |
| bool OnWaitSyncToken(const gpu::SyncToken&) override { return false; } |
| void OnDescheduleUntilFinished() override {} |
| void OnRescheduleAfterFinished() override {} |
| void OnSwapBuffers(uint64_t swap_id, uint32_t flags) override {} |
| void ScheduleGrContextCleanup() override {} |
| |
| int32_t blob_count() { return blob_count_; } |
| |
| protected: |
| void SetUp() override { GpuServiceTest::SetUp(); } |
| |
| std::string MakeKey(size_t len, uint8_t start_byte) { |
| std::string binary_key(len, '.'); |
| |
| for (size_t i = 0; i < len; ++i) |
| binary_key[i] = static_cast<uint8_t>((start_byte + i) & 0xFF); |
| |
| return binary_key; |
| } |
| |
| std::string MakeBlob(size_t len, uint8_t start_byte) { |
| return MakeKey(len, start_byte); |
| } |
| |
| void Set(const std::string& binary_key, const std::string& binary_blob) { |
| PassthroughProgramCache::BlobCacheSet( |
| binary_key.data(), static_cast<EGLsizeiANDROID>(binary_key.size()), |
| binary_blob.data(), static_cast<EGLsizeiANDROID>(binary_blob.size())); |
| } |
| |
| std::string Get(const std::string& binary_key) { |
| EGLsizeiANDROID key_size = static_cast<EGLsizeiANDROID>(binary_key.size()); |
| EGLsizeiANDROID blob_size = PassthroughProgramCache::BlobCacheGet( |
| binary_key.data(), key_size, nullptr, 0); |
| |
| if (blob_size <= 0) |
| return ""; |
| |
| // Note: after C++17, this can directly be a std::string as it has a |
| // non-const `char *data()` member. |
| std::vector<uint8_t> binary_blob(blob_size); |
| EGLsizeiANDROID blob_size_after = PassthroughProgramCache::BlobCacheGet( |
| binary_key.data(), key_size, binary_blob.data(), blob_size); |
| |
| EXPECT_EQ(blob_size, blob_size_after); |
| |
| return std::string(binary_blob.begin(), binary_blob.end()); |
| } |
| |
| std::unique_ptr<PassthroughProgramCache> cache_; |
| int32_t blob_count_; |
| }; |
| |
| TEST_F(PassthroughProgramCacheTest, LoadProgram) { |
| const int kKeyLength = 10; |
| const int kBlobLength = 20; |
| |
| for (uint8_t key_start = 0; key_start < 5; ++key_start) { |
| std::string binary_key = MakeKey(kKeyLength, key_start); |
| std::string binary_blob = MakeBlob(kBlobLength, key_start + 10); |
| |
| // Encode the strings to pretend like they came from disk. |
| std::string key_string_64; |
| std::string value_string_64; |
| base::Base64Encode(binary_key, &key_string_64); |
| base::Base64Encode(binary_blob, &value_string_64); |
| |
| cache_->LoadProgram(key_string_64, value_string_64); |
| |
| // Make sure the blob was inserted. |
| EXPECT_EQ(binary_blob, Get(binary_key)); |
| |
| // Test that similar keys but with different length are not retrieved |
| // (especially making sure the '\0' character at the beginning of the key is |
| // not causing confusion). |
| for (size_t i = 0; i <= kKeyLength * 2; ++i) { |
| if (i != kKeyLength) { |
| EXPECT_EQ("", Get(MakeKey(i, key_start))); |
| } |
| } |
| } |
| } |
| |
| TEST_F(PassthroughProgramCacheTest, BlobSet) { |
| const int kKeyLength = 10; |
| const int kBlobLength = 20; |
| |
| for (uint8_t key_start = 0; key_start < 5; ++key_start) { |
| std::string binary_key = MakeKey(kKeyLength, key_start); |
| std::string binary_blob = MakeBlob(kBlobLength, key_start + 25); |
| |
| Set(binary_key, binary_blob); |
| |
| // Make sure the blob was inserted. |
| EXPECT_EQ(binary_blob, Get(binary_key)); |
| |
| // Test that similar keys but with different length are not retrieved |
| // (especially making sure the '\0' character at the beginning of the key is |
| // not causing confusion). |
| for (size_t i = 0; i <= kKeyLength * 2; ++i) { |
| if (i != kKeyLength) { |
| EXPECT_EQ("", Get(MakeKey(i, key_start))); |
| } |
| } |
| } |
| } |
| |
| TEST_F(PassthroughProgramCacheTest, Eviction) { |
| const int kKeyLength = 10; |
| const int kBlobLength = 20; |
| |
| std::string binary_key = MakeKey(kKeyLength, 0); |
| std::string binary_blob = MakeBlob(kBlobLength, 0); |
| |
| Set(binary_key, binary_blob); |
| EXPECT_EQ(binary_blob, Get(binary_key)); |
| |
| std::string evicting_binary_key = MakeKey(kKeyLength, 1); |
| std::string evicting_binary_blob = |
| MakeBlob(kCacheSizeBytes - kBlobLength + 1, 0); |
| |
| Set(evicting_binary_key, evicting_binary_blob); |
| |
| // Make sure the new blob is inserted. |
| EXPECT_EQ(evicting_binary_blob, Get(evicting_binary_key)); |
| // And the old blob is removed. |
| EXPECT_EQ("", Get(binary_key)); |
| } |
| |
| TEST_F(PassthroughProgramCacheTest, Clear) { |
| const int kKeyLength = 10; |
| const int kBlobLength = 20; |
| |
| std::string binary_key = MakeKey(kKeyLength, 1); |
| std::string binary_blob = MakeBlob(kBlobLength, 1); |
| |
| Set(binary_key, binary_blob); |
| EXPECT_EQ(binary_blob, Get(binary_key)); |
| |
| cache_->Clear(); |
| |
| // Make sure the blob is removed. |
| EXPECT_EQ("", Get(binary_key)); |
| } |
| |
| TEST_F(PassthroughProgramCacheTest, OverwriteOnNewSave) { |
| const int kKeyLength = 10; |
| const int kBlobLength = 20; |
| const int kBlobLength2 = 15; |
| const int kBlobLength3 = 25; |
| |
| std::string binary_key = MakeKey(kKeyLength, 0); |
| std::string binary_blob = MakeBlob(kBlobLength, 0); |
| std::string binary_blob2 = MakeBlob(kBlobLength2, 78); |
| std::string binary_blob3 = MakeBlob(kBlobLength3, 113); |
| |
| Set(binary_key, binary_blob); |
| EXPECT_EQ(binary_blob, Get(binary_key)); |
| |
| Set(binary_key, binary_blob2); |
| |
| // Make sure the new blob replaces the old one. |
| EXPECT_EQ(binary_blob2, Get(binary_key)); |
| |
| Set(binary_key, binary_blob3); |
| EXPECT_EQ(binary_blob3, Get(binary_key)); |
| } |
| |
| TEST_F(PassthroughProgramCacheTest, Trim) { |
| const int kKeyLength = 10; |
| const int kBlobLength = 20; |
| |
| std::string binary_key = MakeKey(kKeyLength, 10); |
| std::string binary_blob = MakeBlob(kBlobLength, 11); |
| |
| std::string binary_key2 = MakeKey(kKeyLength, 12); |
| std::string binary_blob2 = MakeBlob(kBlobLength, 13); |
| |
| Set(binary_key, binary_blob); |
| Set(binary_key2, binary_blob2); |
| |
| EXPECT_EQ(binary_blob, Get(binary_key)); |
| EXPECT_EQ(binary_blob2, Get(binary_key2)); |
| |
| // Trimming to exact size of the two blobs shouldn't evict anything. |
| cache_->Trim(kBlobLength * 2); |
| EXPECT_EQ(binary_blob, Get(binary_key)); |
| EXPECT_EQ(binary_blob2, Get(binary_key2)); |
| |
| // Trimming to even a byte under that should evict the first (oldest) blob. |
| cache_->Trim(kBlobLength * 2 - 1); |
| EXPECT_EQ("", Get(binary_key)); |
| EXPECT_EQ(binary_blob2, Get(binary_key2)); |
| |
| // Trimming to exact size of the blob that's left shouldn't evict anything. |
| cache_->Trim(kBlobLength); |
| EXPECT_EQ("", Get(binary_key)); |
| EXPECT_EQ(binary_blob2, Get(binary_key2)); |
| |
| // Trimming more should evict the second blob too. |
| cache_->Trim(kBlobLength - 1); |
| EXPECT_EQ("", Get(binary_key)); |
| EXPECT_EQ("", Get(binary_key2)); |
| |
| // Insert the blobs again and this time go from full directly to 0 limit. |
| Set(binary_key, binary_blob); |
| Set(binary_key2, binary_blob2); |
| |
| EXPECT_EQ(binary_blob, Get(binary_key)); |
| EXPECT_EQ(binary_blob2, Get(binary_key2)); |
| |
| cache_->Trim(0); |
| EXPECT_EQ("", Get(binary_key)); |
| EXPECT_EQ("", Get(binary_key2)); |
| } |
| |
| } // namespace gles2 |
| } // namespace gpu |