LCOV - code coverage report
Current view: top level - source/renderer - ModelRenderer.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 0 286 0.0 %
Date: 2021-09-24 14:46:47 Functions: 0 18 0.0 %

          Line data    Source code
       1             : /* Copyright (C) 2021 Wildfire Games.
       2             :  * This file is part of 0 A.D.
       3             :  *
       4             :  * 0 A.D. is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 2 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * 0 A.D. is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "precompiled.h"
      19             : 
      20             : #include "graphics/Color.h"
      21             : #include "graphics/LightEnv.h"
      22             : #include "graphics/Material.h"
      23             : #include "graphics/Model.h"
      24             : #include "graphics/ModelDef.h"
      25             : #include "graphics/ShaderManager.h"
      26             : #include "graphics/TextureManager.h"
      27             : #include "lib/allocators/DynamicArena.h"
      28             : #include "lib/allocators/STLAllocators.h"
      29             : #include "lib/hash.h"
      30             : #include "lib/ogl.h"
      31             : #include "maths/Vector3D.h"
      32             : #include "maths/Vector4D.h"
      33             : #include "ps/CLogger.h"
      34             : #include "ps/CStrInternStatic.h"
      35             : #include "ps/Profile.h"
      36             : #include "renderer/MikktspaceWrap.h"
      37             : #include "renderer/ModelRenderer.h"
      38             : #include "renderer/ModelVertexRenderer.h"
      39             : #include "renderer/Renderer.h"
      40             : #include "renderer/RenderModifiers.h"
      41             : #include "renderer/SkyManager.h"
      42             : #include "renderer/TimeManager.h"
      43             : #include "renderer/WaterManager.h"
      44             : 
      45             : ///////////////////////////////////////////////////////////////////////////////////////////////
      46             : // ModelRenderer implementation
      47             : 
      48           0 : void ModelRenderer::Init()
      49             : {
      50           0 : }
      51             : 
      52             : // Helper function to copy object-space position and normal vectors into arrays.
      53           0 : void ModelRenderer::CopyPositionAndNormals(
      54             :         const CModelDefPtr& mdef,
      55             :         const VertexArrayIterator<CVector3D>& Position,
      56             :         const VertexArrayIterator<CVector3D>& Normal)
      57             : {
      58           0 :     size_t numVertices = mdef->GetNumVertices();
      59           0 :     SModelVertex* vertices = mdef->GetVertices();
      60             : 
      61           0 :     for (size_t j = 0; j < numVertices; ++j)
      62             :     {
      63           0 :         Position[j] = vertices[j].m_Coords;
      64           0 :         Normal[j] = vertices[j].m_Norm;
      65             :     }
      66           0 : }
      67             : 
      68             : // Helper function to transform position and normal vectors into world-space.
      69           0 : void ModelRenderer::BuildPositionAndNormals(
      70             :         CModel* model,
      71             :         const VertexArrayIterator<CVector3D>& Position,
      72             :         const VertexArrayIterator<CVector3D>& Normal)
      73             : {
      74           0 :     CModelDefPtr mdef = model->GetModelDef();
      75           0 :     size_t numVertices = mdef->GetNumVertices();
      76           0 :     SModelVertex* vertices = mdef->GetVertices();
      77             : 
      78           0 :     if (model->IsSkinned())
      79             :     {
      80             :         // boned model - calculate skinned vertex positions/normals
      81             : 
      82             :         // Avoid the noisy warnings that occur inside SkinPoint/SkinNormal in
      83             :         // some broken situations
      84           0 :         if (numVertices && vertices[0].m_Blend.m_Bone[0] == 0xff)
      85             :         {
      86           0 :             LOGERROR("Model %s is boned with unboned animation", mdef->GetName().string8());
      87           0 :             return;
      88             :         }
      89             : 
      90           0 :         CModelDef::SkinPointsAndNormals(numVertices, Position, Normal, vertices, mdef->GetBlendIndices(), model->GetAnimatedBoneMatrices());
      91             :     }
      92             :     else
      93             :     {
      94           0 :         PROFILE("software transform");
      95             :         // just copy regular positions, transform normals to world space
      96           0 :         const CMatrix3D& transform = model->GetTransform();
      97           0 :         const CMatrix3D& invtransform = model->GetInvTransform();
      98           0 :         for (size_t j = 0; j < numVertices; ++j)
      99             :         {
     100           0 :             transform.Transform(vertices[j].m_Coords, Position[j]);
     101           0 :             invtransform.RotateTransposed(vertices[j].m_Norm, Normal[j]);
     102             :         }
     103             :     }
     104             : }
     105             : 
     106             : 
     107             : // Helper function for lighting
     108           0 : void ModelRenderer::BuildColor4ub(
     109             :         CModel* model,
     110             :         const VertexArrayIterator<CVector3D>& Normal,
     111             :         const VertexArrayIterator<SColor4ub>& Color)
     112             : {
     113           0 :     PROFILE("lighting vertices");
     114             : 
     115           0 :     CModelDefPtr mdef = model->GetModelDef();
     116           0 :     size_t numVertices = mdef->GetNumVertices();
     117           0 :     const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
     118           0 :     CColor shadingColor = model->GetShadingColor();
     119             : 
     120           0 :     for (size_t j = 0; j < numVertices; ++j)
     121             :     {
     122           0 :         RGBColor tempcolor = lightEnv.EvaluateUnitScaled(Normal[j]);
     123           0 :         tempcolor.X *= shadingColor.r;
     124           0 :         tempcolor.Y *= shadingColor.g;
     125           0 :         tempcolor.Z *= shadingColor.b;
     126           0 :         Color[j] = ConvertRGBColorTo4ub(tempcolor);
     127             :     }
     128           0 : }
     129             : 
     130             : 
     131           0 : void ModelRenderer::GenTangents(const CModelDefPtr& mdef, std::vector<float>& newVertices, bool gpuSkinning)
     132             : {
     133           0 :     MikkTSpace ms(mdef, newVertices, gpuSkinning);
     134           0 :     ms.Generate();
     135           0 : }
     136             : 
     137             : 
     138             : // Copy UV coordinates
     139           0 : void ModelRenderer::BuildUV(
     140             :         const CModelDefPtr& mdef,
     141             :         const VertexArrayIterator<float[2]>& UV,
     142             :         int UVset)
     143             : {
     144           0 :     const size_t numVertices = mdef->GetNumVertices();
     145           0 :     const size_t numberOfUVPerVertex = mdef->GetNumUVsPerVertex();
     146             : 
     147           0 :     for (size_t j = 0; j < numVertices; ++j)
     148             :     {
     149           0 :         const CVector2D& uv = mdef->GetUVCoordinates()[j * numberOfUVPerVertex + UVset];
     150           0 :         UV[j][0] = uv.X;
     151           0 :         UV[j][1] = 1.0 - uv.Y;
     152             :     }
     153           0 : }
     154             : 
     155             : 
     156             : // Build default indices array.
     157           0 : void ModelRenderer::BuildIndices(
     158             :         const CModelDefPtr& mdef,
     159             :         const VertexArrayIterator<u16>& Indices)
     160             : {
     161           0 :     size_t idxidx = 0;
     162           0 :     SModelFace* faces = mdef->GetFaces();
     163             : 
     164           0 :     for (size_t j = 0; j < mdef->GetNumFaces(); ++j)
     165             :     {
     166           0 :         SModelFace& face = faces[j];
     167           0 :         Indices[idxidx++] = face.m_Verts[0];
     168           0 :         Indices[idxidx++] = face.m_Verts[1];
     169           0 :         Indices[idxidx++] = face.m_Verts[2];
     170             :     }
     171           0 : }
     172             : 
     173             : 
     174             : 
     175             : ///////////////////////////////////////////////////////////////////////////////////////////////
     176             : // ShaderModelRenderer implementation
     177             : 
     178             : 
     179             : /**
     180             :  * Internal data of the ShaderModelRenderer.
     181             :  *
     182             :  * Separated into the source file to increase implementation hiding (and to
     183             :  * avoid some causes of recompiles).
     184             :  */
     185             : struct ShaderModelRenderer::ShaderModelRendererInternals
     186             : {
     187           0 :     ShaderModelRendererInternals(ShaderModelRenderer* r) : m_Renderer(r) { }
     188             : 
     189             :     /// Back-link to "our" renderer
     190             :     ShaderModelRenderer* m_Renderer;
     191             : 
     192             :     /// ModelVertexRenderer used for vertex transformations
     193             :     ModelVertexRendererPtr vertexRenderer;
     194             : 
     195             :     /// List of submitted models for rendering in this frame
     196             :     std::vector<CModel*> submissions[CRenderer::CULL_MAX];
     197             : };
     198             : 
     199             : 
     200             : // Construction/Destruction
     201           0 : ShaderModelRenderer::ShaderModelRenderer(ModelVertexRendererPtr vertexrenderer)
     202             : {
     203           0 :     m = new ShaderModelRendererInternals(this);
     204           0 :     m->vertexRenderer = vertexrenderer;
     205           0 : }
     206             : 
     207           0 : ShaderModelRenderer::~ShaderModelRenderer()
     208             : {
     209           0 :     delete m;
     210           0 : }
     211           0 : 
     212             : // Submit one model.
     213             : void ShaderModelRenderer::Submit(int cullGroup, CModel* model)
     214           0 : {
     215           0 :     CModelRData* rdata = (CModelRData*)model->GetRenderData();
     216             : 
     217           0 :     // Ensure model data is valid
     218           0 :     const void* key = m->vertexRenderer.get();
     219             :     if (!rdata || rdata->GetKey() != key)
     220             :     {
     221           0 :         rdata = m->vertexRenderer->CreateModelData(key, model);
     222             :         model->SetRenderData(rdata);
     223           0 :         model->SetDirty(~0u);
     224             :     }
     225             : 
     226           0 :     m->submissions[cullGroup].push_back(model);
     227           0 : }
     228             : 
     229           0 : 
     230           0 : // Call update for all submitted models and enter the rendering phase
     231           0 : void ShaderModelRenderer::PrepareModels()
     232             : {
     233             :     for (int cullGroup = 0; cullGroup < CRenderer::CULL_MAX; ++cullGroup)
     234           0 :     {
     235           0 :         for (size_t i = 0; i < m->submissions[cullGroup].size(); ++i)
     236             :         {
     237             :             CModel* model = m->submissions[cullGroup][i];
     238             : 
     239           0 :             model->ValidatePosition();
     240             : 
     241           0 :             CModelRData* rdata = static_cast<CModelRData*>(model->GetRenderData());
     242             :             ENSURE(rdata->GetKey() == m->vertexRenderer.get());
     243           0 : 
     244             :             m->vertexRenderer->UpdateModelData(model, rdata, rdata->m_UpdateFlags);
     245           0 :             rdata->m_UpdateFlags = 0;
     246             :         }
     247           0 :     }
     248             : }
     249           0 : 
     250           0 : 
     251             : // Clear the submissions list
     252           0 : void ShaderModelRenderer::EndFrame()
     253           0 : {
     254             :     for (int cullGroup = 0; cullGroup < CRenderer::CULL_MAX; ++cullGroup)
     255             :         m->submissions[cullGroup].clear();
     256           0 : }
     257             : 
     258             : 
     259             : // Helper structs for ShaderModelRenderer::Render():
     260           0 : 
     261             : struct SMRSortByDistItem
     262           0 : {
     263           0 :     size_t techIdx;
     264           0 :     CModel* model;
     265             :     float dist;
     266             : };
     267             : 
     268             : struct SMRBatchModel
     269             : {
     270             :     bool operator()(CModel* a, CModel* b)
     271             :     {
     272             :         if (a->GetModelDef() < b->GetModelDef())
     273             :             return true;
     274             :         if (b->GetModelDef() < a->GetModelDef())
     275             :             return false;
     276             : 
     277             :         if (a->GetMaterial().GetDiffuseTexture() < b->GetMaterial().GetDiffuseTexture())
     278           0 :             return true;
     279             :         if (b->GetMaterial().GetDiffuseTexture() < a->GetMaterial().GetDiffuseTexture())
     280           0 :             return false;
     281             : 
     282           0 :         return a->GetMaterial().GetStaticUniforms() < b->GetMaterial().GetStaticUniforms();
     283             :     }
     284             : };
     285           0 : 
     286             : struct SMRCompareSortByDistItem
     287           0 : {
     288             :     bool operator()(const SMRSortByDistItem& a, const SMRSortByDistItem& b)
     289             :     {
     290           0 :         // Prefer items with greater distance, so we draw back-to-front
     291             :         return (a.dist > b.dist);
     292             : 
     293             :         // (Distances will almost always be distinct, so we don't need to bother
     294             :         // tie-breaking on modeldef/texture/etc)
     295             :     }
     296           0 : };
     297             : 
     298             : class SMRMaterialBucketKey
     299           0 : {
     300             : public:
     301             :     SMRMaterialBucketKey(CStrIntern effect, const CShaderDefines& defines)
     302             :         : effect(effect), defines(defines) { }
     303             : 
     304             :     SMRMaterialBucketKey(const SMRMaterialBucketKey& entity) = default;
     305             : 
     306             :     CStrIntern effect;
     307             :     CShaderDefines defines;
     308             : 
     309             :     bool operator==(const SMRMaterialBucketKey& b) const
     310           0 :     {
     311             :         return (effect == b.effect && defines == b.defines);
     312             :     }
     313             : 
     314             : private:
     315             :     SMRMaterialBucketKey& operator=(const SMRMaterialBucketKey&);
     316             : };
     317             : 
     318             : struct SMRMaterialBucketKeyHash
     319           0 : {
     320             :     size_t operator()(const SMRMaterialBucketKey& key) const
     321             :     {
     322             :         size_t hash = 0;
     323             :         hash_combine(hash, key.effect.GetHash());
     324             :         hash_combine(hash, key.defines.GetHash());
     325             :         return hash;
     326             :     }
     327             : };
     328           0 : 
     329             : struct SMRTechBucket
     330           0 : {
     331           0 :     CShaderTechniquePtr tech;
     332           0 :     CModel** models;
     333           0 :     size_t numModels;
     334             : 
     335             :     // Model list is stored as pointers, not as a std::vector,
     336             :     // so that sorting lists of this struct is fast
     337           0 : };
     338             : 
     339             : struct SMRCompareTechBucket
     340             : {
     341             :     bool operator()(const SMRTechBucket& a, const SMRTechBucket& b)
     342             :     {
     343             :         return a.tech < b.tech;
     344             :     }
     345             : };
     346             : 
     347             : void ShaderModelRenderer::Render(const RenderModifierPtr& modifier, const CShaderDefines& context, int cullGroup, int flags)
     348             : {
     349           0 :     if (m->submissions[cullGroup].empty())
     350             :         return;
     351           0 : 
     352             :     CMatrix3D worldToCam;
     353             :     g_Renderer.GetViewCamera().GetOrientation().GetInverse(worldToCam);
     354             : 
     355           0 :     /*
     356             :      * Rendering approach:
     357           0 :      *
     358           0 :      * m->submissions contains the list of CModels to render.
     359             :      *
     360           0 :      * The data we need to render a model is:
     361           0 :      *  - CShaderTechnique
     362             :      *  - CTexture
     363             :      *  - CShaderUniforms
     364             :      *  - CModelDef (mesh data)
     365             :      *  - CModel (model instance data)
     366             :      *
     367             :      * For efficient rendering, we need to batch the draw calls to minimise state changes.
     368             :      * (Uniform and texture changes are assumed to be cheaper than binding new mesh data,
     369             :      * and shader changes are assumed to be most expensive.)
     370             :      * First, group all models that share a technique to render them together.
     371             :      * Within those groups, sub-group by CModelDef.
     372             :      * Within those sub-groups, sub-sub-group by CTexture.
     373             :      * Within those sub-sub-groups, sub-sub-sub-group by CShaderUniforms.
     374             :      *
     375             :      * Alpha-blended models have to be sorted by distance from camera,
     376             :      * then we can batch as long as the order is preserved.
     377             :      * Non-alpha-blended models can be arbitrarily reordered to maximise batching.
     378             :      *
     379             :      * For each model, the CShaderTechnique is derived from:
     380             :      *  - The current global 'context' defines
     381             :      *  - The CModel's material's defines
     382             :      *  - The CModel's material's shader effect name
     383             :      *
     384             :      * There are a smallish number of materials, and a smaller number of techniques.
     385             :      *
     386             :      * To minimise technique lookups, we first group models by material,
     387             :      * in 'materialBuckets' (a hash table).
     388             :      *
     389             :      * For each material bucket we then look up the appropriate shader technique.
     390             :      * If the technique requires sort-by-distance, the model is added to the
     391             :      * 'sortByDistItems' list with its computed distance.
     392             :      * Otherwise, the bucket's list of models is sorted by modeldef+texture+uniforms,
     393             :      * then the technique and model list is added to 'techBuckets'.
     394             :      *
     395             :      * 'techBuckets' is then sorted by technique, to improve batching when multiple
     396             :      * materials map onto the same technique.
     397             :      *
     398             :      * (Note that this isn't perfect batching: we don't sort across models in
     399             :      * multiple buckets that share a technique. In practice that shouldn't reduce
     400             :      * batching much (we rarely have one mesh used with multiple materials),
     401             :      * and it saves on copying and lets us sort smaller lists.)
     402             :      *
     403             :      * Extra tech buckets are added for the sorted-by-distance models without reordering.
     404             :      * Finally we render by looping over each tech bucket, then looping over the model
     405             :      * list in each, rebinding the GL state whenever it changes.
     406             :      */
     407             : 
     408             :     using Arena = Allocators::DynamicArena<256 * KiB>;
     409             : 
     410             :     Arena arena;
     411             :     using ModelListAllocator = ProxyAllocator<CModel*, Arena>;
     412             :     using ModelList_t = std::vector<CModel*, ModelListAllocator>;
     413             :     using MaterialBuckets_t = std::unordered_map<
     414             :         SMRMaterialBucketKey,
     415             :         ModelList_t,
     416           0 :         SMRMaterialBucketKeyHash,
     417             :         std::equal_to<SMRMaterialBucketKey>,
     418           0 :         ProxyAllocator<
     419           0 :             std::pair<const SMRMaterialBucketKey, ModelList_t>,
     420           0 :             Arena> >;
     421           0 : 
     422             :     MaterialBuckets_t materialBuckets((MaterialBuckets_t::allocator_type(arena)));
     423             : 
     424             :     {
     425             :         PROFILE3("bucketing by material");
     426             : 
     427             :         for (size_t i = 0; i < m->submissions[cullGroup].size(); ++i)
     428             :         {
     429             :             CModel* model = m->submissions[cullGroup][i];
     430           0 : 
     431             :             uint32_t condFlags = 0;
     432           0 : 
     433           0 :             const CShaderConditionalDefines& condefs = model->GetMaterial().GetConditionalDefines();
     434             :             for (size_t j = 0; j < condefs.GetSize(); ++j)
     435           0 :             {
     436             :                 const CShaderConditionalDefines::CondDefine& item = condefs.GetItem(j);
     437           0 :                 int type = item.m_CondType;
     438             :                 switch (type)
     439           0 :                 {
     440             :                     case DCOND_DISTANCE:
     441           0 :                     {
     442           0 :                         CVector3D modelpos = model->GetTransform().GetTranslation();
     443             :                         float dist = worldToCam.Transform(modelpos).Z;
     444           0 : 
     445           0 :                         float dmin = item.m_CondArgs[0];
     446           0 :                         float dmax = item.m_CondArgs[1];
     447             : 
     448           0 :                         if ((dmin < 0 || dist >= dmin) && (dmax < 0 || dist < dmax))
     449           0 :                             condFlags |= (1 << j);
     450           0 : 
     451           0 :                         break;
     452             :                     }
     453           0 :                 }
     454           0 :             }
     455             : 
     456           0 :             CShaderDefines defs = model->GetMaterial().GetShaderDefines(condFlags);
     457           0 :             SMRMaterialBucketKey key(model->GetMaterial().GetShaderEffect(), defs);
     458             : 
     459           0 :             MaterialBuckets_t::iterator it = materialBuckets.find(key);
     460             :             if (it == materialBuckets.end())
     461             :             {
     462             :                 std::pair<MaterialBuckets_t::iterator, bool> inserted = materialBuckets.insert(
     463             :                     std::make_pair(key, ModelList_t(ModelList_t::allocator_type(arena))));
     464           0 :                 inserted.first->second.reserve(32);
     465           0 :                 inserted.first->second.push_back(model);
     466             :             }
     467           0 :             else
     468           0 :             {
     469             :                 it->second.push_back(model);
     470           0 :             }
     471           0 :         }
     472           0 :     }
     473           0 : 
     474             :     using SortByDistItemsAllocator = ProxyAllocator<SMRSortByDistItem, Arena>;
     475             :     std::vector<SMRSortByDistItem, SortByDistItemsAllocator> sortByDistItems((SortByDistItemsAllocator(arena)));
     476             : 
     477           0 :     using SortByTechItemsAllocator = ProxyAllocator<CShaderTechniquePtr, Arena>;
     478             :     std::vector<CShaderTechniquePtr, SortByTechItemsAllocator> sortByDistTechs((SortByTechItemsAllocator(arena)));
     479             :         // indexed by sortByDistItems[i].techIdx
     480             :         // (which stores indexes instead of CShaderTechniquePtr directly
     481             :         // to avoid the shared_ptr copy cost when sorting; maybe it'd be better
     482           0 :         // if we just stored raw CShaderTechnique* and assumed the shader manager
     483           0 :         // will keep it alive long enough)
     484             : 
     485           0 :     using TechBucketsAllocator =  ProxyAllocator<SMRTechBucket, Arena>;
     486           0 :     std::vector<SMRTechBucket, TechBucketsAllocator> techBuckets((TechBucketsAllocator(arena)));
     487             : 
     488             :     {
     489             :         PROFILE3("processing material buckets");
     490             :         for (MaterialBuckets_t::iterator it = materialBuckets.begin(); it != materialBuckets.end(); ++it)
     491             :         {
     492             :             CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(it->first.effect, context, it->first.defines);
     493           0 : 
     494           0 :             // Skip invalid techniques (e.g. from data file errors)
     495             :             if (!tech)
     496           0 :                 continue;
     497           0 : 
     498           0 :             if (tech->GetSortByDistance())
     499             :             {
     500           0 :                 // Add the tech into a vector so we can index it
     501             :                 // (There might be duplicates in this list, but that doesn't really matter)
     502             :                 if (sortByDistTechs.empty() || sortByDistTechs.back() != tech)
     503           0 :                     sortByDistTechs.push_back(tech);
     504           0 :                 size_t techIdx = sortByDistTechs.size() - 1;
     505             : 
     506           0 :                 // Add each model into sortByDistItems
     507             :                 for (size_t i = 0; i < it->second.size(); ++i)
     508             :                 {
     509             :                     SMRSortByDistItem itemWithDist;
     510           0 :                     itemWithDist.techIdx = techIdx;
     511           0 : 
     512           0 :                     CModel* model = it->second[i];
     513             :                     itemWithDist.model = model;
     514             : 
     515           0 :                     CVector3D modelpos = model->GetTransform().GetTranslation();
     516             :                     itemWithDist.dist = worldToCam.Transform(modelpos).Z;
     517           0 : 
     518           0 :                     sortByDistItems.push_back(itemWithDist);
     519             :                 }
     520           0 :             }
     521           0 :             else
     522             :             {
     523           0 :                 // Sort model list by modeldef+texture, for batching
     524           0 :                 // TODO: This only sorts by base texture. While this is an OK approximation
     525             :                 // for most cases (as related samplers are usually used together), it would be better
     526           0 :                 // to take all the samplers into account when sorting here.
     527             :                 std::sort(it->second.begin(), it->second.end(), SMRBatchModel());
     528             : 
     529             :                 // Add a tech bucket pointing at this model list
     530             :                 SMRTechBucket techBucket = { tech, &it->second[0], it->second.size() };
     531             :                 techBuckets.push_back(techBucket);
     532             :             }
     533             :         }
     534             :     }
     535           0 : 
     536             :     {
     537             :         PROFILE3("sorting tech buckets");
     538           0 :         // Sort by technique, for better batching
     539           0 :         std::sort(techBuckets.begin(), techBuckets.end(), SMRCompareTechBucket());
     540             :     }
     541             : 
     542             :     // List of models corresponding to sortByDistItems[i].model
     543             :     // (This exists primarily because techBuckets wants a CModel**;
     544           0 :     // we could avoid the cost of copying into this list by adding
     545           0 :     // a stride length into techBuckets and not requiring contiguous CModel*s)
     546             :     std::vector<CModel*, ModelListAllocator> sortByDistModels((ModelListAllocator(arena)));
     547           0 : 
     548             :     if (!sortByDistItems.empty())
     549             :     {
     550             :         {
     551             :             PROFILE3("sorting items by dist");
     552             :             std::sort(sortByDistItems.begin(), sortByDistItems.end(), SMRCompareSortByDistItem());
     553             :         }
     554           0 : 
     555             :         {
     556           0 :             PROFILE3("batching dist-sorted items");
     557             : 
     558           0 :             sortByDistModels.reserve(sortByDistItems.size());
     559           0 : 
     560           0 :             // Find runs of distance-sorted models that share a technique,
     561             :             // and create a new tech bucket for each run
     562             : 
     563           0 :             size_t start = 0; // start of current run
     564           0 :             size_t currentTechIdx = sortByDistItems[start].techIdx;
     565             : 
     566           0 :             for (size_t end = 0; end < sortByDistItems.size(); ++end)
     567             :             {
     568             :                 sortByDistModels.push_back(sortByDistItems[end].model);
     569             : 
     570             :                 size_t techIdx = sortByDistItems[end].techIdx;
     571           0 :                 if (techIdx != currentTechIdx)
     572           0 :                 {
     573             :                     // Start of a new run - push the old run into a new tech bucket
     574           0 :                     SMRTechBucket techBucket = { sortByDistTechs[currentTechIdx], &sortByDistModels[start], end - start };
     575             :                     techBuckets.push_back(techBucket);
     576           0 :                     start = end;
     577             :                     currentTechIdx = techIdx;
     578           0 :                 }
     579           0 :             }
     580             : 
     581             :             // Add the tech bucket for the final run
     582           0 :             SMRTechBucket techBucket = { sortByDistTechs[currentTechIdx], &sortByDistModels[start], sortByDistItems.size() - start };
     583           0 :             techBuckets.push_back(techBucket);
     584           0 :         }
     585           0 :     }
     586             : 
     587             :     {
     588             :         PROFILE3("rendering bucketed submissions");
     589             : 
     590           0 :         size_t idxTechStart = 0;
     591           0 : 
     592             :         // This vector keeps track of texture changes during rendering. It is kept outside the
     593             :         // loops to avoid excessive reallocations. The token allocation of 64 elements
     594             :         // should be plenty, though it is reallocated below (at a cost) if necessary.
     595           0 :         using TextureListAllocator = ProxyAllocator<CTexture*, Arena>;
     596           0 :         std::vector<CTexture*, TextureListAllocator> currentTexs((TextureListAllocator(arena)));
     597             :         currentTexs.reserve(64);
     598           0 : 
     599             :         // texBindings holds the identifier bindings in the shader, which can no longer be defined
     600             :         // statically in the ShaderRenderModifier class. texBindingNames uses interned strings to
     601             :         // keep track of when bindings need to be reevaluated.
     602             :         using BindingListAllocator = ProxyAllocator<CShaderProgram::Binding, Arena>;
     603           0 :         std::vector<CShaderProgram::Binding, BindingListAllocator> texBindings((BindingListAllocator(arena)));
     604           0 :         texBindings.reserve(64);
     605           0 : 
     606             :         using BindingNamesListAllocator = ProxyAllocator<CStrIntern, Arena>;
     607             :         std::vector<CStrIntern, BindingNamesListAllocator> texBindingNames((BindingNamesListAllocator(arena)));
     608             :         texBindingNames.reserve(64);
     609             : 
     610           0 :         while (idxTechStart < techBuckets.size())
     611           0 :         {
     612           0 :             CShaderTechniquePtr currentTech = techBuckets[idxTechStart].tech;
     613             : 
     614           0 :             // Find runs [idxTechStart, idxTechEnd) in techBuckets of the same technique
     615           0 :             size_t idxTechEnd;
     616           0 :             for (idxTechEnd = idxTechStart + 1; idxTechEnd < techBuckets.size(); ++idxTechEnd)
     617             :             {
     618           0 :                 if (techBuckets[idxTechEnd].tech != currentTech)
     619             :                     break;
     620           0 :             }
     621             : 
     622             :             // For each of the technique's passes, render all the models in this run
     623           0 :             for (int pass = 0; pass < currentTech->GetNumPasses(); ++pass)
     624           0 :             {
     625             :                 currentTech->BeginPass(pass);
     626           0 : 
     627             :                 const CShaderProgramPtr& shader = currentTech->GetShader(pass);
     628             :                 int streamflags = shader->GetStreamFlags();
     629             : 
     630             :                 modifier->BeginPass(shader);
     631           0 : 
     632             :                 m->vertexRenderer->BeginPass(streamflags);
     633           0 : 
     634             :                 // When the shader technique changes, textures need to be
     635           0 :                 // rebound, so ensure there are no remnants from the last pass.
     636           0 :                 // (the vector size is set to 0, but memory is not freed)
     637             :                 currentTexs.clear();
     638           0 :                 texBindings.clear();
     639             :                 texBindingNames.clear();
     640           0 : 
     641             :                 CModelDef* currentModeldef = NULL;
     642             :                 CShaderUniforms currentStaticUniforms;
     643             : 
     644             :                 for (size_t idx = idxTechStart; idx < idxTechEnd; ++idx)
     645           0 :                 {
     646           0 :                     CModel** models = techBuckets[idx].models;
     647           0 :                     size_t numModels = techBuckets[idx].numModels;
     648             :                     for (size_t i = 0; i < numModels; ++i)
     649           0 :                     {
     650           0 :                         CModel* model = models[i];
     651             : 
     652           0 :                         if (flags && !(model->GetFlags() & flags))
     653             :                             continue;
     654           0 : 
     655           0 :                         const CMaterial::SamplersVector& samplers = model->GetMaterial().GetSamplers();
     656           0 :                         size_t samplersNum = samplers.size();
     657             : 
     658           0 :                         // make sure the vectors are the right virtual sizes, and also
     659             :                         // reallocate if there are more samplers than expected.
     660           0 :                         if (currentTexs.size() != samplersNum)
     661             :                         {
     662             :                             currentTexs.resize(samplersNum, NULL);
     663           0 :                             texBindings.resize(samplersNum, CShaderProgram::Binding());
     664           0 :                             texBindingNames.resize(samplersNum, CStrIntern());
     665             : 
     666             :                             // ensure they are definitely empty
     667             :                             std::fill(texBindings.begin(), texBindings.end(), CShaderProgram::Binding());
     668           0 :                             std::fill(currentTexs.begin(), currentTexs.end(), (CTexture*)NULL);
     669             :                             std::fill(texBindingNames.begin(), texBindingNames.end(), CStrIntern());
     670           0 :                         }
     671           0 : 
     672           0 :                         // bind the samplers to the shader
     673             :                         for (size_t s = 0; s < samplersNum; ++s)
     674             :                         {
     675           0 :                             const CMaterial::TextureSampler& samp = samplers[s];
     676           0 : 
     677           0 :                             // check that the handles are current
     678             :                             // and reevaluate them if necessary
     679             :                             if (texBindingNames[s] != samp.Name || !texBindings[s].Active())
     680             :                             {
     681           0 :                                 texBindings[s] = shader->GetTextureBinding(samp.Name);
     682             :                                 texBindingNames[s] = samp.Name;
     683           0 :                             }
     684             : 
     685             :                             // same with the actual sampler bindings
     686             :                             CTexture* newTex = samp.Sampler.get();
     687           0 :                             if (texBindings[s].Active() && newTex != currentTexs[s])
     688             :                             {
     689           0 :                                 shader->BindTexture(texBindings[s], newTex->GetHandle());
     690           0 :                                 currentTexs[s] = newTex;
     691             :                             }
     692             :                         }
     693             : 
     694           0 :                         // Bind modeldef when it changes
     695           0 :                         CModelDef* newModeldef = model->GetModelDef().get();
     696             :                         if (newModeldef != currentModeldef)
     697           0 :                         {
     698           0 :                             currentModeldef = newModeldef;
     699             :                             m->vertexRenderer->PrepareModelDef(shader, streamflags, *currentModeldef);
     700             :                         }
     701             : 
     702             :                         // Bind all uniforms when any change
     703           0 :                         CShaderUniforms newStaticUniforms = model->GetMaterial().GetStaticUniforms();
     704           0 :                         if (newStaticUniforms != currentStaticUniforms)
     705             :                         {
     706           0 :                             currentStaticUniforms = newStaticUniforms;
     707           0 :                             currentStaticUniforms.BindUniforms(shader);
     708             :                         }
     709             : 
     710             :                         const CShaderRenderQueries& renderQueries = model->GetMaterial().GetRenderQueries();
     711           0 : 
     712           0 :                         for (size_t q = 0; q < renderQueries.GetSize(); ++q)
     713             :                         {
     714           0 :                             CShaderRenderQueries::RenderQuery rq = renderQueries.GetItem(q);
     715           0 :                             if (rq.first == RQUERY_TIME)
     716             :                             {
     717             :                                 CShaderProgram::Binding binding = shader->GetUniformBinding(rq.second);
     718             :                                 if (binding.Active())
     719             :                                 {
     720           0 :                                     double time = g_Renderer.GetTimeManager().GetGlobalTime();
     721             :                                     shader->Uniform(binding, time, 0.0f, 0.0f, 0.0f);
     722           0 :                                 }
     723           0 :                             }
     724             :                             else if (rq.first == RQUERY_WATER_TEX)
     725           0 :                             {
     726           0 :                                 WaterManager* WaterMgr = g_Renderer.GetWaterManager();
     727             :                                 double time = WaterMgr->m_WaterTexTimer;
     728           0 :                                 double period = 1.6;
     729           0 :                                 int curTex = static_cast<int>(time * 60.0 / period) % 60;
     730             : 
     731             :                                 if (WaterMgr->m_RenderWater && WaterMgr->WillRenderFancyWater())
     732           0 :                                     shader->BindTexture(str_waterTex, WaterMgr->m_NormalMap[curTex]);
     733             :                                 else
     734           0 :                                     shader->BindTexture(str_waterTex, g_Renderer.GetTextureManager().GetErrorTexture());
     735           0 :                             }
     736           0 :                             else if (rq.first == RQUERY_SKY_CUBE)
     737           0 :                             {
     738             :                                 shader->BindTexture(str_skyCube, g_Renderer.GetSkyManager()->GetSkyCube());
     739           0 :                             }
     740           0 :                         }
     741             : 
     742           0 :                         modifier->PrepareModel(shader, model);
     743             : 
     744           0 :                         CModelRData* rdata = static_cast<CModelRData*>(model->GetRenderData());
     745             :                         ENSURE(rdata->GetKey() == m->vertexRenderer.get());
     746           0 : 
     747             :                         m->vertexRenderer->RenderModel(shader, streamflags, model, rdata);
     748             :                     }
     749             :                 }
     750           0 : 
     751             :                 m->vertexRenderer->EndPass(streamflags);
     752           0 : 
     753           0 :                 currentTech->EndPass(pass);
     754             :             }
     755           0 : 
     756             :             idxTechStart = idxTechEnd;
     757             :         }
     758             :     }
     759           0 : }

Generated by: LCOV version 1.13