diff options
41 files changed, 2569 insertions, 418 deletions
diff --git a/gpu/command_buffer/build_gles2_cmd_buffer.py b/gpu/command_buffer/build_gles2_cmd_buffer.py index 8a272e9..fbaa67d 100755 --- a/gpu/command_buffer/build_gles2_cmd_buffer.py +++ b/gpu/command_buffer/build_gles2_cmd_buffer.py @@ -898,15 +898,21 @@ _FUNCTION_INFO = { 'DeleteBuffers': { 'type': 'DELn', 'gl_test_func': 'glDeleteBuffersARB', + 'resource_type': 'Buffer', + 'resource_types': 'Buffers', }, 'DeleteFramebuffers': { 'type': 'DELn', 'gl_test_func': 'glDeleteFramebuffersEXT', + 'resource_type': 'Framebuffer', + 'resource_types': 'Framebuffers', }, 'DeleteProgram': {'type': 'Delete', 'decoder_func': 'DoDeleteProgram'}, 'DeleteRenderbuffers': { 'type': 'DELn', 'gl_test_func': 'glDeleteRenderbuffersEXT', + 'resource_type': 'Renderbuffer', + 'resource_types': 'Renderbuffers', }, 'DeleteShader': {'type': 'Delete', 'decoder_func': 'DoDeleteShader'}, 'DeleteSharedIdsCHROMIUM': { @@ -918,7 +924,11 @@ _FUNCTION_INFO = { 'extension': True, 'chromium': True, }, - 'DeleteTextures': {'type': 'DELn'}, + 'DeleteTextures': { + 'type': 'DELn', + 'resource_type': 'Texture', + 'resource_types': 'Textures', + }, 'DepthRangef': {'decoder_func': 'glDepthRange'}, 'DepthMask': {'decoder_func': 'DoDepthMask', 'expectation': False}, 'DetachShader': {'decoder_func': 'DoDetachShader'}, @@ -949,8 +959,12 @@ _FUNCTION_INFO = { 'Finish': { 'impl_func': False, 'client_test': False, + 'decoder_func': 'DoFinish', + }, + 'Flush': { + 'impl_func': False, + 'decoder_func': 'DoFlush', }, - 'Flush': {'impl_func': False}, 'FramebufferRenderbuffer': { 'decoder_func': 'DoFramebufferRenderbuffer', 'gl_test_func': 'glFramebufferRenderbufferEXT', @@ -963,10 +977,29 @@ _FUNCTION_INFO = { 'decoder_func': 'DoGenerateMipmap', 'gl_test_func': 'glGenerateMipmapEXT', }, - 'GenBuffers': {'type': 'GENn', 'gl_test_func': 'glGenBuffersARB'}, - 'GenFramebuffers': {'type': 'GENn', 'gl_test_func': 'glGenFramebuffersEXT'}, - 'GenRenderbuffers': {'type': 'GENn', 'gl_test_func': 'glGenRenderbuffersEXT'}, - 'GenTextures': {'type': 'GENn', 'gl_test_func': 'glGenTextures'}, + 'GenBuffers': { + 'type': 'GENn', + 'gl_test_func': 'glGenBuffersARB', + 'resource_type': 'Buffer', + 'resource_types': 'Buffers', + }, + 'GenFramebuffers': { + 'type': 'GENn', + 'gl_test_func': 'glGenFramebuffersEXT', + 'resource_type': 'Framebuffer', + 'resource_types': 'Framebuffers', + }, + 'GenRenderbuffers': { + 'type': 'GENn', 'gl_test_func': 'glGenRenderbuffersEXT', + 'resource_type': 'Renderbuffer', + 'resource_types': 'Renderbuffers', + }, + 'GenTextures': { + 'type': 'GENn', + 'gl_test_func': 'glGenTextures', + 'resource_type': 'Texture', + 'resource_types': 'Textures', + }, 'GenSharedIdsCHROMIUM': { 'type': 'Custom', 'decoder_func': 'DoGenSharedIdsCHROMIUM', @@ -1556,13 +1589,44 @@ _FUNCTION_INFO = { 'unit_test': False, 'pepper_interface': 'InstancedArrays', }, - 'GenQueriesEXT': {'type': 'Todo'}, - 'DeleteQueriesEXT': {'type': 'Todo'}, - 'IsQueryEXT': {'type': 'Todo'}, - 'BeginQueryEXT': {'type': 'Todo'}, - 'EndQueryEXT': {'type': 'Todo'}, - 'GetQueryivEXT': {'type': 'Todo'}, - 'GetQueryObjectuivEXT': {'type': 'Todo'}, + 'GenQueriesEXT': { + 'type': 'GENn', + 'gl_test_func': 'glGenQueriesARB', + 'resource_type': 'Query', + 'resource_types': 'Queries', + }, + 'DeleteQueriesEXT': { + 'type': 'DELn', + 'gl_test_func': 'glDeleteQueriesARB', + 'resource_type': 'Query', + 'resource_types': 'Queries', + }, + 'IsQueryEXT': { + 'gen_cmd': False, + 'client_test': False, + }, + 'BeginQueryEXT': { + 'type': 'Manual', + 'cmd_args': 'GLenumQueryTarget target, GLidQuery id, void* sync_data', + 'immediate': False, + 'gl_test_func': 'glBeginQuery', + }, + 'EndQueryEXT': { + 'type': 'Manual', + 'cmd_args': 'GLenumQueryTarget target, GLuint submit_count', + 'gl_test_func': 'glEndnQuery', + 'client_test': False, + }, + 'GetQueryivEXT': { + 'gen_cmd': False, + 'client_test': False, + 'gl_test_func': 'glGetQueryiv', + }, + 'GetQueryObjectuivEXT': { + 'gen_cmd': False, + 'client_test': False, + 'gl_test_func': 'glGetQueryObjectuiv', + }, } @@ -1651,18 +1715,6 @@ class CWriter(object): while not done: splitter = string[0:end].rfind(',') if splitter < 0 or (splitter > 0 and string[splitter - 1] == '"'): - if last_splitter == -1: - break - return last_splitter - elif splitter >= 80: - end = splitter - else: - return splitter - end = len(string) - last_splitter = -1 - while not done: - splitter = string[0:end].rfind(' ') - if splitter < 0 or (splitter > 0 and string[splitter - 1] == '"'): return last_splitter elif splitter >= 80: end = splitter @@ -2688,7 +2740,7 @@ class GENnHandler(TypeHandler): 'name': func.original_name, 'typed_args': func.MakeTypedOriginalArgString(""), 'args': func.MakeOriginalArgString(""), - 'resource_type': func.GetInfo('resource_type') or func.name[3:], + 'resource_types': func.GetInfo('resource_types'), 'count_name': func.GetOriginalArgs()[0].name, } file.Write("%(return_type)s %(name)s(%(typed_args)s) {\n" % args) @@ -2697,7 +2749,7 @@ class GENnHandler(TypeHandler): for arg in func.GetOriginalArgs(): arg.WriteClientSideValidationCode(file, func) code = """ GPU_CLIENT_SINGLE_THREAD_CHECK(); - id_handlers_[id_namespaces::k%(resource_type)s]-> + id_handlers_[id_namespaces::k%(resource_types)s]-> MakeIds(0, %(args)s); helper_->%(name)sImmediate(%(args)s); %(log_code)s @@ -2717,17 +2769,17 @@ TEST_F(GLES2ImplementationTest, %(name)s) { }; Cmds expected; expected.gen.Init(arraysize(ids), &ids[0]); - expected.data[0] = k%(type)ssStartId; - expected.data[1] = k%(type)ssStartId + 1; + expected.data[0] = k%(types)sStartId; + expected.data[1] = k%(types)sStartId + 1; gl_->%(name)s(arraysize(ids), &ids[0]); EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); - EXPECT_EQ(k%(type)ssStartId, ids[0]); - EXPECT_EQ(k%(type)ssStartId + 1, ids[1]); + EXPECT_EQ(k%(types)sStartId, ids[0]); + EXPECT_EQ(k%(types)sStartId + 1, ids[1]); } """ file.Write(code % { 'name': func.name, - 'type': func.name[3:-1], + 'types': func.GetInfo('resource_types'), }) def WriteServiceUnitTest(self, func, file): @@ -2746,7 +2798,7 @@ TEST_F(%(test_name)s, %(name)sValidArgs) { } """ self.WriteValidUnitTest(func, file, valid_test, { - 'resource_name': func.name[3:-1], + 'resource_name': func.GetInfo('resource_type'), }) invalid_test = """ TEST_F(%(test_name)s, %(name)sInvalidArgs) { @@ -2759,7 +2811,7 @@ TEST_F(%(test_name)s, %(name)sInvalidArgs) { } """ self.WriteValidUnitTest(func, file, invalid_test, { - 'resource_name': func.GetOriginalArgs()[1].name[0:-1] + 'resource_name': func.GetInfo('resource_type').lower(), }) def WriteImmediateServiceUnitTest(self, func, file): @@ -2779,7 +2831,7 @@ TEST_F(%(test_name)s, %(name)sValidArgs) { } """ self.WriteValidUnitTest(func, file, valid_test, { - 'resource_name': func.original_name[3:-1], + 'resource_name': func.GetInfo('resource_type'), }) invalid_test = """ TEST_F(%(test_name)s, %(name)sInvalidArgs) { @@ -2792,7 +2844,7 @@ TEST_F(%(test_name)s, %(name)sInvalidArgs) { } """ self.WriteValidUnitTest(func, file, invalid_test, { - 'resource_name': func.GetOriginalArgs()[1].name[0:-1] + 'resource_name': func.GetInfo('resource_type').lower(), }) def WriteImmediateCmdComputeSize(self, func, file): @@ -3004,22 +3056,22 @@ class DELnHandler(TypeHandler): """Overrriden from TypeHandler.""" code = """ TEST_F(GLES2ImplementationTest, %(name)s) { - GLuint ids[2] = { k%(type)ssStartId, k%(type)ssStartId + 1 }; + GLuint ids[2] = { k%(types)sStartId, k%(types)sStartId + 1 }; struct Cmds { %(name)sImmediate del; GLuint data[2]; }; Cmds expected; expected.del.Init(arraysize(ids), &ids[0]); - expected.data[0] = k%(type)ssStartId; - expected.data[1] = k%(type)ssStartId + 1; + expected.data[0] = k%(types)sStartId; + expected.data[1] = k%(types)sStartId + 1; gl_->%(name)s(arraysize(ids), &ids[0]); EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); } """ file.Write(code % { 'name': func.name, - 'type': func.GetOriginalArgs()[1].name[0:-1].capitalize(), + 'types': func.GetInfo('resource_types'), }) def WriteServiceUnitTest(self, func, file): @@ -3041,9 +3093,8 @@ TEST_F(%(test_name)s, %(name)sValidArgs) { } """ self.WriteValidUnitTest(func, file, valid_test, { - 'resource_name': func.GetOriginalArgs()[1].name[0:-1], - 'upper_resource_name': - func.GetOriginalArgs()[1].name[0:-1].capitalize(), + 'resource_name': func.GetInfo('resource_type').lower(), + 'upper_resource_name': func.GetInfo('resource_type'), }) invalid_test = """ TEST_F(%(test_name)s, %(name)sInvalidArgs) { @@ -3075,9 +3126,8 @@ TEST_F(%(test_name)s, %(name)sValidArgs) { } """ self.WriteValidUnitTest(func, file, valid_test, { - 'resource_name': func.GetOriginalArgs()[1].name[0:-1], - 'upper_resource_name': - func.GetOriginalArgs()[1].name[0:-1].capitalize(), + 'resource_name': func.GetInfo('resource_type').lower(), + 'upper_resource_name': func.GetInfo('resource_type'), }) invalid_test = """ TEST_F(%(test_name)s, %(name)sInvalidArgs) { @@ -3110,7 +3160,7 @@ TEST_F(%(test_name)s, %(name)sInvalidArgs) { 'name': func.original_name, 'typed_args': func.MakeTypedOriginalArgString(""), 'args': func.MakeOriginalArgString(""), - 'resource_type': func.name[6:-1].lower(), + 'resource_type': func.GetInfo('resource_type').lower(), 'count_name': func.GetOriginalArgs()[0].name, } file.Write("%(return_type)s %(name)s(%(typed_args)s) {\n" % args) diff --git a/gpu/command_buffer/client/gles2_c_lib_autogen.h b/gpu/command_buffer/client/gles2_c_lib_autogen.h index 8698dce..6d86539 100644 --- a/gpu/command_buffer/client/gles2_c_lib_autogen.h +++ b/gpu/command_buffer/client/gles2_c_lib_autogen.h @@ -520,11 +520,11 @@ void GLES2TexStorage2DEXT( gles2::GetGLContext()->TexStorage2DEXT( target, levels, internalFormat, width, height); } -void GLES2GenQueriesEXT(GLsizei n, GLuint* ids) { - gles2::GetGLContext()->GenQueriesEXT(n, ids); +void GLES2GenQueriesEXT(GLsizei n, GLuint* queries) { + gles2::GetGLContext()->GenQueriesEXT(n, queries); } -void GLES2DeleteQueriesEXT(GLsizei n, const GLuint* ids) { - gles2::GetGLContext()->DeleteQueriesEXT(n, ids); +void GLES2DeleteQueriesEXT(GLsizei n, const GLuint* queries) { + gles2::GetGLContext()->DeleteQueriesEXT(n, queries); } GLboolean GLES2IsQueryEXT(GLuint id) { return gles2::GetGLContext()->IsQueryEXT(id); diff --git a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h index 524fd74..77e1ff2 100644 --- a/gpu/command_buffer/client/gles2_cmd_helper_autogen.h +++ b/gpu/command_buffer/client/gles2_cmd_helper_autogen.h @@ -1524,57 +1524,53 @@ } } - void GenQueriesEXT(GLsizei n, uint32 ids_shm_id, uint32 ids_shm_offset) { + void GenQueriesEXT( + GLsizei n, uint32 queries_shm_id, uint32 queries_shm_offset) { gles2::GenQueriesEXT* c = GetCmdSpace<gles2::GenQueriesEXT>(); if (c) { - c->Init(n, ids_shm_id, ids_shm_offset); + c->Init(n, queries_shm_id, queries_shm_offset); } } - void DeleteQueriesEXT(GLsizei n, uint32 ids_shm_id, uint32 ids_shm_offset) { - gles2::DeleteQueriesEXT* c = GetCmdSpace<gles2::DeleteQueriesEXT>(); + void GenQueriesEXTImmediate(GLsizei n, GLuint* queries) { + const uint32 size = gles2::GenQueriesEXTImmediate::ComputeSize(n); + gles2::GenQueriesEXTImmediate* c = + GetImmediateCmdSpaceTotalSize<gles2::GenQueriesEXTImmediate>(size); if (c) { - c->Init(n, ids_shm_id, ids_shm_offset); + c->Init(n, queries); } } - void IsQueryEXT(GLuint id) { - gles2::IsQueryEXT* c = GetCmdSpace<gles2::IsQueryEXT>(); + void DeleteQueriesEXT( + GLsizei n, uint32 queries_shm_id, uint32 queries_shm_offset) { + gles2::DeleteQueriesEXT* c = GetCmdSpace<gles2::DeleteQueriesEXT>(); if (c) { - c->Init(id); + c->Init(n, queries_shm_id, queries_shm_offset); } } - void BeginQueryEXT(GLenum target, GLuint id) { - gles2::BeginQueryEXT* c = GetCmdSpace<gles2::BeginQueryEXT>(); + void DeleteQueriesEXTImmediate(GLsizei n, const GLuint* queries) { + const uint32 size = gles2::DeleteQueriesEXTImmediate::ComputeSize(n); + gles2::DeleteQueriesEXTImmediate* c = + GetImmediateCmdSpaceTotalSize<gles2::DeleteQueriesEXTImmediate>(size); if (c) { - c->Init(target, id); + c->Init(n, queries); } } - void EndQueryEXT(GLenum target) { - gles2::EndQueryEXT* c = GetCmdSpace<gles2::EndQueryEXT>(); - if (c) { - c->Init(target); - } - } - - void GetQueryivEXT( - GLenum target, GLenum pname, uint32 params_shm_id, - uint32 params_shm_offset) { - gles2::GetQueryivEXT* c = GetCmdSpace<gles2::GetQueryivEXT>(); + void BeginQueryEXT( + GLenum target, GLuint id, uint32 sync_data_shm_id, + uint32 sync_data_shm_offset) { + gles2::BeginQueryEXT* c = GetCmdSpace<gles2::BeginQueryEXT>(); if (c) { - c->Init(target, pname, params_shm_id, params_shm_offset); + c->Init(target, id, sync_data_shm_id, sync_data_shm_offset); } } - void GetQueryObjectuivEXT( - GLuint id, GLenum pname, uint32 params_shm_id, - uint32 params_shm_offset) { - gles2::GetQueryObjectuivEXT* c = - GetCmdSpace<gles2::GetQueryObjectuivEXT>(); + void EndQueryEXT(GLenum target, GLuint submit_count) { + gles2::EndQueryEXT* c = GetCmdSpace<gles2::EndQueryEXT>(); if (c) { - c->Init(id, pname, params_shm_id, params_shm_offset); + c->Init(target, submit_count); } } diff --git a/gpu/command_buffer/client/gles2_implementation.cc b/gpu/command_buffer/client/gles2_implementation.cc index 7093cd8..c3a2c24 100644 --- a/gpu/command_buffer/client/gles2_implementation.cc +++ b/gpu/command_buffer/client/gles2_implementation.cc @@ -6,11 +6,13 @@ #include "../client/gles2_implementation.h" +#include <map> #include <set> #include <queue> #include <GLES2/gl2ext.h> #include "../client/mapped_memory.h" #include "../client/program_info_manager.h" +#include "../client/query_tracker.h" #include "../client/transfer_buffer.h" #include "../common/gles2_cmd_utils.h" #include "../common/id_allocator.h" @@ -604,7 +606,8 @@ GLES2Implementation::GLES2Implementation( debug_(false), sharing_resources_(share_resources), bind_generates_resource_(bind_generates_resource), - use_count_(0) { + use_count_(0), + current_query_(NULL) { GPU_DCHECK(helper); GPU_DCHECK(transfer_buffer); GPU_CLIENT_LOG_CODE_BLOCK({ @@ -685,6 +688,7 @@ bool GLES2Implementation::Initialize( new TextureUnit[gl_state_.max_combined_texture_image_units]); program_info_manager_.reset(ProgramInfoManager::Create(sharing_resources_)); + query_tracker_.reset(new QueryTracker(mapped_memory_.get())); #if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS) id_handlers_[id_namespaces::kBuffers]->MakeIds( @@ -700,6 +704,13 @@ bool GLES2Implementation::Initialize( } GLES2Implementation::~GLES2Implementation() { + // Make sure the queries are finished otherwise we'll delete the + // shared memory (mapped_memory_) which will free the memory used + // by the queries. The GPU process when validating that memory is still + // shared will fail and abort (ie, it will stop running). + Finish(); + query_tracker_.reset(); + #if defined(GLES2_SUPPORT_CLIENT_SIDE_ARRAYS) DeleteBuffers(arraysize(reserved_ids_), &reserved_ids_[0]); #endif @@ -2970,6 +2981,180 @@ void GLES2Implementation::PostSubBufferCHROMIUM( } } +void GLES2Implementation::DeleteQueriesEXTHelper( + GLsizei n, const GLuint* queries) { + if (!id_handlers_[id_namespaces::kQueries]->FreeIds(n, queries)) { + SetGLError( + GL_INVALID_VALUE, + "glDeleteTextures: id not created by this context."); + return; + } + // When you delete a query you can't mark its memory as unused until it's + // completed. + // Note: If you don't do this you won't mess up the service but you will mess + // up yourself. + + // TODO(gman): Consider making this faster by putting pending quereies + // on some queue to be removed when they are finished. + bool query_pending = false; + for (GLsizei ii = 0; ii < n; ++ii) { + QueryTracker::Query* query = query_tracker_->GetQuery(queries[ii]); + if (query && query->Pending()) { + query_pending = true; + break; + } + } + + if (query_pending) { + Finish(); + } + + for (GLsizei ii = 0; ii < n; ++ii) { + QueryTracker::Query* query = query_tracker_->GetQuery(queries[ii]); + if (query && query->Pending()) { + GPU_CHECK(!query->CheckResultsAvailable(helper_)); + } + query_tracker_->RemoveQuery(queries[ii]); + } + helper_->DeleteQueriesEXTImmediate(n, queries); +} + +GLboolean GLES2Implementation::IsQueryEXT(GLuint id) { + GPU_CLIENT_SINGLE_THREAD_CHECK(); + GPU_CLIENT_LOG("[" << this << "] IsQueryEXT(" << id << ")"); + + // TODO(gman): To be spec compliant IDs from other contexts sharing + // resources need to return true here even though you can't share + // queries across contexts? + return query_tracker_->GetQuery(id) != NULL; +} + +void GLES2Implementation::BeginQueryEXT(GLenum target, GLuint id) { + GPU_CLIENT_SINGLE_THREAD_CHECK(); + GPU_CLIENT_LOG("[" << this << "] BeginQueryEXT(" + << GLES2Util::GetStringQueryTarget(target) + << ", " << id << ")"); + + // if any outstanding queries INV_OP + if (current_query_) { + SetGLError( + GL_INVALID_OPERATION, "glBeginQueryEXT: query already in progress"); + return; + } + + // id = 0 INV_OP + if (id == 0) { + SetGLError(GL_INVALID_OPERATION, "glBeginQueryEXT: id is 0"); + return; + } + + // TODO(gman) if id not GENned INV_OPERATION + + // if id does not have an object + QueryTracker::Query* query = query_tracker_->GetQuery(id); + if (!query) { + query = query_tracker_->CreateQuery(id, target); + } else if (query->target() != target) { + SetGLError(GL_INVALID_OPERATION, "glBeginQueryEXT: target does not match"); + return; + } + + current_query_ = query; + + // init memory, inc count + query->MarkAsActive(); + + // tell service about id, shared memory and count + helper_->BeginQueryEXT(target, id, query->shm_id(), query->shm_offset()); +} + +void GLES2Implementation::EndQueryEXT(GLenum target) { + GPU_CLIENT_SINGLE_THREAD_CHECK(); + GPU_CLIENT_LOG("[" << this << "] EndQueryEXT(" + << GLES2Util::GetStringQueryTarget(target) << ")"); + + if (!current_query_) { + SetGLError(GL_INVALID_OPERATION, "glEndQueryEXT: no active query"); + return; + } + + if (current_query_->target() != target) { + SetGLError(GL_INVALID_OPERATION, + "glEndQueryEXT: target does not match active query"); + return; + } + + helper_->EndQueryEXT(target, current_query_->submit_count()); + current_query_->MarkAsPending(helper_->InsertToken()); + current_query_ = NULL; +} + +void GLES2Implementation::GetQueryivEXT( + GLenum target, GLenum pname, GLint* params) { + GPU_CLIENT_SINGLE_THREAD_CHECK(); + GPU_CLIENT_LOG("[" << this << "] GetQueryivEXT(" + << GLES2Util::GetStringQueryTarget(target) << ", " + << GLES2Util::GetStringQueryParameter(pname) << ", " + << static_cast<const void*>(params) << ")"); + + if (pname != GL_CURRENT_QUERY_EXT) { + SetGLError(GL_INVALID_ENUM, "glGetQueryivEXT: invalid pname"); + return; + } + *params = (current_query_ && current_query_->target() == target) ? + current_query_->id() : 0; + GPU_CLIENT_LOG(" " << *params); +} + +void GLES2Implementation::GetQueryObjectuivEXT( + GLuint id, GLenum pname, GLuint* params) { + GPU_CLIENT_SINGLE_THREAD_CHECK(); + GPU_CLIENT_LOG("[" << this << "] GetQueryivEXT(" << id << ", " + << GLES2Util::GetStringQueryObjectParameter(pname) << ", " + << static_cast<const void*>(params) << ")"); + + QueryTracker::Query* query = query_tracker_->GetQuery(id); + if (!query) { + SetGLError(GL_INVALID_OPERATION, "glQueryObjectuivEXT: unknown query id"); + return; + } + + if (query == current_query_) { + SetGLError( + GL_INVALID_OPERATION, + "glQueryObjectuivEXT: query active. Did you to call glEndQueryEXT?"); + return; + } + + if (query->NeverUsed()) { + SetGLError( + GL_INVALID_OPERATION, + "glQueryObjectuivEXT: Never used. Did you call glBeginQueryEXT?"); + return; + } + + switch (pname) { + case GL_QUERY_RESULT_EXT: + if (!query->CheckResultsAvailable(helper_)) { + helper_->WaitForToken(query->token()); + if (!query->CheckResultsAvailable(helper_)) { + // TODO(gman): Speed this up. + Finish(); + GPU_CHECK(query->CheckResultsAvailable(helper_)); + } + } + *params = query->GetResult(); + break; + case GL_QUERY_RESULT_AVAILABLE_EXT: + *params = query->CheckResultsAvailable(helper_); + break; + default: + SetGLError(GL_INVALID_ENUM, "glQueryObjectuivEXT: unknown pname"); + break; + } + GPU_CLIENT_LOG(" " << *params); +} + void GLES2Implementation::DrawArraysInstancedANGLE( GLenum mode, GLint first, GLsizei count, GLsizei primcount) { GPU_CLIENT_SINGLE_THREAD_CHECK(); @@ -3070,6 +3255,5 @@ void GLES2Implementation::DrawElementsInstancedANGLE( #endif } - } // namespace gles2 } // namespace gpu diff --git a/gpu/command_buffer/client/gles2_implementation.h b/gpu/command_buffer/client/gles2_implementation.h index dbdb271..2b5312a 100644 --- a/gpu/command_buffer/client/gles2_implementation.h +++ b/gpu/command_buffer/client/gles2_implementation.h @@ -16,6 +16,7 @@ #include "../common/gles2_cmd_utils.h" #include "../common/scoped_ptr.h" #include "../client/gles2_cmd_helper.h" +#include "../client/query_tracker.h" #include "../client/ring_buffer.h" #include "gles2_impl_export.h" @@ -216,6 +217,7 @@ class GLES2_IMPL_EXPORT GLES2Implementation { private: friend class ClientSideBufferHelper; + friend class GLES2ImplementationTest; // Used to track whether an extension is available enum ExtensionStatus { @@ -393,6 +395,7 @@ class GLES2_IMPL_EXPORT GLES2Implementation { void DeleteTexturesHelper(GLsizei n, const GLuint* textures); bool DeleteProgramHelper(GLuint program); bool DeleteShaderHelper(GLuint shader); + void DeleteQueriesEXTHelper(GLsizei n, const GLuint* textures); void BufferDataHelper( GLenum target, GLsizeiptr size, const void* data, GLenum usage); @@ -516,6 +519,9 @@ class GLES2_IMPL_EXPORT GLES2Implementation { scoped_ptr<ProgramInfoManager> program_info_manager_; + scoped_ptr<QueryTracker> query_tracker_; + QueryTracker::Query* current_query_; + DISALLOW_COPY_AND_ASSIGN(GLES2Implementation); }; diff --git a/gpu/command_buffer/client/gles2_implementation_autogen.h b/gpu/command_buffer/client/gles2_implementation_autogen.h index 9b33d7c..6ec6e14 100644 --- a/gpu/command_buffer/client/gles2_implementation_autogen.h +++ b/gpu/command_buffer/client/gles2_implementation_autogen.h @@ -1409,41 +1409,54 @@ void TexStorage2DEXT( helper_->TexStorage2DEXT(target, levels, internalFormat, width, height); } -void GenQueriesEXT(GLsizei n, GLuint* ids) { - // TODO: for now this is a no-op - SetGLError(GL_INVALID_OPERATION, "glGenQueriesEXT not implemented"); +void GenQueriesEXT(GLsizei n, GLuint* queries) { + GPU_CLIENT_LOG("[" << this << "] glGenQueriesEXT(" << n << ", " + << static_cast<const void*>(queries) << ")"); // NOLINT + if (n < 0) { + SetGLError(GL_INVALID_VALUE, "glGenQueriesEXT: n < 0"); + return; + } + GPU_CLIENT_SINGLE_THREAD_CHECK(); + id_handlers_[id_namespaces::kQueries]-> + MakeIds(0, n, queries); + helper_->GenQueriesEXTImmediate(n, queries); + GPU_CLIENT_LOG_CODE_BLOCK({ + for (GLsizei i = 0; i < n; ++i) { + GPU_CLIENT_LOG(" " << i << ": " << queries[i]); + } + }); } -void DeleteQueriesEXT(GLsizei n, const GLuint* ids) { - // TODO: for now this is a no-op - SetGLError(GL_INVALID_OPERATION, "glDeleteQueriesEXT not implemented"); +void DeleteQueriesEXT(GLsizei n, const GLuint* queries) { + GPU_CLIENT_SINGLE_THREAD_CHECK(); + GPU_CLIENT_LOG("[" << this << "] glDeleteQueriesEXT(" << n << ", " + << static_cast<const void*>(queries) << ")"); // NOLINT + GPU_CLIENT_LOG_CODE_BLOCK({ + for (GLsizei i = 0; i < n; ++i) { + GPU_CLIENT_LOG(" " << i << ": " << queries[i]); + } + }); + GPU_CLIENT_DCHECK_CODE_BLOCK({ + for (GLsizei i = 0; i < n; ++i) { + GPU_DCHECK(queries[i] != 0); + } + }); + if (n < 0) { + SetGLError(GL_INVALID_VALUE, "glDeleteQueriesEXT: n < 0"); + return; + } + DeleteQueriesEXTHelper(n, queries); } -GLboolean IsQueryEXT(GLuint id) { - // TODO: for now this is a no-op - SetGLError(GL_INVALID_OPERATION, "glIsQueryEXT not implemented"); - return 0; -} +GLboolean IsQueryEXT(GLuint id); -void BeginQueryEXT(GLenum target, GLuint id) { - // TODO: for now this is a no-op - SetGLError(GL_INVALID_OPERATION, "glBeginQueryEXT not implemented"); -} +void BeginQueryEXT(GLenum target, GLuint id); -void EndQueryEXT(GLenum target) { - // TODO: for now this is a no-op - SetGLError(GL_INVALID_OPERATION, "glEndQueryEXT not implemented"); -} +void EndQueryEXT(GLenum target); -void GetQueryivEXT(GLenum target, GLenum pname, GLint* params) { - // TODO: for now this is a no-op - SetGLError(GL_INVALID_OPERATION, "glGetQueryivEXT not implemented"); -} +void GetQueryivEXT(GLenum target, GLenum pname, GLint* params); -void GetQueryObjectuivEXT(GLuint id, GLenum pname, GLuint* params) { - // TODO: for now this is a no-op - SetGLError(GL_INVALID_OPERATION, "glGetQueryObjectuivEXT not implemented"); -} +void GetQueryObjectuivEXT(GLuint id, GLenum pname, GLuint* params); void SwapBuffers(); diff --git a/gpu/command_buffer/client/gles2_implementation_unittest.cc b/gpu/command_buffer/client/gles2_implementation_unittest.cc index 9bfc9ce..9aa84de 100644 --- a/gpu/command_buffer/client/gles2_implementation_unittest.cc +++ b/gpu/command_buffer/client/gles2_implementation_unittest.cc @@ -320,6 +320,7 @@ class GLES2ImplementationTest : public testing::Test { static const GLuint kProgramsAndShadersStartId = 1; static const GLuint kRenderbuffersStartId = 1; static const GLuint kTexturesStartId = 1; + static const GLuint kQueriesStartId = 1; typedef MockTransferBuffer::ExpectedMemoryInfo ExpectedMemoryInfo; @@ -336,6 +337,10 @@ class GLES2ImplementationTest : public testing::Test { kInitialValue; } + QueryTracker::Query* GetQuery(GLuint id) { + return gl_->query_tracker_->GetQuery(id); + } + void Initialize(bool shared_resources, bool bind_generates_resource) { command_buffer_.reset(new StrictMock<MockClientCommandBuffer>()); ASSERT_TRUE(command_buffer_->Initialize()); @@ -457,6 +462,15 @@ class GLES2ImplementationTest : public testing::Test { return transfer_buffer_->GetExpectedResultMemory(size); } + int CheckError() { + ExpectedMemoryInfo result = + GetExpectedResultMemory(sizeof(GetError::Result)); + EXPECT_CALL(*command_buffer(), OnFlush()) + .WillOnce(SetMemory(result.ptr, GLuint(GL_NO_ERROR))) + .RetiresOnSaturation(); + return gl_->GetError(); + } + Sequence sequence_; scoped_ptr<MockClientCommandBuffer> command_buffer_; scoped_ptr<GLES2CmdHelper> helper_; @@ -509,6 +523,7 @@ const GLuint GLES2ImplementationTest::kFramebuffersStartId; const GLuint GLES2ImplementationTest::kProgramsAndShadersStartId; const GLuint GLES2ImplementationTest::kRenderbuffersStartId; const GLuint GLES2ImplementationTest::kTexturesStartId; +const GLuint GLES2ImplementationTest::kQueriesStartId; #endif TEST_F(GLES2ImplementationTest, ShaderSource) { @@ -2301,6 +2316,126 @@ TEST_F(GLES2ImplementationTest, BufferDataLargerThanTransferBuffer) { EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); } +TEST_F(GLES2ImplementationTest, BeginEndQueryEXT) { + // Test GetQueryivEXT returns 0 if no current query. + GLint param = -1; + gl_->GetQueryivEXT(GL_ANY_SAMPLES_PASSED_EXT, GL_CURRENT_QUERY_EXT, ¶m); + EXPECT_EQ(0, param); + + GLuint expected_ids[2] = { 1, 2 }; // These must match what's actually genned. + struct GenCmds { + GenQueriesEXTImmediate gen; + GLuint data[2]; + }; + GenCmds expected_gen_cmds; + expected_gen_cmds.gen.Init(arraysize(expected_ids), &expected_ids[0]); + GLuint ids[arraysize(expected_ids)] = { 0, }; + gl_->GenQueriesEXT(arraysize(expected_ids), &ids[0]); + EXPECT_EQ(0, memcmp( + &expected_gen_cmds, commands_, sizeof(expected_gen_cmds))); + GLuint id1 = ids[0]; + GLuint id2 = ids[1]; + ClearCommands(); + + // Test BeginQueryEXT fails if id = 0. + gl_->BeginQueryEXT(GL_ANY_SAMPLES_PASSED_EXT, 0); + EXPECT_TRUE(NoCommandsWritten()); + EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); + + // Test BeginQueryEXT fails if id not GENed. + // TODO(gman): + + // Test BeginQueryEXT inserts command. + struct BeginCmds { + BeginQueryEXT begin_query; + }; + BeginCmds expected_begin_cmds; + const void* commands = GetPut(); + gl_->BeginQueryEXT(GL_ANY_SAMPLES_PASSED_EXT, id1); + QueryTracker::Query* query = GetQuery(id1); + ASSERT_TRUE(query != NULL); + expected_begin_cmds.begin_query.Init( + GL_ANY_SAMPLES_PASSED_EXT, id1, query->shm_id(), query->shm_offset()); + EXPECT_EQ(0, memcmp( + &expected_begin_cmds, commands, sizeof(expected_begin_cmds))); + ClearCommands(); + + // Test GetQueryivEXT returns id. + param = -1; + gl_->GetQueryivEXT(GL_ANY_SAMPLES_PASSED_EXT, GL_CURRENT_QUERY_EXT, ¶m); + EXPECT_EQ(id1, static_cast<GLuint>(param)); + gl_->GetQueryivEXT( + GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT, GL_CURRENT_QUERY_EXT, ¶m); + EXPECT_EQ(0, param); + + // Test BeginQueryEXT fails if between Begin/End. + gl_->BeginQueryEXT(GL_ANY_SAMPLES_PASSED_EXT, id2); + EXPECT_TRUE(NoCommandsWritten()); + EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); + + // Test EndQueryEXT fails if target not same as current query. + ClearCommands(); + gl_->EndQueryEXT(GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT); + EXPECT_TRUE(NoCommandsWritten()); + EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); + + // Test EndQueryEXT sends command + struct EndCmds { + EndQueryEXT end_query; + }; + EndCmds expected_end_cmds; + expected_end_cmds.end_query.Init( + GL_ANY_SAMPLES_PASSED_EXT, query->submit_count()); + commands = GetPut(); + gl_->EndQueryEXT(GL_ANY_SAMPLES_PASSED_EXT); + EXPECT_EQ(0, memcmp( + &expected_end_cmds, commands, sizeof(expected_end_cmds))); + + // Test EndQueryEXT fails if no current query. + ClearCommands(); + gl_->EndQueryEXT(GL_ANY_SAMPLES_PASSED_EXT); + EXPECT_TRUE(NoCommandsWritten()); + EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); + + // Test 2nd Begin/End increments count. + uint32 old_submit_count = query->submit_count(); + gl_->BeginQueryEXT(GL_ANY_SAMPLES_PASSED_EXT, id1); + EXPECT_NE(old_submit_count, query->submit_count()); + expected_end_cmds.end_query.Init( + GL_ANY_SAMPLES_PASSED_EXT, query->submit_count()); + commands = GetPut(); + gl_->EndQueryEXT(GL_ANY_SAMPLES_PASSED_EXT); + EXPECT_EQ(0, memcmp( + &expected_end_cmds, commands, sizeof(expected_end_cmds))); + + // Test BeginQueryEXT fails if target changed. + ClearCommands(); + gl_->BeginQueryEXT(GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT, id1); + EXPECT_TRUE(NoCommandsWritten()); + EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); + + // Test GetQueryObjectuivEXT fails if unused id + GLuint available = 0xBDu; + ClearCommands(); + gl_->GetQueryObjectuivEXT(id2, GL_QUERY_RESULT_AVAILABLE_EXT, &available); + EXPECT_TRUE(NoCommandsWritten()); + EXPECT_EQ(0xBDu, available); + EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); + + // Test GetQueryObjectuivEXT fails if bad id + ClearCommands(); + gl_->GetQueryObjectuivEXT(4567, GL_QUERY_RESULT_AVAILABLE_EXT, &available); + EXPECT_TRUE(NoCommandsWritten()); + EXPECT_EQ(0xBDu, available); + EXPECT_EQ(GL_INVALID_OPERATION, CheckError()); + + // Test GetQueryObjectuivEXT CheckResultsAvailable + ClearCommands(); + gl_->GetQueryObjectuivEXT(id1, GL_QUERY_RESULT_AVAILABLE_EXT, &available); + EXPECT_TRUE(NoCommandsWritten()); + EXPECT_EQ(0u, available); +} + #include "gpu/command_buffer/client/gles2_implementation_unittest_autogen.h" } // namespace gles2 diff --git a/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h b/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h index 8b6bdb1..f69981f 100644 --- a/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h +++ b/gpu/command_buffer/client/gles2_implementation_unittest_autogen.h @@ -1563,6 +1563,37 @@ TEST_F(GLES2ImplementationTest, TexStorage2DEXT) { gl_->TexStorage2DEXT(GL_TEXTURE_2D, 2, GL_RGB565, 4, 5); EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); } + +TEST_F(GLES2ImplementationTest, GenQueriesEXT) { + GLuint ids[2] = { 0, }; + struct Cmds { + GenQueriesEXTImmediate gen; + GLuint data[2]; + }; + Cmds expected; + expected.gen.Init(arraysize(ids), &ids[0]); + expected.data[0] = kQueriesStartId; + expected.data[1] = kQueriesStartId + 1; + gl_->GenQueriesEXT(arraysize(ids), &ids[0]); + EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); + EXPECT_EQ(kQueriesStartId, ids[0]); + EXPECT_EQ(kQueriesStartId + 1, ids[1]); +} + +TEST_F(GLES2ImplementationTest, DeleteQueriesEXT) { + GLuint ids[2] = { kQueriesStartId, kQueriesStartId + 1 }; + struct Cmds { + DeleteQueriesEXTImmediate del; + GLuint data[2]; + }; + Cmds expected; + expected.del.Init(arraysize(ids), &ids[0]); + expected.data[0] = kQueriesStartId; + expected.data[1] = kQueriesStartId + 1; + gl_->DeleteQueriesEXT(arraysize(ids), &ids[0]); + EXPECT_EQ(0, memcmp(&expected, commands_, sizeof(expected))); +} +// TODO: Implement unit test for BeginQueryEXT // TODO: Implement unit test for GenSharedIdsCHROMIUM // TODO: Implement unit test for DeleteSharedIdsCHROMIUM // TODO: Implement unit test for RegisterSharedIdsCHROMIUM diff --git a/gpu/command_buffer/client/query_tracker.cc b/gpu/command_buffer/client/query_tracker.cc new file mode 100644 index 0000000..164a0f4 --- /dev/null +++ b/gpu/command_buffer/client/query_tracker.cc @@ -0,0 +1,130 @@ +// 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 "../client/query_tracker.h" + +#if !defined(__native_client__) + #include "base/atomicops.h" +#endif +#include "../client/cmd_buffer_helper.h" +#include "../client/mapped_memory.h" + +#include <map> + +namespace gpu { +namespace gles2 { + +QuerySyncManager::QuerySyncManager(MappedMemoryManager* manager) + : mapped_memory_(manager) { + GPU_DCHECK(manager); +} + +QuerySyncManager::~QuerySyncManager() { + while (!buckets_.empty()) { + mapped_memory_->Free(buckets_.front()); + buckets_.pop(); + } +} + +bool QuerySyncManager::Alloc(QuerySyncManager::QueryInfo* info) { + GPU_DCHECK(info); + if (free_queries_.empty()) { + int32 shm_id; + unsigned int shm_offset; + void* mem = mapped_memory_->Alloc( + kSyncsPerBucket * sizeof(QuerySync), &shm_id, &shm_offset); + if (!mem) { + return false; + } + QuerySync* syncs = static_cast<QuerySync*>(mem); + buckets_.push(syncs); + for (size_t ii = 0; ii < kSyncsPerBucket; ++ii) { + free_queries_.push(QueryInfo(shm_id, shm_offset, syncs)); + ++syncs; + shm_offset += sizeof(*syncs); + } + } + *info = free_queries_.front(); + info->sync->Reset(); + free_queries_.pop(); + return true; +} + +void QuerySyncManager::Free(const QuerySyncManager::QueryInfo& info) { + free_queries_.push(info); +} + +bool QueryTracker::Query::CheckResultsAvailable( + CommandBufferHelper* helper) { + if (Pending()) { + if (info_.sync->process_count == submit_count_) { + // Need a MemoryBarrier here so that sync->result read after + // sync->process_count. + #if defined(__native_client__) + __sync_synchronize(); + #else + base::subtle::MemoryBarrier(); + #endif + result_ = info_.sync->result; + state_ = kComplete; + } else { + if (!flushed_) { + // TODO(gman): We could reduce the number of flushes by having a + // flush count, recording that count at the time we insert the + // EndQuery command and then only flushing here if we've have not + // passed that count yet. + flushed_ = true; + helper->Flush(); + } + } + } + return state_ == kComplete; +} + +uint32 QueryTracker::Query::GetResult() const { + GPU_DCHECK(state_ == kComplete || state_ == kUninitialized); + return result_; +} + +QueryTracker::QueryTracker(MappedMemoryManager* manager) + : query_sync_manager_(manager) { +} + +QueryTracker::~QueryTracker() { + queries_.clear(); +} + +QueryTracker::Query* QueryTracker::CreateQuery(GLuint id, GLenum target) { + GPU_DCHECK_NE(0u, id); + QuerySyncManager::QueryInfo info; + if (!query_sync_manager_.Alloc(&info)) { + return NULL; + } + Query* query = new Query(id, target, info); + std::pair<QueryMap::iterator, bool> result = + queries_.insert(std::make_pair(id, query)); + GPU_DCHECK(result.second); + return query; +} + +QueryTracker::Query* QueryTracker::GetQuery( + GLuint client_id) { + QueryMap::iterator it = queries_.find(client_id); + return it != queries_.end() ? it->second : NULL; +} + +void QueryTracker::RemoveQuery(GLuint client_id) { + QueryMap::iterator it = queries_.find(client_id); + if (it != queries_.end()) { + Query* query = it->second; + GPU_DCHECK(!query->Pending()); + query_sync_manager_.Free(query->info_); + queries_.erase(it); + delete query; + } +} + +} // namespace gles2 +} // namespace gpu + diff --git a/gpu/command_buffer/client/query_tracker.h b/gpu/command_buffer/client/query_tracker.h new file mode 100644 index 0000000..c76e6c8 --- /dev/null +++ b/gpu/command_buffer/client/query_tracker.h @@ -0,0 +1,167 @@ +// 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. + +#ifndef GPU_COMMAND_BUFFER_CLIENT_QUERY_TRACKER_H_ +#define GPU_COMMAND_BUFFER_CLIENT_QUERY_TRACKER_H_ + +#include <GLES2/gl2.h> + +#include <queue> +#include "../../gpu_export.h" +#if defined(__native_client__) + #include <map> +#else + #include "base/hash_tables.h" +#endif +#include "../common/gles2_cmd_format.h" + +namespace gpu { + +class CommandBufferHelper; +class MappedMemoryManager; + +namespace gles2 { + +// Manages buckets of QuerySync instances in mapped memory. +class GPU_EXPORT QuerySyncManager { + public: + static const size_t kSyncsPerBucket = 4096; + + struct QueryInfo { + QueryInfo(int32 id, uint32 offset, QuerySync* sync_mem) + : shm_id(id), + shm_offset(offset), + sync(sync_mem) { + } + QueryInfo() { + } + + int32 shm_id; + uint32 shm_offset; + QuerySync* sync; + }; + + explicit QuerySyncManager(MappedMemoryManager* manager); + ~QuerySyncManager(); + + bool Alloc(QueryInfo* info); + void Free(const QueryInfo& sync); + + private: + MappedMemoryManager* mapped_memory_; + std::queue<QuerySync*> buckets_; + std::queue<QueryInfo> free_queries_; + + DISALLOW_COPY_AND_ASSIGN(QuerySyncManager); +}; + +// Tracks queries for client side of command buffer. +class GPU_EXPORT QueryTracker { + public: + class GPU_EXPORT Query { + public: + enum State { + kUninitialized, // never used + kActive, // between begin - end + kPending, // not yet complete + kComplete // completed + }; + + Query(GLuint id, GLenum target, const QuerySyncManager::QueryInfo& info) + : id_(id), + target_(target), + info_(info), + state_(kUninitialized), + submit_count_(0), + token_(0), + flushed_(false), + result_(0) { + } + + GLenum target() const { + return target_; + } + + GLenum id() const { + return id_; + } + + int32 shm_id() const { + return info_.shm_id; + } + + uint32 shm_offset() const { + return info_.shm_offset; + } + + void MarkAsActive() { + state_ = kActive; + ++submit_count_; + } + + void MarkAsPending(int32 token) { + token_ = token; + state_ = kPending; + flushed_ = false; + } + + uint32 submit_count() const { + return submit_count_; + } + + int32 token() const { + return token_; + } + + bool NeverUsed() const { + return state_ == kUninitialized; + } + + bool Pending() const { + return state_ == kPending; + } + + bool CheckResultsAvailable(CommandBufferHelper* helper); + + uint32 GetResult() const; + + private: + friend class QueryTracker; + friend class QueryTrackerTest; + + GLuint id_; + GLenum target_; + QuerySyncManager::QueryInfo info_; + State state_; + uint32 submit_count_; + int32 token_; + bool flushed_; + uint32 result_; + }; + + QueryTracker(MappedMemoryManager* manager); + ~QueryTracker(); + + Query* CreateQuery(GLuint id, GLenum target); + Query* GetQuery(GLuint id); + void RemoveQuery(GLuint id); + + private: + #if defined(__native_client__) + // TODO(gman): Figure out something for NaCl + typedef std::map<GLuint, Query*> QueryMap; + #else + typedef base::hash_map<GLuint, Query*> QueryMap; + #endif + + QueryMap queries_; + QuerySyncManager query_sync_manager_; + + DISALLOW_COPY_AND_ASSIGN(QueryTracker); +}; + +} // namespace gles2 +} // namespace gpu + +#endif // GPU_COMMAND_BUFFER_CLIENT_QUERY_TRACKER_H_ diff --git a/gpu/command_buffer/client/query_tracker_unittest.cc b/gpu/command_buffer/client/query_tracker_unittest.cc new file mode 100644 index 0000000..16da7c8 --- /dev/null +++ b/gpu/command_buffer/client/query_tracker_unittest.cc @@ -0,0 +1,171 @@ +// 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. + +// Tests for the QueryTracker. + +#include "gpu/command_buffer/client/query_tracker.h" + +#include <GLES2/gl2ext.h> +#include "base/memory/scoped_ptr.h" +#include "gpu/command_buffer/client/client_test_helper.h" +#include "gpu/command_buffer/client/gles2_cmd_helper.h" +#include "gpu/command_buffer/client/mapped_memory.h" +#include "gpu/command_buffer/common/command_buffer.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace gpu { +namespace gles2 { + +class QuerySyncManagerTest : public testing::Test { + protected: + static const int32 kNumCommandEntries = 400; + static const int32 kCommandBufferSizeBytes = + kNumCommandEntries * sizeof(CommandBufferEntry); + + virtual void SetUp() { + command_buffer_.reset(new MockClientCommandBuffer()); + helper_.reset(new GLES2CmdHelper(command_buffer_.get())); + helper_->Initialize(kCommandBufferSizeBytes); + mapped_memory_.reset(new MappedMemoryManager(helper_.get())); + sync_manager_.reset(new QuerySyncManager(mapped_memory_.get())); + } + + virtual void TearDown() { + sync_manager_.reset(); + mapped_memory_.reset(); + helper_.reset(); + command_buffer_.reset(); + } + + scoped_ptr<CommandBuffer> command_buffer_; + scoped_ptr<GLES2CmdHelper> helper_; + scoped_ptr<MappedMemoryManager> mapped_memory_; + scoped_ptr<QuerySyncManager> sync_manager_; +}; + +TEST_F(QuerySyncManagerTest, Basic) { + QuerySyncManager::QueryInfo infos[4]; + memset(&infos, 0xBD, sizeof(infos)); + + for (size_t ii = 0; ii < arraysize(infos); ++ii) { + EXPECT_TRUE(sync_manager_->Alloc(&infos[ii])); + EXPECT_NE(0, infos[ii].shm_id); + ASSERT_TRUE(infos[ii].sync != NULL); + EXPECT_EQ(0u, infos[ii].sync->process_count); + EXPECT_EQ(0u, infos[ii].sync->result); + } + + for (size_t ii = 0; ii < arraysize(infos); ++ii) { + sync_manager_->Free(infos[ii]); + } +} + +TEST_F(QuerySyncManagerTest, DontFree) { + QuerySyncManager::QueryInfo infos[4]; + memset(&infos, 0xBD, sizeof(infos)); + + for (size_t ii = 0; ii < arraysize(infos); ++ii) { + EXPECT_TRUE(sync_manager_->Alloc(&infos[ii])); + } +} + +class QueryTrackerTest : public testing::Test { + protected: + static const int32 kNumCommandEntries = 400; + static const int32 kCommandBufferSizeBytes = + kNumCommandEntries * sizeof(CommandBufferEntry); + + virtual void SetUp() { + command_buffer_.reset(new MockClientCommandBuffer()); + helper_.reset(new GLES2CmdHelper(command_buffer_.get())); + helper_->Initialize(kCommandBufferSizeBytes); + mapped_memory_.reset(new MappedMemoryManager(helper_.get())); + query_tracker_.reset(new QueryTracker(mapped_memory_.get())); + } + + virtual void TearDown() { + query_tracker_.reset(); + mapped_memory_.reset(); + helper_.reset(); + command_buffer_.reset(); + } + + QuerySync* GetSync(QueryTracker::Query* query) { + return query->info_.sync; + } + + scoped_ptr<CommandBuffer> command_buffer_; + scoped_ptr<GLES2CmdHelper> helper_; + scoped_ptr<MappedMemoryManager> mapped_memory_; + scoped_ptr<QueryTracker> query_tracker_; +}; + +TEST_F(QueryTrackerTest, Basic) { + const GLuint kId1 = 123; + const GLuint kId2 = 124; + + // Check we can create a Query. + QueryTracker::Query* query = query_tracker_->CreateQuery( + kId1, GL_ANY_SAMPLES_PASSED_EXT); + ASSERT_TRUE(query != NULL); + // Check we can get the same Query. + EXPECT_EQ(query, query_tracker_->GetQuery(kId1)); + // Check we get nothing for a non-existent query. + EXPECT_TRUE(query_tracker_->GetQuery(kId2) == NULL); + // Check we can delete the query. + query_tracker_->RemoveQuery(kId1); + // Check we get nothing for a non-existent query. + EXPECT_TRUE(query_tracker_->GetQuery(kId1) == NULL); +} + +TEST_F(QueryTrackerTest, Query) { + const GLuint kId1 = 123; + const int32 kToken = 46; + const uint32 kResult = 456; + + // Create a Query. + QueryTracker::Query* query = query_tracker_->CreateQuery( + kId1, GL_ANY_SAMPLES_PASSED_EXT); + ASSERT_TRUE(query != NULL); + EXPECT_TRUE(query->NeverUsed()); + EXPECT_FALSE(query->Pending()); + EXPECT_EQ(0, query->token()); + EXPECT_EQ(0u, query->submit_count()); + + // Check MarkAsActive. + query->MarkAsActive(); + EXPECT_FALSE(query->NeverUsed()); + EXPECT_FALSE(query->Pending()); + EXPECT_EQ(0, query->token()); + EXPECT_EQ(1u, query->submit_count()); + + // Check MarkAsPending. + query->MarkAsPending(kToken); + EXPECT_FALSE(query->NeverUsed()); + EXPECT_TRUE(query->Pending()); + EXPECT_EQ(kToken, query->token()); + EXPECT_EQ(1u, query->submit_count()); + + // Check CheckResultsAvailable. + EXPECT_FALSE(query->CheckResultsAvailable(helper_.get())); + EXPECT_FALSE(query->NeverUsed()); + EXPECT_TRUE(query->Pending()); + + // Simulate GPU process marking it as available. + QuerySync* sync = GetSync(query); + sync->process_count = query->submit_count(); + sync->result = kResult; + + // Check CheckResultsAvailable. + EXPECT_TRUE(query->CheckResultsAvailable(helper_.get())); + EXPECT_EQ(kResult, query->GetResult()); + EXPECT_FALSE(query->NeverUsed()); + EXPECT_FALSE(query->Pending()); +} + +} // namespace gles2 +} // namespace gpu + + diff --git a/gpu/command_buffer/cmd_buffer_functions.txt b/gpu/command_buffer/cmd_buffer_functions.txt index 82fd73b..688a9d3 100644 --- a/gpu/command_buffer/cmd_buffer_functions.txt +++ b/gpu/command_buffer/cmd_buffer_functions.txt @@ -149,8 +149,8 @@ GL_APICALL void GL_APIENTRY glViewport (GLint x, GLint y, GLsizei width, GL_APICALL void GL_APIENTRY glBlitFramebufferEXT (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenumBlitFilter filter); GL_APICALL void GL_APIENTRY glRenderbufferStorageMultisampleEXT (GLenumRenderBufferTarget target, GLsizei samples, GLenumRenderBufferFormat internalformat, GLsizei width, GLsizei height); GL_APICALL void GL_APIENTRY glTexStorage2DEXT (GLenumTextureTarget target, GLsizei levels, GLenumTextureInternalFormatStorage internalFormat, GLsizei width, GLsizei height); -GL_APICALL void GL_APIENTRY glGenQueriesEXT (GLsizeiNotNegative n, GLuint* ids); -GL_APICALL void GL_APIENTRY glDeleteQueriesEXT (GLsizeiNotNegative n, const GLuint* ids); +GL_APICALL void GL_APIENTRY glGenQueriesEXT (GLsizeiNotNegative n, GLuint* queries); +GL_APICALL void GL_APIENTRY glDeleteQueriesEXT (GLsizeiNotNegative n, const GLuint* queries); GL_APICALL GLboolean GL_APIENTRY glIsQueryEXT (GLidQuery id); GL_APICALL void GL_APIENTRY glBeginQueryEXT (GLenumQueryTarget target, GLidQuery id); GL_APICALL void GL_APIENTRY glEndQueryEXT (GLenumQueryTarget target); diff --git a/gpu/command_buffer/common/gl_mock.h b/gpu/command_buffer/common/gl_mock.h index feba409..8acc132 100644 --- a/gpu/command_buffer/common/gl_mock.h +++ b/gpu/command_buffer/common/gl_mock.h @@ -483,6 +483,21 @@ class MockGLInterface : public GLInterface { GLsync sync, GLenum pname, GLsizei bufSize, GLsizei* length, GLint* values)); + MOCK_METHOD2(GenQueriesARB, void(GLsizei n, GLuint* ids)); + + MOCK_METHOD2(DeleteQueriesARB, void(GLsizei n, const GLuint* ids)); + + MOCK_METHOD1(IsQueryARB, GLboolean(GLuint id)); + + MOCK_METHOD2(BeginQueryARB, void(GLenum target, GLuint id)); + + MOCK_METHOD1(EndQueryARB, void(GLenum target)); + + MOCK_METHOD3(GetQueryivARB, void(GLenum target, GLenum pname, GLint* params)); + + MOCK_METHOD3(GetQueryObjectuivARB, void( + GLuint id, GLenum pname, GLuint* params)); + MOCK_METHOD1(SetSurfaceCHROMIUM, void(GLuint)); MOCK_METHOD0(GetGraphicsResetStatusARB, GLenum()); diff --git a/gpu/command_buffer/common/gles2_cmd_format.h b/gpu/command_buffer/common/gles2_cmd_format.h index 972771b..5120b39 100644 --- a/gpu/command_buffer/common/gles2_cmd_format.h +++ b/gpu/command_buffer/common/gles2_cmd_format.h @@ -137,6 +137,17 @@ struct ProgramInfoHeader { // ProgramInput inputs[num_attribs + num_uniforms]; }; +// The format of QuerySync used by EXT_occlusion_query_boolean +struct QuerySync { + void Reset() { + process_count = 0; + result = 0; + } + + uint32 process_count; + uint32 result; +}; + COMPILE_ASSERT(sizeof(ProgramInput) == 20, ProgramInput_size_not_20); COMPILE_ASSERT(offsetof(ProgramInput, type) == 0, OffsetOf_ProgramInput_type_not_0); diff --git a/gpu/command_buffer/common/gles2_cmd_format_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_autogen.h index a0e05c8..f227dee 100644 --- a/gpu/command_buffer/common/gles2_cmd_format_autogen.h +++ b/gpu/command_buffer/common/gles2_cmd_format_autogen.h @@ -8536,23 +8536,25 @@ struct GenQueriesEXT { header.SetCmd<ValueType>(); } - void Init(GLsizei _n, uint32 _ids_shm_id, uint32 _ids_shm_offset) { + void Init(GLsizei _n, uint32 _queries_shm_id, uint32 _queries_shm_offset) { SetHeader(); n = _n; - ids_shm_id = _ids_shm_id; - ids_shm_offset = _ids_shm_offset; + queries_shm_id = _queries_shm_id; + queries_shm_offset = _queries_shm_offset; } void* Set( - void* cmd, GLsizei _n, uint32 _ids_shm_id, uint32 _ids_shm_offset) { - static_cast<ValueType*>(cmd)->Init(_n, _ids_shm_id, _ids_shm_offset); + void* cmd, GLsizei _n, uint32 _queries_shm_id, + uint32 _queries_shm_offset) { + static_cast<ValueType*>( + cmd)->Init(_n, _queries_shm_id, _queries_shm_offset); return NextCmdAddress<ValueType>(cmd); } gpu::CommandHeader header; int32 n; - uint32 ids_shm_id; - uint32 ids_shm_offset; + uint32 queries_shm_id; + uint32 queries_shm_offset; }; COMPILE_ASSERT(sizeof(GenQueriesEXT) == 16, @@ -8561,10 +8563,52 @@ COMPILE_ASSERT(offsetof(GenQueriesEXT, header) == 0, OffsetOf_GenQueriesEXT_header_not_0); COMPILE_ASSERT(offsetof(GenQueriesEXT, n) == 4, OffsetOf_GenQueriesEXT_n_not_4); -COMPILE_ASSERT(offsetof(GenQueriesEXT, ids_shm_id) == 8, - OffsetOf_GenQueriesEXT_ids_shm_id_not_8); -COMPILE_ASSERT(offsetof(GenQueriesEXT, ids_shm_offset) == 12, - OffsetOf_GenQueriesEXT_ids_shm_offset_not_12); +COMPILE_ASSERT(offsetof(GenQueriesEXT, queries_shm_id) == 8, + OffsetOf_GenQueriesEXT_queries_shm_id_not_8); +COMPILE_ASSERT(offsetof(GenQueriesEXT, queries_shm_offset) == 12, + OffsetOf_GenQueriesEXT_queries_shm_offset_not_12); + +struct GenQueriesEXTImmediate { + typedef GenQueriesEXTImmediate ValueType; + static const CommandId kCmdId = kGenQueriesEXTImmediate; + static const cmd::ArgFlags kArgFlags = cmd::kAtLeastN; + + static uint32 ComputeDataSize(GLsizei n) { + return static_cast<uint32>(sizeof(GLuint) * n); // NOLINT + } + + static uint32 ComputeSize(GLsizei n) { + return static_cast<uint32>( + sizeof(ValueType) + ComputeDataSize(n)); // NOLINT + } + + void SetHeader(GLsizei n) { + header.SetCmdByTotalSize<ValueType>(ComputeSize(n)); + } + + void Init(GLsizei _n, GLuint* _queries) { + SetHeader(_n); + n = _n; + memcpy(ImmediateDataAddress(this), + _queries, ComputeDataSize(_n)); + } + + void* Set(void* cmd, GLsizei _n, GLuint* _queries) { + static_cast<ValueType*>(cmd)->Init(_n, _queries); + const uint32 size = ComputeSize(_n); + return NextImmediateCmdAddressTotalSize<ValueType>(cmd, size); + } + + gpu::CommandHeader header; + int32 n; +}; + +COMPILE_ASSERT(sizeof(GenQueriesEXTImmediate) == 8, + Sizeof_GenQueriesEXTImmediate_is_not_8); +COMPILE_ASSERT(offsetof(GenQueriesEXTImmediate, header) == 0, + OffsetOf_GenQueriesEXTImmediate_header_not_0); +COMPILE_ASSERT(offsetof(GenQueriesEXTImmediate, n) == 4, + OffsetOf_GenQueriesEXTImmediate_n_not_4); struct DeleteQueriesEXT { typedef DeleteQueriesEXT ValueType; @@ -8579,23 +8623,25 @@ struct DeleteQueriesEXT { header.SetCmd<ValueType>(); } - void Init(GLsizei _n, uint32 _ids_shm_id, uint32 _ids_shm_offset) { + void Init(GLsizei _n, uint32 _queries_shm_id, uint32 _queries_shm_offset) { SetHeader(); n = _n; - ids_shm_id = _ids_shm_id; - ids_shm_offset = _ids_shm_offset; + queries_shm_id = _queries_shm_id; + queries_shm_offset = _queries_shm_offset; } void* Set( - void* cmd, GLsizei _n, uint32 _ids_shm_id, uint32 _ids_shm_offset) { - static_cast<ValueType*>(cmd)->Init(_n, _ids_shm_id, _ids_shm_offset); + void* cmd, GLsizei _n, uint32 _queries_shm_id, + uint32 _queries_shm_offset) { + static_cast<ValueType*>( + cmd)->Init(_n, _queries_shm_id, _queries_shm_offset); return NextCmdAddress<ValueType>(cmd); } gpu::CommandHeader header; int32 n; - uint32 ids_shm_id; - uint32 ids_shm_offset; + uint32 queries_shm_id; + uint32 queries_shm_offset; }; COMPILE_ASSERT(sizeof(DeleteQueriesEXT) == 16, @@ -8604,44 +8650,52 @@ COMPILE_ASSERT(offsetof(DeleteQueriesEXT, header) == 0, OffsetOf_DeleteQueriesEXT_header_not_0); COMPILE_ASSERT(offsetof(DeleteQueriesEXT, n) == 4, OffsetOf_DeleteQueriesEXT_n_not_4); -COMPILE_ASSERT(offsetof(DeleteQueriesEXT, ids_shm_id) == 8, - OffsetOf_DeleteQueriesEXT_ids_shm_id_not_8); -COMPILE_ASSERT(offsetof(DeleteQueriesEXT, ids_shm_offset) == 12, - OffsetOf_DeleteQueriesEXT_ids_shm_offset_not_12); +COMPILE_ASSERT(offsetof(DeleteQueriesEXT, queries_shm_id) == 8, + OffsetOf_DeleteQueriesEXT_queries_shm_id_not_8); +COMPILE_ASSERT(offsetof(DeleteQueriesEXT, queries_shm_offset) == 12, + OffsetOf_DeleteQueriesEXT_queries_shm_offset_not_12); + +struct DeleteQueriesEXTImmediate { + typedef DeleteQueriesEXTImmediate ValueType; + static const CommandId kCmdId = kDeleteQueriesEXTImmediate; + static const cmd::ArgFlags kArgFlags = cmd::kAtLeastN; -struct IsQueryEXT { - typedef IsQueryEXT ValueType; - static const CommandId kCmdId = kIsQueryEXT; - static const cmd::ArgFlags kArgFlags = cmd::kFixed; + static uint32 ComputeDataSize(GLsizei n) { + return static_cast<uint32>(sizeof(GLuint) * n); // NOLINT + } - static uint32 ComputeSize() { - return static_cast<uint32>(sizeof(ValueType)); // NOLINT + static uint32 ComputeSize(GLsizei n) { + return static_cast<uint32>( + sizeof(ValueType) + ComputeDataSize(n)); // NOLINT } - void SetHeader() { - header.SetCmd<ValueType>(); + void SetHeader(GLsizei n) { + header.SetCmdByTotalSize<ValueType>(ComputeSize(n)); } - void Init(GLuint _id) { - SetHeader(); - id = _id; + void Init(GLsizei _n, const GLuint* _queries) { + SetHeader(_n); + n = _n; + memcpy(ImmediateDataAddress(this), + _queries, ComputeDataSize(_n)); } - void* Set(void* cmd, GLuint _id) { - static_cast<ValueType*>(cmd)->Init(_id); - return NextCmdAddress<ValueType>(cmd); + void* Set(void* cmd, GLsizei _n, const GLuint* _queries) { + static_cast<ValueType*>(cmd)->Init(_n, _queries); + const uint32 size = ComputeSize(_n); + return NextImmediateCmdAddressTotalSize<ValueType>(cmd, size); } gpu::CommandHeader header; - uint32 id; + int32 n; }; -COMPILE_ASSERT(sizeof(IsQueryEXT) == 8, - Sizeof_IsQueryEXT_is_not_8); -COMPILE_ASSERT(offsetof(IsQueryEXT, header) == 0, - OffsetOf_IsQueryEXT_header_not_0); -COMPILE_ASSERT(offsetof(IsQueryEXT, id) == 4, - OffsetOf_IsQueryEXT_id_not_4); +COMPILE_ASSERT(sizeof(DeleteQueriesEXTImmediate) == 8, + Sizeof_DeleteQueriesEXTImmediate_is_not_8); +COMPILE_ASSERT(offsetof(DeleteQueriesEXTImmediate, header) == 0, + OffsetOf_DeleteQueriesEXTImmediate_header_not_0); +COMPILE_ASSERT(offsetof(DeleteQueriesEXTImmediate, n) == 4, + OffsetOf_DeleteQueriesEXTImmediate_n_not_4); struct BeginQueryEXT { typedef BeginQueryEXT ValueType; @@ -8656,30 +8710,43 @@ struct BeginQueryEXT { header.SetCmd<ValueType>(); } - void Init(GLenum _target, GLuint _id) { + void Init( + GLenum _target, GLuint _id, uint32 _sync_data_shm_id, + uint32 _sync_data_shm_offset) { SetHeader(); target = _target; id = _id; + sync_data_shm_id = _sync_data_shm_id; + sync_data_shm_offset = _sync_data_shm_offset; } - void* Set(void* cmd, GLenum _target, GLuint _id) { - static_cast<ValueType*>(cmd)->Init(_target, _id); + void* Set( + void* cmd, GLenum _target, GLuint _id, uint32 _sync_data_shm_id, + uint32 _sync_data_shm_offset) { + static_cast<ValueType*>( + cmd)->Init(_target, _id, _sync_data_shm_id, _sync_data_shm_offset); return NextCmdAddress<ValueType>(cmd); } gpu::CommandHeader header; uint32 target; uint32 id; + uint32 sync_data_shm_id; + uint32 sync_data_shm_offset; }; -COMPILE_ASSERT(sizeof(BeginQueryEXT) == 12, - Sizeof_BeginQueryEXT_is_not_12); +COMPILE_ASSERT(sizeof(BeginQueryEXT) == 20, + Sizeof_BeginQueryEXT_is_not_20); COMPILE_ASSERT(offsetof(BeginQueryEXT, header) == 0, OffsetOf_BeginQueryEXT_header_not_0); COMPILE_ASSERT(offsetof(BeginQueryEXT, target) == 4, OffsetOf_BeginQueryEXT_target_not_4); COMPILE_ASSERT(offsetof(BeginQueryEXT, id) == 8, OffsetOf_BeginQueryEXT_id_not_8); +COMPILE_ASSERT(offsetof(BeginQueryEXT, sync_data_shm_id) == 12, + OffsetOf_BeginQueryEXT_sync_data_shm_id_not_12); +COMPILE_ASSERT(offsetof(BeginQueryEXT, sync_data_shm_offset) == 16, + OffsetOf_BeginQueryEXT_sync_data_shm_offset_not_16); struct EndQueryEXT { typedef EndQueryEXT ValueType; @@ -8694,128 +8761,30 @@ struct EndQueryEXT { header.SetCmd<ValueType>(); } - void Init(GLenum _target) { + void Init(GLenum _target, GLuint _submit_count) { SetHeader(); target = _target; + submit_count = _submit_count; } - void* Set(void* cmd, GLenum _target) { - static_cast<ValueType*>(cmd)->Init(_target); + void* Set(void* cmd, GLenum _target, GLuint _submit_count) { + static_cast<ValueType*>(cmd)->Init(_target, _submit_count); return NextCmdAddress<ValueType>(cmd); } gpu::CommandHeader header; uint32 target; + uint32 submit_count; }; -COMPILE_ASSERT(sizeof(EndQueryEXT) == 8, - Sizeof_EndQueryEXT_is_not_8); +COMPILE_ASSERT(sizeof(EndQueryEXT) == 12, + Sizeof_EndQueryEXT_is_not_12); COMPILE_ASSERT(offsetof(EndQueryEXT, header) == 0, OffsetOf_EndQueryEXT_header_not_0); COMPILE_ASSERT(offsetof(EndQueryEXT, target) == 4, OffsetOf_EndQueryEXT_target_not_4); - -struct GetQueryivEXT { - typedef GetQueryivEXT ValueType; - static const CommandId kCmdId = kGetQueryivEXT; - static const cmd::ArgFlags kArgFlags = cmd::kFixed; - - static uint32 ComputeSize() { - return static_cast<uint32>(sizeof(ValueType)); // NOLINT - } - - void SetHeader() { - header.SetCmd<ValueType>(); - } - - void Init( - GLenum _target, GLenum _pname, uint32 _params_shm_id, - uint32 _params_shm_offset) { - SetHeader(); - target = _target; - pname = _pname; - params_shm_id = _params_shm_id; - params_shm_offset = _params_shm_offset; - } - - void* Set( - void* cmd, GLenum _target, GLenum _pname, uint32 _params_shm_id, - uint32 _params_shm_offset) { - static_cast<ValueType*>( - cmd)->Init(_target, _pname, _params_shm_id, _params_shm_offset); - return NextCmdAddress<ValueType>(cmd); - } - - gpu::CommandHeader header; - uint32 target; - uint32 pname; - uint32 params_shm_id; - uint32 params_shm_offset; -}; - -COMPILE_ASSERT(sizeof(GetQueryivEXT) == 20, - Sizeof_GetQueryivEXT_is_not_20); -COMPILE_ASSERT(offsetof(GetQueryivEXT, header) == 0, - OffsetOf_GetQueryivEXT_header_not_0); -COMPILE_ASSERT(offsetof(GetQueryivEXT, target) == 4, - OffsetOf_GetQueryivEXT_target_not_4); -COMPILE_ASSERT(offsetof(GetQueryivEXT, pname) == 8, - OffsetOf_GetQueryivEXT_pname_not_8); -COMPILE_ASSERT(offsetof(GetQueryivEXT, params_shm_id) == 12, - OffsetOf_GetQueryivEXT_params_shm_id_not_12); -COMPILE_ASSERT(offsetof(GetQueryivEXT, params_shm_offset) == 16, - OffsetOf_GetQueryivEXT_params_shm_offset_not_16); - -struct GetQueryObjectuivEXT { - typedef GetQueryObjectuivEXT ValueType; - static const CommandId kCmdId = kGetQueryObjectuivEXT; - static const cmd::ArgFlags kArgFlags = cmd::kFixed; - - static uint32 ComputeSize() { - return static_cast<uint32>(sizeof(ValueType)); // NOLINT - } - - void SetHeader() { - header.SetCmd<ValueType>(); - } - - void Init( - GLuint _id, GLenum _pname, uint32 _params_shm_id, - uint32 _params_shm_offset) { - SetHeader(); - id = _id; - pname = _pname; - params_shm_id = _params_shm_id; - params_shm_offset = _params_shm_offset; - } - - void* Set( - void* cmd, GLuint _id, GLenum _pname, uint32 _params_shm_id, - uint32 _params_shm_offset) { - static_cast<ValueType*>( - cmd)->Init(_id, _pname, _params_shm_id, _params_shm_offset); - return NextCmdAddress<ValueType>(cmd); - } - - gpu::CommandHeader header; - uint32 id; - uint32 pname; - uint32 params_shm_id; - uint32 params_shm_offset; -}; - -COMPILE_ASSERT(sizeof(GetQueryObjectuivEXT) == 20, - Sizeof_GetQueryObjectuivEXT_is_not_20); -COMPILE_ASSERT(offsetof(GetQueryObjectuivEXT, header) == 0, - OffsetOf_GetQueryObjectuivEXT_header_not_0); -COMPILE_ASSERT(offsetof(GetQueryObjectuivEXT, id) == 4, - OffsetOf_GetQueryObjectuivEXT_id_not_4); -COMPILE_ASSERT(offsetof(GetQueryObjectuivEXT, pname) == 8, - OffsetOf_GetQueryObjectuivEXT_pname_not_8); -COMPILE_ASSERT(offsetof(GetQueryObjectuivEXT, params_shm_id) == 12, - OffsetOf_GetQueryObjectuivEXT_params_shm_id_not_12); -COMPILE_ASSERT(offsetof(GetQueryObjectuivEXT, params_shm_offset) == 16, - OffsetOf_GetQueryObjectuivEXT_params_shm_offset_not_16); +COMPILE_ASSERT(offsetof(EndQueryEXT, submit_count) == 8, + OffsetOf_EndQueryEXT_submit_count_not_8); struct SwapBuffers { typedef SwapBuffers ValueType; diff --git a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h index 4bc2341..fa73330 100644 --- a/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h +++ b/gpu/command_buffer/common/gles2_cmd_format_test_autogen.h @@ -3329,12 +3329,29 @@ TEST_F(GLES2FormatTest, GenQueriesEXT) { cmd.header.command); EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u); EXPECT_EQ(static_cast<GLsizei>(11), cmd.n); - EXPECT_EQ(static_cast<uint32>(12), cmd.ids_shm_id); - EXPECT_EQ(static_cast<uint32>(13), cmd.ids_shm_offset); + EXPECT_EQ(static_cast<uint32>(12), cmd.queries_shm_id); + EXPECT_EQ(static_cast<uint32>(13), cmd.queries_shm_offset); CheckBytesWrittenMatchesExpectedSize( next_cmd, sizeof(cmd)); } +TEST_F(GLES2FormatTest, GenQueriesEXTImmediate) { + static GLuint ids[] = { 12, 23, 34, }; + GenQueriesEXTImmediate& cmd = *GetBufferAs<GenQueriesEXTImmediate>(); + void* next_cmd = cmd.Set( + &cmd, static_cast<GLsizei>(arraysize(ids)), ids); + EXPECT_EQ(static_cast<uint32>(GenQueriesEXTImmediate::kCmdId), + cmd.header.command); + EXPECT_EQ(sizeof(cmd) + + RoundSizeToMultipleOfEntries(cmd.n * 4u), + cmd.header.size * 4u); + EXPECT_EQ(static_cast<GLsizei>(arraysize(ids)), cmd.n); + CheckBytesWrittenMatchesExpectedSize( + next_cmd, sizeof(cmd) + + RoundSizeToMultipleOfEntries(arraysize(ids) * 4u)); + // TODO(gman): Check that ids were inserted; +} + TEST_F(GLES2FormatTest, DeleteQueriesEXT) { DeleteQueriesEXT& cmd = *GetBufferAs<DeleteQueriesEXT>(); void* next_cmd = cmd.Set( @@ -3346,23 +3363,27 @@ TEST_F(GLES2FormatTest, DeleteQueriesEXT) { cmd.header.command); EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u); EXPECT_EQ(static_cast<GLsizei>(11), cmd.n); - EXPECT_EQ(static_cast<uint32>(12), cmd.ids_shm_id); - EXPECT_EQ(static_cast<uint32>(13), cmd.ids_shm_offset); + EXPECT_EQ(static_cast<uint32>(12), cmd.queries_shm_id); + EXPECT_EQ(static_cast<uint32>(13), cmd.queries_shm_offset); CheckBytesWrittenMatchesExpectedSize( next_cmd, sizeof(cmd)); } -TEST_F(GLES2FormatTest, IsQueryEXT) { - IsQueryEXT& cmd = *GetBufferAs<IsQueryEXT>(); +TEST_F(GLES2FormatTest, DeleteQueriesEXTImmediate) { + static GLuint ids[] = { 12, 23, 34, }; + DeleteQueriesEXTImmediate& cmd = *GetBufferAs<DeleteQueriesEXTImmediate>(); void* next_cmd = cmd.Set( - &cmd, - static_cast<GLuint>(11)); - EXPECT_EQ(static_cast<uint32>(IsQueryEXT::kCmdId), + &cmd, static_cast<GLsizei>(arraysize(ids)), ids); + EXPECT_EQ(static_cast<uint32>(DeleteQueriesEXTImmediate::kCmdId), cmd.header.command); - EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u); - EXPECT_EQ(static_cast<GLuint>(11), cmd.id); + EXPECT_EQ(sizeof(cmd) + + RoundSizeToMultipleOfEntries(cmd.n * 4u), + cmd.header.size * 4u); + EXPECT_EQ(static_cast<GLsizei>(arraysize(ids)), cmd.n); CheckBytesWrittenMatchesExpectedSize( - next_cmd, sizeof(cmd)); + next_cmd, sizeof(cmd) + + RoundSizeToMultipleOfEntries(arraysize(ids) * 4u)); + // TODO(gman): Check that ids were inserted; } TEST_F(GLES2FormatTest, BeginQueryEXT) { @@ -3370,12 +3391,16 @@ TEST_F(GLES2FormatTest, BeginQueryEXT) { void* next_cmd = cmd.Set( &cmd, static_cast<GLenum>(11), - static_cast<GLuint>(12)); + static_cast<GLuint>(12), + static_cast<uint32>(13), + static_cast<uint32>(14)); EXPECT_EQ(static_cast<uint32>(BeginQueryEXT::kCmdId), cmd.header.command); EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u); EXPECT_EQ(static_cast<GLenum>(11), cmd.target); EXPECT_EQ(static_cast<GLuint>(12), cmd.id); + EXPECT_EQ(static_cast<uint32>(13), cmd.sync_data_shm_id); + EXPECT_EQ(static_cast<uint32>(14), cmd.sync_data_shm_offset); CheckBytesWrittenMatchesExpectedSize( next_cmd, sizeof(cmd)); } @@ -3384,49 +3409,13 @@ TEST_F(GLES2FormatTest, EndQueryEXT) { EndQueryEXT& cmd = *GetBufferAs<EndQueryEXT>(); void* next_cmd = cmd.Set( &cmd, - static_cast<GLenum>(11)); - EXPECT_EQ(static_cast<uint32>(EndQueryEXT::kCmdId), - cmd.header.command); - EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u); - EXPECT_EQ(static_cast<GLenum>(11), cmd.target); - CheckBytesWrittenMatchesExpectedSize( - next_cmd, sizeof(cmd)); -} - -TEST_F(GLES2FormatTest, GetQueryivEXT) { - GetQueryivEXT& cmd = *GetBufferAs<GetQueryivEXT>(); - void* next_cmd = cmd.Set( - &cmd, static_cast<GLenum>(11), - static_cast<GLenum>(12), - static_cast<uint32>(13), - static_cast<uint32>(14)); - EXPECT_EQ(static_cast<uint32>(GetQueryivEXT::kCmdId), + static_cast<GLuint>(12)); + EXPECT_EQ(static_cast<uint32>(EndQueryEXT::kCmdId), cmd.header.command); EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u); EXPECT_EQ(static_cast<GLenum>(11), cmd.target); - EXPECT_EQ(static_cast<GLenum>(12), cmd.pname); - EXPECT_EQ(static_cast<uint32>(13), cmd.params_shm_id); - EXPECT_EQ(static_cast<uint32>(14), cmd.params_shm_offset); - CheckBytesWrittenMatchesExpectedSize( - next_cmd, sizeof(cmd)); -} - -TEST_F(GLES2FormatTest, GetQueryObjectuivEXT) { - GetQueryObjectuivEXT& cmd = *GetBufferAs<GetQueryObjectuivEXT>(); - void* next_cmd = cmd.Set( - &cmd, - static_cast<GLuint>(11), - static_cast<GLenum>(12), - static_cast<uint32>(13), - static_cast<uint32>(14)); - EXPECT_EQ(static_cast<uint32>(GetQueryObjectuivEXT::kCmdId), - cmd.header.command); - EXPECT_EQ(sizeof(cmd), cmd.header.size * 4u); - EXPECT_EQ(static_cast<GLuint>(11), cmd.id); - EXPECT_EQ(static_cast<GLenum>(12), cmd.pname); - EXPECT_EQ(static_cast<uint32>(13), cmd.params_shm_id); - EXPECT_EQ(static_cast<uint32>(14), cmd.params_shm_offset); + EXPECT_EQ(static_cast<GLuint>(12), cmd.submit_count); CheckBytesWrittenMatchesExpectedSize( next_cmd, sizeof(cmd)); } diff --git a/gpu/command_buffer/common/gles2_cmd_ids_autogen.h b/gpu/command_buffer/common/gles2_cmd_ids_autogen.h index 9a311a8..37859a7 100644 --- a/gpu/command_buffer/common/gles2_cmd_ids_autogen.h +++ b/gpu/command_buffer/common/gles2_cmd_ids_autogen.h @@ -197,31 +197,30 @@ OP(RenderbufferStorageMultisampleEXT) /* 440 */ \ OP(TexStorage2DEXT) /* 441 */ \ OP(GenQueriesEXT) /* 442 */ \ - OP(DeleteQueriesEXT) /* 443 */ \ - OP(IsQueryEXT) /* 444 */ \ - OP(BeginQueryEXT) /* 445 */ \ - OP(EndQueryEXT) /* 446 */ \ - OP(GetQueryivEXT) /* 447 */ \ - OP(GetQueryObjectuivEXT) /* 448 */ \ - OP(SwapBuffers) /* 449 */ \ - OP(GetMaxValueInBufferCHROMIUM) /* 450 */ \ - OP(GenSharedIdsCHROMIUM) /* 451 */ \ - OP(DeleteSharedIdsCHROMIUM) /* 452 */ \ - OP(RegisterSharedIdsCHROMIUM) /* 453 */ \ - OP(EnableFeatureCHROMIUM) /* 454 */ \ - OP(ResizeCHROMIUM) /* 455 */ \ - OP(GetRequestableExtensionsCHROMIUM) /* 456 */ \ - OP(RequestExtensionCHROMIUM) /* 457 */ \ - OP(GetMultipleIntegervCHROMIUM) /* 458 */ \ - OP(GetProgramInfoCHROMIUM) /* 459 */ \ - OP(CreateStreamTextureCHROMIUM) /* 460 */ \ - OP(DestroyStreamTextureCHROMIUM) /* 461 */ \ - OP(GetTranslatedShaderSourceANGLE) /* 462 */ \ - OP(PostSubBufferCHROMIUM) /* 463 */ \ - OP(TexImageIOSurface2DCHROMIUM) /* 464 */ \ - OP(DrawArraysInstancedANGLE) /* 465 */ \ - OP(DrawElementsInstancedANGLE) /* 466 */ \ - OP(VertexAttribDivisorANGLE) /* 467 */ \ + OP(GenQueriesEXTImmediate) /* 443 */ \ + OP(DeleteQueriesEXT) /* 444 */ \ + OP(DeleteQueriesEXTImmediate) /* 445 */ \ + OP(BeginQueryEXT) /* 446 */ \ + OP(EndQueryEXT) /* 447 */ \ + OP(SwapBuffers) /* 448 */ \ + OP(GetMaxValueInBufferCHROMIUM) /* 449 */ \ + OP(GenSharedIdsCHROMIUM) /* 450 */ \ + OP(DeleteSharedIdsCHROMIUM) /* 451 */ \ + OP(RegisterSharedIdsCHROMIUM) /* 452 */ \ + OP(EnableFeatureCHROMIUM) /* 453 */ \ + OP(ResizeCHROMIUM) /* 454 */ \ + OP(GetRequestableExtensionsCHROMIUM) /* 455 */ \ + OP(RequestExtensionCHROMIUM) /* 456 */ \ + OP(GetMultipleIntegervCHROMIUM) /* 457 */ \ + OP(GetProgramInfoCHROMIUM) /* 458 */ \ + OP(CreateStreamTextureCHROMIUM) /* 459 */ \ + OP(DestroyStreamTextureCHROMIUM) /* 460 */ \ + OP(GetTranslatedShaderSourceANGLE) /* 461 */ \ + OP(PostSubBufferCHROMIUM) /* 462 */ \ + OP(TexImageIOSurface2DCHROMIUM) /* 463 */ \ + OP(DrawArraysInstancedANGLE) /* 464 */ \ + OP(DrawElementsInstancedANGLE) /* 465 */ \ + OP(VertexAttribDivisorANGLE) /* 466 */ \ enum CommandId { kStartPoint = cmd::kLastCommonId, // All GLES2 commands start after this. diff --git a/gpu/command_buffer/service/common_decoder.h b/gpu/command_buffer/service/common_decoder.h index 7ac9abb..1b8cf38 100644 --- a/gpu/command_buffer/service/common_decoder.h +++ b/gpu/command_buffer/service/common_decoder.h @@ -105,20 +105,6 @@ class GPU_EXPORT CommonDecoder : NON_EXPORTED_BASE(public AsyncAPIInterface) { // Gets a bucket. Returns NULL if the bucket does not exist. Bucket* GetBucket(uint32 bucket_id) const; - protected: - // Executes a common command. - // Parameters: - // command: the command index. - // arg_count: the number of CommandBufferEntry arguments. - // cmd_data: the command data. - // Returns: - // error::kNoError if no error was found, one of - // error::Error otherwise. - error::Error DoCommonCommand( - unsigned int command, - unsigned int arg_count, - const void* cmd_data); - // Gets the address of shared memory data, given a shared memory ID and an // offset. Also checks that the size is consistent with the shared memory // size. @@ -140,6 +126,20 @@ class GPU_EXPORT CommonDecoder : NON_EXPORTED_BASE(public AsyncAPIInterface) { return static_cast<T>(GetAddressAndCheckSize(shm_id, offset, size)); } + protected: + // Executes a common command. + // Parameters: + // command: the command index. + // arg_count: the number of CommandBufferEntry arguments. + // cmd_data: the command data. + // Returns: + // error::kNoError if no error was found, one of + // error::Error otherwise. + error::Error DoCommonCommand( + unsigned int command, + unsigned int arg_count, + const void* cmd_data); + // Gets an name for a common command. const char* GetCommonCommandName(cmd::CommandId command_id) const; diff --git a/gpu/command_buffer/service/context_group.cc b/gpu/command_buffer/service/context_group.cc index bfe311b..f3c2de3 100644 --- a/gpu/command_buffer/service/context_group.cc +++ b/gpu/command_buffer/service/context_group.cc @@ -37,6 +37,7 @@ ContextGroup::ContextGroup(bool bind_generates_resource) new NonReusedIdAllocator); id_namespaces_[id_namespaces::kRenderbuffers].reset(new IdAllocator); id_namespaces_[id_namespaces::kTextures].reset(new IdAllocator); + id_namespaces_[id_namespaces::kQueries].reset(new IdAllocator); } ContextGroup::~ContextGroup() { diff --git a/gpu/command_buffer/service/feature_info.cc b/gpu/command_buffer/service/feature_info.cc index aa819d2..b31a5e5 100644 --- a/gpu/command_buffer/service/feature_info.cc +++ b/gpu/command_buffer/service/feature_info.cc @@ -439,6 +439,14 @@ void FeatureInfo::AddFeatures(const char* desired_features) { } } + if (ext.Desire("GL_EXT_occlusion_query_boolean") && + (ext.Have("GL_EXT_occlusion_query_boolean") || + ext.Have("GL_ARB_occlusion_query2"))) { + // TODO(gman): Comment in the next line once this really works. + // AddExtensionString("GL_EXT_occlusion_query_boolean"); + feature_flags_.occlusion_query_boolean = true; + } + if (ext.Desire("GL_ANGLE_instanced_arrays") && (ext.Have("GL_ANGLE_instanced_arrays") || (ext.Have("GL_ARB_instanced_arrays") && diff --git a/gpu/command_buffer/service/feature_info.h b/gpu/command_buffer/service/feature_info.h index bdb4c0a..4bac947 100644 --- a/gpu/command_buffer/service/feature_info.h +++ b/gpu/command_buffer/service/feature_info.h @@ -32,7 +32,8 @@ class GPU_EXPORT FeatureInfo : public base::RefCounted<FeatureInfo> { angle_translated_shader_source(false), angle_pack_reverse_row_order(false), arb_texture_rectangle(false), - angle_instanced_arrays(false) { + angle_instanced_arrays(false), + occlusion_query_boolean(false) { } bool chromium_framebuffer_multisample; @@ -47,6 +48,7 @@ class GPU_EXPORT FeatureInfo : public base::RefCounted<FeatureInfo> { bool angle_pack_reverse_row_order; bool arb_texture_rectangle; bool angle_instanced_arrays; + bool occlusion_query_boolean; }; FeatureInfo(); diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc index 0ecc3d8..96440a0 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc @@ -36,6 +36,7 @@ #include "gpu/command_buffer/service/gles2_cmd_validation.h" #include "gpu/command_buffer/service/gpu_switches.h" #include "gpu/command_buffer/service/program_manager.h" +#include "gpu/command_buffer/service/query_manager.h" #include "gpu/command_buffer/service/renderbuffer_manager.h" #include "gpu/command_buffer/service/shader_manager.h" #include "gpu/command_buffer/service/shader_translator.h" @@ -550,6 +551,7 @@ class GLES2DecoderImpl : public base::SupportsWeakPtr<GLES2DecoderImpl>, virtual gfx::GLContext* GetGLContext() { return context_.get(); } virtual gfx::GLSurface* GetGLSurface() { return surface_.get(); } virtual ContextGroup* GetContextGroup() { return group_.get(); } + virtual QueryManager* GetQueryManager() { return query_manager_.get(); } virtual void SetGLError(GLenum error, const char* msg); virtual void SetResizeCallback( @@ -651,6 +653,8 @@ class GLES2DecoderImpl : public base::SupportsWeakPtr<GLES2DecoderImpl>, void DeleteFramebuffersHelper(GLsizei n, const GLuint* client_ids); bool GenRenderbuffersHelper(GLsizei n, const GLuint* client_ids); void DeleteRenderbuffersHelper(GLsizei n, const GLuint* client_ids); + bool GenQueriesEXTHelper(GLsizei n, const GLuint* client_ids); + void DeleteQueriesEXTHelper(GLsizei n, const GLuint* client_ids); // TODO(gman): Cache these pointers? BufferManager* buffer_manager() { @@ -1068,6 +1072,12 @@ class GLES2DecoderImpl : public base::SupportsWeakPtr<GLES2DecoderImpl>, // Wrapper for glEnableVertexAttribArray. void DoEnableVertexAttribArray(GLuint index); + // Wrapper for glFinish. + void DoFinish(); + + // Wrapper for glFlush. + void DoFlush(); + // Wrapper for glFramebufferRenderbufffer. void DoFramebufferRenderbuffer( GLenum target, GLenum attachment, GLenum renderbuffertarget, @@ -1478,6 +1488,9 @@ class GLES2DecoderImpl : public base::SupportsWeakPtr<GLES2DecoderImpl>, scoped_ptr<Texture> offscreen_resolved_color_texture_; GLenum offscreen_saved_color_format_; + scoped_ptr<QueryManager> query_manager_; + QueryManager::Query::Ref current_query_; + base::Callback<void(gfx::Size)> resize_callback_; MsgCallback msg_callback_; @@ -2009,6 +2022,8 @@ bool GLES2DecoderImpl::Initialize( vertex_attrib_manager_.reset(new VertexAttribManager()); vertex_attrib_manager_->Initialize(group_->max_vertex_attribs()); + query_manager_.reset(new QueryManager()); + util_.set_num_compressed_texture_formats( validators_->compressed_texture_format.GetValues().size()); @@ -2729,6 +2744,7 @@ void GLES2DecoderImpl::Destroy() { texture_units_.reset(); bound_array_buffer_ = NULL; bound_element_array_buffer_ = NULL; + current_query_ = NULL; current_program_ = NULL; bound_read_framebuffer_ = NULL; bound_draw_framebuffer_ = NULL; @@ -2786,6 +2802,11 @@ void GLES2DecoderImpl::Destroy() { offscreen_resolved_color_texture_->Invalidate(); } + if (query_manager_.get()) { + query_manager_->Destroy(have_context); + query_manager_.reset(); + } + if (group_) { group_->Destroy(have_context); group_ = NULL; @@ -3137,6 +3158,20 @@ bool GLES2DecoderImpl::CreateShaderHelper(GLenum type, GLuint client_id) { return true; } +void GLES2DecoderImpl::DoFinish() { + glFinish(); + if (!query_manager_->ProcessPendingQueries(this)) { + current_decoder_error_ = error::kOutOfBounds; + } +} + +void GLES2DecoderImpl::DoFlush() { + glFlush(); + if (!query_manager_->ProcessPendingQueries(this)) { + current_decoder_error_ = error::kOutOfBounds; + } +} + void GLES2DecoderImpl::DoActiveTexture(GLenum texture_unit) { GLuint texture_index = texture_unit - GL_TEXTURE0; if (texture_index >= group_->max_texture_units()) { @@ -5197,6 +5232,9 @@ error::Error GLES2DecoderImpl::DoDrawArrays(bool instanced, } else { glDrawArraysInstancedANGLE(mode, first, count, primcount); } + if (!query_manager_->ProcessPendingQueries(this)) { + current_decoder_error_ = error::kOutOfBounds; + } if (textures_set) { RestoreStateForNonRenderableTextures(); } @@ -5307,6 +5345,9 @@ error::Error GLES2DecoderImpl::DoDrawElements(bool instanced, } else { glDrawElementsInstancedANGLE(mode, count, type, indices, primcount); } + if (!query_manager_->ProcessPendingQueries(this)) { + current_decoder_error_ = error::kOutOfBounds; + } if (textures_set) { RestoreStateForNonRenderableTextures(); } @@ -7890,6 +7931,123 @@ bool GLES2DecoderImpl::WasContextLost() { return false; } +bool GLES2DecoderImpl::GenQueriesEXTHelper( + GLsizei n, const GLuint* client_ids) { + for (GLsizei ii = 0; ii < n; ++ii) { + if (query_manager_->GetQuery(client_ids[ii])) { + return false; + } + } + scoped_array<GLuint> service_ids(new GLuint[n]); + glGenQueriesARB(n, service_ids.get()); + for (GLsizei ii = 0; ii < n; ++ii) { + query_manager_->CreateQuery(client_ids[ii], service_ids[ii]); + } + return true; +} + +void GLES2DecoderImpl::DeleteQueriesEXTHelper( + GLsizei n, const GLuint* client_ids) { + for (GLsizei ii = 0; ii < n; ++ii) { + QueryManager::Query* query = query_manager_->GetQuery(client_ids[ii]); + if (query && !query->IsDeleted()) { + if (query == current_query_) { + current_query_ = NULL; + } + GLuint service_id = query->service_id(); + glDeleteQueriesARB(1, &service_id); + query_manager_->RemoveQuery(client_ids[ii]); + } + } +} + +error::Error GLES2DecoderImpl::HandleBeginQueryEXT( + uint32 immediate_data_size, const gles2::BeginQueryEXT& c) { + GLenum target = static_cast<GLenum>(c.target); + GLuint client_id = static_cast<GLuint>(c.id); + int32 sync_shm_id = static_cast<int32>(c.sync_data_shm_id); + uint32 sync_shm_offset = static_cast<uint32>(c.sync_data_shm_offset); + + if (!feature_info_->feature_flags().occlusion_query_boolean) { + SetGLError(GL_INVALID_OPERATION, "glBeginQueryEXT: not enabled"); + return error::kNoError; + } + + if (current_query_) { + SetGLError( + GL_INVALID_OPERATION, "glBeginQueryEXT: query already in progress"); + return error::kNoError; + } + + if (client_id == 0) { + SetGLError(GL_INVALID_OPERATION, "glBeginQueryEXT: id is 0"); + return error::kNoError; + } + + QueryManager::Query* query = query_manager_->GetQuery(client_id); + if (!query) { + // Checks id was made by glGenQueries + IdAllocatorInterface* id_allocator = + group_->GetIdAllocator(id_namespaces::kQueries); + if (!id_allocator->InUse(client_id)) { + SetGLError(GL_INVALID_OPERATION, + "glBeginQueryEXT: id not made by glGenQueriesEXT"); + return error::kNoError; + } + // Makes object and assoicates with memory. + GLuint service_id = 0; + glGenQueriesARB(1, &service_id); + DCHECK_NE(0u, service_id); + query = query_manager_->CreateQuery(client_id, service_id); + } + + QuerySync* sync = GetSharedMemoryAs<QuerySync*>( + sync_shm_id, sync_shm_offset, sizeof(*sync)); + if (!sync) { + DLOG(ERROR) << "Invalid shared memory referenced by query"; + return error::kOutOfBounds; + } + + if (!query->IsInitialized()) { + query->Initialize(target, sync_shm_id, sync_shm_offset); + } else if (query->target() != target) { + SetGLError(GL_INVALID_OPERATION, "glBeginQueryEXT: target does not match"); + return error::kNoError; + } else if (query->shm_id() != sync_shm_id || + query->shm_offset() != sync_shm_offset) { + DLOG(ERROR) << "Shared memory used by query not the same as before"; + return error::kInvalidArguments; + } + + query_manager_->RemovePendingQuery(query); + + glBeginQueryARB(target, query->service_id()); + current_query_ = query; + + return error::kNoError; +} + +error::Error GLES2DecoderImpl::HandleEndQueryEXT( + uint32 immediate_data_size, const gles2::EndQueryEXT& c) { + GLenum target = static_cast<GLenum>(c.target); + uint32 submit_count = static_cast<GLuint>(c.submit_count); + + if (!current_query_) { + SetGLError(GL_INVALID_OPERATION, "glEndQueryEXT: No active query"); + return error::kNoError; + } + if (current_query_->target() != target) { + SetGLError(GL_INVALID_OPERATION, + "glEndQueryEXT: target does not match active query"); + return error::kNoError; + } + glEndQueryARB(target); + query_manager_->AddPendingQuery(current_query_, submit_count); + current_query_ = NULL; + + return error::kNoError; +} + error::Error GLES2DecoderImpl::HandleCreateStreamTextureCHROMIUM( uint32 immediate_data_size, const gles2::CreateStreamTextureCHROMIUM& c) { diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.h b/gpu/command_buffer/service/gles2_cmd_decoder.h index 89e1dd7..58031d6 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder.h @@ -27,6 +27,7 @@ namespace gles2 { class ContextGroup; class GLES2Util; +class QueryManager; struct DisallowedFeatures { DisallowedFeatures() @@ -126,6 +127,9 @@ class GPU_EXPORT GLES2Decoder : public CommonDecoder { // Gets the associated ContextGroup virtual ContextGroup* GetContextGroup() = 0; + // Gets the QueryManager for this context. + virtual QueryManager* GetQueryManager() = 0; + // Sets a callback which is called when a glResizeCHROMIUM command // is processed. virtual void SetResizeCallback( diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h index 79b726d..d0815dd 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h @@ -650,13 +650,13 @@ error::Error GLES2DecoderImpl::HandleEnableVertexAttribArray( error::Error GLES2DecoderImpl::HandleFinish( uint32 immediate_data_size, const gles2::Finish& c) { - glFinish(); + DoFinish(); return error::kNoError; } error::Error GLES2DecoderImpl::HandleFlush( uint32 immediate_data_size, const gles2::Flush& c) { - glFlush(); + DoFlush(); return error::kNoError; } @@ -2609,50 +2609,69 @@ error::Error GLES2DecoderImpl::HandleTexStorage2DEXT( error::Error GLES2DecoderImpl::HandleGenQueriesEXT( uint32 immediate_data_size, const gles2::GenQueriesEXT& c) { - // TODO: for now this is a no-op - SetGLError(GL_INVALID_OPERATION, "glGenQueriesEXT not implemented"); - return error::kNoError; -} - -error::Error GLES2DecoderImpl::HandleDeleteQueriesEXT( - uint32 immediate_data_size, const gles2::DeleteQueriesEXT& c) { - // TODO: for now this is a no-op - SetGLError(GL_INVALID_OPERATION, "glDeleteQueriesEXT not implemented"); - return error::kNoError; -} - -error::Error GLES2DecoderImpl::HandleIsQueryEXT( - uint32 immediate_data_size, const gles2::IsQueryEXT& c) { - // TODO: for now this is a no-op - SetGLError(GL_INVALID_OPERATION, "glIsQueryEXT not implemented"); - return error::kNoError; -} - -error::Error GLES2DecoderImpl::HandleBeginQueryEXT( - uint32 immediate_data_size, const gles2::BeginQueryEXT& c) { - // TODO: for now this is a no-op - SetGLError(GL_INVALID_OPERATION, "glBeginQueryEXT not implemented"); + GLsizei n = static_cast<GLsizei>(c.n); + uint32 data_size; + if (!SafeMultiplyUint32(n, sizeof(GLuint), &data_size)) { + return error::kOutOfBounds; + } + GLuint* queries = GetSharedMemoryAs<GLuint*>( + c.queries_shm_id, c.queries_shm_offset, data_size); + if (queries == NULL) { + return error::kOutOfBounds; + } + if (!GenQueriesEXTHelper(n, queries)) { + return error::kInvalidArguments; + } return error::kNoError; } -error::Error GLES2DecoderImpl::HandleEndQueryEXT( - uint32 immediate_data_size, const gles2::EndQueryEXT& c) { - // TODO: for now this is a no-op - SetGLError(GL_INVALID_OPERATION, "glEndQueryEXT not implemented"); +error::Error GLES2DecoderImpl::HandleGenQueriesEXTImmediate( + uint32 immediate_data_size, const gles2::GenQueriesEXTImmediate& c) { + GLsizei n = static_cast<GLsizei>(c.n); + uint32 data_size; + if (!SafeMultiplyUint32(n, sizeof(GLuint), &data_size)) { + return error::kOutOfBounds; + } + GLuint* queries = GetImmediateDataAs<GLuint*>( + c, data_size, immediate_data_size); + if (queries == NULL) { + return error::kOutOfBounds; + } + if (!GenQueriesEXTHelper(n, queries)) { + return error::kInvalidArguments; + } return error::kNoError; } -error::Error GLES2DecoderImpl::HandleGetQueryivEXT( - uint32 immediate_data_size, const gles2::GetQueryivEXT& c) { - // TODO: for now this is a no-op - SetGLError(GL_INVALID_OPERATION, "glGetQueryivEXT not implemented"); +error::Error GLES2DecoderImpl::HandleDeleteQueriesEXT( + uint32 immediate_data_size, const gles2::DeleteQueriesEXT& c) { + GLsizei n = static_cast<GLsizei>(c.n); + uint32 data_size; + if (!SafeMultiplyUint32(n, sizeof(GLuint), &data_size)) { + return error::kOutOfBounds; + } + const GLuint* queries = GetSharedMemoryAs<const GLuint*>( + c.queries_shm_id, c.queries_shm_offset, data_size); + if (queries == NULL) { + return error::kOutOfBounds; + } + DeleteQueriesEXTHelper(n, queries); return error::kNoError; } -error::Error GLES2DecoderImpl::HandleGetQueryObjectuivEXT( - uint32 immediate_data_size, const gles2::GetQueryObjectuivEXT& c) { - // TODO: for now this is a no-op - SetGLError(GL_INVALID_OPERATION, "glGetQueryObjectuivEXT not implemented"); +error::Error GLES2DecoderImpl::HandleDeleteQueriesEXTImmediate( + uint32 immediate_data_size, const gles2::DeleteQueriesEXTImmediate& c) { + GLsizei n = static_cast<GLsizei>(c.n); + uint32 data_size; + if (!SafeMultiplyUint32(n, sizeof(GLuint), &data_size)) { + return error::kOutOfBounds; + } + const GLuint* queries = GetImmediateDataAs<const GLuint*>( + c, data_size, immediate_data_size); + if (queries == NULL) { + return error::kOutOfBounds; + } + DeleteQueriesEXTHelper(n, queries); return error::kNoError; } diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h index 05994eb..7392280 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h @@ -25,6 +25,8 @@ class StreamTextureManager; namespace gles2 { class ContextGroup; +class QueryManager; + class MockGLES2Decoder : public GLES2Decoder { public: MockGLES2Decoder(); @@ -47,6 +49,7 @@ class MockGLES2Decoder : public GLES2Decoder { MOCK_METHOD0(GetGLSurface, gfx::GLSurface*()); MOCK_METHOD0(GetGLContext, gfx::GLContext*()); MOCK_METHOD0(GetContextGroup, ContextGroup*()); + MOCK_METHOD0(GetQueryManager, gpu::gles2::QueryManager*()); MOCK_METHOD1(SetResizeCallback, void(const base::Callback<void(gfx::Size)>&)); MOCK_METHOD1(SetStreamTextureManager, void(StreamTextureManager*)); MOCK_METHOD3(DoCommand, error::Error(unsigned int command, diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc index d42c599..94f2e95 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc @@ -6562,6 +6562,96 @@ TEST_F(GLES2DecoderWithShaderTest, EXPECT_EQ(GL_NO_ERROR, GetGLError()); } +TEST_F(GLES2DecoderTest, BeingQueryEXTDisabled) { + // Test something fails if off. +} + +TEST_F(GLES2DecoderManualInitTest, BeingEndQueryEXT) { + InitDecoder( + "GL_EXT_occlusion_query_boolean", // extensions + true, // has alpha + false, // has depth + false, // has stencil + true, // request alpha + false, // request depth + false, // request stencil + true); // bind generates resource + + // Test end fails if no begin. + EndQueryEXT end_cmd; + end_cmd.Init(GL_ANY_SAMPLES_PASSED_EXT, 1); + EXPECT_EQ(error::kNoError, ExecuteCmd(end_cmd)); + EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); + + // Test a non-generated id fails. + BeginQueryEXT begin_cmd; + begin_cmd.Init( + GL_ANY_SAMPLES_PASSED_EXT, kInvalidClientId, + kSharedMemoryId, kSharedMemoryOffset); + EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd)); + EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); + + // Test id = 0 fails. + begin_cmd.Init( + GL_ANY_SAMPLES_PASSED_EXT, 0, kSharedMemoryId, kSharedMemoryOffset); + EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd)); + EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); + + EXPECT_CALL(*gl_, GenQueriesARB(1, _)) + .WillOnce(SetArgumentPointee<1>(kNewServiceId)) + .RetiresOnSaturation(); + GenHelper<GenQueriesEXTImmediate>(kNewClientId); + + // Test bad shared memory fails + begin_cmd.Init( + GL_ANY_SAMPLES_PASSED_EXT, kNewClientId, + kInvalidSharedMemoryId, kSharedMemoryOffset); + EXPECT_NE(error::kNoError, ExecuteCmd(begin_cmd)); + begin_cmd.Init( + GL_ANY_SAMPLES_PASSED_EXT, kNewClientId, + kSharedMemoryId, kInvalidSharedMemoryOffset); + EXPECT_NE(error::kNoError, ExecuteCmd(begin_cmd)); + + // Test valid parameters work. + EXPECT_CALL(*gl_, BeginQueryARB(GL_ANY_SAMPLES_PASSED_EXT, kNewServiceId)) + .Times(1) + .RetiresOnSaturation(); + begin_cmd.Init( + GL_ANY_SAMPLES_PASSED_EXT, kNewClientId, + kSharedMemoryId, kSharedMemoryOffset); + EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd)); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); + + QueryManager* query_manager = decoder_->GetQueryManager(); + ASSERT_TRUE(query_manager != NULL); + QueryManager::Query* query = query_manager->GetQuery(kNewClientId); + ASSERT_TRUE(query != NULL); + EXPECT_FALSE(query->pending()); + + // Test trying begin again fails + EXPECT_EQ(error::kNoError, ExecuteCmd(begin_cmd)); + EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); + + // Test end fails with different target + end_cmd.Init(GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT, 1); + EXPECT_EQ(error::kNoError, ExecuteCmd(end_cmd)); + EXPECT_EQ(GL_INVALID_OPERATION, GetGLError()); + + // Test end succeeds + EXPECT_CALL(*gl_, EndQueryARB(GL_ANY_SAMPLES_PASSED_EXT)) + .Times(1) + .RetiresOnSaturation(); + end_cmd.Init(GL_ANY_SAMPLES_PASSED_EXT, 1); + EXPECT_EQ(error::kNoError, ExecuteCmd(end_cmd)); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); + EXPECT_TRUE(query->pending()); + + EXPECT_CALL(*gl_, DeleteQueriesARB(1, _)) + .Times(1) + .RetiresOnSaturation(); +} + + // TODO(gman): Complete this test. // TEST_F(GLES2DecoderTest, CompressedTexImage2DGLError) { // } diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_2.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_2.cc index 0293bb4..f92a0f6 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_2.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_2.cc @@ -33,6 +33,74 @@ class GLES2DecoderTest2 : public GLES2DecoderTestBase { }; template <> +void GLES2DecoderTestBase::SpecializedSetup<GenQueriesEXT, 0>( + bool valid) { + if (!valid) { + // Make the client_query_id_ so that trying to make it again + // will fail. + GetSharedMemoryAs<GLuint*>()[0] = client_query_id_; + EXPECT_CALL(*gl_, GenQueriesARB(1, _)) + .WillOnce(SetArgumentPointee<1>(kNewServiceId)); + GenQueriesEXT cmd; + cmd.Init(1, shared_memory_id_, shared_memory_offset_); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); + } + // In the valid case this deletes the one created in the test. In the invalid + // case it deleted the one above. + EXPECT_CALL(*gl_, DeleteQueriesARB(1, _)) + .Times(1) + .RetiresOnSaturation(); +}; + +template <> +void GLES2DecoderTestBase::SpecializedSetup<GenQueriesEXTImmediate, 0>( + bool valid) { + if (!valid) { + // Make the client_query_id_ so that trying to make it again + // will fail. + GetSharedMemoryAs<GLuint*>()[0] = client_query_id_; + EXPECT_CALL(*gl_, GenQueriesARB(1, _)) + .WillOnce(SetArgumentPointee<1>(kNewServiceId)); + GenQueriesEXT cmd; + cmd.Init(1, shared_memory_id_, shared_memory_offset_); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); + } + // In the valid case this deletes the one created in the test. In the invalid + // case it deleted the one above. + EXPECT_CALL(*gl_, DeleteQueriesARB(1, _)) + .Times(1) + .RetiresOnSaturation(); +}; + +template <> +void GLES2DecoderTestBase::SpecializedSetup<DeleteQueriesEXT, 0>( + bool valid) { + if (valid) { + // Make the client_query_id_ so that trying to delete it will succeed. + GetSharedMemoryAs<GLuint*>()[0] = client_query_id_; + EXPECT_CALL(*gl_, GenQueriesARB(1, _)) + .WillOnce(SetArgumentPointee<1>(kServiceQueryId)); + GenQueriesEXT cmd; + cmd.Init(1, shared_memory_id_, shared_memory_offset_); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); + } +}; + +template <> +void GLES2DecoderTestBase::SpecializedSetup<DeleteQueriesEXTImmediate, 0>( + bool valid) { + if (valid) { + // Make the client_query_id_ so that trying to delete it will succeed. + GetSharedMemoryAs<GLuint*>()[0] = client_query_id_; + EXPECT_CALL(*gl_, GenQueriesARB(1, _)) + .WillOnce(SetArgumentPointee<1>(kServiceQueryId)); + GenQueriesEXT cmd; + cmd.Init(1, shared_memory_id_, shared_memory_offset_); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); + } +}; + +template <> void GLES2DecoderTestBase::SpecializedSetup<LinkProgram, 0>(bool /* valid */) { const GLuint kClientVertexShaderId = 5001; const GLuint kServiceVertexShaderId = 6001; diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_2_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_2_autogen.h index ffe0dad..ea1be28 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_2_autogen.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_2_autogen.h @@ -1806,23 +1806,107 @@ TEST_F(GLES2DecoderTest2, ViewportInvalidArgs3_0) { // TODO(gman): BlitFramebufferEXT // TODO(gman): RenderbufferStorageMultisampleEXT // TODO(gman): TexStorage2DEXT -// TODO(gman): GenQueriesEXT -// TODO(gman): DeleteQueriesEXT +TEST_F(GLES2DecoderTest2, GenQueriesEXTValidArgs) { + EXPECT_CALL(*gl_, GenQueriesARB(1, _)) + .WillOnce(SetArgumentPointee<1>(kNewServiceId)); + GetSharedMemoryAs<GLuint*>()[0] = kNewClientId; + SpecializedSetup<GenQueriesEXT, 0>(true); + GenQueriesEXT cmd; + cmd.Init(1, shared_memory_id_, shared_memory_offset_); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); + EXPECT_TRUE(GetQueryInfo(kNewClientId) != NULL); +} -// TODO(gman): IsQueryEXT +TEST_F(GLES2DecoderTest2, GenQueriesEXTInvalidArgs) { + EXPECT_CALL(*gl_, GenQueriesARB(_, _)).Times(0); + GetSharedMemoryAs<GLuint*>()[0] = client_query_id_; + SpecializedSetup<GenQueriesEXT, 0>(false); + GenQueriesEXT cmd; + cmd.Init(1, shared_memory_id_, shared_memory_offset_); + EXPECT_EQ(error::kInvalidArguments, ExecuteCmd(cmd)); +} -// TODO(gman): BeginQueryEXT +TEST_F(GLES2DecoderTest2, GenQueriesEXTImmediateValidArgs) { + EXPECT_CALL(*gl_, GenQueriesARB(1, _)) + .WillOnce(SetArgumentPointee<1>(kNewServiceId)); + GenQueriesEXTImmediate* cmd = GetImmediateAs<GenQueriesEXTImmediate>(); + GLuint temp = kNewClientId; + SpecializedSetup<GenQueriesEXTImmediate, 0>(true); + cmd->Init(1, &temp); + EXPECT_EQ(error::kNoError, + ExecuteImmediateCmd(*cmd, sizeof(temp))); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); + EXPECT_TRUE(GetQueryInfo(kNewClientId) != NULL); +} -// TODO(gman): EndQueryEXT +TEST_F(GLES2DecoderTest2, GenQueriesEXTImmediateInvalidArgs) { + EXPECT_CALL(*gl_, GenQueriesARB(_, _)).Times(0); + GenQueriesEXTImmediate* cmd = GetImmediateAs<GenQueriesEXTImmediate>(); + SpecializedSetup<GenQueriesEXTImmediate, 0>(false); + cmd->Init(1, &client_query_id_); + EXPECT_EQ(error::kInvalidArguments, + ExecuteImmediateCmd(*cmd, sizeof(&client_query_id_))); +} -// TODO(gman): GetQueryivEXT +TEST_F(GLES2DecoderTest2, DeleteQueriesEXTValidArgs) { + EXPECT_CALL( + *gl_, + DeleteQueriesARB(1, Pointee(kServiceQueryId))) + .Times(1); + GetSharedMemoryAs<GLuint*>()[0] = client_query_id_; + SpecializedSetup<DeleteQueriesEXT, 0>(true); + DeleteQueriesEXT cmd; + cmd.Init(1, shared_memory_id_, shared_memory_offset_); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); + EXPECT_TRUE( + GetQueryInfo(client_query_id_) == NULL); +} -// TODO(gman): GetQueryObjectuivEXT +TEST_F(GLES2DecoderTest2, DeleteQueriesEXTInvalidArgs) { + GetSharedMemoryAs<GLuint*>()[0] = kInvalidClientId; + SpecializedSetup<DeleteQueriesEXT, 0>(false); + DeleteQueriesEXT cmd; + cmd.Init(1, shared_memory_id_, shared_memory_offset_); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); +} + +TEST_F(GLES2DecoderTest2, DeleteQueriesEXTImmediateValidArgs) { + EXPECT_CALL( + *gl_, + DeleteQueriesARB(1, Pointee(kServiceQueryId))) + .Times(1); + DeleteQueriesEXTImmediate& cmd = + *GetImmediateAs<DeleteQueriesEXTImmediate>(); + SpecializedSetup<DeleteQueriesEXTImmediate, 0>(true); + cmd.Init(1, &client_query_id_); + EXPECT_EQ(error::kNoError, + ExecuteImmediateCmd(cmd, sizeof(client_query_id_))); + EXPECT_EQ(GL_NO_ERROR, GetGLError()); + EXPECT_TRUE( + GetQueryInfo(client_query_id_) == NULL); +} + +TEST_F(GLES2DecoderTest2, DeleteQueriesEXTImmediateInvalidArgs) { + DeleteQueriesEXTImmediate& cmd = + *GetImmediateAs<DeleteQueriesEXTImmediate>(); + SpecializedSetup<DeleteQueriesEXTImmediate, 0>(false); + GLuint temp = kInvalidClientId; + cmd.Init(1, &temp); + EXPECT_EQ(error::kNoError, + ExecuteImmediateCmd(cmd, sizeof(temp))); +} +// TODO(gman): BeginQueryEXT + +// TODO(gman): EndQueryEXT // TODO(gman): SwapBuffers // TODO(gman): GetMaxValueInBufferCHROMIUM // TODO(gman): GenSharedIdsCHROMIUM +// TODO(gman): DeleteSharedIdsCHROMIUM + #endif // GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_UNITTEST_2_AUTOGEN_H_ diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_3_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_3_autogen.h index bef7a9e..56f5195 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_3_autogen.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_3_autogen.h @@ -10,8 +10,6 @@ #ifndef GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_UNITTEST_3_AUTOGEN_H_ #define GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_UNITTEST_3_AUTOGEN_H_ -// TODO(gman): DeleteSharedIdsCHROMIUM - // TODO(gman): RegisterSharedIdsCHROMIUM // TODO(gman): EnableFeatureCHROMIUM diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc index ec5f1dd..7e44be7 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc @@ -44,7 +44,8 @@ GLES2DecoderTestBase::GLES2DecoderTestBase() client_texture_id_(106), client_element_buffer_id_(107), client_vertex_shader_id_(121), - client_fragment_shader_id_(122) { + client_fragment_shader_id_(122), + client_query_id_(123) { memset(immediate_buffer_, 0xEE, sizeof(immediate_buffer_)); } @@ -798,6 +799,7 @@ const GLuint GLES2DecoderTestBase::kServiceTextureId; const GLuint GLES2DecoderTestBase::kServiceProgramId; const GLuint GLES2DecoderTestBase::kServiceShaderId; const GLuint GLES2DecoderTestBase::kServiceElementBufferId; +const GLuint GLES2DecoderTestBase::kServiceQueryId; const int32 GLES2DecoderTestBase::kSharedMemoryId; const size_t GLES2DecoderTestBase::kSharedBufferSize; diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h index 7217a58..7dcdddb 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h @@ -14,6 +14,7 @@ #include "gpu/command_buffer/service/framebuffer_manager.h" #include "gpu/command_buffer/service/gles2_cmd_decoder.h" #include "gpu/command_buffer/service/program_manager.h" +#include "gpu/command_buffer/service/query_manager.h" #include "gpu/command_buffer/service/renderbuffer_manager.h" #include "gpu/command_buffer/service/shader_manager.h" #include "gpu/command_buffer/service/texture_manager.h" @@ -50,6 +51,7 @@ class GLES2DecoderTestBase : public testing::Test { static const GLuint kServiceProgramId = 305; static const GLuint kServiceShaderId = 306; static const GLuint kServiceElementBufferId = 308; + static const GLuint kServiceQueryId = 309; static const int32 kSharedMemoryId = 401; static const size_t kSharedBufferSize = 2048; @@ -193,16 +195,20 @@ class GLES2DecoderTestBase : public testing::Test { return group_->renderbuffer_manager()->GetRenderbufferInfo(service_id); } - TextureManager::TextureInfo* GetTextureInfo(GLuint service_id) { - return group_->texture_manager()->GetTextureInfo(service_id); + TextureManager::TextureInfo* GetTextureInfo(GLuint client_id) { + return group_->texture_manager()->GetTextureInfo(client_id); } - ShaderManager::ShaderInfo* GetShaderInfo(GLuint service_id) { - return group_->shader_manager()->GetShaderInfo(service_id); + ShaderManager::ShaderInfo* GetShaderInfo(GLuint client_id) { + return group_->shader_manager()->GetShaderInfo(client_id); } - ProgramManager::ProgramInfo* GetProgramInfo(GLuint service_id) { - return group_->program_manager()->GetProgramInfo(service_id); + ProgramManager::ProgramInfo* GetProgramInfo(GLuint client_id) { + return group_->program_manager()->GetProgramInfo(client_id); + } + + QueryManager::Query* GetQueryInfo(GLuint client_id) { + return decoder_->GetQueryManager()->GetQuery(client_id); } ProgramManager* program_manager() { @@ -414,6 +420,7 @@ class GLES2DecoderTestBase : public testing::Test { GLuint client_element_buffer_id_; GLuint client_vertex_shader_id_; GLuint client_fragment_shader_id_; + GLuint client_query_id_; uint32 shared_memory_id_; uint32 shared_memory_offset_; diff --git a/gpu/command_buffer/service/query_manager.cc b/gpu/command_buffer/service/query_manager.cc new file mode 100644 index 0000000..08cecea --- /dev/null +++ b/gpu/command_buffer/service/query_manager.cc @@ -0,0 +1,177 @@ +// 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/query_manager.h" +#include "base/atomicops.h" +#include "base/logging.h" +#include "gpu/command_buffer/common/gles2_cmd_format.h" +#include "gpu/command_buffer/service/common_decoder.h" + +namespace gpu { +namespace gles2 { + +QueryManager::QueryManager() + : query_count_(0) { +} + +QueryManager::~QueryManager() { + DCHECK(queries_.empty()); + + // If this triggers, that means something is keeping a reference to + // a Query belonging to this. + CHECK_EQ(query_count_, 0u); +} + +void QueryManager::Destroy(bool have_context) { + pending_queries_.clear(); + while (!queries_.empty()) { + Query* query = queries_.begin()->second; + if (have_context) { + if (!query->IsDeleted()) { + GLuint service_id = query->service_id(); + glDeleteQueriesARB(1, &service_id); + query->MarkAsDeleted(); + } + } + queries_.erase(queries_.begin()); + } +} + +QueryManager::Query* QueryManager::CreateQuery( + GLuint client_id, + GLuint service_id) { + Query::Ref query(new Query(this, service_id)); + std::pair<QueryMap::iterator, bool> result = + queries_.insert(std::make_pair(client_id, query)); + DCHECK(result.second); + return query.get(); +} + +QueryManager::Query* QueryManager::GetQuery( + GLuint client_id) { + QueryMap::iterator it = queries_.find(client_id); + return it != queries_.end() ? it->second : NULL; +} + +void QueryManager::RemoveQuery(GLuint client_id) { + QueryMap::iterator it = queries_.find(client_id); + if (it != queries_.end()) { + Query* query = it->second; + RemovePendingQuery(query); + query->MarkAsDeleted(); + queries_.erase(it); + } +} + +void QueryManager::StartTracking(QueryManager::Query* /* query */) { + ++query_count_; +} + +void QueryManager::StopTracking(QueryManager::Query* /* query */) { + --query_count_; +} + +QueryManager::Query::Query( + QueryManager* manager, + GLuint service_id) + : manager_(manager), + service_id_(service_id), + target_(0), + shm_id_(0), + shm_offset_(0), + pending_(false) { + DCHECK(manager); + manager_->StartTracking(this); +} + +QueryManager::Query::~Query() { + if (manager_) { + manager_->StopTracking(this); + manager_ = NULL; + } +} + +void QueryManager::Query::Initialize( + GLenum target, int32 shm_id, uint32 shm_offset) { + DCHECK(!IsInitialized()); + target_ = target; + shm_id_ = shm_id; + shm_offset_ = shm_offset; +} + +bool QueryManager::GetClientId(GLuint service_id, GLuint* client_id) const { + DCHECK(client_id); + // This doesn't need to be fast. It's only used during slow queries. + for (QueryMap::const_iterator it = queries_.begin(); + it != queries_.end(); ++it) { + if (it->second->service_id() == service_id) { + *client_id = it->first; + return true; + } + } + return false; +} + +bool QueryManager::ProcessPendingQueries(CommonDecoder* decoder) { + DCHECK(decoder); + while (!pending_queries_.empty()) { + Query* query = pending_queries_.front().get(); + GLuint available = 0; + glGetQueryObjectuivARB( + query->service_id(), GL_QUERY_RESULT_AVAILABLE_EXT, &available); + if (!available) { + return true; + } + GLuint result = 0; + glGetQueryObjectuivARB( + query->service_id(), GL_QUERY_RESULT_EXT, &result); + QuerySync* sync = decoder->GetSharedMemoryAs<QuerySync*>( + query->shm_id(), query->shm_offset(), sizeof(*sync)); + if (!sync) { + return false; + } + + sync->result = result; + // Need a MemoryBarrier here so that sync->result is written before + // sync->process_count. + base::subtle::MemoryBarrier(); + sync->process_count = query->submit_count(); + + query->MarkAsCompleted(); + pending_queries_.pop_front(); + } + + return true; +} + +void QueryManager::AddPendingQuery(Query* query, uint32 submit_count) { + DCHECK(query); + DCHECK(query->IsInitialized()); + DCHECK(!query->IsDeleted()); + RemovePendingQuery(query); + query->MarkAsPending(submit_count); + pending_queries_.push_back(query); +} + +void QueryManager::RemovePendingQuery(Query* query) { + DCHECK(query); + if (query->pending()) { + // TODO(gman): Speed this up if this is a common operation. This would only + // happen if you do being/end begin/end on the same query without waiting + // for the first one to finish. + for (QueryQueue::iterator it = pending_queries_.begin(); + it != pending_queries_.end(); ++it) { + if (it->get() == query) { + pending_queries_.erase(it); + break; + } + } + query->MarkAsCompleted(); + } +} + +} // namespace gles2 +} // namespace gpu + + diff --git a/gpu/command_buffer/service/query_manager.h b/gpu/command_buffer/service/query_manager.h new file mode 100644 index 0000000..8e9e46a --- /dev/null +++ b/gpu/command_buffer/service/query_manager.h @@ -0,0 +1,163 @@ +// 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. + +#ifndef GPU_COMMAND_BUFFER_SERVICE_QUERY_MANAGER_H_ +#define GPU_COMMAND_BUFFER_SERVICE_QUERY_MANAGER_H_ + +#include <deque> +#include "base/basictypes.h" +#include "base/hash_tables.h" +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "gpu/command_buffer/service/gl_utils.h" +#include "gpu/gpu_export.h" + +namespace gpu { + +class CommonDecoder; + +namespace gles2 { + +// This class keeps track of the queries and their state +// As Queries are not shared there is one QueryManager per context. +class GPU_EXPORT QueryManager { + public: + class GPU_EXPORT Query : public base::RefCounted<Query> { + public: + typedef scoped_refptr<Query> Ref; + + Query(QueryManager* manager, GLuint service_id); + + void Initialize(GLenum target, int32 shm_id, uint32 shm_offset); + + bool IsInitialized() const { + return target_ != 0; + } + + GLuint service_id() const { + return service_id_; + } + + GLenum target() const { + return target_; + } + + bool IsDeleted() const { + return service_id_ == 0; + } + + bool IsValid() const { + return target() && !IsDeleted(); + } + + int32 shm_id() const { + return shm_id_; + } + + uint32 shm_offset() const { + return shm_offset_; + } + + bool pending() const { + return pending_; + } + + private: + friend class QueryManager; + friend class QueryManagerTest; + friend class base::RefCounted<Query>; + + ~Query(); + + void MarkAsPending(uint32 submit_count) { + DCHECK(!pending_); + pending_ = true; + submit_count_ = submit_count; + } + + void MarkAsCompleted() { + DCHECK(pending_); + pending_ = false; + } + + uint32 submit_count() const { + return submit_count_; + } + + void MarkAsDeleted() { + service_id_ = 0; + } + + // The manager that owns this Query. + QueryManager* manager_; + + // Service side query id. + GLuint service_id_; + + // The type of query. + GLenum target_; + + // The shared memory used with this Query. + int32 shm_id_; + uint32 shm_offset_; + + // Count to set process count do when completed. + uint32 submit_count_; + + // True if in the Queue. + bool pending_; + }; + + QueryManager(); + ~QueryManager(); + + // Must call before destruction. + void Destroy(bool have_context); + + // Creates a Query for the given query. + Query* CreateQuery(GLuint client_id, GLuint service_id); + + // Gets the query info for the given query. + Query* GetQuery(GLuint client_id); + + // Removes a query info for the given query. + void RemoveQuery(GLuint client_id); + + // Gets a client id for a given service id. + bool GetClientId(GLuint service_id, GLuint* client_id) const; + + // Adds to queue of queries waiting for completion. + void AddPendingQuery(Query* query, uint32 submit_count); + + // Removes a query from the queue of pending queries. + void RemovePendingQuery(Query* query); + + // Processes pending queries. Returns false if any queries are pointing + // to invalid shared memory. + bool ProcessPendingQueries(CommonDecoder* decoder); + + private: + void StartTracking(Query* query); + void StopTracking(Query* query); + + // Counts the number of Queries allocated with 'this' as their manager. + // Allows checking no Query will outlive this. + unsigned query_count_; + + // Info for each query in the system. + typedef base::hash_map<GLuint, Query::Ref> QueryMap; + QueryMap queries_; + + // Queries waiting for completion. + typedef std::deque<Query::Ref> QueryQueue; + QueryQueue pending_queries_; + + DISALLOW_COPY_AND_ASSIGN(QueryManager); +}; + +} // namespace gles2 +} // namespace gpu + +#endif // GPU_COMMAND_BUFFER_SERVICE_QUERY_MANAGER_H_ diff --git a/gpu/command_buffer/service/query_manager_unittest.cc b/gpu/command_buffer/service/query_manager_unittest.cc new file mode 100644 index 0000000..e230768 --- /dev/null +++ b/gpu/command_buffer/service/query_manager_unittest.cc @@ -0,0 +1,457 @@ +// 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/query_manager.h" +#include "gpu/command_buffer/common/gl_mock.h" +#include "gpu/command_buffer/common/gles2_cmd_format.h" +#include "gpu/command_buffer/service/cmd_buffer_engine.h" +#include "gpu/command_buffer/service/common_decoder.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::_; +using ::testing::InSequence; +using ::testing::SetArgumentPointee; + +namespace gpu { +namespace gles2 { + +class MockDecoder : public CommonDecoder { + public: + virtual ~MockDecoder() { } + MOCK_METHOD3(DoCommand, error::Error( + unsigned int command, + unsigned int arg_count, + const void* cmd_data)); + MOCK_CONST_METHOD1(GetCommandName, const char* (unsigned int command_id)); +}; + +class QueryManagerTest : public testing::Test { + public: + static const int32 kSharedMemoryId = 401; + static const size_t kSharedBufferSize = 2048; + static const uint32 kSharedMemoryOffset = 132; + static const int32 kInvalidSharedMemoryId = 402; + static const uint32 kInvalidSharedMemoryOffset = kSharedBufferSize + 1; + static const uint32 kInitialResult = 0xBDBDBDBDu; + static const uint8 kInitialMemoryValue = 0xBDu; + + QueryManagerTest() { + } + ~QueryManagerTest() { + } + + protected: + virtual void SetUp() { + gl_.reset(new ::testing::StrictMock< ::gfx::MockGLInterface>()); + ::gfx::GLInterface::SetGLInterface(gl_.get()); + engine_.reset(new MockCommandBufferEngine()); + decoder_.reset(new MockDecoder()); + decoder_->set_engine(engine_.get()); + manager_.reset(new QueryManager()); + } + + virtual void TearDown() { + decoder_.reset(); + manager_->Destroy(false); + manager_.reset(); + engine_.reset(); + ::gfx::GLInterface::SetGLInterface(NULL); + gl_.reset(); + } + + // Use StrictMock to make 100% sure we know how GL will be called. + scoped_ptr< ::testing::StrictMock< ::gfx::MockGLInterface> > gl_; + scoped_ptr<MockDecoder> decoder_; + scoped_ptr<QueryManager> manager_; + + private: + class MockCommandBufferEngine : public CommandBufferEngine { + public: + MockCommandBufferEngine() { + data_.reset(new int8[kSharedBufferSize]); + ClearSharedMemory(); + valid_buffer_.ptr = data_.get(); + valid_buffer_.size = kSharedBufferSize; + } + + virtual ~MockCommandBufferEngine() { + } + + virtual Buffer GetSharedMemoryBuffer(int32 shm_id) OVERRIDE { + return shm_id == kSharedMemoryId ? valid_buffer_ : invalid_buffer_; + } + + void ClearSharedMemory() { + memset(data_.get(), kInitialMemoryValue, kSharedBufferSize); + } + + virtual void set_token(int32 token) OVERRIDE { + DCHECK(false); + } + + virtual bool SetGetBuffer(int32 /* transfer_buffer_id */) OVERRIDE { + DCHECK(false); + return false; + } + + // Overridden from CommandBufferEngine. + virtual bool SetGetOffset(int32 offset) OVERRIDE { + DCHECK(false); + return false; + } + + // Overridden from CommandBufferEngine. + virtual int32 GetGetOffset() OVERRIDE { + DCHECK(false); + return 0; + } + + private: + scoped_array<int8> data_; + Buffer valid_buffer_; + Buffer invalid_buffer_; + }; + + scoped_ptr<MockCommandBufferEngine> engine_; +}; + +// GCC requires these declarations, but MSVC requires they not be present +#ifndef COMPILER_MSVC +const int32 QueryManagerTest::kSharedMemoryId; +const size_t QueryManagerTest::kSharedBufferSize; +const uint32 QueryManagerTest::kSharedMemoryOffset; +const int32 QueryManagerTest::kInvalidSharedMemoryId; +const uint32 QueryManagerTest::kInvalidSharedMemoryOffset; +const uint32 QueryManagerTest::kInitialResult; +const uint8 QueryManagerTest::kInitialMemoryValue; +#endif + +TEST_F(QueryManagerTest, Basic) { + const GLuint kClient1Id = 1; + const GLuint kService1Id = 11; + const GLuint kClient2Id = 2; + + // Check we can create a Query. + QueryManager::Query::Ref query( + manager_->CreateQuery(kClient1Id, kService1Id)); + ASSERT_TRUE(query.get() != NULL); + // Check we can get the same Query. + EXPECT_EQ(query.get(), manager_->GetQuery(kClient1Id)); + // Check we can get the client id. + GLuint client_id = -1; + EXPECT_TRUE(manager_->GetClientId(kService1Id, &client_id)); + EXPECT_EQ(kClient1Id, client_id); + // Check we get nothing for a non-existent query. + EXPECT_TRUE(manager_->GetQuery(kClient2Id) == NULL); + // Check we can delete the query. + manager_->RemoveQuery(kClient1Id); + // Check we get nothing for a non-existent query. + EXPECT_TRUE(manager_->GetQuery(kClient1Id) == NULL); + // Check query is deleted + EXPECT_TRUE(query->IsDeleted()); +} + +TEST_F(QueryManagerTest, Destroy) { + const GLuint kClient1Id = 1; + const GLuint kService1Id = 11; + + // Create Query. + QueryManager::Query::Ref query( + manager_->CreateQuery(kClient1Id, kService1Id)); + ASSERT_TRUE(query.get() != NULL); + EXPECT_CALL(*gl_, DeleteQueriesARB(1, ::testing::Pointee(kService1Id))) + .Times(1) + .RetiresOnSaturation(); + manager_->Destroy(true); + // Check we get nothing for a non-existent query. + EXPECT_TRUE(manager_->GetQuery(kClient1Id) == NULL); + // Check query is deleted + EXPECT_TRUE(query->IsDeleted()); +} + +TEST_F(QueryManagerTest, QueryBasic) { + const GLuint kClient1Id = 1; + const GLuint kService1Id = 11; + + // Create Query. + QueryManager::Query::Ref query( + manager_->CreateQuery(kClient1Id, kService1Id)); + ASSERT_TRUE(query.get() != NULL); + + EXPECT_FALSE(query->IsValid()); + EXPECT_FALSE(query->IsDeleted()); + EXPECT_FALSE(query->IsInitialized()); + EXPECT_FALSE(query->pending()); +} + +TEST_F(QueryManagerTest, QueryInitialize) { + const GLuint kClient1Id = 1; + const GLuint kService1Id = 11; + const GLenum kTarget = GL_ANY_SAMPLES_PASSED_EXT; + + // Create Query. + QueryManager::Query::Ref query( + manager_->CreateQuery(kClient1Id, kService1Id)); + ASSERT_TRUE(query.get() != NULL); + + query->Initialize(kTarget, kSharedMemoryId, kSharedMemoryOffset); + EXPECT_EQ(kTarget, query->target()); + EXPECT_EQ(kSharedMemoryId, query->shm_id()); + EXPECT_EQ(kSharedMemoryOffset, query->shm_offset()); + + EXPECT_TRUE(query->IsValid()); + EXPECT_FALSE(query->IsDeleted()); + EXPECT_TRUE(query->IsInitialized()); + EXPECT_FALSE(query->pending()); +} + +TEST_F(QueryManagerTest, ProcessPendingQuery) { + const GLuint kClient1Id = 1; + const GLuint kService1Id = 11; + const GLenum kTarget = GL_ANY_SAMPLES_PASSED_EXT; + const uint32 kSubmitCount = 123; + const GLuint kResult = 456; + + // Check nothing happens if there are no pending queries. + EXPECT_TRUE(manager_->ProcessPendingQueries(decoder_.get())); + + // Create Query. + QueryManager::Query::Ref query( + manager_->CreateQuery(kClient1Id, kService1Id)); + ASSERT_TRUE(query.get() != NULL); + query->Initialize(kTarget, kSharedMemoryId, kSharedMemoryOffset); + + // Setup shared memory like client would. + QuerySync* sync = decoder_->GetSharedMemoryAs<QuerySync*>( + kSharedMemoryId, kSharedMemoryOffset, sizeof(*sync)); + ASSERT_TRUE(sync != NULL); + sync->Reset(); + + // Queue it + manager_->AddPendingQuery(query.get(), kSubmitCount); + EXPECT_TRUE(query->pending()); + + // Process with return not available. + // Expect 1 GL command. + EXPECT_CALL(*gl_, + GetQueryObjectuivARB(kService1Id, GL_QUERY_RESULT_AVAILABLE_EXT, _)) + .WillOnce(SetArgumentPointee<2>(0)) + .RetiresOnSaturation(); + EXPECT_TRUE(manager_->ProcessPendingQueries(decoder_.get())); + EXPECT_TRUE(query->pending()); + EXPECT_EQ(0u, sync->process_count); + EXPECT_EQ(0u, sync->result); + + // Process with return available. + // Expect 2 GL commands. + EXPECT_CALL(*gl_, + GetQueryObjectuivARB(kService1Id, GL_QUERY_RESULT_AVAILABLE_EXT, _)) + .WillOnce(SetArgumentPointee<2>(1)) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, + GetQueryObjectuivARB(kService1Id, GL_QUERY_RESULT_EXT, _)) + .WillOnce(SetArgumentPointee<2>(kResult)) + .RetiresOnSaturation(); + EXPECT_TRUE(manager_->ProcessPendingQueries(decoder_.get())); + EXPECT_FALSE(query->pending()); + EXPECT_EQ(kSubmitCount, sync->process_count); + EXPECT_EQ(kResult, sync->result); + + // Process with no queries. + // Expect no GL commands/ + EXPECT_TRUE(manager_->ProcessPendingQueries(decoder_.get())); +} + +TEST_F(QueryManagerTest, ProcessPendingQueries) { + const GLuint kClient1Id = 1; + const GLuint kService1Id = 11; + const GLuint kClient2Id = 2; + const GLuint kService2Id = 12; + const GLuint kClient3Id = 3; + const GLuint kService3Id = 13; + const GLenum kTarget = GL_ANY_SAMPLES_PASSED_EXT; + const uint32 kSubmitCount1 = 123; + const uint32 kSubmitCount2 = 123; + const uint32 kSubmitCount3 = 123; + const GLuint kResult1 = 456; + const GLuint kResult2 = 457; + const GLuint kResult3 = 458; + + // Create Queries. + QueryManager::Query::Ref query1( + manager_->CreateQuery(kClient1Id, kService1Id)); + QueryManager::Query::Ref query2( + manager_->CreateQuery(kClient2Id, kService2Id)); + QueryManager::Query::Ref query3( + manager_->CreateQuery(kClient3Id, kService3Id)); + ASSERT_TRUE(query1.get() != NULL); + ASSERT_TRUE(query2.get() != NULL); + ASSERT_TRUE(query3.get() != NULL); + + // Setup shared memory like client would. + QuerySync* sync1 = decoder_->GetSharedMemoryAs<QuerySync*>( + kSharedMemoryId, kSharedMemoryOffset, sizeof(*sync1) * 3); + ASSERT_TRUE(sync1 != NULL); + QuerySync* sync2 = sync1 + 1; + QuerySync* sync3 = sync2 + 1; + + sync1->Reset(); + sync2->Reset(); + sync3->Reset(); + + query1->Initialize(kTarget, kSharedMemoryId, kSharedMemoryOffset); + query2->Initialize(kTarget, kSharedMemoryId, + kSharedMemoryOffset + sizeof(*sync1)); + query3->Initialize(kTarget, kSharedMemoryId, + kSharedMemoryOffset + sizeof(*sync1) * 2); + + // Queue them + manager_->AddPendingQuery(query1.get(), kSubmitCount1); + manager_->AddPendingQuery(query2.get(), kSubmitCount2); + manager_->AddPendingQuery(query3.get(), kSubmitCount3); + EXPECT_TRUE(query1->pending()); + EXPECT_TRUE(query2->pending()); + EXPECT_TRUE(query3->pending()); + + // Process with return available for first 2 queries. + // Expect 4 GL commands. + { + InSequence s; + EXPECT_CALL(*gl_, + GetQueryObjectuivARB(kService1Id, GL_QUERY_RESULT_AVAILABLE_EXT, _)) + .WillOnce(SetArgumentPointee<2>(1)) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, + GetQueryObjectuivARB(kService1Id, GL_QUERY_RESULT_EXT, _)) + .WillOnce(SetArgumentPointee<2>(kResult1)) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, + GetQueryObjectuivARB(kService2Id, GL_QUERY_RESULT_AVAILABLE_EXT, _)) + .WillOnce(SetArgumentPointee<2>(1)) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, + GetQueryObjectuivARB(kService2Id, GL_QUERY_RESULT_EXT, _)) + .WillOnce(SetArgumentPointee<2>(kResult2)) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, + GetQueryObjectuivARB(kService3Id, GL_QUERY_RESULT_AVAILABLE_EXT, _)) + .WillOnce(SetArgumentPointee<2>(0)) + .RetiresOnSaturation(); + EXPECT_TRUE(manager_->ProcessPendingQueries(decoder_.get())); + } + EXPECT_FALSE(query1->pending()); + EXPECT_FALSE(query2->pending()); + EXPECT_TRUE(query3->pending()); + EXPECT_EQ(kSubmitCount1, sync1->process_count); + EXPECT_EQ(kSubmitCount2, sync2->process_count); + EXPECT_EQ(kResult1, sync1->result); + EXPECT_EQ(kResult2, sync2->result); + EXPECT_EQ(0u, sync3->process_count); + EXPECT_EQ(0u, sync3->result); + + // Process with renaming query. No result. + // Expect 1 GL commands. + EXPECT_CALL(*gl_, + GetQueryObjectuivARB(kService3Id, GL_QUERY_RESULT_AVAILABLE_EXT, _)) + .WillOnce(SetArgumentPointee<2>(0)) + .RetiresOnSaturation(); + EXPECT_TRUE(manager_->ProcessPendingQueries(decoder_.get())); + EXPECT_TRUE(query3->pending()); + EXPECT_EQ(0u, sync3->process_count); + EXPECT_EQ(0u, sync3->result); + + // Process with renaming query. With result. + // Expect 2 GL commands. + EXPECT_CALL(*gl_, + GetQueryObjectuivARB(kService3Id, GL_QUERY_RESULT_AVAILABLE_EXT, _)) + .WillOnce(SetArgumentPointee<2>(1)) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, + GetQueryObjectuivARB(kService3Id, GL_QUERY_RESULT_EXT, _)) + .WillOnce(SetArgumentPointee<2>(kResult3)) + .RetiresOnSaturation(); + EXPECT_TRUE(manager_->ProcessPendingQueries(decoder_.get())); + EXPECT_FALSE(query3->pending()); + EXPECT_EQ(kSubmitCount3, sync3->process_count); + EXPECT_EQ(kResult3, sync3->result); +} + +TEST_F(QueryManagerTest, ProcessPendingBadSharedMemoryId) { + const GLuint kClient1Id = 1; + const GLuint kService1Id = 11; + const GLenum kTarget = GL_ANY_SAMPLES_PASSED_EXT; + const uint32 kSubmitCount = 123; + const GLuint kResult = 456; + + // Create Query. + QueryManager::Query::Ref query( + manager_->CreateQuery(kClient1Id, kService1Id)); + ASSERT_TRUE(query.get() != NULL); + query->Initialize(kTarget, kInvalidSharedMemoryId, kSharedMemoryOffset); + + // Queue it + manager_->AddPendingQuery(query.get(), kSubmitCount); + + // Process with return available. + // Expect 2 GL commands. + EXPECT_CALL(*gl_, + GetQueryObjectuivARB(kService1Id, GL_QUERY_RESULT_AVAILABLE_EXT, _)) + .WillOnce(SetArgumentPointee<2>(1)) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, + GetQueryObjectuivARB(kService1Id, GL_QUERY_RESULT_EXT, _)) + .WillOnce(SetArgumentPointee<2>(kResult)) + .RetiresOnSaturation(); + EXPECT_FALSE(manager_->ProcessPendingQueries(decoder_.get())); +} + +TEST_F(QueryManagerTest, ProcessPendingBadSharedMemoryOffset) { + const GLuint kClient1Id = 1; + const GLuint kService1Id = 11; + const GLenum kTarget = GL_ANY_SAMPLES_PASSED_EXT; + const uint32 kSubmitCount = 123; + const GLuint kResult = 456; + + // Create Query. + QueryManager::Query::Ref query( + manager_->CreateQuery(kClient1Id, kService1Id)); + ASSERT_TRUE(query.get() != NULL); + query->Initialize(kTarget, kSharedMemoryId, kInvalidSharedMemoryOffset); + + // Queue it + manager_->AddPendingQuery(query.get(), kSubmitCount); + + // Process with return available. + // Expect 2 GL commands. + EXPECT_CALL(*gl_, + GetQueryObjectuivARB(kService1Id, GL_QUERY_RESULT_AVAILABLE_EXT, _)) + .WillOnce(SetArgumentPointee<2>(1)) + .RetiresOnSaturation(); + EXPECT_CALL(*gl_, + GetQueryObjectuivARB(kService1Id, GL_QUERY_RESULT_EXT, _)) + .WillOnce(SetArgumentPointee<2>(kResult)) + .RetiresOnSaturation(); + EXPECT_FALSE(manager_->ProcessPendingQueries(decoder_.get())); +} + +TEST_F(QueryManagerTest, ExitWithPendingQuery) { + const GLuint kClient1Id = 1; + const GLuint kService1Id = 11; + const GLenum kTarget = GL_ANY_SAMPLES_PASSED_EXT; + const uint32 kSubmitCount = 123; + + // Create Query. + QueryManager::Query::Ref query( + manager_->CreateQuery(kClient1Id, kService1Id)); + ASSERT_TRUE(query.get() != NULL); + query->Initialize(kTarget, kSharedMemoryId, kSharedMemoryOffset); + + // Queue it + manager_->AddPendingQuery(query.get(), kSubmitCount); +} + +} // namespace gles2 +} // namespace gpu + + diff --git a/gpu/command_buffer_client.gypi b/gpu/command_buffer_client.gypi index c0447ef..f49d780 100644 --- a/gpu/command_buffer_client.gypi +++ b/gpu/command_buffer_client.gypi @@ -20,6 +20,8 @@ 'command_buffer/client/fenced_allocator.h', 'command_buffer/client/mapped_memory.cc', 'command_buffer/client/mapped_memory.h', + 'command_buffer/client/query_tracker.cc', + 'command_buffer/client/query_tracker.h', 'command_buffer/client/ring_buffer.cc', 'command_buffer/client/ring_buffer.h', 'command_buffer/client/transfer_buffer.cc', diff --git a/gpu/command_buffer_service.gypi b/gpu/command_buffer_service.gypi index 9bb7dae..ae3d569 100644 --- a/gpu/command_buffer_service.gypi +++ b/gpu/command_buffer_service.gypi @@ -54,6 +54,8 @@ 'command_buffer/service/mocks.h', 'command_buffer/service/program_manager.h', 'command_buffer/service/program_manager.cc', + 'command_buffer/service/query_manager.h', + 'command_buffer/service/query_manager.cc', 'command_buffer/service/renderbuffer_manager.h', 'command_buffer/service/renderbuffer_manager.cc', 'command_buffer/service/shader_manager.h', diff --git a/gpu/gpu_common.gypi b/gpu/gpu_common.gypi index 43209be..fb2f270 100644 --- a/gpu/gpu_common.gypi +++ b/gpu/gpu_common.gypi @@ -143,6 +143,7 @@ 'command_buffer/client/fenced_allocator_test.cc', 'command_buffer/client/gles2_implementation_unittest.cc', 'command_buffer/client/mapped_memory_unittest.cc', + 'command_buffer/client/query_tracker_unittest.cc', 'command_buffer/client/program_info_manager_unittest.cc', 'command_buffer/client/ring_buffer_test.cc', 'command_buffer/client/transfer_buffer_unittest.cc', @@ -176,6 +177,7 @@ 'command_buffer/service/mocks.cc', 'command_buffer/service/mocks.h', 'command_buffer/service/program_manager_unittest.cc', + 'command_buffer/service/query_manager_unittest.cc', 'command_buffer/service/renderbuffer_manager_unittest.cc', 'command_buffer/service/shader_manager_unittest.cc', 'command_buffer/service/shader_translator_unittest.cc', diff --git a/ppapi/native_client/src/shared/ppapi_proxy/nacl.scons b/ppapi/native_client/src/shared/ppapi_proxy/nacl.scons index 9fb03c4..a58528e 100644 --- a/ppapi/native_client/src/shared/ppapi_proxy/nacl.scons +++ b/ppapi/native_client/src/shared/ppapi_proxy/nacl.scons @@ -34,6 +34,7 @@ command_buffer_client_srcs = [ 'command_buffer/client/program_info_manager.cc', 'command_buffer/client/gles2_lib.cc', 'command_buffer/client/mapped_memory.cc', + 'command_buffer/client/query_tracker.cc', 'command_buffer/client/ring_buffer.cc', 'command_buffer/client/transfer_buffer.cc', 'command_buffer/common/id_allocator.cc', diff --git a/ppapi/native_client/src/shared/ppapi_proxy/ppapi_proxy_untrusted.gyp b/ppapi/native_client/src/shared/ppapi_proxy/ppapi_proxy_untrusted.gyp index 952fec0..f144eb2 100644 --- a/ppapi/native_client/src/shared/ppapi_proxy/ppapi_proxy_untrusted.gyp +++ b/ppapi/native_client/src/shared/ppapi_proxy/ppapi_proxy_untrusted.gyp @@ -37,6 +37,7 @@ '<(DEPTH)/gpu/command_buffer/client/transfer_buffer.cc', '<(DEPTH)/gpu/command_buffer/client/gles2_lib.cc', '<(DEPTH)/gpu/command_buffer/client/mapped_memory.cc', + '<(DEPTH)/gpu/command_buffer/client/query_tracker.cc', '<(DEPTH)/gpu/command_buffer/client/ring_buffer.cc', '<(DEPTH)/gpu/command_buffer/common/id_allocator.cc', diff --git a/ui/gfx/gl/generate_bindings.py b/ui/gfx/gl/generate_bindings.py index 2a255c7..ed80602 100755 --- a/ui/gfx/gl/generate_bindings.py +++ b/ui/gfx/gl/generate_bindings.py @@ -18,7 +18,10 @@ GL_FUNCTIONS = [ 'names': ['glAttachShader'], 'arguments': 'GLuint program, GLuint shader', }, { 'return_type': 'void', - 'names': ['glBeginQuery', 'glBeginQueryEXT', 'glBeginQueryARB'], + 'names': ['glBeginQuery'], + 'arguments': 'GLenum target, GLuint id', }, +{ 'return_type': 'void', + 'names': ['glBeginQueryARB', 'glBeginQueryEXT'], 'arguments': 'GLenum target, GLuint id', }, { 'return_type': 'void', 'names': ['glBindAttribLocation'], @@ -134,7 +137,7 @@ GL_FUNCTIONS = [ 'names': ['glCullFace'], 'arguments': 'GLenum mode', }, { 'return_type': 'void', - 'names': ['glDeleteBuffersARB', 'glDeleteBuffers', 'glDeleteQueriesEXT'], + 'names': ['glDeleteBuffersARB', 'glDeleteBuffers'], 'arguments': 'GLsizei n, const GLuint* buffers', }, { 'return_type': 'void', 'names': ['glDeleteFramebuffersEXT', 'glDeleteFramebuffers'], @@ -146,6 +149,9 @@ GL_FUNCTIONS = [ 'names': ['glDeleteQueries'], 'arguments': 'GLsizei n, const GLuint* ids', }, { 'return_type': 'void', + 'names': ['glDeleteQueriesARB', 'glDeleteQueriesEXT'], + 'arguments': 'GLsizei n, const GLuint* ids', }, +{ 'return_type': 'void', 'names': ['glDeleteRenderbuffersEXT', 'glDeleteRenderbuffers'], 'arguments': 'GLsizei n, const GLuint* renderbuffers', }, { 'return_type': 'void', @@ -201,7 +207,10 @@ GL_FUNCTIONS = [ 'names': ['glEnableVertexAttribArray'], 'arguments': 'GLuint index', }, { 'return_type': 'void', - 'names': ['glEndQuery', 'glEndQueryARB', 'glEndQueryEXT'], + 'names': ['glEndQuery'], + 'arguments': 'GLenum target', }, +{ 'return_type': 'void', + 'names': ['glEndQueryARB', 'glEndQueryEXT'], 'arguments': 'GLenum target', }, { 'return_type': 'void', 'names': ['glFinish'], @@ -226,7 +235,10 @@ GL_FUNCTIONS = [ 'names': ['glGenBuffersARB', 'glGenBuffers'], 'arguments': 'GLsizei n, GLuint* buffers', }, { 'return_type': 'void', - 'names': ['glGenQueries', 'glGenQueriesARB', 'glGenQueriesEXT'], + 'names': ['glGenQueries'], + 'arguments': 'GLsizei n, GLuint* ids', }, +{ 'return_type': 'void', + 'names': ['glGenQueriesARB', 'glGenQueriesEXT'], 'arguments': 'GLsizei n, GLuint* ids', }, { 'return_type': 'void', 'names': ['glGenerateMipmapEXT', 'glGenerateMipmap'], @@ -291,7 +303,10 @@ GL_FUNCTIONS = [ 'arguments': 'GLuint program, GLsizei bufsize, GLsizei* length, char* infolog', }, { 'return_type': 'void', - 'names': ['glGetQueryiv', 'glGetQueryivARB', 'glGetQueryivEXT'], + 'names': ['glGetQueryiv'], + 'arguments': 'GLenum target, GLenum pname, GLint* params', }, +{ 'return_type': 'void', + 'names': ['glGetQueryivARB', 'glGetQueryivEXT'], 'arguments': 'GLenum target, GLenum pname, GLint* params', }, { 'return_type': 'void', 'names': ['glGetQueryObjecti64v'], @@ -303,8 +318,10 @@ GL_FUNCTIONS = [ 'names': ['glGetQueryObjectui64v'], 'arguments': 'GLuint id, GLenum pname, GLuint64* params', }, { 'return_type': 'void', - 'names': ['glGetQueryObjectuiv', 'glGetQueryObjectuivARB', - 'glGetQueryObjectuivEXT'], + 'names': ['glGetQueryObjectuiv'], + 'arguments': 'GLuint id, GLenum pname, GLuint* params', }, +{ 'return_type': 'void', + 'names': ['glGetQueryObjectuivARB', 'glGetQueryObjectuivEXT'], 'arguments': 'GLuint id, GLenum pname, GLuint* params', }, { 'return_type': 'void', 'names': ['glGetRenderbufferParameterivEXT', 'glGetRenderbufferParameteriv'], @@ -377,6 +394,9 @@ GL_FUNCTIONS = [ 'names': ['glIsProgram'], 'arguments': 'GLuint program', }, { 'return_type': 'GLboolean', + 'names': ['glIsQueryARB', 'glIsQueryEXT'], + 'arguments': 'GLuint query', }, +{ 'return_type': 'GLboolean', 'names': ['glIsRenderbufferEXT', 'glIsRenderbuffer'], 'arguments': 'GLuint renderbuffer', }, { 'return_type': 'GLboolean', diff --git a/ui/gfx/gl/gl_interface.h b/ui/gfx/gl/gl_interface.h index 684f9b3..e5f1b46 100644 --- a/ui/gfx/gl/gl_interface.h +++ b/ui/gfx/gl/gl_interface.h @@ -30,6 +30,8 @@ class GL_EXPORT GLInterface { virtual void BeginQuery(GLenum target, GLuint id) = 0; + virtual void BeginQueryARB(GLenum target, GLuint id) = 0; + virtual void BindAttribLocation(GLuint program, GLuint index, const char* name) = 0; @@ -156,6 +158,8 @@ class GL_EXPORT GLInterface { virtual void DeleteQueries(GLsizei n, const GLuint* ids) = 0; + virtual void DeleteQueriesARB(GLsizei n, const GLuint* ids) = 0; + virtual void DeleteShader(GLuint shader) = 0; virtual void DeleteTextures(GLsizei n, const GLuint* textures) = 0; @@ -197,6 +201,8 @@ class GL_EXPORT GLInterface { virtual void EndQuery(GLenum target) = 0; + virtual void EndQueryARB(GLenum target) = 0; + virtual void Finish() = 0; virtual void Flush() = 0; @@ -222,6 +228,8 @@ class GL_EXPORT GLInterface { virtual void GenQueries(GLsizei n, GLuint* ids) = 0; + virtual void GenQueriesARB(GLsizei n, GLuint* ids) = 0; + virtual void GenRenderbuffersEXT(GLsizei n, GLuint* renderbuffers) = 0; virtual void GenTextures(GLsizei n, GLuint* textures) = 0; @@ -276,6 +284,8 @@ class GL_EXPORT GLInterface { virtual void GetQueryiv(GLenum target, GLenum pname, GLint* params) = 0; + virtual void GetQueryivARB(GLenum target, GLenum pname, GLint* params) = 0; + virtual void GetQueryObjecti64v(GLuint id, GLenum pname, GLint64* params) = 0; virtual void GetQueryObjectiv(GLuint id, GLenum pname, GLint* params) = 0; @@ -286,6 +296,10 @@ class GL_EXPORT GLInterface { virtual void GetQueryObjectuiv(GLuint id, GLenum pname, GLuint* params) = 0; + virtual void GetQueryObjectuivARB(GLuint id, + GLenum pname, + GLuint* params) = 0; + virtual void GetRenderbufferParameterivEXT(GLenum target, GLenum pname, GLint* params) = 0; @@ -366,6 +380,8 @@ class GL_EXPORT GLInterface { virtual GLboolean IsProgram(GLuint program) = 0; + virtual GLboolean IsQueryARB(GLuint query) = 0; + virtual GLboolean IsRenderbufferEXT(GLuint renderbuffer) = 0; virtual GLboolean IsShader(GLuint shader) = 0; |