Mercurial > hg > orthanc-stone
changeset 947:1091b2adeb5a toa2019081001
Fixed animation frame stopping when returning false + big work on the OpenGL
objects to make them lost context-safe + debug code to forcefully tag a
context as lost + debug macros
line wrap: on
line diff
--- a/Applications/Generic/GuiAdapter.cpp Tue Aug 06 15:07:23 2019 +0200 +++ b/Applications/Generic/GuiAdapter.cpp Sat Aug 10 13:07:31 2019 +0200 @@ -550,10 +550,21 @@ // SDL ONLY void GuiAdapter::OnAnimationFrame() { + std::vector<size_t> disabledAnimationHandlers; for (size_t i = 0; i < animationFrameHandlers_.size(); i++) { // TODO: fix time - (*(animationFrameHandlers_[i].first))(0, animationFrameHandlers_[i].second); + bool goOn = (*(animationFrameHandlers_[i].first))(0, animationFrameHandlers_[i].second); + + // If the function returns false, we need to emulate what happens in Web + // and remove the function from the handlers... + if (!goOn) + disabledAnimationHandlers.push_back(i); + } + for (size_t i = 0; i < disabledAnimationHandlers.size(); i++) + { + ORTHANC_ASSERT(animationFrameHandlers_.begin() + disabledAnimationHandlers[i] < animationFrameHandlers_.end()); + animationFrameHandlers_.erase(animationFrameHandlers_.begin() + disabledAnimationHandlers[i]); } } @@ -695,6 +706,10 @@ } } + + // extern void Debug_SetContextToBeKilled(std::string title); + // extern void Debug_SetContextToBeRestored(std::string title); + // SDL ONLY void GuiAdapter::RequestAnimationFrame(OnAnimationFrameFunc func, void* userData) { @@ -846,6 +861,23 @@ // window.GetWindow().ToggleMaximize(); //TODO: move to particular handler break; + // This commented out code was used to debug the context + // loss/restoring code (2019-08-10) + // case SDLK_k: + // { + // SDL_Window* window = SDL_GetWindowFromID(event.window.windowID); + // std::string windowTitle(SDL_GetWindowTitle(window)); + // Debug_SetContextToBeKilled(windowTitle); + // } + // break; + // case SDLK_l: + // { + // SDL_Window* window = SDL_GetWindowFromID(event.window.windowID); + // std::string windowTitle(SDL_GetWindowTitle(window)); + // Debug_SetContextToBeRestored(windowTitle); + // } + // break; + case SDLK_q: stop = true; break;
--- a/Applications/Sdl/SdlOpenGLContext.cpp Tue Aug 06 15:07:23 2019 +0200 +++ b/Applications/Sdl/SdlOpenGLContext.cpp Sat Aug 10 13:07:31 2019 +0200 @@ -20,6 +20,7 @@ #include "SdlOpenGLContext.h" +#include "../../Framework/StoneException.h" #if ORTHANC_ENABLE_SDL == 1 @@ -38,8 +39,10 @@ SdlOpenGLContext::SdlOpenGLContext(const char* title, unsigned int width, unsigned int height, - bool allowDpiScaling) : - window_(title, width, height, true /* enable OpenGL */, allowDpiScaling) + bool allowDpiScaling) + : window_(title, width, height, true /* enable OpenGL */, allowDpiScaling) + , context_(NULL) + , contextLost_(false) { context_ = SDL_GL_CreateContext(window_.GetObject()); @@ -83,15 +86,53 @@ } + bool SdlOpenGLContext::IsContextLost() const + { + return contextLost_; + } + + + void SdlOpenGLContext::SetLostContext() + { + contextLost_ = true; + } + + void SdlOpenGLContext::RestoreLostContext() + { + contextLost_ = false; + } + + // extern bool Debug_MustContextBeKilled(std::string title); + // extern void Debug_Context_ClearKillFlag(std::string title); + void SdlOpenGLContext::MakeCurrent() { + if (IsContextLost()) + throw OpenGLContextLostException(context_); + + // <DEBUG STUFF> + // This is used for context loss simulation + // SDL_Window* internalWindow = GetWindow().GetObject(); + // std::string title(SDL_GetWindowTitle(internalWindow)); + + // if (Debug_MustContextBeKilled(title)) + // { + // Debug_Context_ClearKillFlag(title); + // SetLostContext(); + // throw OpenGLContextLostException(context_); + // } + // </DEBUG STUFF> + if (SDL_GL_MakeCurrent(window_.GetObject(), context_) != 0) { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, - "Cannot set current OpenGL context"); + const char* errText = SDL_GetError(); + std::stringstream ss; + ss << "Cannot set current OpenGL context. SDL error text: " << errText; + std::string errStr = ss.str(); + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, errStr.c_str()); } - // This makes our buffer swap syncronized with the monitor's vertical refresh + // This makes our buffer swap synchronized with the monitor's vertical refresh SDL_GL_SetSwapInterval(1); }
--- a/Applications/Sdl/SdlOpenGLContext.h Tue Aug 06 15:07:23 2019 +0200 +++ b/Applications/Sdl/SdlOpenGLContext.h Sat Aug 10 13:07:31 2019 +0200 @@ -26,6 +26,8 @@ #include "../../Framework/OpenGL/IOpenGLContext.h" #include "SdlWindow.h" +#include <Core/Enumerations.h> + namespace OrthancStone { class SdlOpenGLContext : public OpenGL::IOpenGLContext @@ -33,6 +35,7 @@ private: SdlWindow window_; SDL_GLContext context_; + bool contextLost_; public: SdlOpenGLContext(const char* title, @@ -47,13 +50,23 @@ return window_; } - virtual void MakeCurrent(); + virtual bool IsContextLost() const ORTHANC_OVERRIDE; + + virtual void SetLostContext() ORTHANC_OVERRIDE; + virtual void RestoreLostContext() ORTHANC_OVERRIDE; - virtual void SwapBuffer(); + virtual void MakeCurrent() ORTHANC_OVERRIDE; + + virtual void SwapBuffer() ORTHANC_OVERRIDE; - virtual unsigned int GetCanvasWidth() const; + virtual unsigned int GetCanvasWidth() const ORTHANC_OVERRIDE; + + virtual unsigned int GetCanvasHeight() const ORTHANC_OVERRIDE; - virtual unsigned int GetCanvasHeight() const; + virtual void* DebugGetInternalContext() const ORTHANC_OVERRIDE + { + return context_; + } }; }
--- a/Framework/Fonts/GlyphAlphabet.cpp Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/Fonts/GlyphAlphabet.cpp Sat Aug 10 13:07:31 2019 +0200 @@ -34,7 +34,6 @@ assert(it->second != NULL); delete it->second; } - content_.clear(); lineHeight_ = 0; }
--- a/Framework/OpenGL/IOpenGLContext.h Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/OpenGL/IOpenGLContext.h Sat Aug 10 13:07:31 2019 +0200 @@ -34,6 +34,8 @@ { } + virtual bool IsContextLost() const = 0; + virtual void MakeCurrent() = 0; virtual void SwapBuffer() = 0; @@ -41,6 +43,13 @@ virtual unsigned int GetCanvasWidth() const = 0; virtual unsigned int GetCanvasHeight() const = 0; + + virtual void* DebugGetInternalContext() const = 0; + + // This is for manual context loss (debug purposes) + virtual void SetLostContext() = 0; + virtual void RestoreLostContext() = 0; + }; } }
--- a/Framework/OpenGL/OpenGLIncludes.h Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/OpenGL/OpenGLIncludes.h Sat Aug 10 13:07:31 2019 +0200 @@ -39,3 +39,74 @@ # include <GL/gl.h> # include <GL/glext.h> #endif + +#if ORTHANC_ENABLE_SDL == 1 +#include <SDL_video.h> + +#define ORTHANC_OPENGL_CHECK(name) \ +if(true) \ +{ \ + GLenum error = glGetError(); \ + if (error != GL_NO_ERROR) { \ + SDL_GLContext ctx = SDL_GL_GetCurrentContext(); \ + LOG(ERROR) << "Error when calling " << name << " | current context is: 0x" << std::hex << ctx << " | error code is " << error; \ + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,"OpenGL error in " name " | See log."); \ + } \ +} else (void)0 + +#define ORTHANC_OPENGL_TRACE_CURRENT_CONTEXT(msg) \ +if(true) \ +{ \ + SDL_GLContext ctx = SDL_GL_GetCurrentContext(); \ + LOG(TRACE) << msg << " | Current OpenGL context is " << std::hex << ctx; \ +} else (void)0 + +#define ORTHANC_CHECK_CURRENT_CONTEXT(context) \ +if(true) \ +{ \ + SDL_GLContext actualCtx = SDL_GL_GetCurrentContext(); \ + void* expectedCtx = context.DebugGetInternalContext(); \ + if(expectedCtx != actualCtx) \ + { \ + LOG(ERROR) << "Expected context was " << std::hex << expectedCtx << " while actual context is " << std::hex << actualCtx; \ + } \ +} else (void)0 + +#endif + +#if ORTHANC_ENABLE_WASM == 1 +#include <emscripten/html5.h> + +#define ORTHANC_OPENGL_CHECK(name) \ +if(true) \ +{ \ + GLenum error = glGetError(); \ + if (error != GL_NO_ERROR) { \ + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_get_current_context(); \ + EM_BOOL lost = emscripten_is_webgl_context_lost(ctx); \ + LOG(ERROR) << "Error when calling " << name << " | current context is: 0x" << std::hex << ctx << " | error code is " << error << " | emscripten_is_webgl_context_lost = " << lost; \ + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError,"OpenGL error in " name " | See log."); \ + } \ +} else (void)0 + +#define ORTHANC_OPENGL_TRACE_CURRENT_CONTEXT(msg) \ +if(true) \ +{ \ + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_get_current_context(); \ + LOG(TRACE) << msg << " | Current OpenGL context is " << std::hex << ctx; \ +} else (void)0 + +#define ORTHANC_CHECK_CURRENT_CONTEXT(context) \ +if(true) \ +{ \ + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_get_current_context(); \ + void* actualCtx = reinterpret_cast<void*>(ctx); \ + void* expectedCtx = context.DebugGetInternalContext(); \ + if(expectedCtx != actualCtx) \ + { \ + LOG(ERROR) << "Expected context was " << std::hex << expectedCtx << " while actual context is " << std::hex << actualCtx; \ + } \ +} else (void)0 + +#endif +
--- a/Framework/OpenGL/OpenGLProgram.cpp Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/OpenGL/OpenGLProgram.cpp Sat Aug 10 13:07:31 2019 +0200 @@ -22,17 +22,19 @@ #include "OpenGLProgram.h" #include "OpenGLShader.h" +#include "IOpenGLContext.h" #include <Core/OrthancException.h> - namespace OrthancStone { namespace OpenGL { - OpenGLProgram::OpenGLProgram() + OpenGLProgram::OpenGLProgram(OpenGL::IOpenGLContext& context) + : context_(context) { program_ = glCreateProgram(); + ORTHANC_OPENGL_CHECK("glCreateProgram"); if (program_ == 0) { throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, @@ -43,17 +45,45 @@ OpenGLProgram::~OpenGLProgram() { - assert(program_ != 0); - glDeleteProgram(program_); + try + { + if (!context_.IsContextLost()) + { + ORTHANC_CHECK_CURRENT_CONTEXT(context_); + ORTHANC_OPENGL_TRACE_CURRENT_CONTEXT("About to call glDeleteProgram"); + assert(program_ != 0); + glDeleteProgram(program_); + ORTHANC_OPENGL_CHECK("glDeleteProgram"); + } + } + catch (const Orthanc::OrthancException& e) + { + if (e.HasDetails()) + { + LOG(ERROR) << "OrthancException in ~OpenGLProgram: " << e.What() << " Details: " << e.GetDetails(); + } + else + { + LOG(ERROR) << "OrthancException in ~OpenGLProgram: " << e.What(); + } + } + catch (const std::exception& e) + { + LOG(ERROR) << "std::exception in ~OpenGLProgram: " << e.what(); + } + catch (...) + { + LOG(ERROR) << "Unknown exception in ~OpenGLProgram"; + } } - void OpenGLProgram::Use() { + //ORTHANC_OPENGL_TRACE_CURRENT_CONTEXT("About to call glUseProgram"); glUseProgram(program_); + ORTHANC_OPENGL_CHECK("glUseProgram"); } - - + void OpenGLProgram::CompileShaders(const std::string& vertexCode, const std::string& fragmentCode) { @@ -63,15 +93,19 @@ OpenGLShader fragmentShader(GL_FRAGMENT_SHADER, fragmentCode); glAttachShader(program_, vertexShader.Release()); + ORTHANC_OPENGL_CHECK("glAttachShader"); glAttachShader(program_, fragmentShader.Release()); + ORTHANC_OPENGL_CHECK("glAttachShader"); glLinkProgram(program_); + ORTHANC_OPENGL_CHECK("glLinkProgram"); glValidateProgram(program_); + ORTHANC_OPENGL_CHECK("glValidateProgram"); } - GLint OpenGLProgram::GetUniformLocation(const std::string& name) { GLint location = glGetUniformLocation(program_, name.c_str()); + ORTHANC_OPENGL_CHECK("glGetUniformLocation"); if (location == -1) { @@ -88,6 +122,7 @@ GLint OpenGLProgram::GetAttributeLocation(const std::string& name) { GLint location = glGetAttribLocation(program_, name.c_str()); + ORTHANC_OPENGL_CHECK("glGetAttribLocation"); if (location == -1) {
--- a/Framework/OpenGL/OpenGLProgram.h Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/OpenGL/OpenGLProgram.h Sat Aug 10 13:07:31 2019 +0200 @@ -30,14 +30,15 @@ { namespace OpenGL { + class IOpenGLContext; + class OpenGLProgram : public boost::noncopyable { - private: - GLuint program_; - public: // WARNING: A global OpenGL context must be active to create this object! - OpenGLProgram(); + // the context is only passed so that it can be checked for loss + // when destructing the program resource + OpenGLProgram(OpenGL::IOpenGLContext& context); ~OpenGLProgram(); @@ -50,6 +51,9 @@ GLint GetUniformLocation(const std::string& name); GLint GetAttributeLocation(const std::string& name); + private: + GLuint program_; + OpenGL::IOpenGLContext& context_; }; } }
--- a/Framework/OpenGL/OpenGLShader.cpp Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/OpenGL/OpenGLShader.cpp Sat Aug 10 13:07:31 2019 +0200 @@ -37,6 +37,7 @@ sourceString[0] = source.c_str(); sourceStringLengths[0] = static_cast<GLint>(source.length()); GLuint shader = glCreateShader(type); + ORTHANC_OPENGL_CHECK("glCreateShader"); if (shader == 0) { @@ -47,18 +48,24 @@ { // Assign and compile the source to the shader object glShaderSource(shader, 1, sourceString, sourceStringLengths); + ORTHANC_OPENGL_CHECK("glShaderSource"); glCompileShader(shader); + ORTHANC_OPENGL_CHECK("glCompileShader"); // Check if there were errors int infoLen = 0; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); + ORTHANC_OPENGL_CHECK("glGetShaderiv"); if (infoLen > 1) // Might be equal to 1, which amounts to no error { std::string infoLog; infoLog.resize(infoLen + 1); glGetShaderInfoLog(shader, infoLen, NULL, &infoLog[0]); + ORTHANC_OPENGL_CHECK("glGetShaderInfoLog"); + ORTHANC_OPENGL_TRACE_CURRENT_CONTEXT("About to call glDeleteShader"); glDeleteShader(shader); + ORTHANC_OPENGL_CHECK("glDeleteShader"); throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Error while creating an OpenGL shader: " + infoLog); @@ -81,13 +88,36 @@ OpenGLShader::~OpenGLShader() { - if (isValid_) + try + { + if (isValid_) + { + ORTHANC_OPENGL_TRACE_CURRENT_CONTEXT("About to call glDeleteShader"); + glDeleteShader(shader_); + ORTHANC_OPENGL_CHECK("glDeleteShader"); + } + } + catch (const Orthanc::OrthancException& e) { - glDeleteShader(shader_); + if (e.HasDetails()) + { + LOG(ERROR) << "OrthancException in ~OpenGLShader: " << e.What() << " Details: " << e.GetDetails(); + } + else + { + LOG(ERROR) << "OrthancException in ~OpenGLShader: " << e.What(); + } + } + catch (const std::exception& e) + { + LOG(ERROR) << "std::exception in ~OpenGLShader: " << e.what(); + } + catch (...) + { + LOG(ERROR) << "Unknown exception in ~OpenGLShader"; } } - GLuint OpenGLShader::Release() { if (isValid_)
--- a/Framework/OpenGL/OpenGLTexture.cpp Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/OpenGL/OpenGLTexture.cpp Sat Aug 10 13:07:31 2019 +0200 @@ -20,6 +20,7 @@ #include "OpenGLTexture.h" +#include "IOpenGLContext.h" #include <Core/OrthancException.h> @@ -27,46 +28,82 @@ { namespace OpenGL { - OpenGLTexture::OpenGLTexture() : - width_(0), - height_(0) + OpenGLTexture::OpenGLTexture(OpenGL::IOpenGLContext& context) + : width_(0) + , height_(0) + , context_(context) { - // Generate a texture object - glGenTextures(1, &texture_); - if (texture_ == 0) + if (!context_.IsContextLost()) { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, - "Cannot create an OpenGL program"); + // context is made current externally. Let's check this! + ORTHANC_CHECK_CURRENT_CONTEXT(context_); + // Generate a texture object + glGenTextures(1, &texture_); + if (texture_ == 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, + "Cannot create an OpenGL program"); + } } } - OpenGLTexture::~OpenGLTexture() { - assert(texture_ != 0); - glDeleteTextures(1, &texture_); + try + { + if (!context_.IsContextLost()) + { + // context is made current externally. Let's check this! + ORTHANC_CHECK_CURRENT_CONTEXT(context_); + assert(texture_ != 0); + ORTHANC_OPENGL_TRACE_CURRENT_CONTEXT("About to call glDeleteTextures"); + glDeleteTextures(1, &texture_); + } + } + catch (const Orthanc::OrthancException& e) + { + if (e.HasDetails()) + { + LOG(ERROR) << "OrthancException in ~OpenGLTexture: " << e.What() << " Details: " << e.GetDetails(); + } + else + { + LOG(ERROR) << "OrthancException in ~OpenGLTexture: " << e.What(); + } + } + catch (const std::exception& e) + { + LOG(ERROR) << "std::exception in ~OpenGLTexture: " << e.what(); + } + catch (...) + { + LOG(ERROR) << "Unknown exception in ~OpenGLTexture"; + } } - void OpenGLTexture::Load(const Orthanc::ImageAccessor& image, bool isLinearInterpolation) { - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // Disable byte-alignment restriction - - if (image.GetPitch() != image.GetBytesPerPixel() * image.GetWidth()) + // context is made current externally. Let's check this! + ORTHANC_CHECK_CURRENT_CONTEXT(context_); + if (!context_.IsContextLost()) { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented, - "Unsupported non-zero padding"); - } + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // Disable byte-alignment restriction - // Bind it - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, texture_); + if (image.GetPitch() != image.GetBytesPerPixel() * image.GetWidth()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented, + "Unsupported non-zero padding"); + } - GLenum sourceFormat, internalFormat; + // Bind it + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture_); - switch (image.GetFormat()) - { + GLenum sourceFormat, internalFormat; + + switch (image.GetFormat()) + { case Orthanc::PixelFormat_Grayscale8: sourceFormat = GL_RED; internalFormat = GL_RED; @@ -84,22 +121,23 @@ default: throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented, - "No support for this format in OpenGL textures: " + - std::string(EnumerationToString(image.GetFormat()))); - } + "No support for this format in OpenGL textures: " + + std::string(EnumerationToString(image.GetFormat()))); + } + + width_ = image.GetWidth(); + height_ = image.GetHeight(); + + GLint interpolation = (isLinearInterpolation ? GL_LINEAR : GL_NEAREST); - width_ = image.GetWidth(); - height_ = image.GetHeight(); - - GLint interpolation = (isLinearInterpolation ? GL_LINEAR : GL_NEAREST); - - // Load the texture from the image buffer - glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, image.GetWidth(), image.GetHeight(), - 0, sourceFormat, GL_UNSIGNED_BYTE, image.GetBuffer()); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, interpolation); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, interpolation); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + // Load the texture from the image buffer + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, image.GetWidth(), image.GetHeight(), + 0, sourceFormat, GL_UNSIGNED_BYTE, image.GetBuffer()); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, interpolation); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, interpolation); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } }
--- a/Framework/OpenGL/OpenGLTexture.h Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/OpenGL/OpenGLTexture.h Sat Aug 10 13:07:31 2019 +0200 @@ -32,15 +32,18 @@ { namespace OpenGL { + class IOpenGLContext; + class OpenGLTexture : public boost::noncopyable { private: GLuint texture_; unsigned int width_; unsigned int height_; + OpenGL::IOpenGLContext& context_; public: - OpenGLTexture(); + OpenGLTexture(OpenGL::IOpenGLContext& context); ~OpenGLTexture();
--- a/Framework/OpenGL/WebAssemblyOpenGLContext.cpp Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/OpenGL/WebAssemblyOpenGLContext.cpp Sat Aug 10 13:07:31 2019 +0200 @@ -21,6 +21,8 @@ #include "WebAssemblyOpenGLContext.h" +#include "../StoneException.h" + #include <Core/OrthancException.h> #include <emscripten/html5.h> @@ -35,14 +37,16 @@ class WebAssemblyOpenGLContext::PImpl { private: - std::string canvas_; - EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context_; - unsigned int canvasWidth_; - unsigned int canvasHeight_; + std::string canvas_; + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context_; + unsigned int canvasWidth_; + unsigned int canvasHeight_; + bool isContextLost_; public: - PImpl(const std::string& canvas) : - canvas_(canvas) + PImpl(const std::string& canvas) + : canvas_(canvas) + , isContextLost_(false) { // Context configuration EmscriptenWebGLContextAttributes attr; @@ -60,6 +64,27 @@ UpdateSize(); } + void* DebugGetInternalContext() const + { + return reinterpret_cast<void*>(context_); + } + + bool IsContextLost() const + { + bool apiFlag = (emscripten_is_webgl_context_lost(context_) != 0); + bool ownFlag = isContextLost_; + if (ownFlag != apiFlag) + { + LOG(WARNING) << "Context loss, according to emscripten, is: " << apiFlag << " | while, according to internal state, is: " << ownFlag; + } + return ownFlag | apiFlag; + } + + void SetLostContext() + { + isContextLost_ = true; + } + ~PImpl() { emscripten_webgl_destroy_context(context_); @@ -72,11 +97,17 @@ void MakeCurrent() { + if (IsContextLost()) + { + LOG(ERROR) << "MakeCurrent() called on lost context " << context_; + throw OpenGLContextLostException(reinterpret_cast<void*>(context_)); + } + if (emscripten_is_webgl_context_lost(context_)) { - LOG(ERROR) << "OpenGL context has been lost! for canvas: " << canvas_; - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, - "OpenGL context has been lost!"); + LOG(ERROR) << "OpenGL context has been lost for canvas: " << canvas_; + SetLostContext(); + throw OpenGLContextLostException(reinterpret_cast<void*>(context_)); } if (emscripten_webgl_make_context_current(context_) != EMSCRIPTEN_RESULT_SUCCESS) @@ -146,13 +177,32 @@ { } + bool WebAssemblyOpenGLContext::IsContextLost() const + { + return pimpl_->IsContextLost(); + } + + void WebAssemblyOpenGLContext::RestoreLostContext() + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + + void WebAssemblyOpenGLContext::SetLostContext() + { + pimpl_->SetLostContext(); + } + + void* WebAssemblyOpenGLContext::DebugGetInternalContext() const + { + return pimpl_->DebugGetInternalContext(); + } + void WebAssemblyOpenGLContext::MakeCurrent() { assert(pimpl_.get() != NULL); pimpl_->MakeCurrent(); } - void WebAssemblyOpenGLContext::SwapBuffer() { assert(pimpl_.get() != NULL);
--- a/Framework/OpenGL/WebAssemblyOpenGLContext.h Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/OpenGL/WebAssemblyOpenGLContext.h Sat Aug 10 13:07:31 2019 +0200 @@ -39,6 +39,8 @@ #include "IOpenGLContext.h" +#include <Core/Enumerations.h> + #include <boost/shared_ptr.hpp> namespace OrthancStone @@ -54,13 +56,20 @@ public: WebAssemblyOpenGLContext(const std::string& canvas); - virtual void MakeCurrent(); + virtual bool IsContextLost() const ORTHANC_OVERRIDE; + + virtual void SetLostContext() ORTHANC_OVERRIDE; + virtual void RestoreLostContext() ORTHANC_OVERRIDE; - virtual void SwapBuffer(); + virtual void MakeCurrent() ORTHANC_OVERRIDE; - virtual unsigned int GetCanvasWidth() const; + virtual void SwapBuffer() ORTHANC_OVERRIDE; + + virtual unsigned int GetCanvasWidth() const ORTHANC_OVERRIDE; - virtual unsigned int GetCanvasHeight() const; + virtual unsigned int GetCanvasHeight() const ORTHANC_OVERRIDE; + + virtual void* DebugGetInternalContext() const ORTHANC_OVERRIDE; void UpdateSize();
--- a/Framework/Scene2D/CairoCompositor.cpp Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/Scene2D/CairoCompositor.cpp Sat Aug 10 13:07:31 2019 +0200 @@ -44,7 +44,6 @@ } } - Internals::CompositorHelper::ILayerRenderer* CairoCompositor::Create(const ISceneLayer& layer) { switch (layer.GetType())
--- a/Framework/Scene2D/Internals/OpenGLAdvancedPolylineRenderer.h Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/Scene2D/Internals/OpenGLAdvancedPolylineRenderer.h Sat Aug 10 13:07:31 2019 +0200 @@ -47,7 +47,10 @@ unsigned int canvasWidth, unsigned int canvasHeight) { - program_.Apply(*data_, transform, true, true); + if (!context_.IsContextLost()) + { + program_.Apply(*data_, transform, true, true); + } } virtual void Update(const ISceneLayer& layer)
--- a/Framework/Scene2D/Internals/OpenGLBasicPolylineRenderer.cpp Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/Scene2D/Internals/OpenGLBasicPolylineRenderer.cpp Sat Aug 10 13:07:31 2019 +0200 @@ -34,53 +34,55 @@ layer_.Copy(layer); } - void OpenGLBasicPolylineRenderer::Render(const AffineTransform2D& transform) { - AffineTransform2D t = AffineTransform2D::Combine( - AffineTransform2D::CreateOpenGLClipspace(context_.GetCanvasWidth(), context_.GetCanvasHeight()), - transform); + if (!context_.IsContextLost()) + { + AffineTransform2D t = AffineTransform2D::Combine( + AffineTransform2D::CreateOpenGLClipspace(context_.GetCanvasWidth(), context_.GetCanvasHeight()), + transform); - glUseProgram(0); + glUseProgram(0); - glBegin(GL_LINES); + glBegin(GL_LINES); - for (size_t i = 0; i < layer_.GetChainsCount(); i++) - { - const Color& color = layer_.GetColor(i); - glColor3ub(color.GetRed(), color.GetGreen(), color.GetBlue()); + for (size_t i = 0; i < layer_.GetChainsCount(); i++) + { + const Color& color = layer_.GetColor(i); + glColor3ub(color.GetRed(), color.GetGreen(), color.GetBlue()); - const PolylineSceneLayer::Chain& chain = layer_.GetChain(i); + const PolylineSceneLayer::Chain& chain = layer_.GetChain(i); - if (chain.size() > 1) - { - ScenePoint2D previous = chain[0].Apply(t); + if (chain.size() > 1) + { + ScenePoint2D previous = chain[0].Apply(t); - for (size_t j = 1; j < chain.size(); j++) - { - ScenePoint2D p = chain[j].Apply(t); + for (size_t j = 1; j < chain.size(); j++) + { + ScenePoint2D p = chain[j].Apply(t); - glVertex2f(static_cast<GLfloat>(previous.GetX()), - static_cast<GLfloat>(previous.GetY())); - glVertex2f(static_cast<GLfloat>(p.GetX()), - static_cast<GLfloat>(p.GetY())); + glVertex2f(static_cast<GLfloat>(previous.GetX()), + static_cast<GLfloat>(previous.GetY())); + glVertex2f(static_cast<GLfloat>(p.GetX()), + static_cast<GLfloat>(p.GetY())); - previous = p; - } + previous = p; + } - if (layer_.IsClosedChain(i)) - { - ScenePoint2D p = chain[0].Apply(t); + if (layer_.IsClosedChain(i)) + { + ScenePoint2D p = chain[0].Apply(t); - glVertex2f(static_cast<GLfloat>(previous.GetX()), - static_cast<GLfloat>(previous.GetY())); - glVertex2f(static_cast<GLfloat>(p.GetX()), - static_cast<GLfloat>(p.GetY())); + glVertex2f(static_cast<GLfloat>(previous.GetX()), + static_cast<GLfloat>(previous.GetY())); + glVertex2f(static_cast<GLfloat>(p.GetX()), + static_cast<GLfloat>(p.GetY())); + } } } + + glEnd(); } - - glEnd(); }
--- a/Framework/Scene2D/Internals/OpenGLColorTextureProgram.cpp Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/Scene2D/Internals/OpenGLColorTextureProgram.cpp Sat Aug 10 13:07:31 2019 +0200 @@ -36,8 +36,9 @@ { namespace Internals { - OpenGLColorTextureProgram::OpenGLColorTextureProgram(OpenGL::IOpenGLContext& context) : - program_(context, FRAGMENT_SHADER) + OpenGLColorTextureProgram::OpenGLColorTextureProgram(OpenGL::IOpenGLContext& context) + : program_(context, FRAGMENT_SHADER) + , context_(context) { } @@ -46,18 +47,21 @@ const AffineTransform2D& transform, bool useAlpha) { - OpenGLTextureProgram::Execution execution(program_, texture, transform); - - if (useAlpha) + if (!context_.IsContextLost()) { - glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - execution.DrawTriangles(); - glDisable(GL_BLEND); - } - else - { - execution.DrawTriangles(); + OpenGLTextureProgram::Execution execution(program_, texture, transform); + + if (useAlpha) + { + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + execution.DrawTriangles(); + glDisable(GL_BLEND); + } + else + { + execution.DrawTriangles(); + } } } }
--- a/Framework/Scene2D/Internals/OpenGLColorTextureProgram.h Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/Scene2D/Internals/OpenGLColorTextureProgram.h Sat Aug 10 13:07:31 2019 +0200 @@ -29,15 +29,15 @@ { class OpenGLColorTextureProgram : public boost::noncopyable { - private: - OpenGLTextureProgram program_; - public: OpenGLColorTextureProgram(OpenGL::IOpenGLContext& context); void Apply(OpenGL::OpenGLTexture& texture, const AffineTransform2D& transform, bool useAlpha); + private: + OpenGLTextureProgram program_; + OpenGL::IOpenGLContext& context_; }; } }
--- a/Framework/Scene2D/Internals/OpenGLColorTextureRenderer.cpp Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/Scene2D/Internals/OpenGLColorTextureRenderer.cpp Sat Aug 10 13:07:31 2019 +0200 @@ -27,13 +27,16 @@ { void OpenGLColorTextureRenderer::LoadTexture(const ColorTextureSceneLayer& layer) { - context_.MakeCurrent(); - texture_.reset(new OpenGL::OpenGLTexture); - texture_->Load(layer.GetTexture(), layer.IsLinearInterpolation()); - layerTransform_ = layer.GetTransform(); + if (!context_.IsContextLost()) + { + context_.MakeCurrent(); + texture_.reset(new OpenGL::OpenGLTexture(context_)); + texture_->Load(layer.GetTexture(), layer.IsLinearInterpolation()); + layerTransform_ = layer.GetTransform(); + } } - + OpenGLColorTextureRenderer::OpenGLColorTextureRenderer(OpenGL::IOpenGLContext& context, OpenGLColorTextureProgram& program, const ColorTextureSceneLayer& layer) : @@ -48,7 +51,7 @@ unsigned int canvasWidth, unsigned int canvasHeight) { - if (texture_.get() != NULL) + if (!context_.IsContextLost() && texture_.get() != NULL) { program_.Apply(*texture_, AffineTransform2D::Combine(transform, layerTransform_), true); }
--- a/Framework/Scene2D/Internals/OpenGLColorTextureRenderer.h Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/Scene2D/Internals/OpenGLColorTextureRenderer.h Sat Aug 10 13:07:31 2019 +0200 @@ -32,10 +32,10 @@ class OpenGLColorTextureRenderer : public CompositorHelper::ILayerRenderer { private: - OpenGL::IOpenGLContext& context_; - OpenGLColorTextureProgram& program_; - std::auto_ptr<OpenGL::OpenGLTexture> texture_; - AffineTransform2D layerTransform_; + OpenGL::IOpenGLContext& context_; + OpenGLColorTextureProgram& program_; + std::auto_ptr<OpenGL::OpenGLTexture> texture_; + AffineTransform2D layerTransform_; void LoadTexture(const ColorTextureSceneLayer& layer);
--- a/Framework/Scene2D/Internals/OpenGLFloatTextureProgram.cpp Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/Scene2D/Internals/OpenGLFloatTextureProgram.cpp Sat Aug 10 13:07:31 2019 +0200 @@ -61,8 +61,13 @@ { namespace Internals { - OpenGLFloatTextureProgram::Data::Data(const Orthanc::ImageAccessor& texture, - bool isLinearInterpolation) + OpenGLFloatTextureProgram::Data::Data( + OpenGL::IOpenGLContext& context + , const Orthanc::ImageAccessor& texture + , bool isLinearInterpolation) + : texture_(context) + , offset_(0.0f) + , slope_(0.0f) { if (texture.GetFormat() != Orthanc::PixelFormat_Float32) { @@ -127,8 +132,9 @@ } - OpenGLFloatTextureProgram::OpenGLFloatTextureProgram(OpenGL::IOpenGLContext& context) : - program_(context, FRAGMENT_SHADER) + OpenGLFloatTextureProgram::OpenGLFloatTextureProgram(OpenGL::IOpenGLContext& context) + : program_(context, FRAGMENT_SHADER) + , context_(context) { } @@ -139,15 +145,18 @@ float windowWidth, bool invert) { - OpenGLTextureProgram::Execution execution(program_, data.GetTexture(), transform); + if (!context_.IsContextLost()) + { + OpenGLTextureProgram::Execution execution(program_, data.GetTexture(), transform); - glUniform1f(execution.GetUniformLocation("u_slope"), data.GetSlope()); - glUniform1f(execution.GetUniformLocation("u_offset"), data.GetOffset()); - glUniform1f(execution.GetUniformLocation("u_windowCenter"), windowCenter); - glUniform1f(execution.GetUniformLocation("u_windowWidth"), windowWidth); - glUniform1f(execution.GetUniformLocation("u_invert"), invert); + glUniform1f(execution.GetUniformLocation("u_slope"), data.GetSlope()); + glUniform1f(execution.GetUniformLocation("u_offset"), data.GetOffset()); + glUniform1f(execution.GetUniformLocation("u_windowCenter"), windowCenter); + glUniform1f(execution.GetUniformLocation("u_windowWidth"), windowWidth); + glUniform1f(execution.GetUniformLocation("u_invert"), invert); - execution.DrawTriangles(); + execution.DrawTriangles(); + } } } }
--- a/Framework/Scene2D/Internals/OpenGLFloatTextureProgram.h Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/Scene2D/Internals/OpenGLFloatTextureProgram.h Sat Aug 10 13:07:31 2019 +0200 @@ -38,7 +38,7 @@ float slope_; public: - Data(const Orthanc::ImageAccessor& texture, + Data(OpenGL::IOpenGLContext& context, const Orthanc::ImageAccessor& texture, bool isLinearInterpolation); float GetOffset() const @@ -57,9 +57,6 @@ } }; - private: - OpenGLTextureProgram program_; - public: OpenGLFloatTextureProgram(OpenGL::IOpenGLContext& context); @@ -68,6 +65,9 @@ float windowCenter, float windowWidth, bool invert); + private: + OpenGLTextureProgram program_; + OpenGL::IOpenGLContext& context_; }; } }
--- a/Framework/Scene2D/Internals/OpenGLFloatTextureRenderer.cpp Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/Scene2D/Internals/OpenGLFloatTextureRenderer.cpp Sat Aug 10 13:07:31 2019 +0200 @@ -28,15 +28,19 @@ void OpenGLFloatTextureRenderer::UpdateInternal(const FloatTextureSceneLayer& layer, bool loadTexture) { - if (loadTexture) + if (!context_.IsContextLost()) { - context_.MakeCurrent(); - texture_.reset(new OpenGLFloatTextureProgram::Data(layer.GetTexture(), layer.IsLinearInterpolation())); + if (loadTexture) + { + context_.MakeCurrent(); + texture_.reset(new OpenGLFloatTextureProgram::Data( + context_, layer.GetTexture(), layer.IsLinearInterpolation())); + } + + layerTransform_ = layer.GetTransform(); + layer.GetWindowing(windowCenter_, windowWidth_); + invert_ = layer.IsInverted(); } - - layerTransform_ = layer.GetTransform(); - layer.GetWindowing(windowCenter_, windowWidth_); - invert_ = layer.IsInverted(); } @@ -54,7 +58,7 @@ unsigned int canvasWidth, unsigned int canvasHeight) { - if (texture_.get() != NULL) + if (!context_.IsContextLost() && texture_.get() != NULL) { program_.Apply(*texture_, AffineTransform2D::Combine(transform, layerTransform_), windowCenter_, windowWidth_, invert_);
--- a/Framework/Scene2D/Internals/OpenGLInfoPanelRenderer.cpp Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/Scene2D/Internals/OpenGLInfoPanelRenderer.cpp Sat Aug 10 13:07:31 2019 +0200 @@ -27,18 +27,21 @@ { void OpenGLInfoPanelRenderer::LoadTexture(const InfoPanelSceneLayer& layer) { - context_.MakeCurrent(); - texture_.reset(new OpenGL::OpenGLTexture); - texture_->Load(layer.GetTexture(), layer.IsLinearInterpolation()); - anchor_ = layer.GetAnchor(); + if (!context_.IsContextLost()) + { + context_.MakeCurrent(); + texture_.reset(new OpenGL::OpenGLTexture(context_)); + texture_->Load(layer.GetTexture(), layer.IsLinearInterpolation()); + anchor_ = layer.GetAnchor(); + } } - OpenGLInfoPanelRenderer::OpenGLInfoPanelRenderer(OpenGL::IOpenGLContext& context, OpenGLColorTextureProgram& program, const InfoPanelSceneLayer& layer) : context_(context), - program_(program) + program_(program), + anchor_(BitmapAnchor_TopLeft) { LoadTexture(layer); } @@ -48,9 +51,9 @@ unsigned int canvasWidth, unsigned int canvasHeight) { - if (texture_.get() != NULL) + if (!context_.IsContextLost() && texture_.get() != NULL) { - int dx, dy; + int dx = 0, dy = 0; InfoPanelSceneLayer::ComputeAnchorLocation( dx, dy, anchor_, texture_->GetWidth(), texture_->GetHeight(), canvasWidth, canvasHeight);
--- a/Framework/Scene2D/Internals/OpenGLLinesProgram.cpp Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/Scene2D/Internals/OpenGLLinesProgram.cpp Sat Aug 10 13:07:31 2019 +0200 @@ -263,99 +263,102 @@ verticesCount_(0), thickness_(static_cast<float>(layer.GetThickness())) { - // High-level reference: - // https://mattdesl.svbtle.com/drawing-lines-is-hard - // https://forum.libcinder.org/topic/smooth-thick-lines-using-geometry-shader - - size_t countVertices = 0; - for (size_t i = 0; i < layer.GetChainsCount(); i++) + if (!context_.IsContextLost()) { - size_t countSegments = layer.GetChain(i).size() - 1; + // High-level reference: + // https://mattdesl.svbtle.com/drawing-lines-is-hard + // https://forum.libcinder.org/topic/smooth-thick-lines-using-geometry-shader - if (layer.IsClosedChain(i)) + size_t countVertices = 0; + for (size_t i = 0; i < layer.GetChainsCount(); i++) { - countSegments++; - } - - // Each segment is made of 2 triangles. One triangle is - // defined by 3 points in 2D => 6 vertices per segment. - countVertices += countSegments * 2 * 3; - } - - std::vector<float> coords, colors, miterDirections; - coords.reserve(countVertices * COMPONENTS_POSITION); - colors.reserve(countVertices * COMPONENTS_COLOR); - miterDirections.reserve(countVertices * COMPONENTS_MITER); - - for (size_t i = 0; i < layer.GetChainsCount(); i++) - { - const PolylineSceneLayer::Chain& chain = layer.GetChain(i); - - if (chain.size() > 1) - { - std::vector<Segment> segments; - for (size_t j = 1; j < chain.size(); j++) - { - segments.push_back(Segment(chain, j - 1, j)); - } + size_t countSegments = layer.GetChain(i).size() - 1; if (layer.IsClosedChain(i)) { - segments.push_back(Segment(chain, chain.size() - 1, 0)); - } - - // Try and create nice miters - for (size_t j = 1; j < segments.size(); j++) - { - Segment::CreateMiter(segments[j - 1], segments[j]); + countSegments++; } - if (layer.IsClosedChain(i)) + // Each segment is made of 2 triangles. One triangle is + // defined by 3 points in 2D => 6 vertices per segment. + countVertices += countSegments * 2 * 3; + } + + std::vector<float> coords, colors, miterDirections; + coords.reserve(countVertices * COMPONENTS_POSITION); + colors.reserve(countVertices * COMPONENTS_COLOR); + miterDirections.reserve(countVertices * COMPONENTS_MITER); + + for (size_t i = 0; i < layer.GetChainsCount(); i++) + { + const PolylineSceneLayer::Chain& chain = layer.GetChain(i); + + if (chain.size() > 1) { - Segment::CreateMiter(segments.back(), segments.front()); - } + std::vector<Segment> segments; + for (size_t j = 1; j < chain.size(); j++) + { + segments.push_back(Segment(chain, j - 1, j)); + } - for (size_t j = 0; j < segments.size(); j++) - { - if (!segments[j].IsEmpty()) + if (layer.IsClosedChain(i)) + { + segments.push_back(Segment(chain, chain.size() - 1, 0)); + } + + // Try and create nice miters + for (size_t j = 1; j < segments.size(); j++) { - segments[j].AddTriangles(coords, miterDirections, colors, layer.GetColor(i)); + Segment::CreateMiter(segments[j - 1], segments[j]); + } + + if (layer.IsClosedChain(i)) + { + Segment::CreateMiter(segments.back(), segments.front()); + } + + for (size_t j = 0; j < segments.size(); j++) + { + if (!segments[j].IsEmpty()) + { + segments[j].AddTriangles(coords, miterDirections, colors, layer.GetColor(i)); + } } } } - } - assert(coords.size() == colors.size()); + assert(coords.size() == colors.size()); - if (!coords.empty()) - { - verticesCount_ = coords.size() / COMPONENTS_POSITION; + if (!coords.empty()) + { + verticesCount_ = coords.size() / COMPONENTS_POSITION; + + context_.MakeCurrent(); + glGenBuffers(3, buffers_); - context_.MakeCurrent(); - glGenBuffers(3, buffers_); - - glBindBuffer(GL_ARRAY_BUFFER, buffers_[0]); - glBufferData(GL_ARRAY_BUFFER, sizeof(float) * coords.size(), &coords[0], GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, buffers_[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * coords.size(), &coords[0], GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, buffers_[1]); - glBufferData(GL_ARRAY_BUFFER, sizeof(float) * miterDirections.size(), &miterDirections[0], GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, buffers_[1]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * miterDirections.size(), &miterDirections[0], GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, buffers_[2]); - glBufferData(GL_ARRAY_BUFFER, sizeof(float) * colors.size(), &colors[0], GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, buffers_[2]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * colors.size(), &colors[0], GL_STATIC_DRAW); + } } } OpenGLLinesProgram::Data::~Data() { - if (!IsEmpty()) + if (!context_.IsContextLost() && !IsEmpty()) { context_.MakeCurrent(); + ORTHANC_OPENGL_TRACE_CURRENT_CONTEXT("About to call glDeleteBuffers"); glDeleteBuffers(3, buffers_); } } - GLuint OpenGLLinesProgram::Data::GetVerticesBuffer() const { if (IsEmpty()) @@ -398,19 +401,20 @@ OpenGLLinesProgram::OpenGLLinesProgram(OpenGL::IOpenGLContext& context) : context_(context) { - context_.MakeCurrent(); - - program_.reset(new OpenGL::OpenGLProgram); - program_->CompileShaders(VERTEX_SHADER, FRAGMENT_SHADER); + if (!context_.IsContextLost()) + { + context_.MakeCurrent(); + program_.reset(new OpenGL::OpenGLProgram(context_)); + program_->CompileShaders(VERTEX_SHADER, FRAGMENT_SHADER); + } } - void OpenGLLinesProgram::Apply(const Data& data, const AffineTransform2D& transform, bool antialiasing, bool scaleIndependantThickness) { - if (!data.IsEmpty()) + if (!context_.IsContextLost() && !data.IsEmpty()) { context_.MakeCurrent(); program_->Use();
--- a/Framework/Scene2D/Internals/OpenGLLookupTableTextureRenderer.cpp Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/Scene2D/Internals/OpenGLLookupTableTextureRenderer.cpp Sat Aug 10 13:07:31 2019 +0200 @@ -28,86 +28,88 @@ void OpenGLLookupTableTextureRenderer::LoadTexture( const LookupTableTextureSceneLayer& layer) { - const Orthanc::ImageAccessor& source = layer.GetTexture(); - const unsigned int width = source.GetWidth(); - const unsigned int height = source.GetHeight(); - - if ((texture_.get() == NULL) || - (texture_->GetWidth() != width) || - (texture_->GetHeight() != height)) + if (!context_.IsContextLost()) { - - texture_.reset(new Orthanc::Image( - Orthanc::PixelFormat_RGBA32, - width, - height, - false)); - } - - { + const Orthanc::ImageAccessor& source = layer.GetTexture(); + const unsigned int width = source.GetWidth(); + const unsigned int height = source.GetHeight(); - const float a = layer.GetMinValue(); - float slope = 0; - - if (layer.GetMinValue() >= layer.GetMaxValue()) - { - slope = 0; - } - else + if ((texture_.get() == NULL) || + (texture_->GetWidth() != width) || + (texture_->GetHeight() != height)) { - slope = 256.0f / (layer.GetMaxValue() - layer.GetMinValue()); - } - Orthanc::ImageAccessor target; - texture_->GetWriteableAccessor(target); - - const std::vector<uint8_t>& lut = layer.GetLookupTable(); - if (lut.size() != 4 * 256) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + texture_.reset(new Orthanc::Image( + Orthanc::PixelFormat_RGBA32, + width, + height, + false)); } - assert(source.GetFormat() == Orthanc::PixelFormat_Float32 && - target.GetFormat() == Orthanc::PixelFormat_RGBA32 && - sizeof(float) == 4); + { + + const float a = layer.GetMinValue(); + float slope = 0; + + if (layer.GetMinValue() >= layer.GetMaxValue()) + { + slope = 0; + } + else + { + slope = 256.0f / (layer.GetMaxValue() - layer.GetMinValue()); + } + + Orthanc::ImageAccessor target; + texture_->GetWriteableAccessor(target); - for (unsigned int y = 0; y < height; y++) - { - const float* p = reinterpret_cast<const float*>(source.GetConstRow(y)); - uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y)); + const std::vector<uint8_t>& lut = layer.GetLookupTable(); + if (lut.size() != 4 * 256) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } - for (unsigned int x = 0; x < width; x++) + assert(source.GetFormat() == Orthanc::PixelFormat_Float32 && + target.GetFormat() == Orthanc::PixelFormat_RGBA32 && + sizeof(float) == 4); + + for (unsigned int y = 0; y < height; y++) { - float v = (*p - a) * slope; - if (v <= 0) + const float* p = reinterpret_cast<const float*>(source.GetConstRow(y)); + uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y)); + + for (unsigned int x = 0; x < width; x++) { - v = 0; - } - else if (v >= 255) - { - v = 255; - } + float v = (*p - a) * slope; + if (v <= 0) + { + v = 0; + } + else if (v >= 255) + { + v = 255; + } - uint8_t vv = static_cast<uint8_t>(v); + uint8_t vv = static_cast<uint8_t>(v); - q[0] = lut[4 * vv + 0]; // R - q[1] = lut[4 * vv + 1]; // G - q[2] = lut[4 * vv + 2]; // B - q[3] = lut[4 * vv + 3]; // A + q[0] = lut[4 * vv + 0]; // R + q[1] = lut[4 * vv + 1]; // G + q[2] = lut[4 * vv + 2]; // B + q[3] = lut[4 * vv + 3]; // A - p++; - q += 4; + p++; + q += 4; + } } } + + context_.MakeCurrent(); + glTexture_.reset(new OpenGL::OpenGLTexture(context_)); + glTexture_->Load(*texture_, layer.IsLinearInterpolation()); + layerTransform_ = layer.GetTransform(); } - - context_.MakeCurrent(); - glTexture_.reset(new OpenGL::OpenGLTexture); - glTexture_->Load(*texture_, layer.IsLinearInterpolation()); - layerTransform_ = layer.GetTransform(); } - OpenGLLookupTableTextureRenderer::OpenGLLookupTableTextureRenderer( OpenGL::IOpenGLContext& context, OpenGLColorTextureProgram& program, @@ -123,7 +125,7 @@ unsigned int canvasWidth, unsigned int canvasHeight) { - if (glTexture_.get() != NULL) + if (!context_.IsContextLost() && glTexture_.get() != NULL) { program_.Apply( *glTexture_,
--- a/Framework/Scene2D/Internals/OpenGLTextProgram.cpp Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/Scene2D/Internals/OpenGLTextProgram.cpp Sat Aug 10 13:07:31 2019 +0200 @@ -60,14 +60,16 @@ OpenGLTextProgram::OpenGLTextProgram(OpenGL::IOpenGLContext& context) : context_(context) { - - context_.MakeCurrent(); + if (!context_.IsContextLost()) + { + context_.MakeCurrent(); - program_.reset(new OpenGL::OpenGLProgram); - program_->CompileShaders(VERTEX_SHADER, FRAGMENT_SHADER); + program_.reset(new OpenGL::OpenGLProgram(context_)); + program_->CompileShaders(VERTEX_SHADER, FRAGMENT_SHADER); - positionLocation_ = program_->GetAttributeLocation("a_position"); - textureLocation_ = program_->GetAttributeLocation("a_texcoord"); + positionLocation_ = program_->GetAttributeLocation("a_position"); + textureLocation_ = program_->GetAttributeLocation("a_texcoord"); + } } @@ -95,26 +97,58 @@ { coordinatesCount_ = coordinates.GetRenderingCoords().size(); - context_.MakeCurrent(); - glGenBuffers(2, buffers_); + if (!context_.IsContextLost()) + { + context_.MakeCurrent(); + glGenBuffers(2, buffers_); + ORTHANC_OPENGL_CHECK("glGenBuffers"); - glBindBuffer(GL_ARRAY_BUFFER, buffers_[0]); - glBufferData(GL_ARRAY_BUFFER, sizeof(float) * coordinatesCount_, - &coordinates.GetRenderingCoords() [0], GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, buffers_[0]); + ORTHANC_OPENGL_CHECK("glBindBuffer"); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * coordinatesCount_, + &coordinates.GetRenderingCoords()[0], GL_STATIC_DRAW); + ORTHANC_OPENGL_CHECK("glBufferData"); - glBindBuffer(GL_ARRAY_BUFFER, buffers_[1]); - glBufferData(GL_ARRAY_BUFFER, sizeof(float) * coordinatesCount_, - &coordinates.GetTextureCoords() [0], GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, buffers_[1]); + ORTHANC_OPENGL_CHECK("glBindBuffer"); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * coordinatesCount_, + &coordinates.GetTextureCoords()[0], GL_STATIC_DRAW); + ORTHANC_OPENGL_CHECK("glBufferData"); + } } } OpenGLTextProgram::Data::~Data() { - if (!IsEmpty()) + if (!context_.IsContextLost() && !IsEmpty()) { - context_.MakeCurrent(); - glDeleteBuffers(2, buffers_); + try + { + context_.MakeCurrent(); + ORTHANC_OPENGL_TRACE_CURRENT_CONTEXT("About to call glDeleteBuffers"); + glDeleteBuffers(2, buffers_); + ORTHANC_OPENGL_CHECK("glDeleteBuffers"); + } + catch (const Orthanc::OrthancException& e) + { + if (e.HasDetails()) + { + LOG(ERROR) << "OrthancException in ~Data: " << e.What() << " Details: " << e.GetDetails(); + } + else + { + LOG(ERROR) << "OrthancException in ~Data: " << e.What(); + } + } + catch (const std::exception& e) + { + LOG(ERROR) << "std::exception in ~Data: " << e.what(); + } + catch (...) + { + LOG(ERROR) << "Unknown exception in ~Data"; + } } } @@ -131,7 +165,6 @@ } } - GLuint OpenGLTextProgram::Data::GetTextureLocationsBuffer() const { if (IsEmpty()) @@ -144,12 +177,11 @@ } } - void OpenGLTextProgram::Apply(OpenGL::OpenGLTexture& fontTexture, const Data& data, const AffineTransform2D& transform) { - if (!data.IsEmpty()) + if (!context_.IsContextLost() && !data.IsEmpty()) { context_.MakeCurrent(); program_->Use();
--- a/Framework/Scene2D/Internals/OpenGLTextRenderer.cpp Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/Scene2D/Internals/OpenGLTextRenderer.cpp Sat Aug 10 13:07:31 2019 +0200 @@ -27,7 +27,10 @@ { void OpenGLTextRenderer::LoadLayer(const TextSceneLayer& layer) { - data_.reset(new OpenGLTextProgram::Data(context_, alphabet_, layer)); + if (!context_.IsContextLost()) + data_.reset(new OpenGLTextProgram::Data(context_, alphabet_, layer)); + else + data_.reset(NULL); } @@ -49,13 +52,12 @@ unsigned int canvasWidth, unsigned int canvasHeight) { - if (data_.get() != NULL) + if (!context_.IsContextLost() && data_.get() != NULL) { program_.Apply(texture_, *data_, transform); } } - void OpenGLTextRenderer::Update(const ISceneLayer& layer) { LoadLayer(dynamic_cast<const TextSceneLayer&>(layer));
--- a/Framework/Scene2D/Internals/OpenGLTextureProgram.cpp Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/Scene2D/Internals/OpenGLTextureProgram.cpp Sat Aug 10 13:07:31 2019 +0200 @@ -22,6 +22,8 @@ #include "OpenGLTextureProgram.h" #include "OpenGLShaderVersionDirective.h" +#include <Core/OrthancException.h> + static const unsigned int COMPONENTS = 2; static const unsigned int COUNT = 6; // 2 triangles in 2D @@ -45,37 +47,41 @@ void OpenGLTextureProgram::InitializeExecution(OpenGL::OpenGLTexture& texture, const AffineTransform2D& transform) { - context_.MakeCurrent(); - program_->Use(); + if (!context_.IsContextLost()) + { + context_.MakeCurrent(); + program_->Use(); - AffineTransform2D scale = AffineTransform2D::CreateScaling + AffineTransform2D scale = AffineTransform2D::CreateScaling (texture.GetWidth(), texture.GetHeight()); - AffineTransform2D t = AffineTransform2D::Combine(transform, scale); + AffineTransform2D t = AffineTransform2D::Combine(transform, scale); - float m[16]; - t.ConvertToOpenGLMatrix(m, context_.GetCanvasWidth(), context_.GetCanvasHeight()); + float m[16]; + t.ConvertToOpenGLMatrix(m, context_.GetCanvasWidth(), context_.GetCanvasHeight()); - texture.Bind(program_->GetUniformLocation("u_texture")); - glUniformMatrix4fv(program_->GetUniformLocation("u_matrix"), 1, GL_FALSE, m); + texture.Bind(program_->GetUniformLocation("u_texture")); + glUniformMatrix4fv(program_->GetUniformLocation("u_matrix"), 1, GL_FALSE, m); - glBindBuffer(GL_ARRAY_BUFFER, buffers_[0]); - glEnableVertexAttribArray(positionLocation_); - glVertexAttribPointer(positionLocation_, COMPONENTS, GL_FLOAT, GL_FALSE, 0, 0); + glBindBuffer(GL_ARRAY_BUFFER, buffers_[0]); + glEnableVertexAttribArray(positionLocation_); + glVertexAttribPointer(positionLocation_, COMPONENTS, GL_FLOAT, GL_FALSE, 0, 0); - glBindBuffer(GL_ARRAY_BUFFER, buffers_[1]); - glEnableVertexAttribArray(textureLocation_); - glVertexAttribPointer(textureLocation_, COMPONENTS, GL_FLOAT, GL_FALSE, 0, 0); + glBindBuffer(GL_ARRAY_BUFFER, buffers_[1]); + glEnableVertexAttribArray(textureLocation_); + glVertexAttribPointer(textureLocation_, COMPONENTS, GL_FLOAT, GL_FALSE, 0, 0); + } } - void OpenGLTextureProgram::FinalizeExecution() { - glDisableVertexAttribArray(positionLocation_); - glDisableVertexAttribArray(textureLocation_); + if (!context_.IsContextLost()) + { + glDisableVertexAttribArray(positionLocation_); + glDisableVertexAttribArray(textureLocation_); + } } - OpenGLTextureProgram::OpenGLTextureProgram(OpenGL::IOpenGLContext& context, const char* fragmentShader) : context_(context) @@ -88,35 +94,44 @@ 0, 1, 1, 1 }; - - context_.MakeCurrent(); - program_.reset(new OpenGL::OpenGLProgram); - program_->CompileShaders(VERTEX_SHADER, fragmentShader); + if (!context_.IsContextLost()) + { + context_.MakeCurrent(); - positionLocation_ = program_->GetAttributeLocation("a_position"); - textureLocation_ = program_->GetAttributeLocation("a_texcoord"); + program_.reset(new OpenGL::OpenGLProgram(context_)); + program_->CompileShaders(VERTEX_SHADER, fragmentShader); - glGenBuffers(2, buffers_); + positionLocation_ = program_->GetAttributeLocation("a_position"); + textureLocation_ = program_->GetAttributeLocation("a_texcoord"); - glBindBuffer(GL_ARRAY_BUFFER, buffers_[0]); - glBufferData(GL_ARRAY_BUFFER, sizeof(float) * COMPONENTS * COUNT, POSITIONS, GL_STATIC_DRAW); + glGenBuffers(2, buffers_); - glBindBuffer(GL_ARRAY_BUFFER, buffers_[1]); - glBufferData(GL_ARRAY_BUFFER, sizeof(float) * COMPONENTS * COUNT, POSITIONS, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, buffers_[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * COMPONENTS * COUNT, POSITIONS, GL_STATIC_DRAW); + + glBindBuffer(GL_ARRAY_BUFFER, buffers_[1]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * COMPONENTS * COUNT, POSITIONS, GL_STATIC_DRAW); + } } - OpenGLTextureProgram::~OpenGLTextureProgram() { - context_.MakeCurrent(); - glDeleteBuffers(2, buffers_); + if (!context_.IsContextLost()) + { + ORTHANC_OPENGL_TRACE_CURRENT_CONTEXT("OpenGLTextureProgram::~OpenGLTextureProgram() | About to call glDeleteBuffers"); + context_.MakeCurrent(); + glDeleteBuffers(2, buffers_); + } } void OpenGLTextureProgram::Execution::DrawTriangles() { - glDrawArrays(GL_TRIANGLES, 0, COUNT); + if (!that_.context_.IsContextLost()) + { + glDrawArrays(GL_TRIANGLES, 0, COUNT); + } } } }
--- a/Framework/Scene2D/OpenGLCompositor.cpp Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/Scene2D/OpenGLCompositor.cpp Sat Aug 10 13:07:31 2019 +0200 @@ -37,10 +37,10 @@ std::auto_ptr<OpenGL::OpenGLTexture> texture_; public: - Font(const GlyphBitmapAlphabet& dict) + Font(OpenGL::IOpenGLContext& context, const GlyphBitmapAlphabet& dict) { alphabet_.reset(new GlyphTextureAlphabet(dict)); - texture_.reset(new OpenGL::OpenGLTexture); + texture_.reset(new OpenGL::OpenGLTexture(context)); std::auto_ptr<Orthanc::ImageAccessor> bitmap(alphabet_->ReleaseTexture()); texture_->Load(*bitmap, true /* enable linear interpolation */); @@ -76,19 +76,21 @@ Internals::CompositorHelper::ILayerRenderer* OpenGLCompositor::Create(const ISceneLayer& layer) { - switch (layer.GetType()) + if (!context_.IsContextLost()) { + switch (layer.GetType()) + { case ISceneLayer::Type_InfoPanel: return new Internals::OpenGLInfoPanelRenderer - (context_, colorTextureProgram_, dynamic_cast<const InfoPanelSceneLayer&>(layer)); + (context_, colorTextureProgram_, dynamic_cast<const InfoPanelSceneLayer&>(layer)); case ISceneLayer::Type_ColorTexture: return new Internals::OpenGLColorTextureRenderer - (context_, colorTextureProgram_, dynamic_cast<const ColorTextureSceneLayer&>(layer)); + (context_, colorTextureProgram_, dynamic_cast<const ColorTextureSceneLayer&>(layer)); case ISceneLayer::Type_FloatTexture: return new Internals::OpenGLFloatTextureRenderer - (context_, floatTextureProgram_, dynamic_cast<const FloatTextureSceneLayer&>(layer)); + (context_, floatTextureProgram_, dynamic_cast<const FloatTextureSceneLayer&>(layer)); case ISceneLayer::Type_LookupTableTexture: return new Internals::OpenGLLookupTableTextureRenderer @@ -96,7 +98,7 @@ case ISceneLayer::Type_Polyline: return new Internals::OpenGLAdvancedPolylineRenderer - (context_, linesProgram_, dynamic_cast<const PolylineSceneLayer&>(layer)); + (context_, linesProgram_, dynamic_cast<const PolylineSceneLayer&>(layer)); //return new Internals::OpenGLBasicPolylineRenderer(context_, dynamic_cast<const PolylineSceneLayer&>(layer)); case ISceneLayer::Type_Text: @@ -110,12 +112,18 @@ else { return new Internals::OpenGLTextRenderer - (context_, textProgram_, font->GetAlphabet(), font->GetTexture(), l); + (context_, textProgram_, font->GetAlphabet(), font->GetTexture(), l); } } default: return NULL; + } + } + else + { + // context is lost. returning null. + return NULL; } } @@ -134,48 +142,59 @@ OpenGLCompositor::~OpenGLCompositor() { - for (Fonts::iterator it = fonts_.begin(); it != fonts_.end(); ++it) + if (!context_.IsContextLost()) { - assert(it->second != NULL); - delete it->second; + context_.MakeCurrent(); // this can throw if context lost! + for (Fonts::iterator it = fonts_.begin(); it != fonts_.end(); ++it) + { + assert(it->second != NULL); + delete it->second; + } } } void OpenGLCompositor::Refresh() { - context_.MakeCurrent(); + if (!context_.IsContextLost()) + { + context_.MakeCurrent(); // this can throw if context lost! - canvasWidth_ = context_.GetCanvasWidth(); - canvasHeight_ = context_.GetCanvasHeight(); + canvasWidth_ = context_.GetCanvasWidth(); + canvasHeight_ = context_.GetCanvasHeight(); - glViewport(0, 0, canvasWidth_, canvasHeight_); - glClearColor(0, 0, 0, 1); - glClear(GL_COLOR_BUFFER_BIT); + glViewport(0, 0, canvasWidth_, canvasHeight_); + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); - helper_.Refresh(canvasWidth_, canvasHeight_); + helper_.Refresh(canvasWidth_, canvasHeight_); - context_.SwapBuffer(); + context_.SwapBuffer(); + } + } void OpenGLCompositor::SetFont(size_t index, const GlyphBitmapAlphabet& dict) { - context_.MakeCurrent(); - - std::auto_ptr<Font> font(new Font(dict)); - - Fonts::iterator found = fonts_.find(index); - - if (found == fonts_.end()) + if (!context_.IsContextLost()) { - fonts_[index] = font.release(); - } - else - { - assert(found->second != NULL); - delete found->second; + context_.MakeCurrent(); // this can throw if context lost + + std::auto_ptr<Font> font(new Font(context_, dict)); + + Fonts::iterator found = fonts_.find(index); - found->second = font.release(); + if (found == fonts_.end()) + { + fonts_[index] = font.release(); + } + else + { + assert(found->second != NULL); + delete found->second; + + found->second = font.release(); + } } } @@ -185,13 +204,16 @@ unsigned int fontSize, Orthanc::Encoding codepage) { - FontRenderer renderer; - renderer.LoadFont(resource, fontSize); + if (!context_.IsContextLost()) + { + FontRenderer renderer; + renderer.LoadFont(resource, fontSize); - GlyphBitmapAlphabet dict; - dict.LoadCodepage(renderer, codepage); + GlyphBitmapAlphabet dict; + dict.LoadCodepage(renderer, codepage); - SetFont(index, dict); + SetFont(index, dict); + } } #endif }
--- a/Framework/Scene2D/OpenGLCompositor.h Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/Scene2D/OpenGLCompositor.h Sat Aug 10 13:07:31 2019 +0200 @@ -46,7 +46,7 @@ Internals::OpenGLTextProgram textProgram_; unsigned int canvasWidth_; unsigned int canvasHeight_; - + const Font* GetFont(size_t fontIndex) const; virtual Internals::CompositorHelper::ILayerRenderer* Create(const ISceneLayer& layer) ORTHANC_OVERRIDE; @@ -59,8 +59,7 @@ virtual void Refresh() ORTHANC_OVERRIDE; - void SetFont(size_t index, - const GlyphBitmapAlphabet& dict); + void SetFont(size_t index, const GlyphBitmapAlphabet& dict); #if ORTHANC_ENABLE_LOCALE == 1 void SetFont(size_t index,
--- a/Framework/StoneException.h Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/StoneException.h Sat Aug 10 13:07:31 2019 +0200 @@ -38,6 +38,7 @@ ErrorCode_CanOnlyAddOneLayerAtATime, ErrorCode_CommandJsonInvalidFormat, + ErrorCode_WebGLContextLost, ErrorCode_Last }; @@ -65,6 +66,21 @@ } }; + class OpenGLContextLostException : public StoneException + { + public: + explicit OpenGLContextLostException(void* context) + : StoneException(ErrorCode_WebGLContextLost) + , context_(context) + { + } + virtual const char* What() const + { + return "The OpenGL/WebGL context has been lost!"; + } + void* context_; + }; + class StoneOrthancException : public StoneException { protected:
--- a/Framework/Viewport/IViewport.h Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/Viewport/IViewport.h Sat Aug 10 13:07:31 2019 +0200 @@ -58,9 +58,9 @@ #endif protected: - virtual ICompositor& GetCompositor() = 0; + virtual ICompositor* GetCompositor() = 0; - virtual const ICompositor& GetCompositor() const + virtual const ICompositor* GetCompositor() const { IViewport* self = const_cast<IViewport*>(this); return self->GetCompositor();
--- a/Framework/Viewport/SdlViewport.cpp Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/Viewport/SdlViewport.cpp Sat Aug 10 13:07:31 2019 +0200 @@ -31,9 +31,9 @@ unsigned int height, bool allowDpiScaling) : SdlViewport(title), - context_(title, width, height, allowDpiScaling), - compositor_(context_, GetScene()) + context_(title, width, height, allowDpiScaling) { + compositor_.reset(new OpenGLCompositor(context_, GetScene())); } SdlOpenGLViewport::SdlOpenGLViewport(const char* title, @@ -42,12 +42,97 @@ boost::shared_ptr<Scene2D>& scene, bool allowDpiScaling) : SdlViewport(title, scene), - context_(title, width, height, allowDpiScaling), - compositor_(context_, GetScene()) + context_(title, width, height, allowDpiScaling) + { + compositor_.reset(new OpenGLCompositor(context_, GetScene())); + } + + bool SdlOpenGLViewport::OpenGLContextLost() + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + + void SdlOpenGLViewport::DisableCompositor() + { + compositor_.reset(NULL); + } + + void SdlOpenGLViewport::RestoreCompositor() + { + // the context must have been restored! + ORTHANC_ASSERT(!context_.IsContextLost()); + + if (compositor_.get() == NULL) + { + compositor_.reset(new OpenGLCompositor(context_, GetScene())); + } + else + { + std::string windowTitle(SDL_GetWindowTitle(GetWindow().GetObject())); + LOG(WARNING) << "RestoreCompositor() called for \"" << windowTitle << "\" while it was NOT lost! Nothing done."; + } + } + + // extern bool Debug_MustContextBeRestored(std::string title); + // extern void Debug_Context_ClearRestoreFlag(std::string title); + // extern void Debug_Context_ClearKillFlag(std::string title); + + bool Debug_SdlOpenGLViewport_Refresh_BP = false; + + void SdlOpenGLViewport::Refresh() { + // <DEBUG CODE USED FOR CONTEXT LOSS RESTORING> + // try to restore the context if requested + // Debug_Context_ClearRestoreFlag + // Debug_SdlOpenGLViewport_Refresh_BP = true; + // try + // { + // if (Debug_MustContextBeRestored(GetCanvasIdentifier())) + // { + // // to prevent a bug where the context is both labelled as "to be lost" and "to be restored" + // // (occurs when one is hammering away at the keyboard like there's no tomorrow) + // Debug_Context_ClearKillFlag(GetCanvasIdentifier()); + // // this is called manually for loss/restore simulation + // context_.RestoreLostContext(); + // RestoreCompositor(); + // Debug_Context_ClearRestoreFlag(GetCanvasIdentifier()); + // } + // } + // catch (const OpenGLContextLostException& e) + // { + // LOG(ERROR) << "OpenGLContextLostException in SdlOpenGLViewport::Refresh() part 1"; + // } + // Debug_SdlOpenGLViewport_Refresh_BP = false; + // </DEBUG CODE USED FOR CONTEXT LOSS RESTORING> + + try + { + // the compositor COULD be dead! + if (GetCompositor()) + GetCompositor()->Refresh(); + } + catch (const OpenGLContextLostException& e) + { + // we need to wait for the "context restored" callback + LOG(WARNING) << "Context " << std::hex << e.context_ << " is lost! Compositor will be disabled."; + DisableCompositor(); + + // <DEBUG CODE USED FOR CONTEXT LOSS RESTORING> + // in case this was externally triggered... + //Debug_Context_ClearKillFlag(GetCanvasIdentifier()); + // </DEBUG CODE USED FOR CONTEXT LOSS RESTORING> + } + catch (...) + { + // something else nasty happened + throw; + } } + + + SdlCairoViewport::SdlCairoViewport(const char* title, unsigned int width, unsigned int height, @@ -59,6 +144,15 @@ UpdateSdlSurfaceSize(width, height); } + void SdlCairoViewport::DisableCompositor() + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + + void SdlCairoViewport::RestoreCompositor() + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } SdlCairoViewport::~SdlCairoViewport() { @@ -67,11 +161,10 @@ SDL_FreeSurface(sdlSurface_); } } - - + void SdlCairoViewport::Refresh() { - GetCompositor().Refresh(); + GetCompositor()->Refresh(); window_.Render(sdlSurface_); } @@ -82,8 +175,7 @@ UpdateSdlSurfaceSize(width, height); Refresh(); } - - + void SdlCairoViewport::UpdateSdlSurfaceSize(unsigned int width, unsigned int height) {
--- a/Framework/Viewport/SdlViewport.h Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/Viewport/SdlViewport.h Sat Aug 10 13:07:31 2019 +0200 @@ -61,13 +61,14 @@ virtual void UpdateSize(unsigned int width, unsigned int height) = 0; + }; class SdlOpenGLViewport : public SdlViewport { private: SdlOpenGLContext context_; - OpenGLCompositor compositor_; + std::auto_ptr<OpenGLCompositor> compositor_; public: SdlOpenGLViewport(const char* title, @@ -86,15 +87,21 @@ return context_.GetWindow(); } + bool OpenGLContextLost(); + virtual void UpdateSize(unsigned int width, unsigned int height) ORTHANC_OVERRIDE { // nothing to do in OpenGL, the OpenGLCompositor::UpdateSize will be called automatically } + virtual void Refresh() ORTHANC_OVERRIDE; protected: - virtual ICompositor& GetCompositor() ORTHANC_OVERRIDE + virtual void DisableCompositor() ORTHANC_OVERRIDE; + virtual void RestoreCompositor() ORTHANC_OVERRIDE; + + virtual ICompositor* GetCompositor() ORTHANC_OVERRIDE { - return compositor_; + return compositor_.get(); } }; @@ -125,14 +132,17 @@ return window_; } + virtual void DisableCompositor() ORTHANC_OVERRIDE; + virtual void RestoreCompositor() ORTHANC_OVERRIDE; + virtual void Refresh() ORTHANC_OVERRIDE; virtual void UpdateSize(unsigned int width, unsigned int height) ORTHANC_OVERRIDE; protected: - virtual ICompositor& GetCompositor() ORTHANC_OVERRIDE + virtual ICompositor* GetCompositor() ORTHANC_OVERRIDE { - return compositor_; + return &compositor_; } private:
--- a/Framework/Viewport/ViewportBase.h Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/Viewport/ViewportBase.h Sat Aug 10 13:07:31 2019 +0200 @@ -28,10 +28,6 @@ { class ViewportBase : public IViewport { - private: - std::string identifier_; - boost::shared_ptr<Scene2D> scene_; - public: ViewportBase(const std::string& identifier); @@ -50,19 +46,20 @@ virtual ScenePoint2D GetPixelCenterCoordinates(int x, int y) const ORTHANC_OVERRIDE; - virtual void Refresh() ORTHANC_OVERRIDE - { - GetCompositor().Refresh(); - } - virtual unsigned int GetCanvasWidth() const ORTHANC_OVERRIDE { - return GetCompositor().GetCanvasWidth(); + if (GetCompositor()) + return GetCompositor()->GetCanvasWidth(); + else + return 0; } virtual unsigned int GetCanvasHeight() const ORTHANC_OVERRIDE { - return GetCompositor().GetCanvasHeight(); + if (GetCompositor()) + return GetCompositor()->GetCanvasHeight(); + else + return 0; } #if ORTHANC_ENABLE_LOCALE == 1 @@ -71,8 +68,15 @@ unsigned int fontSize, Orthanc::Encoding codepage) ORTHANC_OVERRIDE { - return GetCompositor().SetFont(index, resource, fontSize, codepage); + return GetCompositor()->SetFont(index, resource, fontSize, codepage); } #endif + + private: + std::string identifier_; + boost::shared_ptr<Scene2D> scene_; + protected: + virtual void RestoreCompositor() = 0; + virtual void DisableCompositor() = 0; }; }
--- a/Framework/Viewport/WebAssemblyViewport.cpp Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/Viewport/WebAssemblyViewport.cpp Sat Aug 10 13:07:31 2019 +0200 @@ -43,8 +43,9 @@ void WebAssemblyOpenGLViewport::UpdateSize() { + if(compositor_.get() != NULL) + compositor_->Refresh(); // Then refresh the content of the canvas context_.UpdateSize(); // First read the size of the canvas - compositor_->Refresh(); // Then refresh the content of the canvas } /* @@ -76,6 +77,61 @@ return viewport->OpenGLContextRestored(); } + void WebAssemblyOpenGLViewport::DisableCompositor() + { + compositor_.reset(NULL); + } + + void WebAssemblyOpenGLViewport::Refresh() + { + try + { + // the compositor COULD be dead! + if (GetCompositor()) + GetCompositor()->Refresh(); + } + catch (const OpenGLContextLostException& e) + { + LOG(WARNING) << "Context " << std::hex << e.context_ << " is lost! Compositor will be disabled."; + DisableCompositor(); + // we now need to wait for the "context restored" callback + } + catch (...) + { + // something else nasty happened + throw; + } + } + + void WebAssemblyOpenGLViewport::RestoreCompositor() + { + // the context must have been restored! + ORTHANC_ASSERT(!context_.IsContextLost()); + if (compositor_.get() == NULL) + { + compositor_.reset(new OpenGLCompositor(context_, GetScene())); + } + else + { + LOG(WARNING) << "RestoreCompositor() called for \"" << GetCanvasIdentifier() << "\" while it was NOT lost! Nothing done."; + } + } + + bool WebAssemblyOpenGLViewport::OpenGLContextLost() + { + LOG(ERROR) << "WebAssemblyOpenGLViewport::OpenGLContextLost() for canvas: " << GetCanvasIdentifier(); + DisableCompositor(); + return true; + } + + bool WebAssemblyOpenGLViewport::OpenGLContextRestored() + { + LOG(ERROR) << "WebAssemblyOpenGLViewport::OpenGLContextRestored() for canvas: " << GetCanvasIdentifier(); + RestoreCompositor(); + UpdateSize(); + return false; + } + void WebAssemblyOpenGLViewport::RegisterContextCallbacks() { // TODO: what's the impact of userCapture=true ? @@ -83,15 +139,15 @@ void* that = reinterpret_cast<void*>(this); EMSCRIPTEN_RESULT status = EMSCRIPTEN_RESULT_SUCCESS; - status = emscripten_set_webglcontextlost_callback(canvasId, that, true, WebAssemblyOpenGLViewport_OpenGLContextLost_callback); - if (status != EMSCRIPTEN_RESULT_SUCCESS) - { - std::stringstream ss; - ss << "Error while calling emscripten_set_webglcontextlost_callback for: \"" << GetCanvasIdentifier() << "\""; - std::string msg = ss.str(); - LOG(ERROR) << msg; - ORTHANC_ASSERT(false, msg.c_str()); - } + //status = emscripten_set_webglcontextlost_callback(canvasId, that, true, WebAssemblyOpenGLViewport_OpenGLContextLost_callback); + //if (status != EMSCRIPTEN_RESULT_SUCCESS) + //{ + // std::stringstream ss; + // ss << "Error while calling emscripten_set_webglcontextlost_callback for: \"" << GetCanvasIdentifier() << "\""; + // std::string msg = ss.str(); + // LOG(ERROR) << msg; + // ORTHANC_ASSERT(false, msg.c_str()); + //} status = emscripten_set_webglcontextrestored_callback(canvasId, that, true, WebAssemblyOpenGLViewport_OpenGLContextRestored_callback); if (status != EMSCRIPTEN_RESULT_SUCCESS) { @@ -101,18 +157,7 @@ LOG(ERROR) << msg; ORTHANC_ASSERT(false, msg.c_str()); } - } - - bool WebAssemblyOpenGLViewport::OpenGLContextLost() - { - LOG(ERROR) << "WebAssemblyOpenGLViewport::OpenGLContextLost() for canvas: " << GetCanvasIdentifier(); - return false; - } - - bool WebAssemblyOpenGLViewport::OpenGLContextRestored() - { - LOG(ERROR) << "WebAssemblyOpenGLViewport::OpenGLContextRestored() for canvas: " << GetCanvasIdentifier(); - return false; + LOG(TRACE) << "WebAssemblyOpenGLViewport::RegisterContextCallbacks() SUCCESS!!!"; } WebAssemblyCairoViewport::WebAssemblyCairoViewport(const std::string& canvas) : @@ -162,7 +207,7 @@ void WebAssemblyCairoViewport::Refresh() { LOG(INFO) << "refreshing cairo viewport, TODO: blit to the canvans.getContext('2d')"; - GetCompositor().Refresh(); + GetCompositor()->Refresh(); } }
--- a/Framework/Viewport/WebAssemblyViewport.h Tue Aug 06 15:07:23 2019 +0200 +++ b/Framework/Viewport/WebAssemblyViewport.h Sat Aug 10 13:07:31 2019 +0200 @@ -58,15 +58,20 @@ // This function must be called each time the browser window is resized void UpdateSize(); - virtual ICompositor& GetCompositor() + virtual ICompositor* GetCompositor() ORTHANC_OVERRIDE { - return *compositor_; + return compositor_.get(); } + virtual void Refresh() ORTHANC_OVERRIDE; + bool OpenGLContextLost(); bool OpenGLContextRestored(); private: + virtual void DisableCompositor() ORTHANC_OVERRIDE; + virtual void RestoreCompositor() ORTHANC_OVERRIDE; + void RegisterContextCallbacks(); }; @@ -86,9 +91,9 @@ virtual void Refresh(); - virtual ICompositor& GetCompositor() + virtual ICompositor* GetCompositor() { - return compositor_; + return &compositor_; } };