changeset 956:a7351ad54960

Made IsContextLost automatically set the flag by checking with the emscripten WebGL wrapper + added a LOT of logging messages right before throwing ErrorCode_BadSequenceOfCalls exceptions + increased the http request timeouts from 60 to 600 sec (big datasets in some recent customer use cases) + added IsContext lost through the Viewport/Context layer (to make it reachable from external API) + the same for the underlying device context (for debug)
author Benjamin Golinvaux <bgo@osimis.io>
date Wed, 21 Aug 2019 16:16:30 +0200
parents 118fc5c85d07
children 966c96b694f6
files Applications/Sdl/SdlOpenGLContext.cpp Applications/Sdl/SdlOpenGLContext.h Framework/Loaders/LoaderStateMachine.cpp Framework/Loaders/OrthancMultiframeVolumeLoader.cpp Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.cpp Framework/OpenGL/IOpenGLContext.h Framework/OpenGL/OpenGLShader.cpp Framework/OpenGL/WebAssemblyOpenGLContext.cpp Framework/OpenGL/WebAssemblyOpenGLContext.h Framework/Oracle/GetOrthancImageCommand.cpp Framework/Oracle/GetOrthancWebViewerJpegCommand.cpp Framework/Oracle/OracleCommandWithPayload.cpp Framework/Oracle/OrthancRestApiCommand.cpp Framework/Oracle/ThreadedOracle.cpp Framework/Oracle/WebAssemblyOracle.cpp Framework/Scene2D/Internals/OpenGLLinesProgram.cpp Framework/Scene2D/Internals/OpenGLTextProgram.cpp Framework/Scene2D/Scene2D.cpp Framework/Scene2D/TextureBaseSceneLayer.cpp Framework/Toolbox/DicomInstanceParameters.cpp Framework/Toolbox/DicomStructureSet.cpp Framework/Toolbox/SlicesSorter.cpp Framework/Viewport/IViewport.h Framework/Viewport/SdlViewport.cpp Framework/Viewport/SdlViewport.h Framework/Viewport/ViewportBase.h Framework/Viewport/WebAssemblyViewport.cpp Framework/Viewport/WebAssemblyViewport.h Framework/Volumes/DicomVolumeImage.cpp Framework/Volumes/DicomVolumeImageMPRSlicer.cpp Framework/Volumes/IVolumeSlicer.cpp Framework/Volumes/VolumeReslicer.cpp Framework/Volumes/VolumeSceneLayerSource.cpp Platforms/Generic/Oracle.cpp README.md
diffstat 35 files changed, 241 insertions(+), 48 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/Sdl/SdlOpenGLContext.cpp	Fri Aug 16 16:24:11 2019 +0200
+++ b/Applications/Sdl/SdlOpenGLContext.cpp	Wed Aug 21 16:16:30 2019 +0200
@@ -86,12 +86,11 @@
   }
 
 
-  bool SdlOpenGLContext::IsContextLost() const
+  bool SdlOpenGLContext::IsContextLost()
   {
     return contextLost_;
   }
-
-
+  
   void SdlOpenGLContext::SetLostContext()
   {
     contextLost_ = true;
--- a/Applications/Sdl/SdlOpenGLContext.h	Fri Aug 16 16:24:11 2019 +0200
+++ b/Applications/Sdl/SdlOpenGLContext.h	Wed Aug 21 16:16:30 2019 +0200
@@ -50,7 +50,7 @@
       return window_;
     }
 
-    virtual bool IsContextLost() const ORTHANC_OVERRIDE;
+    virtual bool IsContextLost() ORTHANC_OVERRIDE;
 
     virtual void SetLostContext() ORTHANC_OVERRIDE;
     virtual void RestoreLostContext() ORTHANC_OVERRIDE;
