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; ...@@ -23,7 +23,7 @@ class CommandBufferHelper;
class MockCommandBufferBase : public CommandBufferServiceBase { class MockCommandBufferBase : public CommandBufferServiceBase {
public: public:
static const int32 kTransferBufferBaseId = 0x123; static const int32 kTransferBufferBaseId = 0x123;
static const int32 kMaxTransferBuffers = 6; static const int32 kMaxTransferBuffers = 32;
MockCommandBufferBase(); MockCommandBufferBase();
~MockCommandBufferBase() override; ~MockCommandBufferBase() override;
......
...@@ -25,12 +25,7 @@ QuerySyncManager::Bucket::Bucket(QuerySync* sync_mem, ...@@ -25,12 +25,7 @@ QuerySyncManager::Bucket::Bucket(QuerySync* sync_mem,
unsigned int shm_offset) unsigned int shm_offset)
: syncs(sync_mem), : syncs(sync_mem),
shm_id(shm_id), shm_id(shm_id),
base_shm_offset(shm_offset), 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;
} }
QuerySyncManager::Bucket::~Bucket() = default; QuerySyncManager::Bucket::~Bucket() = default;
...@@ -52,7 +47,9 @@ bool QuerySyncManager::Alloc(QuerySyncManager::QueryInfo* info) { ...@@ -52,7 +47,9 @@ bool QuerySyncManager::Alloc(QuerySyncManager::QueryInfo* info) {
DCHECK(info); DCHECK(info);
Bucket* bucket = nullptr; Bucket* bucket = nullptr;
for (Bucket* bucket_candidate : buckets_) { 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; bucket = bucket_candidate;
break; break;
} }
...@@ -70,27 +67,35 @@ bool QuerySyncManager::Alloc(QuerySyncManager::QueryInfo* info) { ...@@ -70,27 +67,35 @@ bool QuerySyncManager::Alloc(QuerySyncManager::QueryInfo* info) {
buckets_.push_back(bucket); 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 = uint32 shm_offset =
bucket->base_shm_offset + index_in_bucket * sizeof(QuerySync); bucket->base_shm_offset + index_in_bucket * sizeof(QuerySync);
QuerySync* sync = bucket->syncs + index_in_bucket; QuerySync* sync = bucket->syncs + index_in_bucket;
*info = QueryInfo(bucket, bucket->shm_id, shm_offset, sync); *info = QueryInfo(bucket, bucket->shm_id, shm_offset, sync);
info->sync->Reset(); info->sync->Reset();
bucket->free_queries.pop_back(); bucket->in_use_queries[index_in_bucket] = true;
return true; return true;
} }
void QuerySyncManager::Free(const QuerySyncManager::QueryInfo& info) { 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; 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() { void QuerySyncManager::Shrink() {
std::deque<Bucket*> new_buckets; std::deque<Bucket*> new_buckets;
while (!buckets_.empty()) { while (!buckets_.empty()) {
Bucket* bucket = buckets_.front(); Bucket* bucket = buckets_.front();
if (bucket->free_queries.size() < kSyncsPerBucket) { if (bucket->in_use_queries.any()) {
new_buckets.push_back(bucket); new_buckets.push_back(bucket);
} else { } else {
mapped_memory_->Free(bucket->syncs); mapped_memory_->Free(bucket->syncs);
......
...@@ -7,9 +7,9 @@ ...@@ -7,9 +7,9 @@
#include <GLES2/gl2.h> #include <GLES2/gl2.h>
#include <bitset>
#include <deque> #include <deque>
#include <list> #include <list>
#include <vector>
#include "base/atomicops.h" #include "base/atomicops.h"
#include "base/containers/hash_tables.h" #include "base/containers/hash_tables.h"
...@@ -28,7 +28,7 @@ class GLES2Implementation; ...@@ -28,7 +28,7 @@ class GLES2Implementation;
// Manages buckets of QuerySync instances in mapped memory. // Manages buckets of QuerySync instances in mapped memory.
class GLES2_IMPL_EXPORT QuerySyncManager { class GLES2_IMPL_EXPORT QuerySyncManager {
public: public:
static const size_t kSyncsPerBucket = 1024; static const size_t kSyncsPerBucket = 256;
struct Bucket { struct Bucket {
Bucket(QuerySync* sync_mem, int32 shm_id, uint32 shm_offset); Bucket(QuerySync* sync_mem, int32 shm_id, uint32 shm_offset);
...@@ -36,7 +36,7 @@ class GLES2_IMPL_EXPORT QuerySyncManager { ...@@ -36,7 +36,7 @@ class GLES2_IMPL_EXPORT QuerySyncManager {
QuerySync* syncs; QuerySync* syncs;
int32 shm_id; int32 shm_id;
uint32 base_shm_offset; uint32 base_shm_offset;
std::vector<unsigned short> free_queries; std::bitset<kSyncsPerBucket> in_use_queries;
}; };
struct QueryInfo { struct QueryInfo {
QueryInfo(Bucket* bucket, int32 id, uint32 offset, QuerySync* sync_mem) QueryInfo(Bucket* bucket, int32 id, uint32 offset, QuerySync* sync_mem)
......
...@@ -7,6 +7,9 @@ ...@@ -7,6 +7,9 @@
#include "gpu/command_buffer/client/query_tracker.h" #include "gpu/command_buffer/client/query_tracker.h"
#include <GLES2/gl2ext.h> #include <GLES2/gl2ext.h>
#include <vector>
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "gpu/command_buffer/client/client_test_helper.h" #include "gpu/command_buffer/client/client_test_helper.h"
#include "gpu/command_buffer/client/gles2_cmd_helper.h" #include "gpu/command_buffer/client/gles2_cmd_helper.h"
...@@ -108,7 +111,7 @@ class QueryTrackerTest : public testing::Test { ...@@ -108,7 +111,7 @@ class QueryTrackerTest : public testing::Test {
} }
uint32 GetBucketUsedCount(QuerySyncManager::Bucket* bucket) { uint32 GetBucketUsedCount(QuerySyncManager::Bucket* bucket) {
return QuerySyncManager::kSyncsPerBucket - bucket->free_queries.size(); return bucket->in_use_queries.count();
} }
uint32 GetFlushGeneration() { return helper_->flush_generation(); } uint32 GetFlushGeneration() { return helper_->flush_generation(); }
...@@ -235,6 +238,58 @@ TEST_F(QueryTrackerTest, Remove) { ...@@ -235,6 +238,58 @@ TEST_F(QueryTrackerTest, Remove) {
EXPECT_EQ(0u, GetBucketUsedCount(bucket)); 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 gles2
} // namespace gpu } // 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