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
author Benjamin Golinvaux <bgo@osimis.io>
date Sat, 10 Aug 2019 13:07:31 +0200
parents dbe3e1e47019
children 141cc19e6b7d
files Applications/Generic/GuiAdapter.cpp Applications/Sdl/SdlOpenGLContext.cpp Applications/Sdl/SdlOpenGLContext.h Framework/Fonts/GlyphAlphabet.cpp Framework/OpenGL/IOpenGLContext.h Framework/OpenGL/OpenGLIncludes.h Framework/OpenGL/OpenGLProgram.cpp Framework/OpenGL/OpenGLProgram.h Framework/OpenGL/OpenGLShader.cpp Framework/OpenGL/OpenGLTexture.cpp Framework/OpenGL/OpenGLTexture.h Framework/OpenGL/WebAssemblyOpenGLContext.cpp Framework/OpenGL/WebAssemblyOpenGLContext.h Framework/Scene2D/CairoCompositor.cpp Framework/Scene2D/Internals/OpenGLAdvancedPolylineRenderer.h Framework/Scene2D/Internals/OpenGLBasicPolylineRenderer.cpp Framework/Scene2D/Internals/OpenGLColorTextureProgram.cpp Framework/Scene2D/Internals/OpenGLColorTextureProgram.h Framework/Scene2D/Internals/OpenGLColorTextureRenderer.cpp Framework/Scene2D/Internals/OpenGLColorTextureRenderer.h Framework/Scene2D/Internals/OpenGLFloatTextureProgram.cpp Framework/Scene2D/Internals/OpenGLFloatTextureProgram.h Framework/Scene2D/Internals/OpenGLFloatTextureRenderer.cpp Framework/Scene2D/Internals/OpenGLInfoPanelRenderer.cpp Framework/Scene2D/Internals/OpenGLLinesProgram.cpp Framework/Scene2D/Internals/OpenGLLookupTableTextureRenderer.cpp Framework/Scene2D/Internals/OpenGLTextProgram.cpp Framework/Scene2D/Internals/OpenGLTextRenderer.cpp Framework/Scene2D/Internals/OpenGLTextureProgram.cpp Framework/Scene2D/OpenGLCompositor.cpp Framework/Scene2D/OpenGLCompositor.h Framework/StoneException.h Framework/Viewport/IViewport.h Framework/Viewport/SdlViewport.cpp Framework/Viewport/SdlViewport.h Framework/Viewport/ViewportBase.h Framework/Viewport/WebAssemblyViewport.cpp Framework/Viewport/WebAssemblyViewport.h
diffstat 38 files changed, 1063 insertions(+), 454 deletions(-) [+]
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_;
     }
   };