Commit f06df73a authored by bratell's avatar bratell Committed by Commit bot

std::bitset solution for GL Query cache.

This uses even less memory (a few hundred bytes) by sacrificing some
worst case performance (in the order of a microsecond per query) and
maintainability.

Estimated memory savings will be 9-12 KB. (3 KB per QuerySyncManager).

BUG=485536
R=reveman@chromium.org,jbauman@chromium.org

Review URL: https://codereview.chromium.org/1129253006

Cr-Commit-Position: refs/heads/master@{#330933}
parent 779dc719
......@@ -23,7 +23,7 @@ class CommandBufferHelper;
class MockCommandBufferBase : public CommandBufferServiceBase {
public:
static const int32 kTransferBufferBaseId = 0x123;
static const int32 kMaxTransferBuffers = 6;
static const int32 kMaxTransferBuffers = 32;
MockCommandBufferBase();
~MockCommandBufferBase() override;
......
......@@ -25,12 +25,7 @@ QuerySyncManager::Bucket::Bucket(QuerySync* sync_mem,
unsigned int shm_offset)
: syncs(sync_mem),
shm_id(shm_id),
base_shm_offset(shm_offset),
free_queries(kSyncsPerBucket) {
static_assert(kSyncsPerBucket <= USHRT_MAX,
"Can't fit kSyncsPerBucket in unsigned short");
for (size_t ii = 0; ii < kSyncsPerBucket; ++ii)
free_queries[ii] = ii;
base_shm_offset(shm_offset) {
}
QuerySyncManager::Bucket::~Bucket() = default;
......@@ -52,7 +47,9 @@ bool QuerySyncManager::Alloc(QuerySyncManager::QueryInfo* info) {
DCHECK(info);
Bucket* bucket = nullptr;
for (Bucket* bucket_candidate : buckets_) {
if (!bucket_candidate->free_queries.empty()) {
// In C++11 STL this could be replaced with
// if (!bucket_candidate->in_use_queries.all()) { ... }
if (bucket_candidate->in_use_queries.count() != kSyncsPerBucket) {
bucket = bucket_candidate;
break;
}
......@@ -70,27 +67,35 @@ bool QuerySyncManager::Alloc(QuerySyncManager::QueryInfo* info) {
buckets_.push_back(bucket);
}
unsigned short index_in_bucket = bucket->free_queries.back();
unsigned short index_in_bucket = 0;
for (size_t i = 0; i < kSyncsPerBucket; i++) {
if (!bucket->in_use_queries[i]) {
index_in_bucket = i;
break;
}
}
uint32 shm_offset =
bucket->base_shm_offset + index_in_bucket * sizeof(QuerySync);
QuerySync* sync = bucket->syncs + index_in_bucket;
*info = QueryInfo(bucket, bucket->shm_id, shm_offset, sync);
info->sync->Reset();
bucket->free_queries.pop_back();
bucket->in_use_queries[index_in_bucket] = true;
return true;
}
void QuerySyncManager::Free(const QuerySyncManager::QueryInfo& info) {
DCHECK(info.bucket->free_queries.size() < kSyncsPerBucket);
DCHECK_NE(info.bucket->in_use_queries.count(), 0u);
unsigned short index_in_bucket = info.sync - info.bucket->syncs;
info.bucket->free_queries.push_back(index_in_bucket);
DCHECK(info.bucket->in_use_queries[index_in_bucket]);
info.bucket->in_use_queries[index_in_bucket] = false;
}
void QuerySyncManager::Shrink() {
std::deque<Bucket*> new_buckets;
while (!buckets_.empty()) {
Bucket* bucket = buckets_.front();
if (bucket->free_queries.size() < kSyncsPerBucket) {
if (bucket->in_use_queries.any()) {
new_buckets.push_back(bucket);
} else {
mapped_memory_->Free(bucket->syncs);
......
......@@ -7,9 +7,9 @@
#include <GLES2/gl2.h>
#include <bitset>
#include <deque>
#include <list>
#include <vector>
#include "base/atomicops.h"
#include "base/containers/hash_tables.h"
......@@ -28,7 +28,7 @@ class GLES2Implementation;
// Manages buckets of QuerySync instances in mapped memory.
class GLES2_IMPL_EXPORT QuerySyncManager {
public:
static const size_t kSyncsPerBucket = 1024;
static const size_t kSyncsPerBucket = 256;
struct Bucket {
Bucket(QuerySync* sync_mem, int32 shm_id, uint32 shm_offset);
......@@ -36,7 +36,7 @@ class GLES2_IMPL_EXPORT QuerySyncManager {
QuerySync* syncs;
int32 shm_id;
uint32 base_shm_offset;
std::vector<unsigned short> free_queries;
std::bitset<kSyncsPerBucket> in_use_queries;
};
struct QueryInfo {
QueryInfo(Bucket* bucket, int32 id, uint32 offset, QuerySync* sync_mem)
......
......@@ -7,6 +7,9 @@
#include "gpu/command_buffer/client/query_tracker.h"
#include <GLES2/gl2ext.h>
#include <vector>
#include "base/memory/scoped_ptr.h"
#include "gpu/command_buffer/client/client_test_helper.h"
#include "gpu/command_buffer/client/gles2_cmd_helper.h"
......@@ -108,7 +111,7 @@ class QueryTrackerTest : public testing::Test {
}
uint32 GetBucketUsedCount(QuerySyncManager::Bucket* bucket) {
return QuerySyncManager::kSyncsPerBucket - bucket->free_queries.size();
return bucket->in_use_queries.count();
}
uint32 GetFlushGeneration() { return helper_->flush_generation(); }
......@@ -235,6 +238,58 @@ TEST_F(QueryTrackerTest, Remove) {
EXPECT_EQ(0u, GetBucketUsedCount(bucket));
}
TEST_F(QueryTrackerTest, ManyQueries) {
const GLuint kId1 = 123;
const int32 kToken = 46;
const uint32 kResult = 456;
const size_t kTestSize = 4000;
static_assert(kTestSize > QuerySyncManager::kSyncsPerBucket,
"We want to use more than one bucket");
// Create lots of queries.
std::vector<QueryTracker::Query*> queries;
for (size_t i = 0; i < kTestSize; i++) {
QueryTracker::Query* query =
query_tracker_->CreateQuery(kId1 + i, GL_ANY_SAMPLES_PASSED_EXT);
ASSERT_TRUE(query != NULL);
queries.push_back(query);
QuerySyncManager::Bucket* bucket = GetBucket(query);
EXPECT_LE(1u, GetBucketUsedCount(bucket));
}
QuerySyncManager::Bucket* query_0_bucket = GetBucket(queries[0]);
uint32 expected_use_count = QuerySyncManager::kSyncsPerBucket;
EXPECT_EQ(expected_use_count, GetBucketUsedCount(query_0_bucket));
while (!queries.empty()) {
QueryTracker::Query* query = queries.back();
queries.pop_back();
GLuint query_id = kId1 + queries.size();
EXPECT_EQ(query_id, query->id());
query->MarkAsActive();
query->MarkAsPending(kToken);
QuerySyncManager::Bucket* bucket = GetBucket(query);
uint32 use_count_before_remove = GetBucketUsedCount(bucket);
query_tracker_->FreeCompletedQueries();
EXPECT_EQ(use_count_before_remove, GetBucketUsedCount(bucket));
query_tracker_->RemoveQuery(query_id);
// Check we get nothing for a non-existent query.
EXPECT_TRUE(query_tracker_->GetQuery(query_id) == NULL);
// Check that memory was not freed since it was not completed.
EXPECT_EQ(use_count_before_remove, GetBucketUsedCount(bucket));
// Simulate GPU process marking it as available.
QuerySync* sync = GetSync(query);
sync->process_count = query->submit_count();
sync->result = kResult;
// Check FreeCompletedQueries.
query_tracker_->FreeCompletedQueries();
EXPECT_EQ(use_count_before_remove - 1, GetBucketUsedCount(bucket));
}
}
} // namespace gles2
} // namespace gpu
......
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