diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index b3e6b6117d..22af011a46 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -245,3 +245,20 @@ void Batch::setFramebuffer(const FramebufferPointer& framebuffer) { } +void Batch::beginQuery(const QueryPointer& query) { + ADD_COMMAND(beginQuery); + + _params.push_back(_queries.cache(query)); +} + +void Batch::endQuery(const QueryPointer& query) { + ADD_COMMAND(endQuery); + + _params.push_back(_queries.cache(query)); +} + +void Batch::getQuery(const QueryPointer& query) { + ADD_COMMAND(getQuery); + + _params.push_back(_queries.cache(query)); +} diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index 0cc1a6bee3..e4cadbb64d 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -18,6 +18,7 @@ #include <vector> +#include "Query.h" #include "Stream.h" #include "Texture.h" @@ -108,6 +109,11 @@ public: // Framebuffer Stage void setFramebuffer(const FramebufferPointer& framebuffer); + // Query Section + void beginQuery(const QueryPointer& query); + void endQuery(const QueryPointer& query); + void getQuery(const QueryPointer& query); + // TODO: As long as we have gl calls explicitely issued from interface // code, we need to be able to record and batch these calls. THe long // term strategy is to get rid of any GL calls in favor of the HIFI GPU API @@ -176,6 +182,10 @@ public: COMMAND_setFramebuffer, + COMMAND_beginQuery, + COMMAND_endQuery, + COMMAND_getQuery, + // TODO: As long as we have gl calls explicitely issued from interface // code, we need to be able to record and batch these calls. THe long // term strategy is to get rid of any GL calls in favor of the HIFI GPU API @@ -279,6 +289,7 @@ public: typedef Cache<Transform>::Vector TransformCaches; typedef Cache<PipelinePointer>::Vector PipelineCaches; typedef Cache<FramebufferPointer>::Vector FramebufferCaches; + typedef Cache<QueryPointer>::Vector QueryCaches; // Cache Data in a byte array if too big to fit in Param // FOr example Mat4s are going there @@ -303,6 +314,7 @@ public: TransformCaches _transforms; PipelineCaches _pipelines; FramebufferCaches _framebuffers; + QueryCaches _queries; protected: }; diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index 2207575274..4eb0976e3c 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -99,6 +99,15 @@ public: return reinterpret_cast<T*>(framebuffer.getGPUObject()); } + template< typename T > + static void setGPUObject(const Query& query, T* object) { + query.setGPUObject(object); + } + template< typename T > + static T* getGPUObject(const Query& query) { + return reinterpret_cast<T*>(query.getGPUObject()); + } + protected: }; diff --git a/libraries/gpu/src/gpu/GLBackend.cpp b/libraries/gpu/src/gpu/GLBackend.cpp index 54a54ce2a5..d493978b88 100644 --- a/libraries/gpu/src/gpu/GLBackend.cpp +++ b/libraries/gpu/src/gpu/GLBackend.cpp @@ -39,6 +39,9 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::GLBackend::do_setFramebuffer), + (&::gpu::GLBackend::do_beginQuery), + (&::gpu::GLBackend::do_endQuery), + (&::gpu::GLBackend::do_getQuery), (&::gpu::GLBackend::do_glEnable), (&::gpu::GLBackend::do_glDisable), @@ -261,7 +264,6 @@ void GLBackend::do_clearFramebuffer(Batch& batch, uint32 paramOffset) { (void) CHECK_GL_ERROR(); } - // TODO: As long as we have gl calls explicitely issued from interface // code, we need to be able to record and batch these calls. THe long // term strategy is to get rid of any GL calls in favor of the HIFI GPU API diff --git a/libraries/gpu/src/gpu/GLBackend.h b/libraries/gpu/src/gpu/GLBackend.h index 78b0f0838e..16e2ed62e3 100644 --- a/libraries/gpu/src/gpu/GLBackend.h +++ b/libraries/gpu/src/gpu/GLBackend.h @@ -189,6 +189,18 @@ public: static GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer); static GLuint getFramebufferID(const FramebufferPointer& framebuffer); + class GLQuery : public GPUObject { + public: + GLuint _qo = 0; + GLuint64 _result = 0; + + GLQuery(); + ~GLQuery(); + }; + static GLQuery* syncGPUObject(const Query& query); + static GLuint getQueryID(const QueryPointer& query); + + static const int MAX_NUM_ATTRIBUTES = Stream::NUM_INPUT_SLOTS; static const int MAX_NUM_INPUT_BUFFERS = 16; @@ -369,6 +381,11 @@ protected: OutputStageState() {} } _output; + // Query section + void do_beginQuery(Batch& batch, uint32 paramOffset); + void do_endQuery(Batch& batch, uint32 paramOffset); + void do_getQuery(Batch& batch, uint32 paramOffset); + // TODO: As long as we have gl calls explicitely issued from interface // code, we need to be able to record and batch these calls. THe long // term strategy is to get rid of any GL calls in favor of the HIFI GPU API diff --git a/libraries/gpu/src/gpu/GLBackendOutput.cpp b/libraries/gpu/src/gpu/GLBackendOutput.cpp index 30b60ad183..1a7c5d2281 100755 --- a/libraries/gpu/src/gpu/GLBackendOutput.cpp +++ b/libraries/gpu/src/gpu/GLBackendOutput.cpp @@ -166,4 +166,3 @@ void GLBackend::do_setFramebuffer(Batch& batch, uint32 paramOffset) { _output._framebuffer = framebuffer; } } - diff --git a/libraries/gpu/src/gpu/GLBackendQuery.cpp b/libraries/gpu/src/gpu/GLBackendQuery.cpp new file mode 100644 index 0000000000..39db19dafd --- /dev/null +++ b/libraries/gpu/src/gpu/GLBackendQuery.cpp @@ -0,0 +1,106 @@ +// +// GLBackendQuery.cpp +// libraries/gpu/src/gpu +// +// Created by Sam Gateau on 7/7/2015. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GPULogging.h" +#include "GLBackendShared.h" + + +using namespace gpu; + +GLBackend::GLQuery::GLQuery() {} + +GLBackend::GLQuery::~GLQuery() { + if (_qo != 0) { + glDeleteQueries(1, &_qo); + } +} + +GLBackend::GLQuery* GLBackend::syncGPUObject(const Query& query) { + GLQuery* object = Backend::getGPUObject<GLBackend::GLQuery>(query); + + // If GPU object already created and in sync + if (object) { + return object; + } + + // need to have a gpu object? + if (!object) { + GLuint qo; + glGenQueries(1, &qo); + (void) CHECK_GL_ERROR(); + GLuint64 result = -1; + + // All is green, assign the gpuobject to the Query + object = new GLQuery(); + object->_qo = qo; + object->_result = result; + Backend::setGPUObject(query, object); + } + + return object; +} + + + +GLuint GLBackend::getQueryID(const QueryPointer& query) { + if (!query) { + return 0; + } + GLQuery* object = GLBackend::syncGPUObject(*query); + if (object) { + return object->_qo; + } else { + return 0; + } +} + +void GLBackend::do_beginQuery(Batch& batch, uint32 paramOffset) { + auto query = batch._queries.get(batch._params[paramOffset]._uint); + GLQuery* glquery = syncGPUObject(*query); + if (glquery) { + #if (GPU_FEATURE_PROFILE == GPU_LEGACY) + // (EXT_TIMER_QUERY) + glBeginQuery(GL_TIME_ELAPSED_EXT, glquery->_qo); + #else + glBeginQuery(GL_TIME_ELAPSED, glquery->_qo); + #endif + (void)CHECK_GL_ERROR(); + } +} + +void GLBackend::do_endQuery(Batch& batch, uint32 paramOffset) { + auto query = batch._queries.get(batch._params[paramOffset]._uint); + GLQuery* glquery = syncGPUObject(*query); + if (glquery) { + #if (GPU_FEATURE_PROFILE == GPU_LEGACY) + // (EXT_TIMER_QUERY) + glEndQuery(GL_TIME_ELAPSED_EXT); + #else + glEndQuery(GL_TIME_ELAPSED); + #endif + (void)CHECK_GL_ERROR(); + } +} + +void GLBackend::do_getQuery(Batch& batch, uint32 paramOffset) { + auto query = batch._queries.get(batch._params[paramOffset]._uint); + GLQuery* glquery = syncGPUObject(*query); + if (glquery) { + #if (GPU_FEATURE_PROFILE == GPU_LEGACY) + // (EXT_TIMER_QUERY) + #if !defined(Q_OS_LINUX) + glGetQueryObjectui64vEXT(glquery->_qo, GL_QUERY_RESULT, &glquery->_result); + #endif + #else + glGetQueryObjectui64v(glquery->_qo, GL_QUERY_RESULT, &glquery->_result); + #endif + (void)CHECK_GL_ERROR(); + } +} diff --git a/libraries/gpu/src/gpu/Query.cpp b/libraries/gpu/src/gpu/Query.cpp new file mode 100644 index 0000000000..b8ed729c99 --- /dev/null +++ b/libraries/gpu/src/gpu/Query.cpp @@ -0,0 +1,27 @@ +// +// Query.cpp +// interface/src/gpu +// +// Created by Niraj Venkat on 7/7/2015. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "Query.h" + +#include <QDebug> + +using namespace gpu; + +Query::Query() +{ +} + +Query::~Query() +{ +} + +double Query::getElapsedTime() { + return 0.0; +} diff --git a/libraries/gpu/src/gpu/Query.h b/libraries/gpu/src/gpu/Query.h new file mode 100644 index 0000000000..0a4d554e77 --- /dev/null +++ b/libraries/gpu/src/gpu/Query.h @@ -0,0 +1,45 @@ +// +// Query.h +// interface/src/gpu +// +// Created by Niraj Venkat on 7/7/2015. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_gpu_Query_h +#define hifi_gpu_Query_h + +#include <assert.h> +#include <memory> +#include <vector> +#include "GPUConfig.h" + +#include "Format.h" + +namespace gpu { + + class Query { + public: + Query(); + ~Query(); + + uint32 queryResult; + + double getElapsedTime(); + + protected: + + // This shouldn't be used by anything else than the Backend class with the proper casting. + mutable GPUObject* _gpuObject = NULL; + void setGPUObject(GPUObject* gpuObject) const { _gpuObject = gpuObject; } + GPUObject* getGPUObject() const { return _gpuObject; } + friend class Backend; + }; + + typedef std::shared_ptr<Query> QueryPointer; + typedef std::vector< QueryPointer > Queries; +}; + +#endif diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 8de5c8af01..55f4f72574 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -76,6 +76,12 @@ RenderDeferredTask::RenderDeferredTask() : Task() { _jobs.push_back(Job(new DrawOverlay3D::JobModel("DrawOverlay3D"))); _jobs.push_back(Job(new ResetGLState::JobModel())); + + // Give ourselves 3 frmaes of timer queries + _timerQueries.push_back(gpu::QueryPointer(new gpu::Query())); + _timerQueries.push_back(gpu::QueryPointer(new gpu::Query())); + _timerQueries.push_back(gpu::QueryPointer(new gpu::Query())); + _currentTimerQueryIndex = 0; } RenderDeferredTask::~RenderDeferredTask() { @@ -102,6 +108,7 @@ void RenderDeferredTask::run(const SceneContextPointer& sceneContext, const Rend for (auto job : _jobs) { job.run(sceneContext, renderContext); } + }; void DrawOpaqueDeferred::run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext, const ItemIDsBounds& inItems) { diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index 3d11e97634..4040606c62 100755 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -77,6 +77,9 @@ public: virtual void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); + + gpu::Queries _timerQueries; + int _currentTimerQueryIndex = 0; };