Commit 292f005f by Ian Ewell Committed by Commit Bot

Fix context virtualization for timer queries

In the current implementation of query virtualization, queries are paused/resumed during draw calls. Timer queries however are affected by every OpenGL call, so virtualization and context switches for timer queries must happen every time there is a context switch. Thus the logic for context-switching queries was moved to a new function in the GL state manager that is called everytime a makeCurrent call on the context is made. Since queries may be made after a context is made current, the state manager needs to keep track of any new queries that are started in a context, so an additional delegate function was added to the state manager that is called every time a glBeginQuery() call is made that adds the query object to a set. All the queries in that set are paused when a context switch is made and the queries in the new context are then loaded and resumed. BUG=angleproject:1307 Change-Id: I4e596d83739274cb2e14152a39e86e0e51b0f95c Reviewed-on: https://chromium-review.googlesource.com/325811Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Ian Ewell <ewell@google.com>
parent a78d12c5
...@@ -316,6 +316,9 @@ void Context::makeCurrent(egl::Surface *surface) ...@@ -316,6 +316,9 @@ void Context::makeCurrent(egl::Surface *surface)
} }
mFramebufferMap[0] = newDefault; mFramebufferMap[0] = newDefault;
} }
// Notify the renderer of a context switch
mRenderer->onMakeCurrent(getData());
} }
void Context::releaseSurface() void Context::releaseSurface()
......
...@@ -91,9 +91,13 @@ class Renderer : public ImplFactory ...@@ -91,9 +91,13 @@ class Renderer : public ImplFactory
virtual void syncState(const gl::State &state, const gl::State::DirtyBits &dirtyBits) = 0; virtual void syncState(const gl::State &state, const gl::State::DirtyBits &dirtyBits) = 0;
// Disjoint timer queries
virtual GLint getGPUDisjoint() = 0; virtual GLint getGPUDisjoint() = 0;
virtual GLint64 getTimestamp() = 0; virtual GLint64 getTimestamp() = 0;
// Context switching
virtual void onMakeCurrent(const gl::Data &data) = 0;
// Renderer capabilities // Renderer capabilities
const gl::Caps &getRendererCaps() const; const gl::Caps &getRendererCaps() const;
const gl::TextureCapsMap &getRendererTextureCaps() const; const gl::TextureCapsMap &getRendererTextureCaps() const;
......
...@@ -686,6 +686,10 @@ GLint64 RendererD3D::getTimestamp() ...@@ -686,6 +686,10 @@ GLint64 RendererD3D::getTimestamp()
return 0; return 0;
} }
void RendererD3D::onMakeCurrent(const gl::Data &data)
{
}
void RendererD3D::initializeDebugAnnotator() void RendererD3D::initializeDebugAnnotator()
{ {
createAnnotator(); createAnnotator();
......
...@@ -246,6 +246,8 @@ class RendererD3D : public Renderer, public BufferFactoryD3D ...@@ -246,6 +246,8 @@ class RendererD3D : public Renderer, public BufferFactoryD3D
GLint getGPUDisjoint() override; GLint getGPUDisjoint() override;
GLint64 getTimestamp() override; GLint64 getTimestamp() override;
void onMakeCurrent(const gl::Data &data) override;
// In D3D11, faster than calling setTexture a jillion times // In D3D11, faster than calling setTexture a jillion times
virtual gl::Error clearTextures(gl::SamplerType samplerType, size_t rangeStart, size_t rangeEnd) = 0; virtual gl::Error clearTextures(gl::SamplerType samplerType, size_t rangeStart, size_t rangeEnd) = 0;
......
...@@ -68,7 +68,8 @@ QueryGL::~QueryGL() ...@@ -68,7 +68,8 @@ QueryGL::~QueryGL()
gl::Error QueryGL::begin() gl::Error QueryGL::begin()
{ {
mResultSum = 0; mResultSum = 0;
return gl::Error(GL_NO_ERROR); mStateManager->onBeginQuery(this);
return resume();
} }
gl::Error QueryGL::end() gl::Error QueryGL::end()
......
...@@ -429,4 +429,10 @@ GLint64 RendererGL::getTimestamp() ...@@ -429,4 +429,10 @@ GLint64 RendererGL::getTimestamp()
mFunctions->getInteger64v(GL_TIMESTAMP, &result); mFunctions->getInteger64v(GL_TIMESTAMP, &result);
return result; return result;
} }
void RendererGL::onMakeCurrent(const gl::Data &data)
{
// Queries need to be paused/resumed on context switches
mStateManager->onMakeCurrent(data);
}
} }
...@@ -107,6 +107,8 @@ class RendererGL : public Renderer ...@@ -107,6 +107,8 @@ class RendererGL : public Renderer
GLint getGPUDisjoint() override; GLint getGPUDisjoint() override;
GLint64 getTimestamp() override; GLint64 getTimestamp() override;
void onMakeCurrent(const gl::Data &data) override;
const gl::Version &getMaxSupportedESVersion() const; const gl::Version &getMaxSupportedESVersion() const;
const FunctionsGL *getFunctions() const { return mFunctions; } const FunctionsGL *getFunctions() const { return mFunctions; }
StateManagerGL *getStateManager() const { return mStateManager; } StateManagerGL *getStateManager() const { return mStateManager; }
......
...@@ -47,7 +47,7 @@ StateManagerGL::StateManagerGL(const FunctionsGL *functions, const gl::Caps &ren ...@@ -47,7 +47,7 @@ StateManagerGL::StateManagerGL(const FunctionsGL *functions, const gl::Caps &ren
mTransformFeedback(0), mTransformFeedback(0),
mQueries(), mQueries(),
mPrevDrawTransformFeedback(nullptr), mPrevDrawTransformFeedback(nullptr),
mPrevDrawQueries(), mCurrentQueries(),
mPrevDrawContext(0), mPrevDrawContext(0),
mUnpackAlignment(4), mUnpackAlignment(4),
mUnpackRowLength(0), mUnpackRowLength(0),
...@@ -577,9 +577,14 @@ void StateManagerGL::endQuery(GLenum type, GLuint query) ...@@ -577,9 +577,14 @@ void StateManagerGL::endQuery(GLenum type, GLuint query)
mFunctions->endQuery(type); mFunctions->endQuery(type);
} }
void StateManagerGL::onBeginQuery(QueryGL *query)
{
mCurrentQueries.insert(query);
}
void StateManagerGL::onDeleteQueryObject(QueryGL *query) void StateManagerGL::onDeleteQueryObject(QueryGL *query)
{ {
mPrevDrawQueries.erase(query); mCurrentQueries.erase(query);
} }
gl::Error StateManagerGL::setDrawArraysState(const gl::Data &data, gl::Error StateManagerGL::setDrawArraysState(const gl::Data &data,
...@@ -633,7 +638,7 @@ gl::Error StateManagerGL::setDrawElementsState(const gl::Data &data, ...@@ -633,7 +638,7 @@ gl::Error StateManagerGL::setDrawElementsState(const gl::Data &data,
return setGenericDrawState(data); return setGenericDrawState(data);
} }
gl::Error StateManagerGL::setGenericDrawState(const gl::Data &data) gl::Error StateManagerGL::onMakeCurrent(const gl::Data &data)
{ {
const gl::State &state = *data.state; const gl::State &state = *data.state;
...@@ -645,16 +650,35 @@ gl::Error StateManagerGL::setGenericDrawState(const gl::Data &data) ...@@ -645,16 +650,35 @@ gl::Error StateManagerGL::setGenericDrawState(const gl::Data &data)
mPrevDrawTransformFeedback->syncPausedState(true); mPrevDrawTransformFeedback->syncPausedState(true);
} }
for (QueryGL *prevQuery : mPrevDrawQueries) for (QueryGL *prevQuery : mCurrentQueries)
{ {
prevQuery->pause(); prevQuery->pause();
} }
} }
mCurrentQueries.clear();
mPrevDrawTransformFeedback = nullptr; mPrevDrawTransformFeedback = nullptr;
mPrevDrawQueries.clear();
mPrevDrawContext = data.context; mPrevDrawContext = data.context;
// Set the current query state
for (GLenum queryType : QueryTypes)
{
gl::Query *query = state.getActiveQuery(queryType);
if (query != nullptr)
{
QueryGL *queryGL = GetImplAs<QueryGL>(query);
queryGL->resume();
mCurrentQueries.insert(queryGL);
}
}
return gl::Error(GL_NO_ERROR);
}
gl::Error StateManagerGL::setGenericDrawState(const gl::Data &data)
{
const gl::State &state = *data.state;
// Sync the current program state // Sync the current program state
const gl::Program *program = state.getProgram(); const gl::Program *program = state.getProgram();
const ProgramGL *programGL = GetImplAs<ProgramGL>(program); const ProgramGL *programGL = GetImplAs<ProgramGL>(program);
...@@ -751,19 +775,6 @@ gl::Error StateManagerGL::setGenericDrawState(const gl::Data &data) ...@@ -751,19 +775,6 @@ gl::Error StateManagerGL::setGenericDrawState(const gl::Data &data)
mPrevDrawTransformFeedback = nullptr; mPrevDrawTransformFeedback = nullptr;
} }
// Set the current query state
for (GLenum queryType : QueryTypes)
{
gl::Query *query = state.getActiveQuery(queryType);
if (query != nullptr)
{
QueryGL *queryGL = GetImplAs<QueryGL>(query);
queryGL->resume();
mPrevDrawQueries.insert(queryGL);
}
}
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
......
...@@ -59,6 +59,7 @@ class StateManagerGL final : angle::NonCopyable ...@@ -59,6 +59,7 @@ class StateManagerGL final : angle::NonCopyable
void bindTransformFeedback(GLenum type, GLuint transformFeedback); void bindTransformFeedback(GLenum type, GLuint transformFeedback);
void beginQuery(GLenum type, GLuint query); void beginQuery(GLenum type, GLuint query);
void endQuery(GLenum type, GLuint query); void endQuery(GLenum type, GLuint query);
void onBeginQuery(QueryGL *query);
void setAttributeCurrentData(size_t index, const gl::VertexAttribCurrentValueData &data); void setAttributeCurrentData(size_t index, const gl::VertexAttribCurrentValueData &data);
...@@ -135,6 +136,8 @@ class StateManagerGL final : angle::NonCopyable ...@@ -135,6 +136,8 @@ class StateManagerGL final : angle::NonCopyable
GLsizei instanceCount, GLsizei instanceCount,
const GLvoid **outIndices); const GLvoid **outIndices);
gl::Error onMakeCurrent(const gl::Data &data);
void syncState(const gl::State &state, const gl::State::DirtyBits &glDirtyBits); void syncState(const gl::State &state, const gl::State::DirtyBits &glDirtyBits);
private: private:
...@@ -170,7 +173,7 @@ class StateManagerGL final : angle::NonCopyable ...@@ -170,7 +173,7 @@ class StateManagerGL final : angle::NonCopyable
std::map<GLenum, GLuint> mQueries; std::map<GLenum, GLuint> mQueries;
TransformFeedbackGL *mPrevDrawTransformFeedback; TransformFeedbackGL *mPrevDrawTransformFeedback;
std::set<QueryGL *> mPrevDrawQueries; std::set<QueryGL *> mCurrentQueries;
uintptr_t mPrevDrawContext; uintptr_t mPrevDrawContext;
GLint mUnpackAlignment; GLint mUnpackAlignment;
......
...@@ -164,6 +164,75 @@ TEST_P(TimerQueriesTest, TimeElapsed) ...@@ -164,6 +164,75 @@ TEST_P(TimerQueriesTest, TimeElapsed)
EXPECT_LT(result1, result2); EXPECT_LT(result1, result2);
} }
// Tests time elapsed for a non draw call (texture upload)
TEST_P(TimerQueriesTest, TimeElapsedTextureTest)
{
// OSX drivers don't seem to properly time non-draw calls so we skip the test on Mac
if (isOSX())
{
std::cout << "Test skipped on OSX" << std::endl;
return;
}
if (!extensionEnabled("GL_EXT_disjoint_timer_query"))
{
std::cout << "Test skipped because GL_EXT_disjoint_timer_query is not available."
<< std::endl;
return;
}
GLint queryTimeElapsedBits = 0;
glGetQueryivEXT(GL_TIME_ELAPSED_EXT, GL_QUERY_COUNTER_BITS_EXT, &queryTimeElapsedBits);
ASSERT_GL_NO_ERROR();
std::cout << "Time elapsed counter bits: " << queryTimeElapsedBits << std::endl;
// Skip test if the number of bits is 0
if (queryTimeElapsedBits == 0)
{
std::cout << "Test skipped because of 0 counter bits" << std::endl;
return;
}
GLubyte pixels[] = {0, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 0};
// Query and texture initialization
GLuint texture;
GLuint query = 0;
glGenQueriesEXT(1, &query);
glGenTextures(1, &texture);
// Upload a texture inside the query
glBeginQueryEXT(GL_TIME_ELAPSED_EXT, query);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);
glGenerateMipmap(GL_TEXTURE_2D);
glFinish();
glEndQueryEXT(GL_TIME_ELAPSED_EXT);
ASSERT_GL_NO_ERROR();
int timeout = 200000;
GLuint ready = GL_FALSE;
while (ready == GL_FALSE && timeout > 0)
{
angle::Sleep(0);
glGetQueryObjectuivEXT(query, GL_QUERY_RESULT_AVAILABLE_EXT, &ready);
timeout--;
}
ASSERT_LT(0, timeout) << "Query result available timed out" << std::endl;
GLuint64 result = 0;
glGetQueryObjectui64vEXT(query, GL_QUERY_RESULT_EXT, &result);
ASSERT_GL_NO_ERROR();
glDeleteTextures(1, &texture);
glDeleteQueriesEXT(1, &query);
std::cout << "Elapsed time: " << result << std::endl;
EXPECT_LT(0ul, result);
}
// Tests validation of query functions with respect to elapsed time query // Tests validation of query functions with respect to elapsed time query
TEST_P(TimerQueriesTest, TimeElapsedValidationTest) TEST_P(TimerQueriesTest, TimeElapsedValidationTest)
{ {
...@@ -216,6 +285,181 @@ TEST_P(TimerQueriesTest, TimeElapsedValidationTest) ...@@ -216,6 +285,181 @@ TEST_P(TimerQueriesTest, TimeElapsedValidationTest)
EXPECT_GL_ERROR(GL_INVALID_OPERATION); EXPECT_GL_ERROR(GL_INVALID_OPERATION);
} }
// Tests timer queries operating under multiple EGL contexts with mid-query switching
TEST_P(TimerQueriesTest, TimeElapsedMulticontextTest)
{
if (!extensionEnabled("GL_EXT_disjoint_timer_query"))
{
std::cout << "Test skipped because GL_EXT_disjoint_timer_query is not available."
<< std::endl;
return;
}
GLint queryTimeElapsedBits = 0;
glGetQueryivEXT(GL_TIME_ELAPSED_EXT, GL_QUERY_COUNTER_BITS_EXT, &queryTimeElapsedBits);
ASSERT_GL_NO_ERROR();
std::cout << "Time elapsed counter bits: " << queryTimeElapsedBits << std::endl;
// Skip test if the number of bits is 0
if (queryTimeElapsedBits == 0)
{
std::cout << "Test skipped because of 0 counter bits" << std::endl;
return;
}
// D3D multicontext isn't implemented yet
if (GetParam() == ES3_D3D11() || GetParam() == ES2_D3D11())
{
std::cout
<< "Test skipped because the D3D backends cannot support simultaneous timer queries yet"
<< std::endl;
return;
}
EGLint contextAttributes[] = {
EGL_CONTEXT_MAJOR_VERSION_KHR,
GetParam().majorVersion,
EGL_CONTEXT_MINOR_VERSION_KHR,
GetParam().minorVersion,
EGL_NONE,
};
EGLWindow *window = getEGLWindow();
EGLDisplay display = window->getDisplay();
EGLConfig config = window->getConfig();
EGLSurface surface = window->getSurface();
struct ContextInfo
{
EGLContext context;
GLuint program;
GLuint query;
EGLDisplay display;
ContextInfo() : context(EGL_NO_CONTEXT), program(0), query(0), display(EGL_NO_DISPLAY) {}
~ContextInfo()
{
if (context != EGL_NO_CONTEXT && display != EGL_NO_DISPLAY)
{
eglDestroyContext(display, context);
}
}
};
ContextInfo contexts[2];
// Shaders
const std::string cheapVS =
"attribute highp vec4 position; void main(void)\n"
"{\n"
" gl_Position = position;\n"
"}\n";
const std::string cheapPS =
"precision highp float; void main(void)\n"
"{\n"
" gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);\n"
"}\n";
const std::string costlyVS =
"attribute highp vec4 position; varying highp vec4 testPos; void main(void)\n"
"{\n"
" testPos = position;\n"
" gl_Position = position;\n"
"}\n";
const std::string costlyPS =
"precision highp float; varying highp vec4 testPos; void main(void)\n"
"{\n"
" vec4 test = testPos;\n"
" for (int i = 0; i < 500; i++)\n"
" {\n"
" test = sqrt(test);\n"
" }\n"
" gl_FragColor = test;\n"
"}\n";
// Setup the first context with a cheap shader
contexts[0].context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttributes);
contexts[0].display = display;
ASSERT_NE(contexts[0].context, EGL_NO_CONTEXT);
eglMakeCurrent(display, surface, surface, contexts[0].context);
contexts[0].program = CompileProgram(cheapVS, cheapPS);
glGenQueriesEXT(1, &contexts[0].query);
ASSERT_GL_NO_ERROR();
// Setup the second context with an expensive shader
contexts[1].context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttributes);
contexts[1].display = display;
ASSERT_NE(contexts[1].context, EGL_NO_CONTEXT);
eglMakeCurrent(display, surface, surface, contexts[1].context);
contexts[1].program = CompileProgram(costlyVS, costlyPS);
glGenQueriesEXT(1, &contexts[1].query);
ASSERT_GL_NO_ERROR();
// Start the query and draw a quad on the first context without ending the query
eglMakeCurrent(display, surface, surface, contexts[0].context);
glBeginQueryEXT(GL_TIME_ELAPSED_EXT, contexts[0].query);
drawQuad(contexts[0].program, "position", 0.8f);
ASSERT_GL_NO_ERROR();
// Switch contexts, draw the expensive quad and end its query
eglMakeCurrent(display, surface, surface, contexts[1].context);
glBeginQueryEXT(GL_TIME_ELAPSED_EXT, contexts[1].query);
drawQuad(contexts[1].program, "position", 0.8f);
glEndQueryEXT(GL_TIME_ELAPSED_EXT);
ASSERT_GL_NO_ERROR();
// Go back to the first context, end its query, and get the result
eglMakeCurrent(display, surface, surface, contexts[0].context);
glEndQueryEXT(GL_TIME_ELAPSED_EXT);
int timeout = 20000;
GLuint ready = GL_FALSE;
while (ready == GL_FALSE && timeout > 0)
{
angle::Sleep(0);
glGetQueryObjectuivEXT(contexts[0].query, GL_QUERY_RESULT_AVAILABLE_EXT, &ready);
timeout--;
}
ASSERT_LT(0, timeout) << "Query result available timed out" << std::endl;
GLuint64 result1 = 0;
GLuint64 result2 = 0;
glGetQueryObjectui64vEXT(contexts[0].query, GL_QUERY_RESULT_EXT, &result1);
glDeleteQueriesEXT(1, &contexts[0].query);
glDeleteProgram(contexts[0].program);
ASSERT_GL_NO_ERROR();
// Get the 2nd contexts results
eglMakeCurrent(display, surface, surface, contexts[1].context);
timeout = 20000;
ready = GL_FALSE;
while (ready == GL_FALSE && timeout > 0)
{
angle::Sleep(0);
glGetQueryObjectuivEXT(contexts[1].query, GL_QUERY_RESULT_AVAILABLE_EXT, &ready);
timeout--;
}
ASSERT_LT(0, timeout) << "Query result available timed out" << std::endl;
glGetQueryObjectui64vEXT(contexts[1].query, GL_QUERY_RESULT_EXT, &result2);
glDeleteQueriesEXT(1, &contexts[1].query);
glDeleteProgram(contexts[1].program);
ASSERT_GL_NO_ERROR();
// Switch back to main context
eglMakeCurrent(display, surface, surface, window->getContext());
// Compare the results. The cheap quad should be smaller than the expensive one if
// virtualization is working correctly
std::cout << "Elapsed time: " << result1 << " cheap quad" << std::endl;
std::cout << "Elapsed time: " << result2 << " costly quad" << std::endl;
EXPECT_LT(0ul, result1);
EXPECT_LT(0ul, result2);
EXPECT_LT(result1, result2);
}
// Tests GPU timestamp functionality // Tests GPU timestamp functionality
TEST_P(TimerQueriesTest, Timestamp) TEST_P(TimerQueriesTest, Timestamp)
{ {
......
...@@ -463,6 +463,15 @@ bool ANGLETest::isD3DSM3() const ...@@ -463,6 +463,15 @@ bool ANGLETest::isD3DSM3() const
return isD3D9() || isD3D11_FL93(); return isD3D9() || isD3D11_FL93();
} }
bool ANGLETest::isOSX() const
{
#ifdef __APPLE__
return true;
#else
return false;
#endif
}
EGLint ANGLETest::getPlatformRenderer() const EGLint ANGLETest::getPlatformRenderer() const
{ {
assert(mEGLWindow); assert(mEGLWindow);
......
...@@ -142,6 +142,7 @@ class ANGLETest : public ::testing::TestWithParam<angle::PlatformParameters> ...@@ -142,6 +142,7 @@ class ANGLETest : public ::testing::TestWithParam<angle::PlatformParameters>
bool isD3D9() const; bool isD3D9() const;
// Is D3D9 or SM9_3 renderer. // Is D3D9 or SM9_3 renderer.
bool isD3DSM3() const; bool isD3DSM3() const;
bool isOSX() const;
EGLint getPlatformRenderer() const; EGLint getPlatformRenderer() const;
void ignoreD3D11SDKLayersWarnings(); void ignoreD3D11SDKLayersWarnings();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment