| // |
| // Copyright 2015 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| |
| // RendererGL.cpp: Implements the class methods for RendererGL. |
| |
| #include "libANGLE/renderer/gl/RendererGL.h" |
| |
| #include <EGL/eglext.h> |
| |
| #include "common/debug.h" |
| #include "libANGLE/AttributeMap.h" |
| #include "libANGLE/ContextState.h" |
| #include "libANGLE/Path.h" |
| #include "libANGLE/Surface.h" |
| #include "libANGLE/renderer/gl/BlitGL.h" |
| #include "libANGLE/renderer/gl/BufferGL.h" |
| #include "libANGLE/renderer/gl/CompilerGL.h" |
| #include "libANGLE/renderer/gl/ContextGL.h" |
| #include "libANGLE/renderer/gl/FenceNVGL.h" |
| #include "libANGLE/renderer/gl/FenceSyncGL.h" |
| #include "libANGLE/renderer/gl/FramebufferGL.h" |
| #include "libANGLE/renderer/gl/FunctionsGL.h" |
| #include "libANGLE/renderer/gl/PathGL.h" |
| #include "libANGLE/renderer/gl/ProgramGL.h" |
| #include "libANGLE/renderer/gl/QueryGL.h" |
| #include "libANGLE/renderer/gl/RenderbufferGL.h" |
| #include "libANGLE/renderer/gl/SamplerGL.h" |
| #include "libANGLE/renderer/gl/ShaderGL.h" |
| #include "libANGLE/renderer/gl/StateManagerGL.h" |
| #include "libANGLE/renderer/gl/SurfaceGL.h" |
| #include "libANGLE/renderer/gl/TextureGL.h" |
| #include "libANGLE/renderer/gl/TransformFeedbackGL.h" |
| #include "libANGLE/renderer/gl/VertexArrayGL.h" |
| #include "libANGLE/renderer/gl/renderergl_utils.h" |
| |
| namespace |
| { |
| |
| std::vector<GLuint> GatherPaths(const std::vector<gl::Path *> &paths) |
| { |
| std::vector<GLuint> ret; |
| ret.reserve(paths.size()); |
| |
| for (const auto *p : paths) |
| { |
| const auto *pathObj = rx::GetImplAs<rx::PathGL>(p); |
| ret.push_back(pathObj->getPathID()); |
| } |
| return ret; |
| } |
| |
| } // namespace |
| |
| #ifndef NDEBUG |
| static void INTERNAL_GL_APIENTRY LogGLDebugMessage(GLenum source, |
| GLenum type, |
| GLuint id, |
| GLenum severity, |
| GLsizei length, |
| const GLchar *message, |
| const void *userParam) |
| { |
| std::string sourceText; |
| switch (source) |
| { |
| case GL_DEBUG_SOURCE_API: |
| sourceText = "OpenGL"; |
| break; |
| case GL_DEBUG_SOURCE_WINDOW_SYSTEM: |
| sourceText = "Windows"; |
| break; |
| case GL_DEBUG_SOURCE_SHADER_COMPILER: |
| sourceText = "Shader Compiler"; |
| break; |
| case GL_DEBUG_SOURCE_THIRD_PARTY: |
| sourceText = "Third Party"; |
| break; |
| case GL_DEBUG_SOURCE_APPLICATION: |
| sourceText = "Application"; |
| break; |
| case GL_DEBUG_SOURCE_OTHER: |
| sourceText = "Other"; |
| break; |
| default: |
| sourceText = "UNKNOWN"; |
| break; |
| } |
| |
| std::string typeText; |
| switch (type) |
| { |
| case GL_DEBUG_TYPE_ERROR: |
| typeText = "Error"; |
| break; |
| case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: |
| typeText = "Deprecated behavior"; |
| break; |
| case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: |
| typeText = "Undefined behavior"; |
| break; |
| case GL_DEBUG_TYPE_PORTABILITY: |
| typeText = "Portability"; |
| break; |
| case GL_DEBUG_TYPE_PERFORMANCE: |
| typeText = "Performance"; |
| break; |
| case GL_DEBUG_TYPE_OTHER: |
| typeText = "Other"; |
| break; |
| case GL_DEBUG_TYPE_MARKER: |
| typeText = "Marker"; |
| break; |
| default: |
| typeText = "UNKNOWN"; |
| break; |
| } |
| |
| std::string severityText; |
| switch (severity) |
| { |
| case GL_DEBUG_SEVERITY_HIGH: |
| severityText = "High"; |
| break; |
| case GL_DEBUG_SEVERITY_MEDIUM: |
| severityText = "Medium"; |
| break; |
| case GL_DEBUG_SEVERITY_LOW: |
| severityText = "Low"; |
| break; |
| case GL_DEBUG_SEVERITY_NOTIFICATION: |
| severityText = "Notification"; |
| break; |
| default: |
| severityText = "UNKNOWN"; |
| break; |
| } |
| |
| if (type == GL_DEBUG_TYPE_ERROR) |
| { |
| ERR() << std::endl |
| << "\tSource: " << sourceText << std::endl |
| << "\tType: " << typeText << std::endl |
| << "\tID: " << gl::Error(id) << std::endl |
| << "\tSeverity: " << severityText << std::endl |
| << "\tMessage: " << message; |
| } |
| else |
| { |
| // TODO(ynovikov): filter into WARN and INFO if INFO is ever implemented |
| WARN() << std::endl |
| << "\tSource: " << sourceText << std::endl |
| << "\tType: " << typeText << std::endl |
| << "\tID: " << gl::Error(id) << std::endl |
| << "\tSeverity: " << severityText << std::endl |
| << "\tMessage: " << message; |
| } |
| } |
| #endif |
| |
| namespace rx |
| { |
| |
| RendererGL::RendererGL(const FunctionsGL *functions, const egl::AttributeMap &attribMap) |
| : mMaxSupportedESVersion(0, 0), |
| mFunctions(functions), |
| mStateManager(nullptr), |
| mBlitter(nullptr), |
| mHasDebugOutput(false), |
| mSkipDrawCalls(false), |
| mCapsInitialized(false) |
| { |
| ASSERT(mFunctions); |
| nativegl_gl::GenerateWorkarounds(mFunctions, &mWorkarounds); |
| mStateManager = new StateManagerGL(mFunctions, getNativeCaps()); |
| 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 |
| if (mHasDebugOutput) |
| { |
| mFunctions->enable(GL_DEBUG_OUTPUT); |
| 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_MEDIUM, 0, |
| nullptr, GL_TRUE); |
| mFunctions->debugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_LOW, 0, |
| nullptr, GL_FALSE); |
| mFunctions->debugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION, |
| 0, nullptr, GL_FALSE); |
| mFunctions->debugMessageCallback(&LogGLDebugMessage, nullptr); |
| } |
| #endif |
| |
| EGLint deviceType = |
| static_cast<EGLint>(attribMap.get(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, EGL_NONE)); |
| if (deviceType == EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE) |
| { |
| mSkipDrawCalls = true; |
| } |
| |
| if (mWorkarounds.initializeCurrentVertexAttributes) |
| { |
| GLint maxVertexAttribs = 0; |
| mFunctions->getIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs); |
| |
| for (GLint i = 0; i < maxVertexAttribs; ++i) |
| { |
| mFunctions->vertexAttrib4f(i, 0.0f, 0.0f, 0.0f, 1.0f); |
| } |
| } |
| } |
| |
| RendererGL::~RendererGL() |
| { |
| SafeDelete(mBlitter); |
| SafeDelete(mStateManager); |
| } |
| |
| gl::Error RendererGL::flush() |
| { |
| mFunctions->flush(); |
| return gl::NoError(); |
| } |
| |
| gl::Error RendererGL::finish() |
| { |
| #ifdef NDEBUG |
| if (mWorkarounds.finishDoesNotCauseQueriesToBeAvailable && mHasDebugOutput) |
| { |
| mFunctions->enable(GL_DEBUG_OUTPUT_SYNCHRONOUS); |
| } |
| #endif |
| |
| mFunctions->finish(); |
| |
| #ifdef NDEBUG |
| if (mWorkarounds.finishDoesNotCauseQueriesToBeAvailable && mHasDebugOutput) |
| { |
| mFunctions->disable(GL_DEBUG_OUTPUT_SYNCHRONOUS); |
| } |
| #endif |
| |
| return gl::NoError(); |
| } |
| |
| gl::Error RendererGL::drawArrays(const gl::ContextState &data, |
| GLenum mode, |
| GLint first, |
| GLsizei count) |
| { |
| ANGLE_TRY(mStateManager->setDrawArraysState(data, first, count, 0)); |
| |
| if (!mSkipDrawCalls) |
| { |
| mFunctions->drawArrays(mode, first, count); |
| } |
| |
| return gl::NoError(); |
| } |
| |
| gl::Error RendererGL::drawArraysInstanced(const gl::ContextState &data, |
| GLenum mode, |
| GLint first, |
| GLsizei count, |
| GLsizei instanceCount) |
| { |
| ANGLE_TRY(mStateManager->setDrawArraysState(data, first, count, instanceCount)); |
| |
| if (!mSkipDrawCalls) |
| { |
| mFunctions->drawArraysInstanced(mode, first, count, instanceCount); |
| } |
| |
| return gl::NoError(); |
| } |
| |
| gl::Error RendererGL::drawElements(const gl::ContextState &data, |
| GLenum mode, |
| GLsizei count, |
| GLenum type, |
| const void *indices, |
| const gl::IndexRange &indexRange) |
| { |
| const void *drawIndexPtr = nullptr; |
| ANGLE_TRY(mStateManager->setDrawElementsState(data, count, type, indices, 0, &drawIndexPtr)); |
| |
| if (!mSkipDrawCalls) |
| { |
| mFunctions->drawElements(mode, count, type, drawIndexPtr); |
| } |
| |
| return gl::NoError(); |
| } |
| |
| gl::Error RendererGL::drawElementsInstanced(const gl::ContextState &data, |
| GLenum mode, |
| GLsizei count, |
| GLenum type, |
| const void *indices, |
| GLsizei instances, |
| const gl::IndexRange &indexRange) |
| { |
| const void *drawIndexPointer = nullptr; |
| ANGLE_TRY(mStateManager->setDrawElementsState(data, count, type, indices, instances, |
| &drawIndexPointer)); |
| |
| if (!mSkipDrawCalls) |
| { |
| mFunctions->drawElementsInstanced(mode, count, type, drawIndexPointer, instances); |
| } |
| |
| return gl::NoError(); |
| } |
| |
| gl::Error RendererGL::drawRangeElements(const gl::ContextState &data, |
| GLenum mode, |
| GLuint start, |
| GLuint end, |
| GLsizei count, |
| GLenum type, |
| const void *indices, |
| const gl::IndexRange &indexRange) |
| { |
| const void *drawIndexPointer = nullptr; |
| ANGLE_TRY( |
| mStateManager->setDrawElementsState(data, count, type, indices, 0, &drawIndexPointer)); |
| |
| if (!mSkipDrawCalls) |
| { |
| mFunctions->drawRangeElements(mode, start, end, count, type, drawIndexPointer); |
| } |
| |
| return gl::NoError(); |
| } |
| |
| gl::Error RendererGL::drawArraysIndirect(const gl::ContextState &data, |
| GLenum mode, |
| const void *indirect) |
| { |
| ANGLE_TRY(mStateManager->setDrawIndirectState(data, GL_NONE)); |
| |
| if (!mSkipDrawCalls) |
| { |
| mFunctions->drawArraysIndirect(mode, indirect); |
| } |
| return gl::NoError(); |
| } |
| |
| gl::Error RendererGL::drawElementsIndirect(const gl::ContextState &data, |
| GLenum mode, |
| GLenum type, |
| const void *indirect) |
| { |
| ANGLE_TRY(mStateManager->setDrawIndirectState(data, type)); |
| |
| if (!mSkipDrawCalls) |
| { |
| mFunctions->drawElementsIndirect(mode, type, indirect); |
| } |
| return gl::NoError(); |
| } |
| |
| void RendererGL::stencilFillPath(const gl::ContextState &state, |
| const gl::Path *path, |
| GLenum fillMode, |
| GLuint mask) |
| { |
| const auto *pathObj = GetImplAs<PathGL>(path); |
| |
| mFunctions->stencilFillPathNV(pathObj->getPathID(), fillMode, mask); |
| |
| ASSERT(mFunctions->getError() == GL_NO_ERROR); |
| } |
| |
| void RendererGL::stencilStrokePath(const gl::ContextState &state, |
| const gl::Path *path, |
| GLint reference, |
| GLuint mask) |
| { |
| const auto *pathObj = GetImplAs<PathGL>(path); |
| |
| mFunctions->stencilStrokePathNV(pathObj->getPathID(), reference, mask); |
| |
| ASSERT(mFunctions->getError() == GL_NO_ERROR); |
| } |
| |
| void RendererGL::coverFillPath(const gl::ContextState &state, |
| const gl::Path *path, |
| GLenum coverMode) |
| { |
| |
| const auto *pathObj = GetImplAs<PathGL>(path); |
| mFunctions->coverFillPathNV(pathObj->getPathID(), coverMode); |
| |
| ASSERT(mFunctions->getError() == GL_NO_ERROR); |
| } |
| |
| void RendererGL::coverStrokePath(const gl::ContextState &state, |
| const gl::Path *path, |
| GLenum coverMode) |
| { |
| const auto *pathObj = GetImplAs<PathGL>(path); |
| mFunctions->coverStrokePathNV(pathObj->getPathID(), coverMode); |
| |
| ASSERT(mFunctions->getError() == GL_NO_ERROR); |
| } |
| |
| void RendererGL::stencilThenCoverFillPath(const gl::ContextState &state, |
| const gl::Path *path, |
| GLenum fillMode, |
| GLuint mask, |
| GLenum coverMode) |
| { |
| |
| const auto *pathObj = GetImplAs<PathGL>(path); |
| mFunctions->stencilThenCoverFillPathNV(pathObj->getPathID(), fillMode, mask, coverMode); |
| |
| ASSERT(mFunctions->getError() == GL_NO_ERROR); |
| } |
| |
| void RendererGL::stencilThenCoverStrokePath(const gl::ContextState &state, |
| const gl::Path *path, |
| GLint reference, |
| GLuint mask, |
| GLenum coverMode) |
| { |
| |
| const auto *pathObj = GetImplAs<PathGL>(path); |
| mFunctions->stencilThenCoverStrokePathNV(pathObj->getPathID(), reference, mask, coverMode); |
| |
| ASSERT(mFunctions->getError() == GL_NO_ERROR); |
| } |
| |
| void RendererGL::coverFillPathInstanced(const gl::ContextState &state, |
| const std::vector<gl::Path *> &paths, |
| GLenum coverMode, |
| GLenum transformType, |
| const GLfloat *transformValues) |
| { |
| const auto &pathObjs = GatherPaths(paths); |
| |
| mFunctions->coverFillPathInstancedNV(static_cast<GLsizei>(pathObjs.size()), GL_UNSIGNED_INT, |
| &pathObjs[0], 0, coverMode, transformType, |
| transformValues); |
| |
| ASSERT(mFunctions->getError() == GL_NO_ERROR); |
| } |
| void RendererGL::coverStrokePathInstanced(const gl::ContextState &state, |
| const std::vector<gl::Path *> &paths, |
| GLenum coverMode, |
| GLenum transformType, |
| const GLfloat *transformValues) |
| { |
| const auto &pathObjs = GatherPaths(paths); |
| |
| mFunctions->coverStrokePathInstancedNV(static_cast<GLsizei>(pathObjs.size()), GL_UNSIGNED_INT, |
| &pathObjs[0], 0, coverMode, transformType, |
| transformValues); |
| |
| ASSERT(mFunctions->getError() == GL_NO_ERROR); |
| } |
| void RendererGL::stencilFillPathInstanced(const gl::ContextState &state, |
| const std::vector<gl::Path *> &paths, |
| GLenum fillMode, |
| GLuint mask, |
| GLenum transformType, |
| const GLfloat *transformValues) |
| { |
| const auto &pathObjs = GatherPaths(paths); |
| |
| mFunctions->stencilFillPathInstancedNV(static_cast<GLsizei>(pathObjs.size()), GL_UNSIGNED_INT, |
| &pathObjs[0], 0, fillMode, mask, transformType, |
| transformValues); |
| |
| ASSERT(mFunctions->getError() == GL_NO_ERROR); |
| } |
| void RendererGL::stencilStrokePathInstanced(const gl::ContextState &state, |
| const std::vector<gl::Path *> &paths, |
| GLint reference, |
| GLuint mask, |
| GLenum transformType, |
| const GLfloat *transformValues) |
| { |
| const auto &pathObjs = GatherPaths(paths); |
| |
| mFunctions->stencilStrokePathInstancedNV(static_cast<GLsizei>(pathObjs.size()), GL_UNSIGNED_INT, |
| &pathObjs[0], 0, reference, mask, transformType, |
| transformValues); |
| |
| ASSERT(mFunctions->getError() == GL_NO_ERROR); |
| } |
| |
| void RendererGL::stencilThenCoverFillPathInstanced(const gl::ContextState &state, |
| const std::vector<gl::Path *> &paths, |
| GLenum coverMode, |
| GLenum fillMode, |
| GLuint mask, |
| GLenum transformType, |
| const GLfloat *transformValues) |
| { |
| const auto &pathObjs = GatherPaths(paths); |
| |
| mFunctions->stencilThenCoverFillPathInstancedNV( |
| static_cast<GLsizei>(pathObjs.size()), GL_UNSIGNED_INT, &pathObjs[0], 0, fillMode, mask, |
| coverMode, transformType, transformValues); |
| |
| ASSERT(mFunctions->getError() == GL_NO_ERROR); |
| } |
| void RendererGL::stencilThenCoverStrokePathInstanced(const gl::ContextState &state, |
| const std::vector<gl::Path *> &paths, |
| GLenum coverMode, |
| GLint reference, |
| GLuint mask, |
| GLenum transformType, |
| const GLfloat *transformValues) |
| { |
| const auto &pathObjs = GatherPaths(paths); |
| |
| mFunctions->stencilThenCoverStrokePathInstancedNV( |
| static_cast<GLsizei>(pathObjs.size()), GL_UNSIGNED_INT, &pathObjs[0], 0, reference, mask, |
| coverMode, transformType, transformValues); |
| |
| ASSERT(mFunctions->getError() == GL_NO_ERROR); |
| } |
| |
| GLenum RendererGL::getResetStatus() |
| { |
| return mFunctions->getGraphicsResetStatus(); |
| } |
| |
| ContextImpl *RendererGL::createContext(const gl::ContextState &state) |
| { |
| return new ContextGL(state, this); |
| } |
| |
| void RendererGL::insertEventMarker(GLsizei length, const char *marker) |
| { |
| mFunctions->debugMessageInsert(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_MARKER, 0, |
| GL_DEBUG_SEVERITY_NOTIFICATION, length, marker); |
| } |
| |
| void RendererGL::pushGroupMarker(GLsizei length, const char *marker) |
| { |
| mFunctions->pushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, length, marker); |
| } |
| |
| void RendererGL::popGroupMarker() |
| { |
| mFunctions->popDebugGroup(); |
| } |
| |
| std::string RendererGL::getVendorString() const |
| { |
| return std::string(reinterpret_cast<const char *>(mFunctions->getString(GL_VENDOR))); |
| } |
| |
| std::string RendererGL::getRendererDescription() const |
| { |
| std::string nativeVendorString( |
| reinterpret_cast<const char *>(mFunctions->getString(GL_VENDOR))); |
| std::string nativeRendererString( |
| reinterpret_cast<const char *>(mFunctions->getString(GL_RENDERER))); |
| |
| std::ostringstream rendererString; |
| rendererString << nativeVendorString << " " << nativeRendererString << " OpenGL"; |
| if (mFunctions->standard == STANDARD_GL_ES) |
| { |
| rendererString << " ES"; |
| } |
| rendererString << " " << mFunctions->version.major << "." << mFunctions->version.minor; |
| if (mFunctions->standard == STANDARD_GL_DESKTOP) |
| { |
| // Some drivers (NVIDIA) use a profile mask of 0 when in compatibility profile. |
| if ((mFunctions->profile & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) != 0 || |
| (mFunctions->isAtLeastGL(gl::Version(3, 2)) && mFunctions->profile == 0)) |
| { |
| rendererString << " compatibility"; |
| } |
| else if ((mFunctions->profile & GL_CONTEXT_CORE_PROFILE_BIT) != 0) |
| { |
| rendererString << " core"; |
| } |
| } |
| |
| return rendererString.str(); |
| } |
| |
| const gl::Version &RendererGL::getMaxSupportedESVersion() const |
| { |
| // Force generation of caps |
| getNativeCaps(); |
| |
| return mMaxSupportedESVersion; |
| } |
| |
| void RendererGL::generateCaps(gl::Caps *outCaps, |
| gl::TextureCapsMap *outTextureCaps, |
| gl::Extensions *outExtensions, |
| gl::Limitations * /* outLimitations */) const |
| { |
| nativegl_gl::GenerateCaps(mFunctions, mWorkarounds, outCaps, outTextureCaps, outExtensions, |
| &mMaxSupportedESVersion); |
| } |
| |
| GLint RendererGL::getGPUDisjoint() |
| { |
| // TODO(ewell): On GLES backends we should find a way to reliably query disjoint events |
| return 0; |
| } |
| |
| GLint64 RendererGL::getTimestamp() |
| { |
| GLint64 result = 0; |
| mFunctions->getInteger64v(GL_TIMESTAMP, &result); |
| return result; |
| } |
| |
| void RendererGL::ensureCapsInitialized() const |
| { |
| if (!mCapsInitialized) |
| { |
| generateCaps(&mNativeCaps, &mNativeTextureCaps, &mNativeExtensions, &mNativeLimitations); |
| mCapsInitialized = true; |
| } |
| } |
| |
| const gl::Caps &RendererGL::getNativeCaps() const |
| { |
| ensureCapsInitialized(); |
| return mNativeCaps; |
| } |
| |
| const gl::TextureCapsMap &RendererGL::getNativeTextureCaps() const |
| { |
| ensureCapsInitialized(); |
| return mNativeTextureCaps; |
| } |
| |
| const gl::Extensions &RendererGL::getNativeExtensions() const |
| { |
| ensureCapsInitialized(); |
| return mNativeExtensions; |
| } |
| |
| const gl::Limitations &RendererGL::getNativeLimitations() const |
| { |
| ensureCapsInitialized(); |
| return mNativeLimitations; |
| } |
| |
| gl::Error RendererGL::dispatchCompute(const gl::ContextState &data, |
| GLuint numGroupsX, |
| GLuint numGroupsY, |
| GLuint numGroupsZ) |
| { |
| ANGLE_TRY(mStateManager->setDispatchComputeState(data)); |
| mFunctions->dispatchCompute(numGroupsX, numGroupsY, numGroupsZ); |
| return gl::NoError(); |
| } |
| |
| } // namespace rx |