text: /****************************************************************************
   * Copyright (C) 2014-2018 Intel Corporation.   All Rights Reserved.
   *
   * Permission is hereby granted, free of charge, to any person obtaining a
   * copy of this software and associated documentation files (the "Software"),
   * to deal in the Software without restriction, including without limitation
   * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   * and/or sell copies of the Software, and to permit persons to whom the
   * Software is furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice (including the next
   * paragraph) shall be included in all copies or substantial portions of the
   * Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
   * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
   * IN THE SOFTWARE.
   *
   * @file context.h
   *
   * @brief Definitions for SWR_CONTEXT and DRAW_CONTEXT
   *        The SWR_CONTEXT is our global context and contains the DC ring,
   *        thread state, etc.
   *
   *        The DRAW_CONTEXT contains all state associated with a draw operation.
   *
   ******************************************************************************/
  #pragma once
  
  #include <condition_variable>
  #include <algorithm>
  
  #include "core/api.h"
  #include "core/utils.h"
  #include "core/arena.h"
  #include "core/fifo.hpp"
  #include "core/knobs.h"
  #include "common/intrin.h"
  #include "core/threads.h"
  #include "ringbuffer.h"
  #include "archrast/archrast.h"
  
  // x.8 fixed point precision values
  #define FIXED_POINT_SHIFT 8
  #define FIXED_POINT_SCALE 256
  
  // x.16 fixed point precision values
  #define FIXED_POINT16_SHIFT 16
  #define FIXED_POINT16_SCALE 65536
  
  struct SWR_CONTEXT;
  struct DRAW_CONTEXT;
  
  struct TRI_FLAGS
  {
      uint32_t frontFacing : 1;
      uint32_t yMajor : 1;
      uint32_t coverageMask : (SIMD_TILE_X_DIM* SIMD_TILE_Y_DIM);
      uint32_t reserved : 32 - 1 - 1 - (SIMD_TILE_X_DIM * SIMD_TILE_Y_DIM);
      float    pointSize;
      uint32_t renderTargetArrayIndex;
      uint32_t viewportIndex;
  };
  
  //////////////////////////////////////////////////////////////////////////
  /// SWR_TRIANGLE_DESC
  /////////////////////////////////////////////////////////////////////////
  struct SWR_TRIANGLE_DESC
  {
      float I[3];
      float J[3];
      float Z[3];
      float OneOverW[3];
      float recipDet;
  
      float* pRecipW;
      float* pAttribs;
      float* pPerspAttribs;
      float* pSamplePos;
      float* pUserClipBuffer;
  
      uint64_t coverageMask[SWR_MAX_NUM_MULTISAMPLES];
      uint64_t innerCoverageMask; // Conservative rasterization inner coverage: marked covered if
                                  // entire pixel is covered
      uint64_t anyCoveredSamples;
  
      TRI_FLAGS triFlags;
  };
  
  struct TRIANGLE_WORK_DESC
  {
      float* pTriBuffer;
      float* pAttribs;
      float* pUserClipBuffer;
      uint32_t  numAttribs;
      TRI_FLAGS triFlags;
  };
  
  struct CLEAR_DESC
  {
      SWR_RECT rect;
      uint32_t attachmentMask;
      uint32_t renderTargetArrayIndex;
      float    clearRTColor[4]; // RGBA_32F
      float    clearDepth;      // [0..1]
      uint8_t  clearStencil;
  };
  
  struct DISCARD_INVALIDATE_TILES_DESC
  {
      uint32_t       attachmentMask;
      SWR_RECT       rect;
      SWR_TILE_STATE newTileState;
      bool           createNewTiles;
      bool           fullTilesOnly;
  };
  
  struct SYNC_DESC
  {
      PFN_CALLBACK_FUNC pfnCallbackFunc;
      uint64_t          userData;
      uint64_t          userData2;
      uint64_t          userData3;
  };
  
  struct STORE_TILES_DESC
  {
      uint32_t       attachmentMask;
      SWR_TILE_STATE postStoreTileState;
      SWR_RECT       rect;
  };
  
  struct COMPUTE_DESC
  {
      uint32_t threadGroupCountX;
      uint32_t threadGroupCountY;
      uint32_t threadGroupCountZ;
  };
  
  typedef void (*PFN_WORK_FUNC)(DRAW_CONTEXT* pDC,
                                uint32_t      workerId,
                                uint32_t      macroTile,
                                void*         pDesc);
  
  enum WORK_TYPE
  {
      SYNC,
      DRAW,
      CLEAR,
      DISCARDINVALIDATETILES,
      STORETILES,
      SHUTDOWN,
  };
  
  OSALIGNSIMD(struct) BE_WORK
  {
      WORK_TYPE     type;
      PFN_WORK_FUNC pfnWork;
      union
      {
          SYNC_DESC                     sync;
          TRIANGLE_WORK_DESC            tri;
          CLEAR_DESC                    clear;
          DISCARD_INVALIDATE_TILES_DESC discardInvalidateTiles;
          STORE_TILES_DESC              storeTiles;
      } desc;
  };
  
  struct DRAW_WORK
  {
      DRAW_CONTEXT* pDC;
      union
      {
          uint32_t numIndices; // DrawIndexed: Number of indices for draw.
          uint32_t numVerts;   // Draw: Number of verts (triangles, lines, etc)
      };
      union
      {
          gfxptr_t xpIB;        // DrawIndexed: App supplied int32 indices
          uint32_t startVertex; // Draw: Starting vertex in VB to render from.
      };
      int32_t  baseVertex;
      uint32_t numInstances;  // Number of instances
      uint32_t startInstance; // Instance offset
      uint32_t startPrimID;   // starting primitiveID for this draw batch
      uint32_t
                 startVertexID; // starting VertexID for this draw batch (only needed for non-indexed draws)
      SWR_FORMAT type;          // index buffer type
  };
  
  typedef void (*PFN_FE_WORK_FUNC)(SWR_CONTEXT*  pContext,
                                   DRAW_CONTEXT* pDC,
                                   uint32_t      workerId,
                                   void*         pDesc);
  struct FE_WORK
  {
      WORK_TYPE        type;
      PFN_FE_WORK_FUNC pfnWork;
      union
      {
          SYNC_DESC                     sync;
          DRAW_WORK                     draw;
          CLEAR_DESC                    clear;
          DISCARD_INVALIDATE_TILES_DESC discardInvalidateTiles;
          STORE_TILES_DESC              storeTiles;
      } desc;
  };
  
  struct GUARDBANDS
  {
      float left[KNOB_NUM_VIEWPORTS_SCISSORS];
      float right[KNOB_NUM_VIEWPORTS_SCISSORS];
      float top[KNOB_NUM_VIEWPORTS_SCISSORS];
      float bottom[KNOB_NUM_VIEWPORTS_SCISSORS];
  };
  
  struct PA_STATE;
  
  // function signature for pipeline stages that execute after primitive assembly
  typedef void (*PFN_PROCESS_PRIMS)(DRAW_CONTEXT*      pDC,
                                    PA_STATE&          pa,
                                    uint32_t           workerId,
                                    simdvector         prims[],
                                    uint32_t           primMask,
                                    simdscalari const& primID,
                                    simdscalari const& viewportIdx,
                                    simdscalari const& rtIdx);
  
  // function signature for pipeline stages that execute after primitive assembly
  typedef void(SIMDCALL* PFN_PROCESS_PRIMS_SIMD16)(DRAW_CONTEXT*        pDC,
                                                   PA_STATE&            pa,
                                                   uint32_t             workerId,
                                                   simd16vector         prims[],
                                                   uint32_t             primMask,
                                                   simd16scalari const& primID,
                                                   simd16scalari const& viewportIdx,
                                                   simd16scalari const& rtIdx);
  
  OSALIGNLINE(struct) API_STATE
  {
      // Vertex Buffers
      SWR_VERTEX_BUFFER_STATE vertexBuffers[KNOB_NUM_STREAMS];
  
      // GS - Geometry Shader State
      SWR_GS_STATE gsState;
      PFN_GS_FUNC  pfnGsFunc;
  
      // FS - Fetch Shader State
      PFN_FETCH_FUNC pfnFetchFunc;
  
      // VS - Vertex Shader State
      PFN_VERTEX_FUNC pfnVertexFunc;
  
      // Index Buffer
      SWR_INDEX_BUFFER_STATE indexBuffer;
  
      // CS - Compute Shader
      PFN_CS_FUNC pfnCsFunc;
      uint32_t    totalThreadsInGroup;
      uint32_t    totalSpillFillSize;
      uint32_t    scratchSpaceSizePerWarp;
      uint32_t    scratchSpaceNumWarps;
  
      // FE - Frontend State
      SWR_FRONTEND_STATE frontendState;
  
      // SOS - Streamout Shader State
      PFN_SO_FUNC pfnSoFunc[MAX_SO_STREAMS];
  
      // Streamout state
      SWR_STREAMOUT_STATE          soState;
      mutable SWR_STREAMOUT_BUFFER soBuffer[MAX_SO_STREAMS];
  
      // Tessellation State
      PFN_HS_FUNC  pfnHsFunc;
      PFN_DS_FUNC  pfnDsFunc;
      SWR_TS_STATE tsState;
  
      // Number of attributes used by the frontend (vs, so, gs)
      uint32_t feNumAttributes;
  
      // RS - Rasterizer State
      SWR_RASTSTATE rastState;
      // floating point multisample offsets
      float samplePos[SWR_MAX_NUM_MULTISAMPLES * 2];
  
      GUARDBANDS gbState;
  
      SWR_VIEWPORT          vp[KNOB_NUM_VIEWPORTS_SCISSORS];
      SWR_VIEWPORT_MATRICES vpMatrices;
  
      SWR_RECT scissorRects[KNOB_NUM_VIEWPORTS_SCISSORS];
      SWR_RECT scissorsInFixedPoint[KNOB_NUM_VIEWPORTS_SCISSORS];
      bool     scissorsTileAligned;
  
      bool               forceFront;
      PRIMITIVE_TOPOLOGY topology;
  
  
      // Backend state
      OSALIGNLINE(SWR_BACKEND_STATE) backendState;
  
      SWR_DEPTH_BOUNDS_STATE depthBoundsState;
  
      // PS - Pixel shader state
      SWR_PS_STATE psState;
  
      SWR_DEPTH_STENCIL_STATE depthStencilState;
  
      // OM - Output Merger State
      SWR_BLEND_STATE    blendState;
      PFN_BLEND_JIT_FUNC pfnBlendFunc[SWR_NUM_RENDERTARGETS];
  
      struct
      {
          uint32_t enableStatsFE : 1;        // Enable frontend pipeline stats
          uint32_t enableStatsBE : 1;        // Enable backend pipeline stats
          uint32_t colorHottileEnable : 8;   // Bitmask of enabled color hottiles
          uint32_t depthHottileEnable : 1;   // Enable depth buffer hottile
          uint32_t stencilHottileEnable : 1; // Enable stencil buffer hottile
      };
  
      PFN_QUANTIZE_DEPTH pfnQuantizeDepth;
  };
  
  class MacroTileMgr;
  class DispatchQueue;
  
  struct RenderOutputBuffers
  {
      uint8_t* pColor[SWR_NUM_RENDERTARGETS];
      uint8_t* pDepth;
      uint8_t* pStencil;
  };
  
  // Plane equation A/B/C coeffs used to evaluate I/J barycentric coords
  struct BarycentricCoeffs
  {
      simdscalar vIa;
      simdscalar vIb;
      simdscalar vIc;
  
      simdscalar vJa;
      simdscalar vJb;
      simdscalar vJc;
  
      simdscalar vZa;
      simdscalar vZb;
      simdscalar vZc;
  
      simdscalar vRecipDet;
  
      simdscalar vAOneOverW;
      simdscalar vBOneOverW;
      simdscalar vCOneOverW;
  };
  
  // pipeline function pointer types
  typedef void (*PFN_BACKEND_FUNC)(
      DRAW_CONTEXT*, uint32_t, uint32_t, uint32_t, SWR_TRIANGLE_DESC&, RenderOutputBuffers&);
  typedef void (*PFN_OUTPUT_MERGER)(SWR_PS_CONTEXT&,
                                    uint8_t* (&)[SWR_NUM_RENDERTARGETS],
                                    uint32_t,
                                    const SWR_BLEND_STATE*,
                                    const PFN_BLEND_JIT_FUNC (&)[SWR_NUM_RENDERTARGETS],
                                    simdscalar&,
                                    simdscalar const&);
  typedef void (*PFN_CALC_PIXEL_BARYCENTRICS)(const BarycentricCoeffs&, SWR_PS_CONTEXT&);
  typedef void (*PFN_CALC_SAMPLE_BARYCENTRICS)(const BarycentricCoeffs&, SWR_PS_CONTEXT&);
  typedef void (*PFN_CALC_CENTROID_BARYCENTRICS)(const BarycentricCoeffs&,
                                                 SWR_PS_CONTEXT&,
                                                 const uint64_t* const,
                                                 const uint32_t,
                                                 simdscalar const&,
                                                 simdscalar const&);
  
  struct BACKEND_FUNCS
  {
      PFN_BACKEND_FUNC pfnBackend;
  };
  
  // Draw State
  struct DRAW_STATE
  {
      API_STATE state;
  
      void* pPrivateState; // Its required the driver sets this up for each draw.
  
      // pipeline function pointers, filled in by API thread when setting up the draw
      BACKEND_FUNCS     backendFuncs;
      PFN_PROCESS_PRIMS pfnProcessPrims;
  #if USE_SIMD16_FRONTEND
      PFN_PROCESS_PRIMS_SIMD16 pfnProcessPrims_simd16;
  #endif
  
      CachingArena* pArena; // This should only be used by API thread.
  };
  
  struct DRAW_DYNAMIC_STATE
  {
      void Reset(uint32_t numThreads)
      {
          SWR_STATS* pSavePtr = pStats;
          memset(this, 0, sizeof(*this));
          pStats = pSavePtr;
          memset(pStats, 0, sizeof(SWR_STATS) * numThreads);
      }
      ///@todo Currently assumes only a single FE can do stream output for a draw.
      uint32_t SoWriteOffset[4];
      bool     SoWriteOffsetDirty[4];
  
      SWR_STATS_FE statsFE; // Only one FE thread per DC.
      SWR_STATS*   pStats;
  };
  
  // Draw Context
  //    The api thread sets up a draw context that exists for the life of the draw.
  //    This draw context maintains all of the state needed for the draw operation.
  struct DRAW_CONTEXT
  {
      SWR_CONTEXT* pContext;
      union
      {
          MacroTileMgr*  pTileMgr;
          DispatchQueue* pDispatch; // Queue for thread groups. (isCompute)
      };
      DRAW_STATE*   pState; // Read-only state. Core should not update this outside of API thread.
      CachingArena* pArena;
  
      uint32_t drawId;
      bool     dependentFE;  // Frontend work is dependent on all previous FE
      bool     dependent;    // Backend work is dependent on all previous BE
      bool     isCompute;    // Is this DC a compute context?
      bool     cleanupState; // True if this is the last draw using an entry in the state ring.
  
      FE_WORK FeWork;
  
      SYNC_DESC retireCallback; // Call this func when this DC is retired.
  
      DRAW_DYNAMIC_STATE dynState;
  
      volatile OSALIGNLINE(bool) doneFE; // Is FE work done for this draw?
      volatile OSALIGNLINE(uint32_t) FeLock;
      volatile OSALIGNLINE(uint32_t) threadsDone;
  };
  
  static_assert((sizeof(DRAW_CONTEXT) & 63) == 0, "Invalid size for DRAW_CONTEXT");
  
  INLINE const API_STATE& GetApiState(const DRAW_CONTEXT* pDC)
  {
      SWR_ASSERT(pDC != nullptr);
      SWR_ASSERT(pDC->pState != nullptr);
  
      return pDC->pState->state;
  }
  
  INLINE void* GetPrivateState(const DRAW_CONTEXT* pDC)
  {
      SWR_ASSERT(pDC != nullptr);
      SWR_ASSERT(pDC->pState != nullptr);
  
      return pDC->pState->pPrivateState;
  }
  
  class HotTileMgr;
  
  struct SWR_CONTEXT
  {
      // Draw Context Ring
      //  Each draw needs its own state in order to support mulitple draws in flight across multiple
      //  threads. We maintain N draw contexts configured as a ring. The size of the ring limits the
      //  maximum number of draws that can be in flight at any given time.
      //
      //  Description:
      //  1. State - When an application first sets state we'll request a new draw context to use.
      //     a. If there are no available draw contexts then we'll have to wait until one becomes
      //     free. b. If one is available then set pCurDrawContext to point to it and mark it in use.
      //     c. All state calls set state on pCurDrawContext.
      //  2. Draw - Creates submits a work item that is associated with current draw context.
      //     a. Set pPrevDrawContext = pCurDrawContext
      //     b. Set pCurDrawContext to NULL.
      //  3. State - When an applications sets state after draw
      //     a. Same as step 1.
      //     b. State is copied from prev draw context to current.
      RingBuffer<DRAW_CONTEXT> dcRing;
  
      DRAW_CONTEXT* pCurDrawContext;  // This points to DC entry in ring for an unsubmitted draw.
      DRAW_CONTEXT* pPrevDrawContext; // This points to DC entry for the previous context submitted
                                      // that we can copy state from.
  
      MacroTileMgr*  pMacroTileManagerArray;
      DispatchQueue* pDispatchQueueArray;
  
      // Draw State Ring
      //  When draw are very large (lots of primitives) then the API thread will break these up.
      //  These split draws all have identical state. So instead of storing the state directly
      //  in the Draw Context (DC) we instead store it in a Draw State (DS). This allows multiple DCs
      //  to reference a single entry in the DS ring.
      RingBuffer<DRAW_STATE> dsRing;
  
      uint32_t curStateId; // Current index to the next available entry in the DS ring.
  
      uint32_t NumWorkerThreads;
      uint32_t NumFEThreads;
      uint32_t NumBEThreads;
  
      THREAD_POOL              threadPool; // Thread pool associated with this context
      SWR_THREADING_INFO       threadInfo;
      SWR_API_THREADING_INFO   apiThreadInfo;
      SWR_WORKER_PRIVATE_STATE workerPrivateState;
  
      uint32_t MAX_DRAWS_IN_FLIGHT;
  
      std::condition_variable FifosNotEmpty;
      std::mutex              WaitLock;
  
      uint32_t privateStateSize;
  
      HotTileMgr* pHotTileMgr;
  
      // Callback functions, passed in at create context time
      PFN_LOAD_TILE                   pfnLoadTile;
      PFN_STORE_TILE                  pfnStoreTile;
      PFN_CLEAR_TILE                  pfnClearTile;
      PFN_TRANSLATE_GFXPTR_FOR_READ   pfnTranslateGfxptrForRead;
      PFN_TRANSLATE_GFXPTR_FOR_WRITE  pfnTranslateGfxptrForWrite;
      PFN_MAKE_GFXPTR                 pfnMakeGfxPtr;
      PFN_UPDATE_SO_WRITE_OFFSET      pfnUpdateSoWriteOffset;
      PFN_UPDATE_STATS                pfnUpdateStats;
      PFN_UPDATE_STATS_FE             pfnUpdateStatsFE;
  
  
      // Global Stats
      SWR_STATS* pStats;
  
      // Scratch space for workers.
      uint8_t** ppScratch;
  
      volatile OSALIGNLINE(uint32_t) drawsOutstandingFE;
  
      OSALIGNLINE(CachingAllocator) cachingArenaAllocator;
      uint32_t frameCount;
  
      uint32_t lastFrameChecked;
      uint64_t lastDrawChecked;
      TileSet* pSingleThreadLockedTiles;
  
      // ArchRast thread contexts.
      HANDLE* pArContext;
  };
  
  #define UPDATE_STAT_BE(name, count)                   \
      if (GetApiState(pDC).enableStatsBE)               \
      {                                                 \
          pDC->dynState.pStats[workerId].name += count; \
      }
  #define UPDATE_STAT_FE(name, count)          \
      if (GetApiState(pDC).enableStatsFE)      \
      {                                        \
          pDC->dynState.statsFE.name += count; \
      }
  
  // ArchRast instrumentation framework
  #define AR_WORKER_CTX pDC->pContext->pArContext[workerId]
  #define AR_API_CTX pDC->pContext->pArContext[pContext->NumWorkerThreads]
  
  #ifdef KNOB_ENABLE_RDTSC
  #define RDTSC_BEGIN(type, drawid) RDTSC_START(type)
  #define RDTSC_END(type, count) RDTSC_STOP(type, count, 0)
  #else
  #define RDTSC_BEGIN(type, count)
  #define RDTSC_END(type, count)
  #endif
  
  #ifdef KNOB_ENABLE_AR
  #define _AR_EVENT(ctx, event) ArchRast::Dispatch(ctx, ArchRast::event)
  #define _AR_FLUSH(ctx, id) ArchRast::FlushDraw(ctx, id)
  #else
  #define _AR_EVENT(ctx, event)
  #define _AR_FLUSH(ctx, id)
  #endif
  
  // Use these macros for api thread.
  #define AR_API_EVENT(event) _AR_EVENT(AR_API_CTX, event)
  
  // Use these macros for worker threads.
  #define AR_EVENT(event) _AR_EVENT(AR_WORKER_CTX, event)
  #define AR_FLUSH(id) _AR_FLUSH(AR_WORKER_CTX, id)
  