text: /***************************************************************************
  # Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
  #
  # Redistribution and use in source and binary forms, with or without
  # modification, are permitted provided that the following conditions
  # are met:
  #  * Redistributions of source code must retain the above copyright
  #    notice, this list of conditions and the following disclaimer.
  #  * Redistributions in binary form must reproduce the above copyright
  #    notice, this list of conditions and the following disclaimer in the
  #    documentation and/or other materials provided with the distribution.
  #  * Neither the name of NVIDIA CORPORATION nor the names of its
  #    contributors may be used to endorse or promote products derived
  #    from this software without specific prior written permission.
  #
  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
  # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  # PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  ***************************************************************************/
  
  #pragma once
  #include "Falcor.h"
  
  #if FALCOR_USE_PYTHON
  
  #include "Utils/PythonEmbedding.h"
  #include <ctime>
  #include <random>
  
  using namespace Falcor;
  
  /** A class that implements a basic "live trained" deep neural network using Python + Tensorflow
      to learn how to relight an image based on light direction.  The training data is generated by
      a raster engine live in front of the viewer.
   */
  class LiveTrainRenderer : public Renderer, std::enable_shared_from_this<LiveTrainRenderer>
  {
  public:
      using SharedPtr = std::shared_ptr<LiveTrainRenderer>;
      using SharedConstPtr = std::shared_ptr<const LiveTrainRenderer>;
  
      /** Constructor & destructors
          */
      virtual ~LiveTrainRenderer() = default;
      static SharedPtr create() { return std::make_shared<LiveTrainRenderer>(); }
  
      /** Callbacks overridden from Renderer base class
          */
      virtual void onLoad(SampleCallbacks* pSample, RenderContext::SharedPtr pRenderContext) override;
      virtual void onFrameRender(SampleCallbacks* pSample, RenderContext::SharedPtr pRenderContext, Fbo::SharedPtr pTargetFbo) override;
      virtual void onResizeSwapChain(SampleCallbacks* pSample, uint32_t width, uint32_t height) override;
      virtual bool onKeyEvent(SampleCallbacks* pSample, const KeyboardEvent& keyEvent) override;
      virtual bool onMouseEvent(SampleCallbacks* pSample, const MouseEvent& mouseEvent) override;
      virtual void onGuiRender(SampleCallbacks* pSample, Gui* pGui) override;
  
      /** Methods to handle/encapsulate basic Falcor scene rendering
       */
      void msaaResolvePass(RenderContext::SharedPtr context);
      void lightingPass(RenderContext::SharedPtr context);
      void initLightingPass();
  
      /** Methods to encapsulate basic training/inference features
       */
      void doPythonInit();
      void doPythonTrain(Texture::SharedPtr fromTex);
      void doPythonInference();
      void doRandomTrain();
  
      /** Encapsulates calls to stringified Python code, plus timing the execution, setting any 
          error flags, and grabbing an error message (if the Python code failed)
             -> Returns the time (in ms) taken by execution.  
             -> If return value < 0, an execution error occurred.
      */
      float executeStringAndSetFlags(const std::string &pyCode);
  
      /** Helper functions to make text additions to the GUI slightly cleaner in onGuiRender()
              Probably should do these "right" at some point...
          */
      void addTextHelper(Gui* pGui, const char *format, int intVal);
      void addTextHelper(Gui* pGui, const char *format, float floatVal);
      void addTextHelper(Gui* pGui, const char *format, float floatVal1, float floatVal2);
  
      /** Load / reload Python.  Stringifies scripts to avoid reloading them each frame.
          */
      void reloadPythonScripts(void);
  
  private:
      Scene::SharedPtr loadScene(const std::string& filename);
      void initNewScene(SceneRenderer::SharedPtr scene);
  
      // Stringified versions of our Python code.
      std::string mPythonInit;
      std::string mPythonTrain;
      std::string mPythonInfer;
      std::string mPythonCreateModel[3] = {
          "model = CreateSimpleImageModel_128()", 
          "model = CreateSimpleImageModel_256()", 
          "model = CreateSimpleImageModel_512()"
      };
  
      // Dropdown & data to select between mPythonCreateModel[] variants
      Gui::DropdownList  mDNNSizeList;
      uint32_t mDNNSize = 0;
  
      // Simplistic random number generation for randomizing light directions
      std::mt19937 mRng;
      float randomFloat() { return ((float)mRng()) / ((float)0xFFFFFFFFu); }
  
      // Our embedded Python interpreter
      PythonEmbedding::SharedPtr mPython;
      pybind11::dict mGlobals;
  
      int mTrainsLeft = 0;
  
      bool mHasFailure = false;
      bool mIsInitialized = false;
      bool mPythonInitialized = false;
  
      bool mDoTraining = false;
      bool mDoInference = false;
      char mSaveName[256];
      bool mContinuousTrain = false;
      float mLastTrainTime = -1.0f;
      float mLastInferenceTime = -1.0f;
      std::string mTestResult = "";
      std::string mTrainCostStr = "";
      uint32_t mNumTrainingRuns = 0;
  
      Texture::SharedPtr mPythonReturnTexture = nullptr;
  
      ///////////////////////////////////////////////////
      //  Below here:  Data for basic Falcor rendering
      ///////////////////////////////////////////////////
      SceneRenderer::SharedPtr        mpScene = nullptr;
  
      struct
      {
          GraphicsVars::SharedPtr pVars;
          GraphicsProgram::SharedPtr pProgram;
      } mLightingPass;
  
      struct ProgramControl
      {
          bool enabled;
          std::string define;
          std::string value;
      };
      enum ControlID
      {
          SuperSampling,
          EnableShadows,
          EnableReflections,
          EnableSSAO,
  
          Count
      };
      std::vector<ProgramControl> mControls;
  
      Fbo::SharedPtr mpMainFbo;
      Fbo::SharedPtr mpResolveFbo;
      Fbo::SharedPtr mpCaptureFbo;
      uint32_t       mSampleCount = 4;
  
      void applyLightingProgramControl(ControlID controlId);
      void initControls(void);
  };
  
  #endif
  