blob: 3916153aaf08756bfa425f56474957979e256f78 [file] [log] [blame]
// 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 {}
void OnDescheduleUntilFinished() override {}
void OnRescheduleAfterFinished() override {}
void OnSwapBuffers(uint64_t swap_id, uint32_t flags) override {}
void ScheduleGrContextCleanup() override {}
void HandleReturnData(base::span<const uint8_t> data) override {}
int32_t blob_count() { return blob_count_; }
protected:
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