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 288 0.0 %
Date: 2022-03-08 13:03:03 Functions: 0 18 0.0 %

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

Generated by: LCOV version 1.13