Commit f0aa8429 by Geoff Lang

Implement QueryGL.

Passes all tests in: * dEQP-GLES3.functional.occlusion_query * angle_end2end_tests BUG=angleproject:887 Change-Id: I643ab4c28cb545de9e7b0e1740e3fd8e2aa9d3d9 Reviewed-on: https://chromium-review.googlesource.com/302338Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Tryjob-Request: Geoff Lang <geofflang@chromium.org> Tested-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent 56627322
...@@ -66,7 +66,13 @@ Context::Context(const egl::Config *config, ...@@ -66,7 +66,13 @@ Context::Context(const egl::Config *config,
: mRenderer(renderer), : mRenderer(renderer),
mConfig(config), mConfig(config),
mCurrentSurface(nullptr), mCurrentSurface(nullptr),
mData(clientVersion, mState, mCaps, mTextureCaps, mExtensions, nullptr) mData(reinterpret_cast<uintptr_t>(this),
clientVersion,
mState,
mCaps,
mTextureCaps,
mExtensions,
nullptr)
{ {
ASSERT(robustAccess == false); // Unimplemented ASSERT(robustAccess == false); // Unimplemented
...@@ -168,7 +174,10 @@ Context::~Context() ...@@ -168,7 +174,10 @@ Context::~Context()
for (auto query : mQueryMap) for (auto query : mQueryMap)
{ {
query.second->release(); if (query.second != nullptr)
{
query.second->release();
}
} }
for (auto vertexArray : mVertexArrayMap) for (auto vertexArray : mVertexArrayMap)
......
...@@ -12,10 +12,15 @@ ...@@ -12,10 +12,15 @@
namespace gl namespace gl
{ {
Data::Data(GLint clientVersionIn, const State &stateIn, const Caps &capsIn, Data::Data(uintptr_t contextIn,
const TextureCapsMap &textureCapsIn, const Extensions &extensionsIn, GLint clientVersionIn,
const State &stateIn,
const Caps &capsIn,
const TextureCapsMap &textureCapsIn,
const Extensions &extensionsIn,
const ResourceManager *resourceManagerIn) const ResourceManager *resourceManagerIn)
: clientVersion(clientVersionIn), : context(contextIn),
clientVersion(clientVersionIn),
state(&stateIn), state(&stateIn),
caps(&capsIn), caps(&capsIn),
textureCaps(&textureCapsIn), textureCaps(&textureCapsIn),
...@@ -27,25 +32,4 @@ Data::~Data() ...@@ -27,25 +32,4 @@ Data::~Data()
{ {
} }
Data::Data(const Data &other)
: clientVersion(other.clientVersion),
state(other.state),
caps(other.caps),
textureCaps(other.textureCaps),
extensions(other.extensions),
resourceManager(other.resourceManager)
{
}
Data &Data::operator=(const Data &other)
{
clientVersion = other.clientVersion;
state = other.state;
caps = other.caps;
textureCaps = other.textureCaps;
extensions = other.extensions;
resourceManager = other.resourceManager;
return *this;
}
} }
...@@ -9,22 +9,25 @@ ...@@ -9,22 +9,25 @@
#ifndef LIBANGLE_DATA_H_ #ifndef LIBANGLE_DATA_H_
#define LIBANGLE_DATA_H_ #define LIBANGLE_DATA_H_
#include "common/angleutils.h"
#include "libANGLE/State.h" #include "libANGLE/State.h"
namespace gl namespace gl
{ {
struct Data final struct Data final : public angle::NonCopyable
{ {
public: public:
Data(GLint clientVersion, const State &state, const Caps &caps, Data(uintptr_t context,
const TextureCapsMap &textureCaps, const Extensions &extensions, GLint clientVersion,
const State &state,
const Caps &caps,
const TextureCapsMap &textureCaps,
const Extensions &extensions,
const ResourceManager *resourceManager); const ResourceManager *resourceManager);
~Data(); ~Data();
Data(const Data &other); uintptr_t context;
Data &operator=(const Data &other);
GLint clientVersion; GLint clientVersion;
const State *state; const State *state;
const Caps *caps; const Caps *caps;
......
...@@ -47,4 +47,13 @@ GLenum Query::getType() const ...@@ -47,4 +47,13 @@ GLenum Query::getType() const
return mQuery->getType(); return mQuery->getType();
} }
rx::QueryImpl *Query::getImplementation()
{
return mQuery;
}
const rx::QueryImpl *Query::getImplementation() const
{
return mQuery;
}
} }
...@@ -38,6 +38,9 @@ class Query : public RefCountObject ...@@ -38,6 +38,9 @@ class Query : public RefCountObject
GLenum getType() const; GLenum getType() const;
rx::QueryImpl *getImplementation();
const rx::QueryImpl *getImplementation() const;
private: private:
rx::QueryImpl *mQuery; rx::QueryImpl *mQuery;
}; };
......
...@@ -908,6 +908,16 @@ void FunctionsGL::initialize() ...@@ -908,6 +908,16 @@ void FunctionsGL::initialize()
AssignGLExtensionEntryPoint(extensions, "GL_ARB_sampler_objects", loadProcAddress("glGetSamplerParameterIiv"), &getSamplerParameterIiv); AssignGLExtensionEntryPoint(extensions, "GL_ARB_sampler_objects", loadProcAddress("glGetSamplerParameterIiv"), &getSamplerParameterIiv);
AssignGLExtensionEntryPoint(extensions, "GL_ARB_sampler_objects", loadProcAddress("glGetSamplerParameterIuiv"), &getSamplerParameterIuiv); AssignGLExtensionEntryPoint(extensions, "GL_ARB_sampler_objects", loadProcAddress("glGetSamplerParameterIuiv"), &getSamplerParameterIuiv);
// GL_ARB_occlusion_query
AssignGLExtensionEntryPoint(extensions, "GL_ARB_occlusion_query", loadProcAddress("glGenQueriesARB"), &genQueries);
AssignGLExtensionEntryPoint(extensions, "GL_ARB_occlusion_query", loadProcAddress("glDeleteQueriesARB"), &deleteQueries);
AssignGLExtensionEntryPoint(extensions, "GL_ARB_occlusion_query", loadProcAddress("glIsQueryARB"), &isQuery);
AssignGLExtensionEntryPoint(extensions, "GL_ARB_occlusion_query", loadProcAddress("glBeginQueryARB"), &beginQuery);
AssignGLExtensionEntryPoint(extensions, "GL_ARB_occlusion_query", loadProcAddress("glEndQueryARB"), &endQuery);
AssignGLExtensionEntryPoint(extensions, "GL_ARB_occlusion_query", loadProcAddress("glGetQueryivARB"), &getQueryiv);
AssignGLExtensionEntryPoint(extensions, "GL_ARB_occlusion_query", loadProcAddress("glGetQueryObjectivARB"), &getQueryObjectiv);
AssignGLExtensionEntryPoint(extensions, "GL_ARB_occlusion_query", loadProcAddress("glGetQueryObjectuivARB"), &getQueryObjectuiv);
// 1.0 // 1.0
if (isAtLeastGL(gl::Version(1, 0))) if (isAtLeastGL(gl::Version(1, 0)))
{ {
......
...@@ -9,39 +9,160 @@ ...@@ -9,39 +9,160 @@
#include "libANGLE/renderer/gl/QueryGL.h" #include "libANGLE/renderer/gl/QueryGL.h"
#include "common/debug.h" #include "common/debug.h"
#include "libANGLE/renderer/gl/FunctionsGL.h"
#include "libANGLE/renderer/gl/StateManagerGL.h"
namespace
{
GLuint MergeQueryResults(GLenum type, GLuint currentResult, GLuint newResult)
{
switch (type)
{
case GL_ANY_SAMPLES_PASSED:
case GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
return (currentResult == GL_TRUE || newResult == GL_TRUE) ? GL_TRUE : GL_FALSE;
case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
return currentResult + newResult;
default:
UNREACHABLE();
return 0;
}
}
} // anonymous namespace
namespace rx namespace rx
{ {
QueryGL::QueryGL(GLenum type) QueryGL::QueryGL(GLenum type, const FunctionsGL *functions, StateManagerGL *stateManager)
: QueryImpl(type) : QueryImpl(type),
{} mType(type),
mFunctions(functions),
mStateManager(stateManager),
mActiveQuery(0),
mPendingQueries(),
mResultSum(0)
{
}
QueryGL::~QueryGL() QueryGL::~QueryGL()
{} {
mStateManager->deleteQuery(mActiveQuery);
mStateManager->onDeleteQueryObject(this);
while (!mPendingQueries.empty())
{
mStateManager->deleteQuery(mPendingQueries.front());
mPendingQueries.pop_front();
}
}
gl::Error QueryGL::begin() gl::Error QueryGL::begin()
{ {
UNIMPLEMENTED(); mResultSum = 0;
return gl::Error(GL_INVALID_OPERATION); return gl::Error(GL_NO_ERROR);
} }
gl::Error QueryGL::end() gl::Error QueryGL::end()
{ {
UNIMPLEMENTED(); return pause();
return gl::Error(GL_INVALID_OPERATION);
} }
gl::Error QueryGL::getResult(GLuint *params) gl::Error QueryGL::getResult(GLuint *params)
{ {
UNIMPLEMENTED(); ASSERT(mActiveQuery == 0);
return gl::Error(GL_INVALID_OPERATION);
gl::Error error = flush(true);
if (error.isError())
{
return error;
}
ASSERT(mPendingQueries.empty());
*params = mResultSum;
return gl::Error(GL_NO_ERROR);
} }
gl::Error QueryGL::isResultAvailable(GLuint *available) gl::Error QueryGL::isResultAvailable(GLuint *available)
{ {
UNIMPLEMENTED(); ASSERT(mActiveQuery == 0);
return gl::Error(GL_INVALID_OPERATION);
gl::Error error = flush(false);
if (error.isError())
{
return error;
}
*available = mPendingQueries.empty() ? GL_TRUE : GL_FALSE;
return gl::Error(GL_NO_ERROR);
}
gl::Error QueryGL::pause()
{
if (mActiveQuery != 0)
{
mStateManager->endQuery(mType, mActiveQuery);
mPendingQueries.push_back(mActiveQuery);
mActiveQuery = 0;
}
// Flush to make sure the pending queries don't add up too much.
gl::Error error = flush(false);
if (error.isError())
{
return error;
}
return gl::Error(GL_NO_ERROR);
}
gl::Error QueryGL::resume()
{
if (mActiveQuery == 0)
{
// Flush to make sure the pending queries don't add up too much.
gl::Error error = flush(false);
if (error.isError())
{
return error;
}
mFunctions->genQueries(1, &mActiveQuery);
mStateManager->beginQuery(mType, mActiveQuery);
}
return gl::Error(GL_NO_ERROR);
}
gl::Error QueryGL::flush(bool force)
{
while (!mPendingQueries.empty())
{
GLuint id = mPendingQueries.front();
if (!force)
{
GLuint resultAvailable = 0;
mFunctions->getQueryObjectuiv(id, GL_QUERY_RESULT_AVAILABLE, &resultAvailable);
if (resultAvailable == GL_FALSE)
{
return gl::Error(GL_NO_ERROR);
}
}
GLuint result = 0;
mFunctions->getQueryObjectuiv(id, GL_QUERY_RESULT, &result);
mResultSum = MergeQueryResults(mType, mResultSum, result);
mStateManager->deleteQuery(id);
mPendingQueries.pop_front();
}
return gl::Error(GL_NO_ERROR);
} }
} }
...@@ -9,21 +9,47 @@ ...@@ -9,21 +9,47 @@
#ifndef LIBANGLE_RENDERER_GL_QUERYGL_H_ #ifndef LIBANGLE_RENDERER_GL_QUERYGL_H_
#define LIBANGLE_RENDERER_GL_QUERYGL_H_ #define LIBANGLE_RENDERER_GL_QUERYGL_H_
#include <deque>
#include "libANGLE/renderer/QueryImpl.h" #include "libANGLE/renderer/QueryImpl.h"
namespace rx namespace rx
{ {
class FunctionsGL;
class StateManagerGL;
class QueryGL : public QueryImpl class QueryGL : public QueryImpl
{ {
public: public:
QueryGL(GLenum type); QueryGL(GLenum type, const FunctionsGL *functions, StateManagerGL *stateManager);
~QueryGL() override; ~QueryGL() override;
gl::Error begin() override; gl::Error begin() override;
gl::Error end() override; gl::Error end() override;
gl::Error getResult(GLuint *params) override; gl::Error getResult(GLuint *params) override;
gl::Error isResultAvailable(GLuint *available) override; gl::Error isResultAvailable(GLuint *available) override;
// OpenGL is only allowed to have one query of each type active at any given time. Since ANGLE
// virtualizes contexts, queries need to be able to be paused and resumed.
// A query is "paused" by ending it and pushing the ID into a list of queries awaiting readback.
// When it is "resumed", a new query is generated and started.
// When a result is required, the queries are "flushed" by iterating over the list of pending
// queries and merging their results.
gl::Error pause();
gl::Error resume();
private:
gl::Error flush(bool force);
GLenum mType;
const FunctionsGL *mFunctions;
StateManagerGL *mStateManager;
GLuint mActiveQuery;
std::deque<GLuint> mPendingQueries;
GLuint mResultSum;
}; };
} }
......
...@@ -86,6 +86,7 @@ RendererGL::RendererGL(const FunctionsGL *functions, const egl::AttributeMap &at ...@@ -86,6 +86,7 @@ RendererGL::RendererGL(const FunctionsGL *functions, const egl::AttributeMap &at
mFunctions(functions), mFunctions(functions),
mStateManager(nullptr), mStateManager(nullptr),
mBlitter(nullptr), mBlitter(nullptr),
mHasDebugOutput(false),
mSkipDrawCalls(false) mSkipDrawCalls(false)
{ {
ASSERT(mFunctions); ASSERT(mFunctions);
...@@ -93,8 +94,12 @@ RendererGL::RendererGL(const FunctionsGL *functions, const egl::AttributeMap &at ...@@ -93,8 +94,12 @@ RendererGL::RendererGL(const FunctionsGL *functions, const egl::AttributeMap &at
nativegl_gl::GenerateWorkarounds(mFunctions, &mWorkarounds); nativegl_gl::GenerateWorkarounds(mFunctions, &mWorkarounds);
mBlitter = new BlitGL(functions, mWorkarounds, mStateManager); mBlitter = new BlitGL(functions, mWorkarounds, mStateManager);
mHasDebugOutput = mFunctions->isAtLeastGL(gl::Version(4, 3)) ||
mFunctions->hasGLExtension("GL_KHR_debug") ||
mFunctions->isAtLeastGLES(gl::Version(3, 2)) ||
mFunctions->hasGLESExtension("GL_KHR_debug");
#ifndef NDEBUG #ifndef NDEBUG
if (mFunctions->debugMessageControl && mFunctions->debugMessageCallback) if (mHasDebugOutput)
{ {
mFunctions->enable(GL_DEBUG_OUTPUT_SYNCHRONOUS); mFunctions->enable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
mFunctions->debugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_HIGH, 0, nullptr, GL_TRUE); mFunctions->debugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_HIGH, 0, nullptr, GL_TRUE);
...@@ -126,7 +131,22 @@ gl::Error RendererGL::flush() ...@@ -126,7 +131,22 @@ gl::Error RendererGL::flush()
gl::Error RendererGL::finish() gl::Error RendererGL::finish()
{ {
#ifdef NDEBUG
if (mWorkarounds.finishDoesNotCauseQueriesToBeAvailable && mHasDebugOutput)
{
mFunctions->enable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
}
#endif
mFunctions->finish(); mFunctions->finish();
#ifdef NDEBUG
if (mWorkarounds.finishDoesNotCauseQueriesToBeAvailable && mHasDebugOutput)
{
mFunctions->disable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
}
#endif
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
...@@ -280,7 +300,7 @@ VertexArrayImpl *RendererGL::createVertexArray(const gl::VertexArray::Data &data ...@@ -280,7 +300,7 @@ VertexArrayImpl *RendererGL::createVertexArray(const gl::VertexArray::Data &data
QueryImpl *RendererGL::createQuery(GLenum type) QueryImpl *RendererGL::createQuery(GLenum type)
{ {
return new QueryGL(type); return new QueryGL(type, mFunctions, mStateManager);
} }
FenceNVImpl *RendererGL::createFenceNV() FenceNVImpl *RendererGL::createFenceNV()
......
...@@ -124,6 +124,8 @@ class RendererGL : public Renderer ...@@ -124,6 +124,8 @@ class RendererGL : public Renderer
WorkaroundsGL mWorkarounds; WorkaroundsGL mWorkarounds;
bool mHasDebugOutput;
// For performance debugging // For performance debugging
bool mSkipDrawCalls; bool mSkipDrawCalls;
}; };
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "libANGLE/Data.h" #include "libANGLE/Data.h"
#include "libANGLE/Framebuffer.h" #include "libANGLE/Framebuffer.h"
#include "libANGLE/VertexArray.h" #include "libANGLE/VertexArray.h"
#include "libANGLE/Query.h"
#include "libANGLE/renderer/gl/BufferGL.h" #include "libANGLE/renderer/gl/BufferGL.h"
#include "libANGLE/renderer/gl/FramebufferGL.h" #include "libANGLE/renderer/gl/FramebufferGL.h"
#include "libANGLE/renderer/gl/FunctionsGL.h" #include "libANGLE/renderer/gl/FunctionsGL.h"
...@@ -19,10 +20,14 @@ ...@@ -19,10 +20,14 @@
#include "libANGLE/renderer/gl/SamplerGL.h" #include "libANGLE/renderer/gl/SamplerGL.h"
#include "libANGLE/renderer/gl/TextureGL.h" #include "libANGLE/renderer/gl/TextureGL.h"
#include "libANGLE/renderer/gl/VertexArrayGL.h" #include "libANGLE/renderer/gl/VertexArrayGL.h"
#include "libANGLE/renderer/gl/QueryGL.h"
namespace rx namespace rx
{ {
static const GLenum QueryTypes[] = {GL_ANY_SAMPLES_PASSED, GL_ANY_SAMPLES_PASSED_CONSERVATIVE,
GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN};
StateManagerGL::IndexedBufferBinding::IndexedBufferBinding() : offset(0), size(0), buffer(0) StateManagerGL::IndexedBufferBinding::IndexedBufferBinding() : offset(0), size(0), buffer(0)
{ {
} }
...@@ -37,6 +42,9 @@ StateManagerGL::StateManagerGL(const FunctionsGL *functions, const gl::Caps &ren ...@@ -37,6 +42,9 @@ StateManagerGL::StateManagerGL(const FunctionsGL *functions, const gl::Caps &ren
mTextureUnitIndex(0), mTextureUnitIndex(0),
mTextures(), mTextures(),
mSamplers(rendererCaps.maxCombinedTextureImageUnits, 0), mSamplers(rendererCaps.maxCombinedTextureImageUnits, 0),
mQueries(),
mPrevDrawQueries(),
mPrevDrawContext(0),
mUnpackAlignment(4), mUnpackAlignment(4),
mUnpackRowLength(0), mUnpackRowLength(0),
mUnpackSkipRows(0), mUnpackSkipRows(0),
...@@ -111,6 +119,11 @@ StateManagerGL::StateManagerGL(const FunctionsGL *functions, const gl::Caps &ren ...@@ -111,6 +119,11 @@ StateManagerGL::StateManagerGL(const FunctionsGL *functions, const gl::Caps &ren
mIndexedBuffers[GL_UNIFORM_BUFFER].resize(rendererCaps.maxCombinedUniformBlocks); mIndexedBuffers[GL_UNIFORM_BUFFER].resize(rendererCaps.maxCombinedUniformBlocks);
for (GLenum queryType : QueryTypes)
{
mQueries[queryType] = 0;
}
// Initialize point sprite state for desktop GL // Initialize point sprite state for desktop GL
if (mFunctions->standard == STANDARD_GL_DESKTOP) if (mFunctions->standard == STANDARD_GL_DESKTOP)
{ {
...@@ -246,6 +259,22 @@ void StateManagerGL::deleteRenderbuffer(GLuint rbo) ...@@ -246,6 +259,22 @@ void StateManagerGL::deleteRenderbuffer(GLuint rbo)
} }
} }
void StateManagerGL::deleteQuery(GLuint query)
{
if (query != 0)
{
for (auto &activeQuery : mQueries)
{
GLuint activeQueryID = activeQuery.second;
if (activeQueryID == query)
{
GLenum type = activeQuery.first;
endQuery(type, query);
}
}
}
}
void StateManagerGL::useProgram(GLuint program) void StateManagerGL::useProgram(GLuint program)
{ {
if (mProgram != program) if (mProgram != program)
...@@ -487,6 +516,29 @@ void StateManagerGL::bindRenderbuffer(GLenum type, GLuint renderbuffer) ...@@ -487,6 +516,29 @@ void StateManagerGL::bindRenderbuffer(GLenum type, GLuint renderbuffer)
} }
} }
void StateManagerGL::beginQuery(GLenum type, GLuint query)
{
// Make sure this is a valid query type and there is no current active query of this type
ASSERT(mQueries.find(type) != mQueries.end());
ASSERT(mQueries[type] == 0);
ASSERT(query != 0);
mQueries[type] = query;
mFunctions->beginQuery(type, query);
}
void StateManagerGL::endQuery(GLenum type, GLuint query)
{
ASSERT(mQueries[type] == query);
mQueries[type] = 0;
mFunctions->endQuery(type);
}
void StateManagerGL::onDeleteQueryObject(QueryGL *query)
{
mPrevDrawQueries.erase(query);
}
gl::Error StateManagerGL::setDrawArraysState(const gl::Data &data, gl::Error StateManagerGL::setDrawArraysState(const gl::Data &data,
GLint first, GLint first,
GLsizei count, GLsizei count,
...@@ -619,6 +671,31 @@ gl::Error StateManagerGL::setGenericDrawState(const gl::Data &data) ...@@ -619,6 +671,31 @@ gl::Error StateManagerGL::setGenericDrawState(const gl::Data &data)
// Seamless cubemaps are required for ES3 and higher contexts. // Seamless cubemaps are required for ES3 and higher contexts.
setTextureCubemapSeamlessEnabled(data.clientVersion >= 3); setTextureCubemapSeamlessEnabled(data.clientVersion >= 3);
// If the context has changed, pause the previous context's queries
if (data.context != mPrevDrawContext)
{
for (QueryGL *prevQuery : mPrevDrawQueries)
{
prevQuery->pause();
}
}
mPrevDrawQueries.clear();
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();
mPrevDrawQueries.insert(queryGL);
}
}
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
......
...@@ -28,6 +28,7 @@ namespace rx ...@@ -28,6 +28,7 @@ namespace rx
{ {
class FunctionsGL; class FunctionsGL;
class QueryGL;
class StateManagerGL final : angle::NonCopyable class StateManagerGL final : angle::NonCopyable
{ {
...@@ -41,6 +42,7 @@ class StateManagerGL final : angle::NonCopyable ...@@ -41,6 +42,7 @@ class StateManagerGL final : angle::NonCopyable
void deleteBuffer(GLuint buffer); void deleteBuffer(GLuint buffer);
void deleteFramebuffer(GLuint fbo); void deleteFramebuffer(GLuint fbo);
void deleteRenderbuffer(GLuint rbo); void deleteRenderbuffer(GLuint rbo);
void deleteQuery(GLuint query);
void useProgram(GLuint program); void useProgram(GLuint program);
void bindVertexArray(GLuint vao, GLuint elementArrayBuffer); void bindVertexArray(GLuint vao, GLuint elementArrayBuffer);
...@@ -52,6 +54,8 @@ class StateManagerGL final : angle::NonCopyable ...@@ -52,6 +54,8 @@ class StateManagerGL final : angle::NonCopyable
void bindSampler(size_t unit, GLuint sampler); void bindSampler(size_t unit, GLuint sampler);
void bindFramebuffer(GLenum type, GLuint framebuffer); void bindFramebuffer(GLenum type, GLuint framebuffer);
void bindRenderbuffer(GLenum type, GLuint renderbuffer); void bindRenderbuffer(GLenum type, GLuint renderbuffer);
void beginQuery(GLenum type, GLuint query);
void endQuery(GLenum type, GLuint query);
void setAttributeCurrentData(size_t index, const gl::VertexAttribCurrentValueData &data); void setAttributeCurrentData(size_t index, const gl::VertexAttribCurrentValueData &data);
...@@ -115,6 +119,8 @@ class StateManagerGL final : angle::NonCopyable ...@@ -115,6 +119,8 @@ class StateManagerGL final : angle::NonCopyable
void setFramebufferSRGBEnabled(bool enabled); void setFramebufferSRGBEnabled(bool enabled);
void onDeleteQueryObject(QueryGL *query);
gl::Error setDrawArraysState(const gl::Data &data, gl::Error setDrawArraysState(const gl::Data &data,
GLint first, GLint first,
GLsizei count, GLsizei count,
...@@ -156,6 +162,11 @@ class StateManagerGL final : angle::NonCopyable ...@@ -156,6 +162,11 @@ class StateManagerGL final : angle::NonCopyable
std::map<GLenum, std::vector<GLuint>> mTextures; std::map<GLenum, std::vector<GLuint>> mTextures;
std::vector<GLuint> mSamplers; std::vector<GLuint> mSamplers;
std::map<GLenum, GLuint> mQueries;
std::set<QueryGL *> mPrevDrawQueries;
uintptr_t mPrevDrawContext;
GLint mUnpackAlignment; GLint mUnpackAlignment;
GLint mUnpackRowLength; GLint mUnpackRowLength;
GLint mUnpackSkipRows; GLint mUnpackSkipRows;
......
...@@ -18,7 +18,8 @@ struct WorkaroundsGL ...@@ -18,7 +18,8 @@ struct WorkaroundsGL
: avoid1BitAlphaTextureFormats(false), : avoid1BitAlphaTextureFormats(false),
rgba4IsNotSupportedForColorRendering(false), rgba4IsNotSupportedForColorRendering(false),
doesSRGBClearsOnLinearFramebufferAttachments(false), doesSRGBClearsOnLinearFramebufferAttachments(false),
doWhileGLSLCausesGPUHang(false) doWhileGLSLCausesGPUHang(false),
finishDoesNotCauseQueriesToBeAvailable(false)
{ {
} }
...@@ -40,8 +41,8 @@ struct WorkaroundsGL ...@@ -40,8 +41,8 @@ struct WorkaroundsGL
// driver clears to the linearized clear color despite the framebuffer not supporting SRGB // driver clears to the linearized clear color despite the framebuffer not supporting SRGB
// blending. It only seems to do this when the framebuffer has only linear attachments, mixed // blending. It only seems to do this when the framebuffer has only linear attachments, mixed
// attachments appear to get the correct clear color. // attachments appear to get the correct clear color.
bool doesSRGBClearsOnLinearFramebufferAttachments; bool doesSRGBClearsOnLinearFramebufferAttachments;
// On Mac some GLSL constructs involving do-while loops cause GPU hangs, such as the following: // On Mac some GLSL constructs involving do-while loops cause GPU hangs, such as the following:
// int i = 1; // int i = 1;
// do { // do {
...@@ -50,6 +51,11 @@ struct WorkaroundsGL ...@@ -50,6 +51,11 @@ struct WorkaroundsGL
// } while (i > 0) // } while (i > 0)
// Work around this by rewriting the do-while to use another GLSL construct (block + while) // Work around this by rewriting the do-while to use another GLSL construct (block + while)
bool doWhileGLSLCausesGPUHang; bool doWhileGLSLCausesGPUHang;
// Calling glFinish doesn't cause all queries to report that the result is available on some
// (NVIDIA) drivers. It was found that enabling GL_DEBUG_OUTPUT_SYNCHRONOUS before the finish
// causes it to fully finish.
bool finishDoesNotCauseQueriesToBeAvailable;
}; };
} }
......
...@@ -527,6 +527,11 @@ void GenerateCaps(const FunctionsGL *functions, gl::Caps *caps, gl::TextureCapsM ...@@ -527,6 +527,11 @@ void GenerateCaps(const FunctionsGL *functions, gl::Caps *caps, gl::TextureCapsM
functions->isAtLeastGLES(gl::Version(3, 0)) || functions->hasGLESExtension("GL_EXT_draw_buffers"); functions->isAtLeastGLES(gl::Version(3, 0)) || functions->hasGLESExtension("GL_EXT_draw_buffers");
extensions->textureStorage = true; extensions->textureStorage = true;
extensions->textureFilterAnisotropic = functions->hasGLExtension("GL_EXT_texture_filter_anisotropic") || functions->hasGLESExtension("GL_EXT_texture_filter_anisotropic"); extensions->textureFilterAnisotropic = functions->hasGLExtension("GL_EXT_texture_filter_anisotropic") || functions->hasGLESExtension("GL_EXT_texture_filter_anisotropic");
extensions->occlusionQueryBoolean =
functions->isAtLeastGL(gl::Version(1, 5)) ||
functions->hasGLExtension("GL_ARB_occlusion_query2") ||
functions->isAtLeastGLES(gl::Version(3, 0)) ||
functions->hasGLESExtension("GL_EXT_occlusion_query_boolean");
extensions->maxTextureAnisotropy = extensions->textureFilterAnisotropic ? QuerySingleGLFloat(functions, GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT) : 0.0f; extensions->maxTextureAnisotropy = extensions->textureFilterAnisotropic ? QuerySingleGLFloat(functions, GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT) : 0.0f;
extensions->fence = functions->hasGLExtension("GL_NV_fence") || functions->hasGLESExtension("GL_NV_fence"); extensions->fence = functions->hasGLExtension("GL_NV_fence") || functions->hasGLESExtension("GL_NV_fence");
extensions->blendMinMax = functions->isAtLeastGL(gl::Version(1, 5)) || functions->hasGLExtension("GL_EXT_blend_minmax") || extensions->blendMinMax = functions->isAtLeastGL(gl::Version(1, 5)) || functions->hasGLExtension("GL_EXT_blend_minmax") ||
...@@ -577,6 +582,9 @@ void GenerateWorkarounds(const FunctionsGL *functions, WorkaroundsGL *workaround ...@@ -577,6 +582,9 @@ void GenerateWorkarounds(const FunctionsGL *functions, WorkaroundsGL *workaround
#if defined(ANGLE_PLATFORM_APPLE) #if defined(ANGLE_PLATFORM_APPLE)
workarounds->doWhileGLSLCausesGPUHang = true; workarounds->doWhileGLSLCausesGPUHang = true;
#endif #endif
workarounds->finishDoesNotCauseQueriesToBeAvailable =
functions->standard == STANDARD_GL_DESKTOP && vendor == VENDOR_ID_NVIDIA;
} }
} }
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "system_utils.h" #include "system_utils.h"
#include "test_utils/ANGLETest.h" #include "test_utils/ANGLETest.h"
#include "random_utils.h"
using namespace angle; using namespace angle;
...@@ -66,6 +67,13 @@ class OcclusionQueriesTest : public ANGLETest ...@@ -66,6 +67,13 @@ class OcclusionQueriesTest : public ANGLETest
TEST_P(OcclusionQueriesTest, IsOccluded) TEST_P(OcclusionQueriesTest, IsOccluded)
{ {
if (getClientVersion() < 3 && !extensionEnabled("GL_EXT_occlusion_query_boolean"))
{
std::cout << "Test skipped because ES3 or GL_EXT_occlusion_query_boolean are not available."
<< std::endl;
return;
}
glDepthMask(GL_TRUE); glDepthMask(GL_TRUE);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
...@@ -106,6 +114,13 @@ TEST_P(OcclusionQueriesTest, IsOccluded) ...@@ -106,6 +114,13 @@ TEST_P(OcclusionQueriesTest, IsOccluded)
TEST_P(OcclusionQueriesTest, IsNotOccluded) TEST_P(OcclusionQueriesTest, IsNotOccluded)
{ {
if (getClientVersion() < 3 && !extensionEnabled("GL_EXT_occlusion_query_boolean"))
{
std::cout << "Test skipped because ES3 or GL_EXT_occlusion_query_boolean are not available."
<< std::endl;
return;
}
glDepthMask(GL_TRUE); glDepthMask(GL_TRUE);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
...@@ -133,6 +148,13 @@ TEST_P(OcclusionQueriesTest, IsNotOccluded) ...@@ -133,6 +148,13 @@ TEST_P(OcclusionQueriesTest, IsNotOccluded)
TEST_P(OcclusionQueriesTest, Errors) TEST_P(OcclusionQueriesTest, Errors)
{ {
if (getClientVersion() < 3 && !extensionEnabled("GL_EXT_occlusion_query_boolean"))
{
std::cout << "Test skipped because ES3 or GL_EXT_occlusion_query_boolean are not available."
<< std::endl;
return;
}
glDepthMask(GL_TRUE); glDepthMask(GL_TRUE);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
...@@ -186,5 +208,161 @@ TEST_P(OcclusionQueriesTest, Errors) ...@@ -186,5 +208,161 @@ TEST_P(OcclusionQueriesTest, Errors)
EXPECT_GL_NO_ERROR(); EXPECT_GL_NO_ERROR();
} }
// Test that running multiple simultaneous queries from multiple EGL contexts returns the correct
// result for each query. Helps expose bugs in ANGLE's virtual contexts.
TEST_P(OcclusionQueriesTest, MultiContext)
{
if (getClientVersion() < 3 && !extensionEnabled("GL_EXT_occlusion_query_boolean"))
{
std::cout << "Test skipped because ES3 or GL_EXT_occlusion_query_boolean are not available."
<< std::endl;
return;
}
if (GetParam() == ES2_D3D9() || GetParam() == ES2_D3D11() || GetParam() == ES3_D3D11())
{
std::cout << "Test skipped because the D3D backends cannot support simultaneous queries on "
"multiple contexts yet."
<< std::endl;
return;
}
glDepthMask(GL_TRUE);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// draw a quad at depth 0.5
glEnable(GL_DEPTH_TEST);
drawQuad(mProgram, "position", 0.5f);
EGLWindow *window = getEGLWindow();
EGLDisplay display = window->getDisplay();
EGLConfig config = window->getConfig();
EGLSurface surface = window->getSurface();
EGLint contextAttributes[] = {
EGL_CONTEXT_MAJOR_VERSION_KHR,
GetParam().majorVersion,
EGL_CONTEXT_MINOR_VERSION_KHR,
GetParam().minorVersion,
EGL_NONE,
};
const size_t passCount = 5;
struct ContextInfo
{
EGLContext context;
GLuint program;
GLuint query;
bool visiblePasses[passCount];
bool shouldPass;
};
ContextInfo contexts[] = {
{
EGL_NO_CONTEXT, 0, 0, {false, false, false, false, false}, false,
},
{
EGL_NO_CONTEXT, 0, 0, {false, true, false, true, false}, true,
},
{
EGL_NO_CONTEXT, 0, 0, {false, false, false, false, false}, false,
},
{
EGL_NO_CONTEXT, 0, 0, {true, true, false, true, true}, true,
},
{
EGL_NO_CONTEXT, 0, 0, {false, true, true, true, true}, true,
},
{
EGL_NO_CONTEXT, 0, 0, {true, false, false, true, false}, true,
},
{
EGL_NO_CONTEXT, 0, 0, {false, false, false, false, false}, false,
},
{
EGL_NO_CONTEXT, 0, 0, {false, false, false, false, false}, false,
},
};
for (auto &context : contexts)
{
context.context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttributes);
ASSERT_NE(context.context, EGL_NO_CONTEXT);
eglMakeCurrent(display, surface, surface, context.context);
const std::string passthroughVS = SHADER_SOURCE
(
attribute highp vec4 position;
void main(void)
{
gl_Position = position;
}
);
const std::string passthroughPS = SHADER_SOURCE
(
precision highp float;
void main(void)
{
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
);
context.program = CompileProgram(passthroughVS, passthroughPS);
ASSERT_NE(context.program, 0u);
glDepthMask(GL_FALSE);
glEnable(GL_DEPTH_TEST);
glGenQueriesEXT(1, &context.query);
glBeginQueryEXT(GL_ANY_SAMPLES_PASSED_EXT, context.query);
ASSERT_GL_NO_ERROR();
}
for (size_t pass = 0; pass < passCount; pass++)
{
for (const auto &context : contexts)
{
eglMakeCurrent(display, surface, surface, context.context);
float depth =
context.visiblePasses[pass] ? RandomBetween(0.0f, 0.4f) : RandomBetween(0.6f, 1.0f);
drawQuad(context.program, "position", depth);
EXPECT_GL_NO_ERROR();
}
}
for (const auto &context : contexts)
{
eglMakeCurrent(display, surface, surface, context.context);
glEndQueryEXT(GL_ANY_SAMPLES_PASSED_EXT);
GLuint result = GL_TRUE;
glGetQueryObjectuivEXT(context.query, GL_QUERY_RESULT_EXT, &result);
EXPECT_GL_NO_ERROR();
GLuint expectation = context.shouldPass ? GL_TRUE : GL_FALSE;
EXPECT_EQ(expectation, result);
}
eglMakeCurrent(display, surface, surface, window->getContext());
for (auto &context : contexts)
{
eglDestroyContext(display, context.context);
context.context = EGL_NO_CONTEXT;
}
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
ANGLE_INSTANTIATE_TEST(OcclusionQueriesTest, ES2_D3D9(), ES2_D3D11()); ANGLE_INSTANTIATE_TEST(OcclusionQueriesTest,
ES2_D3D9(),
ES2_D3D11(),
ES3_D3D11(),
ES2_OPENGL(),
ES3_OPENGL());
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