--- a/Framework/Loaders/LoaderStateMachine.cpp	Fri Aug 16 16:24:11 2019 +0200
+++ b/Framework/Loaders/LoaderStateMachine.cpp	Wed Aug 21 16:16:30 2019 +0200
@@ -67,6 +67,7 @@
   {
     if (active_)
     {
+      LOG(ERROR) << "LoaderStateMachine::Start() called while active_ is true";
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
 
@@ -106,13 +107,12 @@
 
   void LoaderStateMachine::HandleExceptionMessage(const OracleCommandExceptionMessage& message)
   {
-    LOG(ERROR) << "Error in the state machine, stopping all processing";
+    LOG(ERROR) << "LoaderStateMachine::HandleExceptionMessage: error in the state machine, stopping all processing";
     LOG(ERROR) << "Error: " << message.GetException().What() << " Details: " <<
       message.GetException().GetDetails();
       Clear();
   }
 
-
   template <typename T>
   void LoaderStateMachine::HandleSuccessMessage(const T& message)
   {
@@ -163,6 +163,7 @@
   {
     if (active_)
     {
+      LOG(ERROR) << "LoaderStateMachine::SetSimultaneousDownloads called while active_ is true";
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
     else if (count == 0)
--- a/Framework/Loaders/OrthancMultiframeVolumeLoader.cpp	Fri Aug 16 16:24:11 2019 +0200
+++ b/Framework/Loaders/OrthancMultiframeVolumeLoader.cpp	Wed Aug 21 16:16:30 2019 +0200
@@ -150,6 +150,7 @@
     }
     else
     {
+      LOG(ERROR) << "OrthancMultiframeVolumeLoader::GetInstanceId(): (!IsActive())";
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
   }
--- a/Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.cpp	Fri Aug 16 16:24:11 2019 +0200
+++ b/Framework/Loaders/OrthancSeriesVolumeProgressiveLoader.cpp	Wed Aug 21 16:16:30 2019 +0200
@@ -140,6 +140,7 @@
   {
     if (!HasGeometry())
     {
+      LOG(ERROR) << "OrthancSeriesVolumeProgressiveLoader::SeriesGeometry::CheckSliceIndex(size_t index): (!HasGeometry())";
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
     else if (index >= slices_.size())
@@ -204,6 +205,7 @@
   {
     if (!HasGeometry())
     {
+      LOG(ERROR) << "OrthancSeriesVolumeProgressiveLoader::SeriesGeometry::GetImageGeometry(): (!HasGeometry())";
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
     else
@@ -450,6 +452,7 @@
   {
     if (active_)
     {
+      LOG(ERROR) << "OrthancSeriesVolumeProgressiveLoader::SetSimultaneousDownloads(): (active_)";
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
     else if (count == 0)
@@ -469,6 +472,7 @@
     if (active_)
     {
 //      LOG(TRACE) << "OrthancSeriesVolumeProgressiveLoader::LoadSeries NOT ACTIVE! --> ERROR";
+      LOG(ERROR) << "OrthancSeriesVolumeProgressiveLoader::LoadSeries(const std::string& seriesId): (active_)";
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
     else
--- a/Framework/OpenGL/IOpenGLContext.h	Fri Aug 16 16:24:11 2019 +0200
+++ b/Framework/OpenGL/IOpenGLContext.h	Wed Aug 21 16:16:30 2019 +0200
@@ -34,7 +34,7 @@
       {
       }
 
-      virtual bool IsContextLost() const = 0;
+      virtual bool IsContextLost() = 0;
 
       virtual void MakeCurrent() = 0;
 
--- a/Framework/OpenGL/OpenGLShader.cpp	Fri Aug 16 16:24:11 2019 +0200
+++ b/Framework/OpenGL/OpenGLShader.cpp	Wed Aug 21 16:16:30 2019 +0200
@@ -127,6 +127,7 @@
       }
       else
       {
+        LOG(ERROR) << "OpenGLShader::Release(): (!isValid_)";
         throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
       }
     }
--- a/Framework/OpenGL/WebAssemblyOpenGLContext.cpp	Fri Aug 16 16:24:11 2019 +0200
+++ b/Framework/OpenGL/WebAssemblyOpenGLContext.cpp	Wed Aug 21 16:16:30 2019 +0200
@@ -69,25 +69,48 @@
         return reinterpret_cast<void*>(context_);
       }
 
-      bool IsContextLost() const
-      {
+      bool IsContextLost()
+      {
+        LOG(TRACE) << "IsContextLost() for context " << std::hex() << 
         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_ = apiFlag;
+        return isContextLost_;
+      }
+
+      void SetLostContext()
       {
         isContextLost_ = true;
       }
 
       ~PImpl()
       {
-        emscripten_webgl_destroy_context(context_);
+        try
+        {
+          EMSCRIPTEN_RESULT result = emscripten_webgl_destroy_context(context_);
+          if (result != EMSCRIPTEN_RESULT_SUCCESS)
+          {
+            LOG(ERROR) << "emscripten_webgl_destroy_context returned code " << result;
+          }
+        }
+        catch (const Orthanc::OrthancException& e)
+        {
+          if (e.HasDetails())
+          {
+            LOG(ERROR) << "OrthancException in WebAssemblyOpenGLContext::~PImpl: " << e.What() << " Details: " << e.GetDetails();
+          }
+          else
+          {
+            LOG(ERROR) << "OrthancException in WebAssemblyOpenGLContext::~PImpl: " << e.What();
+          }
+        }
+        catch (const std::exception& e)
+        {
+          LOG(ERROR) << "std::exception in WebAssemblyOpenGLContext::~PImpl: " << e.what();
+        }
+        catch (...)
+        {
+          LOG(ERROR) << "Unknown exception in WebAssemblyOpenGLContext::~PImpl";
+        }
       }
 
       const std::string& GetCanvasIdentifier() const
@@ -177,7 +200,50 @@
     {
     }
 
-    bool WebAssemblyOpenGLContext::IsContextLost() const
+    //bool WebAssemblyOpenGLContext::TryRecreate()
+    //{
+    //  // LOG(ERROR) << "WebAssemblyOpenGLContext::TryRecreate() trying to recreate context";
+    //  try
+    //  {
+    //    std::string canvasId = GetCanvasIdentifier();
+    //    pimpl_.reset(new PImpl(canvasId));
+
+    //    // no exception does not mean the context is fully 
+    //    // functional! Most probably, if we have >= than 16 
+    //    // contexts, context wil remain lost for some time
+    //    bool lost = IsContextLost();
+    //    if (lost) {
+    //      // LOG(ERROR) << "WebAssemblyOpenGLContext::TryRecreate() context is still lost!";
+    //      return false;
+    //    } else {
+    //      return true;
+    //    }
+    //  }
+    //  catch (const Orthanc::OrthancException& e)
+    //  {
+    //    if (e.HasDetails())
+    //    {
+    //      LOG(ERROR) << "OrthancException in WebAssemblyOpenGLContext::TryRecreate: " << e.What() << " Details: " << e.GetDetails();
+    //    }
+    //    else
+    //    {
+    //      LOG(ERROR) << "OrthancException in WebAssemblyOpenGLContext::TryRecreate: " << e.What();
+    //    }
+    //    return false;
+    //  }
+    //  catch (const std::exception& e)
+    //  {
+    //    LOG(ERROR) << "std::exception in WebAssemblyOpenGLContext::TryRecreate: " << e.what();
+    //    return false;
+    //  }
+    //  catch (...)
+    //  {
+    //    LOG(ERROR) << "Unknown exception WebAssemblyOpenGLContext::in TryRecreate";
+    //    return false;
+    //  }
+    //}
+
+    bool WebAssemblyOpenGLContext::IsContextLost()
     {
       return pimpl_->IsContextLost();
     }
--- a/Framework/OpenGL/WebAssemblyOpenGLContext.h	Fri Aug 16 16:24:11 2019 +0200
+++ b/Framework/OpenGL/WebAssemblyOpenGLContext.h	Wed Aug 21 16:16:30 2019 +0200
@@ -56,10 +56,10 @@
     public:
       WebAssemblyOpenGLContext(const std::string& canvas);
 
-      virtual bool IsContextLost() const ORTHANC_OVERRIDE;
-
-      virtual void SetLostContext() ORTHANC_OVERRIDE;
-      virtual void RestoreLostContext() ORTHANC_OVERRIDE;
+      virtual bool IsContextLost() ORTHANC_OVERRIDE;
+
+      virtual void SetLostContext() ORTHANC_OVERRIDE;
+      virtual void RestoreLostContext() ORTHANC_OVERRIDE;
 
       virtual void MakeCurrent() ORTHANC_OVERRIDE;
 
@@ -71,6 +71,11 @@
 
       virtual void* DebugGetInternalContext() const ORTHANC_OVERRIDE;
 
+      /**
+      Returns true if the underlying context has been successfully recreated
+      */
+      //bool TryRecreate();
+
       void UpdateSize();
 
       const std::string& GetCanvasIdentifier() const;
--- a/Framework/Oracle/GetOrthancImageCommand.cpp	Fri Aug 16 16:24:11 2019 +0200
+++ b/Framework/Oracle/GetOrthancImageCommand.cpp	Wed Aug 21 16:16:30 2019 +0200
@@ -45,7 +45,7 @@
 
   GetOrthancImageCommand::GetOrthancImageCommand() :
     uri_("/"),
-    timeout_(60),
+    timeout_(600),
     hasExpectedFormat_(false)
   {
   }
--- a/Framework/Oracle/GetOrthancWebViewerJpegCommand.cpp	Fri Aug 16 16:24:11 2019 +0200
+++ b/Framework/Oracle/GetOrthancWebViewerJpegCommand.cpp	Wed Aug 21 16:16:30 2019 +0200
@@ -49,7 +49,7 @@
   GetOrthancWebViewerJpegCommand::GetOrthancWebViewerJpegCommand() :
     frame_(0),
     quality_(95),
-    timeout_(60),
+    timeout_(600),
     expectedFormat_(Orthanc::PixelFormat_Grayscale8)
   {
   }
--- a/Framework/Oracle/OracleCommandWithPayload.cpp	Fri Aug 16 16:24:11 2019 +0200
+++ b/Framework/Oracle/OracleCommandWithPayload.cpp	Wed Aug 21 16:16:30 2019 +0200
@@ -46,6 +46,7 @@
     }
     else
     {
+      LOG(ERROR) << "OracleCommandWithPayload::GetPayload(): (!HasPayload())";
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
   }
@@ -59,6 +60,7 @@
     }
     else
     {
+      LOG(ERROR) << "OracleCommandWithPayload::ReleasePayload(): (!HasPayload())";
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
   }
--- a/Framework/Oracle/OrthancRestApiCommand.cpp	Fri Aug 16 16:24:11 2019 +0200
+++ b/Framework/Oracle/OrthancRestApiCommand.cpp	Wed Aug 21 16:16:30 2019 +0200
@@ -72,6 +72,7 @@
     }
     else
     {
+      LOG(ERROR) << "OrthancRestApiCommand::GetBody(): method_  not _Post or _Put";
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
   }
--- a/Framework/Oracle/ThreadedOracle.cpp	Fri Aug 16 16:24:11 2019 +0200
+++ b/Framework/Oracle/ThreadedOracle.cpp	Wed Aug 21 16:16:30 2019 +0200
@@ -456,6 +456,7 @@
 
     if (state_ != State_Setup)
     {
+      LOG(ERROR) << "ThreadedOracle::SetOrthancParameters(): (state_ != State_Setup)";
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
     else
@@ -475,6 +476,7 @@
     }
     else if (state_ != State_Setup)
     {
+      LOG(ERROR) << "ThreadedOracle::SetThreadsCount(): (state_ != State_Setup)";
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
     else
@@ -494,6 +496,7 @@
     }
     else if (state_ != State_Setup)
     {
+      LOG(ERROR) << "ThreadedOracle::SetSleepingTimeResolution(): (state_ != State_Setup)";
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
     else
@@ -509,6 +512,7 @@
 
     if (state_ != State_Setup)
     {
+      LOG(ERROR) << "ThreadedOracle::Start(): (state_ != State_Setup)";
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
     else
--- a/Framework/Oracle/WebAssemblyOracle.cpp	Fri Aug 16 16:24:11 2019 +0200
+++ b/Framework/Oracle/WebAssemblyOracle.cpp	Wed Aug 21 16:16:30 2019 +0200
@@ -308,7 +308,8 @@
       if (command_.get() == NULL)
       {
         // Cannot call Execute() twice
-        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);          
+        LOG(ERROR) << "WebAssemblyOracle::Execute(): (command_.get() == NULL)";
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
       }
 
       emscripten_fetch_attr_t attr;
--- a/Framework/Scene2D/Internals/OpenGLLinesProgram.cpp	Fri Aug 16 16:24:11 2019 +0200
+++ b/Framework/Scene2D/Internals/OpenGLLinesProgram.cpp	Wed Aug 21 16:16:30 2019 +0200
@@ -207,6 +207,7 @@
       {
         if (isEmpty_)
         {
+          LOG(ERROR) << "OpenGLLinesProgram -- AddTriangles: (isEmpty_)";
           throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
         }
 
@@ -363,6 +364,7 @@
     {
       if (IsEmpty())
       {
+        LOG(ERROR) << "OpenGLLinesProgram::Data::GetVerticesBuffer(): (IsEmpty())";
         throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
       }
       else
@@ -376,6 +378,7 @@
     {
       if (IsEmpty())
       {
+        LOG(ERROR) << "OpenGLLinesProgram::Data::GetMiterDirectionsBuffer(): (IsEmpty())";
         throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
       }
       else
@@ -389,6 +392,7 @@
     {
       if (IsEmpty())
       {
+        LOG(ERROR) << "OpenGLLinesProgram::Data::GetColorsBuffer(): (IsEmpty())";
         throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
       }
       else
--- a/Framework/Scene2D/Internals/OpenGLTextProgram.cpp	Fri Aug 16 16:24:11 2019 +0200
+++ b/Framework/Scene2D/Internals/OpenGLTextProgram.cpp	Wed Aug 21 16:16:30 2019 +0200
@@ -157,6 +157,7 @@
     {
       if (IsEmpty())
       {
+        LOG(ERROR) << "OpenGLTextProgram::Data::GetSceneLocationsBuffer(): (IsEmpty())";
         throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
       }
       else
@@ -169,6 +170,7 @@
     {
       if (IsEmpty())
       {
+        LOG(ERROR) << "OpenGLTextProgram::Data::GetTextureLocationsBuffer(): (IsEmpty())";
         throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
       }
       else
--- a/Framework/Scene2D/Scene2D.cpp	Fri Aug 16 16:24:11 2019 +0200
+++ b/Framework/Scene2D/Scene2D.cpp	Wed Aug 21 16:16:30 2019 +0200
@@ -48,6 +48,7 @@
     {
       if (layer_.get() == NULL)
       {
+        LOG(ERROR) << "Scene2D::Item::GetLayer(): (layer_.get() == NULL)";
         throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
       }
       else
@@ -60,6 +61,7 @@
     {
       if (layer_.get() == NULL)
       {
+        LOG(ERROR) << "Scene2D::Item::ReleaseLayer(): (layer_.get() == NULL)";
         throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
       }
       else
--- a/Framework/Scene2D/TextureBaseSceneLayer.cpp	Fri Aug 16 16:24:11 2019 +0200
+++ b/Framework/Scene2D/TextureBaseSceneLayer.cpp	Wed Aug 21 16:16:30 2019 +0200
@@ -111,6 +111,7 @@
   {
     if (!HasTexture())
     {
+      LOG(ERROR) << "TextureBaseSceneLayer::GetTexture(): (!HasTexture())";
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
     else
--- a/Framework/Toolbox/DicomInstanceParameters.cpp	Fri Aug 16 16:24:11 2019 +0200
+++ b/Framework/Toolbox/DicomInstanceParameters.cpp	Wed Aug 21 16:16:30 2019 +0200
@@ -276,6 +276,7 @@
     }
     else
     {
+      LOG(ERROR) << "DicomInstanceParameters::GetRescaleIntercept(): !data_.hasRescale_";
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
   }
@@ -289,6 +290,7 @@
     }
     else
     {
+      LOG(ERROR) << "DicomInstanceParameters::GetRescaleSlope(): !data_.hasRescale_";
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
   }
@@ -302,6 +304,7 @@
     }
     else
     {
+      LOG(ERROR) << "DicomInstanceParameters::GetDefaultWindowingCenter(): !data_.hasRescale_";
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
   }
@@ -315,6 +318,7 @@
     }
     else
     {
+      LOG(ERROR) << "DicomInstanceParameters::GetDefaultWindowingWidth(): !data_.hasRescale_";
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
   }
--- a/Framework/Toolbox/DicomStructureSet.cpp	Fri Aug 16 16:24:11 2019 +0200
+++ b/Framework/Toolbox/DicomStructureSet.cpp	Wed Aug 21 16:16:30 2019 +0200
@@ -580,6 +580,7 @@
     if (referencedSlices_.find(sopInstanceUid) != referencedSlices_.end())
     {
       // This geometry is already known
+      LOG(ERROR) << "DicomStructureSet::AddReferencedSlice(): (referencedSlices_.find(sopInstanceUid) != referencedSlices_.end()). sopInstanceUid = " << sopInstanceUid;
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
     else
@@ -659,7 +660,7 @@
       {
         if (!polygon->UpdateReferencedSlice(referencedSlices_))
         {
-          LOG(ERROR) << "Missing information about referenced instance: "
+          LOG(ERROR) << "DicomStructureSet::CheckReferencedSlices(): missing information about referenced instance: "
                      << polygon->GetSopInstanceUid();
           throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
         }
--- a/Framework/Toolbox/SlicesSorter.cpp	Fri Aug 16 16:24:11 2019 +0200
+++ b/Framework/Toolbox/SlicesSorter.cpp	Wed Aug 21 16:16:30 2019 +0200
@@ -72,6 +72,7 @@
       }
       else
       {
+        LOG(ERROR) << "SlicesSorter::SliceWithDepth::GetPayload(): (!HasPayload())";
         throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
       }
     }
@@ -152,6 +153,7 @@
   {
     if (!hasNormal_)
     {
+      LOG(ERROR) << "SlicesSorter::SortInternal(): (!hasNormal_)";
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
 
@@ -305,6 +307,7 @@
 
     if (spacingZ <= 0)
     {
+      LOG(ERROR) << "SlicesSorter::ComputeSpacingBetweenSlices(): (spacingZ <= 0)";
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls,
                                       "Please call the Sort() method before");
     }
--- a/Framework/Viewport/IViewport.h	Fri Aug 16 16:24:11 2019 +0200
+++ b/Framework/Viewport/IViewport.h	Wed Aug 21 16:16:30 2019 +0200
@@ -50,12 +50,16 @@
 
     virtual ScenePoint2D GetPixelCenterCoordinates(int x, int y) const = 0;
 
-#if ORTHANC_ENABLE_LOCALE == 1
-    virtual void SetFont(size_t index,
-      Orthanc::EmbeddedResources::FileResourceId resource,
-      unsigned int fontSize,
-      Orthanc::Encoding codepage) = 0;
-#endif
+    virtual bool IsContextLost() = 0;
+
+    virtual void* DebugGetInternalContext() const = 0;
+
+#if ORTHANC_ENABLE_LOCALE == 1
+    virtual void SetFont(size_t index,
+      Orthanc::EmbeddedResources::FileResourceId resource,
+      unsigned int fontSize,
+      Orthanc::Encoding codepage) = 0;
+#endif
 
   protected:
     virtual ICompositor* GetCompositor() = 0;
--- a/Framework/Viewport/SdlViewport.cpp	Fri Aug 16 16:24:11 2019 +0200
+++ b/Framework/Viewport/SdlViewport.cpp	Wed Aug 21 16:16:30 2019 +0200
@@ -47,11 +47,26 @@
     compositor_.reset(new OpenGLCompositor(context_, GetScene()));
   }
 
+
+  void* SdlOpenGLViewport::DebugGetInternalContext() const
+  {
+    return context_.DebugGetInternalContext();
+  }
+
+  bool SdlOpenGLViewport::IsContextLost() {
+    return context_.IsContextLost();
+  }
+
   bool SdlOpenGLViewport::OpenGLContextLost()
   {
     throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
   }
 
+  bool SdlOpenGLViewport::OpenGLContextRestored()
+  {
+    throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented);
+  }
+
   void SdlOpenGLViewport::DisableCompositor()
   {
     compositor_.reset(NULL);
--- a/Framework/Viewport/SdlViewport.h	Fri Aug 16 16:24:11 2019 +0200
+++ b/Framework/Viewport/SdlViewport.h	Wed Aug 21 16:16:30 2019 +0200
@@ -87,7 +87,11 @@
       return context_.GetWindow();
     }
 
+    virtual void* DebugGetInternalContext() const ORTHANC_OVERRIDE;
+
+    virtual bool IsContextLost() ORTHANC_OVERRIDE;
     bool OpenGLContextLost();
+    bool OpenGLContextRestored();
 
     virtual void UpdateSize(unsigned int width, unsigned int height) ORTHANC_OVERRIDE
     {
--- a/Framework/Viewport/ViewportBase.h	Fri Aug 16 16:24:11 2019 +0200
+++ b/Framework/Viewport/ViewportBase.h	Wed Aug 21 16:16:30 2019 +0200
@@ -62,15 +62,15 @@
         return 0;
     }
 
-#if ORTHANC_ENABLE_LOCALE == 1
-    virtual void SetFont(size_t index,
-      Orthanc::EmbeddedResources::FileResourceId resource,
-      unsigned int fontSize,
-      Orthanc::Encoding codepage) ORTHANC_OVERRIDE
-    {
-      return GetCompositor()->SetFont(index, resource, fontSize, codepage);
-    }
-#endif
+#if ORTHANC_ENABLE_LOCALE == 1
+    virtual void SetFont(size_t index,
+      Orthanc::EmbeddedResources::FileResourceId resource,
+      unsigned int fontSize,
+      Orthanc::Encoding codepage) ORTHANC_OVERRIDE
+    {
+      return GetCompositor()->SetFont(index, resource, fontSize, codepage);
+    }
+#endif
 
   private:
     std::string                 identifier_;
--- a/Framework/Viewport/WebAssemblyViewport.cpp	Fri Aug 16 16:24:11 2019 +0200
+++ b/Framework/Viewport/WebAssemblyViewport.cpp	Wed Aug 21 16:16:30 2019 +0200
@@ -86,9 +86,26 @@
   {
     try
     {
-      // the compositor COULD be dead!
-      if (GetCompositor())
+      if (!GetCompositor())
+      {
+        // this block was added because of (perceived?) bugs in the 
+        // browser where the WebGL contexts are NOT automatically restored 
+        // after being lost. 
+        // the WebGL context has been lost. Sce 
+
+        //LOG(ERROR) << "About to call WebAssemblyOpenGLContext::TryRecreate().";
+        //LOG(ERROR) << "Before calling it, isContextLost == " << context_.IsContextLost();
+
+        if (!context_.IsContextLost()) {
+          LOG(ERROR) << "Context restored!";
+          //LOG(ERROR) << "After calling it, isContextLost == " << context_.IsContextLost();
+          RestoreCompositor();
+          UpdateSize();
+        }
+      }
+      if (GetCompositor()) {
         GetCompositor()->Refresh();
+      }
     }
     catch (const OpenGLContextLostException& e)
     {
@@ -103,6 +120,11 @@
     }
   }
   
+  bool WebAssemblyOpenGLViewport::IsContextLost()
+  {
+    return context_.IsContextLost();
+  }
+
   void WebAssemblyOpenGLViewport::RestoreCompositor()
   {
     // the context must have been restored!
@@ -127,13 +149,27 @@
   bool WebAssemblyOpenGLViewport::OpenGLContextRestored()
   {
     LOG(ERROR) << "WebAssemblyOpenGLViewport::OpenGLContextRestored() for canvas: " << GetCanvasIdentifier();
-    RestoreCompositor();
-    UpdateSize();
+    
+    // maybe the context has already been restored by other means (the 
+    // Refresh() function)
+    if (!GetCompositor())
+    {
+      RestoreCompositor();
+      UpdateSize();
+    }
     return false;
   }
 
+  void* WebAssemblyOpenGLViewport::DebugGetInternalContext() const
+  {
+    return context_.DebugGetInternalContext();
+  }
+
   void WebAssemblyOpenGLViewport::RegisterContextCallbacks()
   {
+#if 0
+    // DISABLED ON 2019-08-20 and replaced by external JS calls because I could
+    // not get emscripten API to work
     // TODO: what's the impact of userCapture=true ?
     const char* canvasId = GetCanvasIdentifier().c_str();
     void* that = reinterpret_cast<void*>(this);
@@ -148,6 +184,7 @@
     //  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)
     {
@@ -158,6 +195,7 @@
       ORTHANC_ASSERT(false, msg.c_str());
     }
     LOG(TRACE) << "WebAssemblyOpenGLViewport::RegisterContextCallbacks() SUCCESS!!!";
+#endif
   }
 
   WebAssemblyCairoViewport::WebAssemblyCairoViewport(const std::string& canvas) :
@@ -209,5 +247,4 @@
     LOG(INFO) << "refreshing cairo viewport, TODO: blit to the canvans.getContext('2d')";
     GetCompositor()->Refresh();
   }
-
 }
--- a/Framework/Viewport/WebAssemblyViewport.h	Fri Aug 16 16:24:11 2019 +0200
+++ b/Framework/Viewport/WebAssemblyViewport.h	Wed Aug 21 16:16:30 2019 +0200
@@ -65,9 +65,18 @@
 
     virtual void Refresh() ORTHANC_OVERRIDE;
 
+    virtual bool IsContextLost() ORTHANC_OVERRIDE;
+
+    // this does NOT return whether the context is lost! This is called to 
+    // tell Stone that the context has been lost
     bool OpenGLContextLost();
+
+    // This should be called to indicate that the context has been lost
     bool OpenGLContextRestored();
 
+
+    virtual void* DebugGetInternalContext() const ORTHANC_OVERRIDE;
+
   private:
     virtual void DisableCompositor() ORTHANC_OVERRIDE;
     virtual void RestoreCompositor() ORTHANC_OVERRIDE;
--- a/Framework/Volumes/DicomVolumeImage.cpp	Fri Aug 16 16:24:11 2019 +0200
+++ b/Framework/Volumes/DicomVolumeImage.cpp	Wed Aug 21 16:16:30 2019 +0200
@@ -32,6 +32,7 @@
   {
     if (!HasGeometry())
     {
+      LOG(ERROR) << "DicomVolumeImage::CheckHasGeometry(): (!HasGeometry())";
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
   }
@@ -91,6 +92,7 @@
     }
     else
     {
+      LOG(ERROR) << "DicomVolumeImage::GetDicomParameters(): (!HasDicomParameters())";
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }      
   }
--- a/Framework/Volumes/DicomVolumeImageMPRSlicer.cpp	Fri Aug 16 16:24:11 2019 +0200
+++ b/Framework/Volumes/DicomVolumeImageMPRSlicer.cpp	Wed Aug 21 16:16:30 2019 +0200
@@ -33,6 +33,7 @@
   {
     if (!valid_)
     {
+      LOG(ERROR) << "DicomVolumeImageMPRSlicer::Slice::CheckValid(): (!valid_)";
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
   }
--- a/Framework/Volumes/IVolumeSlicer.cpp	Fri Aug 16 16:24:11 2019 +0200
+++ b/Framework/Volumes/IVolumeSlicer.cpp	Wed Aug 21 16:16:30 2019 +0200
@@ -27,12 +27,14 @@
 {
   uint64_t IVolumeSlicer::InvalidSlice::GetRevision()
   {
+    LOG(ERROR) << "IVolumeSlicer::InvalidSlice::GetRevision()";
     throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
   }
 
   ISceneLayer* IVolumeSlicer::InvalidSlice::CreateSceneLayer(const ILayerStyleConfigurator* configurator,
                                                              const CoordinateSystem3D& cuttingPlane)
   {
+    LOG(ERROR) << "IVolumeSlicer::InvalidSlice::CreateSceneLayer()";
     throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
   }
 }
--- a/Framework/Volumes/VolumeReslicer.cpp	Fri Aug 16 16:24:11 2019 +0200
+++ b/Framework/Volumes/VolumeReslicer.cpp	Wed Aug 21 16:16:30 2019 +0200
@@ -710,6 +710,8 @@
     }
     else
     {
+      LOG(ERROR) << "VolumeReslicer::GetOutputExtent(): (!success_)";
+
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
   }
@@ -724,6 +726,7 @@
     }
     else
     {
+      LOG(ERROR) << "VolumeReslicer::GetOutputSlice(): (!success_)";
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
   }
@@ -739,6 +742,7 @@
     }
     else
     {
+      LOG(ERROR) << "VolumeReslicer::ReleaseOutputSlice(): (!success_)";
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
   }
@@ -828,6 +832,7 @@
     }
     else
     {
+      LOG(ERROR) << "VolumeReslicer::GetPixelSpacing(): (!success_)";
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
   }
--- a/Framework/Volumes/VolumeSceneLayerSource.cpp	Fri Aug 16 16:24:11 2019 +0200
+++ b/Framework/Volumes/VolumeSceneLayerSource.cpp	Wed Aug 21 16:16:30 2019 +0200
@@ -85,6 +85,7 @@
   {
     if (configurator_.get() == NULL)
     {
+      LOG(ERROR) << "VolumeSceneLayerSource::GetConfigurator(): (configurator_.get() == NULL)";
       throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
     }
       
--- a/Platforms/Generic/Oracle.cpp	Fri Aug 16 16:24:11 2019 +0200
+++ b/Platforms/Generic/Oracle.cpp	Wed Aug 21 16:16:30 2019 +0200
@@ -140,6 +140,7 @@
 
       if (state_ != State_Init)
       {
+        LOG(ERROR) << "Oracle::PImpl::Start: (state_ != State_Init)";
         throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
       }
       
@@ -158,6 +159,7 @@
 
         if (state_ != State_Started)
         {
+          LOG(ERROR) << "Oracle::PImpl::Stop(): (state_ != State_Started)";
           throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
         }
 
--- a/README.md	Fri Aug 16 16:24:11 2019 +0200
+++ b/README.md	Wed Aug 21 16:16:30 2019 +0200
@@ -236,6 +236,15 @@
   url="https://doi.org/10.1007/s10278-018-0082-y"
 }
 
+Build the Application Samples
+-----------------------------
+
+**Visual Studio 2008 (v90) **
+
+```
+cmake -G "Visual Studio 9 2008" -DUSE_LEGACY_JSONCPP=ON -DENABLE_OPENGL=ON -DSTATIC_BUILD=ON -DOPENSSL_NO_CAPIENG=ON -DORTHANC_FRAMEWORK_SOURCE=path -DORTHANC_FRAMEWORK_ROOT="$($pwd)\..\orthanc" -DALLOW_DOWNLOADS=ON -DENABLE_SDL=ON ../orthanc-stone/Applications/Samples
+```
+
 Various notes to be sorted
 ---------------------------
 How to build the newest (2019-04-29) SDL samples under Windows, *inside* a