text: /*
   * Copyright (C) 2015 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  #ifndef TNT_FILAMENT_DETAILS_ENGINE_H
  #define TNT_FILAMENT_DETAILS_ENGINE_H
  
  #include "upcast.h"
  
  #include "Allocators.h"
  #include "PostProcessManager.h"
  #include "ResourceList.h"
  
  #include "components/CameraManager.h"
  #include "components/LightManager.h"
  #include "components/TransformManager.h"
  #include "components/RenderableManager.h"
  
  #include "details/BufferObject.h"
  #include "details/Camera.h"
  #include "details/ColorGrading.h"
  #include "details/DebugRegistry.h"
  #include "details/Fence.h"
  #include "details/IndexBuffer.h"
  #include "details/RenderTarget.h"
  #include "details/SkinningBuffer.h"
  #include "details/Skybox.h"
  
  #include "private/backend/CommandBufferQueue.h"
  #include "private/backend/CommandStream.h"
  #include "private/backend/DriverApi.h"
  
  #include <private/filament/EngineEnums.h>
  #include <private/filament/UniformInterfaceBlock.h>
  
  #include <filament/ColorGrading.h>
  #include <filament/Engine.h>
  #include <filament/IndirectLight.h>
  #include <filament/Material.h>
  #include <filament/MaterialEnums.h>
  #include <filament/Skybox.h>
  #include <filament/Stream.h>
  #include <filament/Texture.h>
  #include <filament/VertexBuffer.h>
  
  #if FILAMENT_ENABLE_MATDBG
  #include <matdbg/DebugServer.h>
  #else
  namespace filament {
  namespace matdbg {
  class DebugServer;
  using MaterialKey = uint32_t;
  } // namespace matdbg
  } // namespace filament
  #endif
  
  #include <filaflat/ShaderBuilder.h>
  
  #include <utils/compiler.h>
  #include <utils/Allocator.h>
  #include <utils/JobSystem.h>
  #include <utils/CountDownLatch.h>
  
  #include <chrono>
  #include <memory>
  #include <random>
  #include <unordered_map>
  
  namespace filament {
  
  class Renderer;
  class MaterialParser;
  
  namespace backend {
  class Driver;
  class Program;
  } // namespace driver
  
  class FFence;
  class FMaterialInstance;
  class FRenderer;
  class FScene;
  class FSwapChain;
  class FView;
  
  class DFG;
  class ResourceAllocator;
  
  /*
   * Concrete implementation of the Engine interface. This keeps track of all hardware resources
   * for a given context.
   */
  class FEngine : public Engine {
  public:
  
      inline void* operator new(std::size_t count) noexcept {
          return utils::aligned_alloc(count * sizeof(FEngine), alignof(FEngine));
      }
  
      inline void operator delete(void* p) noexcept {
          utils::aligned_free(p);
      }
  
      using DriverApi = backend::DriverApi;
      using clock = std::chrono::steady_clock;
      using Epoch = clock::time_point;
      using duration = clock::duration;
  
      // TODO: these should come from a configuration object
      static constexpr float  CONFIG_Z_LIGHT_NEAR            = 5;
      static constexpr float  CONFIG_Z_LIGHT_FAR             = 100;
      static constexpr size_t CONFIG_FROXEL_SLICE_COUNT      = 16;
      static constexpr bool   CONFIG_IBL_USE_IRRADIANCE_MAP  = false;
  
      static constexpr size_t CONFIG_PER_RENDER_PASS_ARENA_SIZE   = filament::CONFIG_PER_RENDER_PASS_ARENA_SIZE;
      static constexpr size_t CONFIG_PER_FRAME_COMMANDS_SIZE      = filament::CONFIG_PER_FRAME_COMMANDS_SIZE;
      static constexpr size_t CONFIG_MIN_COMMAND_BUFFERS_SIZE     = filament::CONFIG_MIN_COMMAND_BUFFERS_SIZE;
      static constexpr size_t CONFIG_COMMAND_BUFFERS_SIZE         = filament::CONFIG_COMMAND_BUFFERS_SIZE;
  
  public:
      static FEngine* create(Backend backend = Backend::DEFAULT,
              Platform* platform = nullptr, void* sharedGLContext = nullptr);
  
  #if UTILS_HAS_THREADING
      static void createAsync(CreateCallback callback, void* user,
              Backend backend = Backend::DEFAULT,
              Platform* platform = nullptr, void* sharedGLContext = nullptr);
  
      static FEngine* getEngine(void* token);
  #endif
  
      static void destroy(FEngine* engine);
  
      ~FEngine() noexcept;
  
      backend::Driver& getDriver() const noexcept { return *mDriver; }
      DriverApi& getDriverApi() noexcept { return mCommandStream; }
      DFG* getDFG() const noexcept { return mDFG.get(); }
  
      // the per-frame Area is used by all Renderer, so they must run in sequence and
      // have freed all allocated memory when done. If this needs to change in the future,
      // we'll simply have to use separate Areas (for instance).
      LinearAllocatorArena& getPerRenderPassAllocator() noexcept { return mPerRenderPassAllocator; }
  
      // Material IDs...
      uint32_t getMaterialId() const noexcept { return mMaterialId++; }
  
      const FMaterial* getDefaultMaterial() const noexcept { return mDefaultMaterial; }
      const FMaterial* getSkyboxMaterial() const noexcept;
      const FIndirectLight* getDefaultIndirectLight() const noexcept { return mDefaultIbl; }
      const FTexture* getDummyCubemap() const noexcept { return mDefaultIblTexture; }
      const FColorGrading* getDefaultColorGrading() const noexcept { return mDefaultColorGrading; }
  
      backend::Handle<backend::HwRenderPrimitive> getFullScreenRenderPrimitive() const noexcept {
          return mFullScreenTriangleRph;
      }
  
      FVertexBuffer* getFullScreenVertexBuffer() const noexcept {
          return mFullScreenTriangleVb;
      }
  
      FIndexBuffer* getFullScreenIndexBuffer() const noexcept {
          return mFullScreenTriangleIb;
      }
  
      PostProcessManager const& getPostProcessManager() const noexcept {
          return mPostProcessManager;
      }
  
      PostProcessManager& getPostProcessManager() noexcept {
          return mPostProcessManager;
      }
  
      FRenderableManager& getRenderableManager() noexcept {
          return mRenderableManager;
      }
  
      FLightManager& getLightManager() noexcept {
          return mLightManager;
      }
  
      FCameraManager& getCameraManager() noexcept {
          return mCameraManager;
      }
  
      FTransformManager& getTransformManager() noexcept {
          return mTransformManager;
      }
  
      utils::EntityManager& getEntityManager() noexcept {
          return mEntityManager;
      }
  
      HeapAllocatorArena& getHeapAllocator() noexcept {
          return mHeapAllocator;
      }
  
      Backend getBackend() const noexcept {
          return mBackend;
      }
  
      Platform* getPlatform() const noexcept {
          return mPlatform;
      }
  
      ResourceAllocator& getResourceAllocator() noexcept {
          assert_invariant(mResourceAllocator);
          return *mResourceAllocator;
      }
  
      void* streamAlloc(size_t size, size_t alignment) noexcept;
  
      Epoch getEngineEpoch() const { return mEngineEpoch; }
      duration getEngineTime() const noexcept {
          return clock::now() - getEngineEpoch();
      }
  
      template <typename T>
      T* create(ResourceList<T>& list, typename T::Builder const& builder) noexcept;
  
      FBufferObject* createBufferObject(const BufferObject::Builder& builder) noexcept;
      FVertexBuffer* createVertexBuffer(const VertexBuffer::Builder& builder) noexcept;
      FIndexBuffer* createIndexBuffer(const IndexBuffer::Builder& builder) noexcept;
      FSkinningBuffer* createSkinningBuffer(const SkinningBuffer::Builder& builder) noexcept;
      FIndirectLight* createIndirectLight(const IndirectLight::Builder& builder) noexcept;
      FMaterial* createMaterial(const Material::Builder& builder) noexcept;
      FTexture* createTexture(const Texture::Builder& builder) noexcept;
      FSkybox* createSkybox(const Skybox::Builder& builder) noexcept;
      FColorGrading* createColorGrading(const ColorGrading::Builder& builder) noexcept;
      FStream* createStream(const Stream::Builder& builder) noexcept;
      FRenderTarget* createRenderTarget(const RenderTarget::Builder& builder) noexcept;
  
      void createRenderable(const RenderableManager::Builder& builder, utils::Entity entity);
      void createLight(const LightManager::Builder& builder, utils::Entity entity);
  
      FRenderer* createRenderer() noexcept;
      FMaterialInstance* createMaterialInstance(const FMaterial* material,
              const FMaterialInstance* other, const char* name) noexcept;
  
      FScene* createScene() noexcept;
      FView* createView() noexcept;
      FFence* createFence(FFence::Type type) noexcept;
      FSwapChain* createSwapChain(void* nativeWindow, uint64_t flags) noexcept;
      FSwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept;
  
      FCamera* createCamera(utils::Entity entity) noexcept;
      FCamera* getCameraComponent(utils::Entity entity) noexcept;
      void destroyCameraComponent(utils::Entity entity) noexcept;
  
  
      bool destroy(const FBufferObject* p);
      bool destroy(const FVertexBuffer* p);
      bool destroy(const FFence* p);
      bool destroy(const FIndexBuffer* p);
      bool destroy(const FSkinningBuffer* p);
      bool destroy(const FIndirectLight* p);
      bool destroy(const FMaterial* p);
      bool destroy(const FMaterialInstance* p);
      bool destroy(const FRenderer* p);
      bool destroy(const FScene* p);
      bool destroy(const FSkybox* p);
      bool destroy(const FColorGrading* p);
      bool destroy(const FStream* p);
      bool destroy(const FTexture* p);
      bool destroy(const FRenderTarget* p);
      bool destroy(const FSwapChain* p);
      bool destroy(const FView* p);
  
      void destroy(utils::Entity e);
  
      void flushAndWait();
  
      // flush the current buffer
      void flush();
  
      // flush the current buffer based on some heuristics
      void flushIfNeeded() {
          auto counter = mFlushCounter + 1;
          if (UTILS_LIKELY(counter < 128)) {
              mFlushCounter = counter;
          } else {
              mFlushCounter = 0;
              flush();
          }
      }
  
      /**
       * Processes the platform's event queue when called from the platform's event-handling thread.
       * Returns false when called from any other thread.
       */
      bool pumpPlatformEvents() {
          return mPlatform->pumpEvents();
      }
  
      void prepare();
      void gc();
  
      filaflat::ShaderBuilder& getVertexShaderBuilder() const noexcept {
          return mVertexShaderBuilder;
      }
  
      filaflat::ShaderBuilder& getFragmentShaderBuilder() const noexcept {
          return mFragmentShaderBuilder;
      }
  
      FDebugRegistry& getDebugRegistry() noexcept {
          return mDebugRegistry;
      }
  
      bool execute();
  
      utils::JobSystem& getJobSystem() noexcept {
          return mJobSystem;
      }
  
      std::default_random_engine& getRandomEngine() {
          return mRandomEngine;
      }
  
  private:
      FEngine(Backend backend, Platform* platform, void* sharedGLContext);
      void init();
      void shutdown();
  
      int loop();
      void flushCommandBuffer(backend::CommandBufferQueue& commandBufferQueue);
  
      template<typename T, typename L>
      bool terminateAndDestroy(const T* p, ResourceList<T, L>& list);
  
      template<typename T, typename L>
      void cleanupResourceList(ResourceList<T, L>& list);
  
      backend::Driver* mDriver = nullptr;
  
      Backend mBackend;
      Platform* mPlatform = nullptr;
      bool mOwnPlatform = false;
      void* mSharedGLContext = nullptr;
      bool mTerminated = false;
      backend::Handle<backend::HwRenderPrimitive> mFullScreenTriangleRph;
      FVertexBuffer* mFullScreenTriangleVb = nullptr;
      FIndexBuffer* mFullScreenTriangleIb = nullptr;
  
      PostProcessManager mPostProcessManager;
  
      utils::EntityManager& mEntityManager;
      FRenderableManager mRenderableManager;
      FTransformManager mTransformManager;
      FLightManager mLightManager;
      FCameraManager mCameraManager;
      ResourceAllocator* mResourceAllocator = nullptr;
  
      ResourceList<FBufferObject> mBufferObjects{ "BufferObject" };
      ResourceList<FRenderer> mRenderers{ "Renderer" };
      ResourceList<FView> mViews{ "View" };
      ResourceList<FScene> mScenes{ "Scene" };
      ResourceList<FFence, utils::LockingPolicy::SpinLock> mFences{"Fence"};
      ResourceList<FSwapChain> mSwapChains{ "SwapChain" };
      ResourceList<FStream> mStreams{ "Stream" };
      ResourceList<FIndexBuffer> mIndexBuffers{ "IndexBuffer" };
      ResourceList<FSkinningBuffer> mSkinningBuffers{ "SkinningBuffer" };
      ResourceList<FVertexBuffer> mVertexBuffers{ "VertexBuffer" };
      ResourceList<FIndirectLight> mIndirectLights{ "IndirectLight" };
      ResourceList<FMaterial> mMaterials{ "Material" };
      ResourceList<FTexture> mTextures{ "Texture" };
      ResourceList<FSkybox> mSkyboxes{ "Skybox" };
      ResourceList<FColorGrading> mColorGradings{ "ColorGrading" };
      ResourceList<FRenderTarget> mRenderTargets{ "RenderTarget" };
  
      mutable uint32_t mMaterialId = 0;
  
      // FMaterialInstance are handled directly by FMaterial
      std::unordered_map<const FMaterial*, ResourceList<FMaterialInstance>> mMaterialInstances;
  
      std::unique_ptr<DFG> mDFG;
  
      std::thread mDriverThread;
      backend::CommandBufferQueue mCommandBufferQueue;
      DriverApi mCommandStream;
      uint32_t mFlushCounter = 0;
  
      LinearAllocatorArena mPerRenderPassAllocator;
      HeapAllocatorArena mHeapAllocator;
  
      utils::JobSystem mJobSystem;
      static uint32_t getJobSystemThreadPoolSize() noexcept;
  
      std::default_random_engine mRandomEngine;
  
      Epoch mEngineEpoch;
  
      mutable FMaterial const* mDefaultMaterial = nullptr;
      mutable FMaterial const* mSkyboxMaterial = nullptr;
  
      mutable FTexture* mDefaultIblTexture = nullptr;
      mutable FIndirectLight* mDefaultIbl = nullptr;
  
      mutable FColorGrading* mDefaultColorGrading = nullptr;
  
      mutable utils::CountDownLatch mDriverBarrier;
  
      mutable filaflat::ShaderBuilder mVertexShaderBuilder;
      mutable filaflat::ShaderBuilder mFragmentShaderBuilder;
      FDebugRegistry mDebugRegistry;
  
      std::thread::id mMainThreadId{};
  
  public:
      // these are the debug properties used by FDebug. They're accessed directly by modules who need them.
      struct {
          struct {
              bool far_uses_shadowcasters = true;
              bool focus_shadowcasters = true;
              bool lispsm = true;
              bool visualize_cascades = false;
              bool tightly_bound_scene = true;
              float dzn = -1.0f;
              float dzf =  1.0f;
          } shadowmap;
          struct {
              bool enabled = true;
              int sampleCount = 7;
              int spiralTurns = 1;
              int kernelSize = 23;
              float stddev = 8.0f;
          } ssao;
          struct {
              bool camera_at_origin = true;
          } view;
          struct {
              // When set to true, the backend will attempt to capture the next frame and write the
              // capture to file. At the moment, only supported by the Metal backend.
              bool doFrameCapture = false;
          } renderer;
          matdbg::DebugServer* server = nullptr;
      } debug;
  };
  
  FILAMENT_UPCAST(Engine)
  
  } // namespace filament
  
  #endif // TNT_FILAMENT_DETAILS_ENGINE_H
  