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: 3 280 1.1 %
Date: 2023-01-19 00:18:29 Functions: 3 29 10.3 %

          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 "maths/Vector3D.h"
      31             : #include "maths/Vector4D.h"
      32             : #include "ps/CLogger.h"
      33             : #include "ps/CStrInternStatic.h"
      34             : #include "ps/Profile.h"
      35             : #include "renderer/MikktspaceWrap.h"
      36             : #include "renderer/ModelRenderer.h"
      37             : #include "renderer/ModelVertexRenderer.h"
      38             : #include "renderer/Renderer.h"
      39             : #include "renderer/RenderModifiers.h"
      40             : #include "renderer/SceneRenderer.h"
      41             : #include "renderer/SkyManager.h"
      42             : #include "renderer/TimeManager.h"
      43             : #include "renderer/WaterManager.h"
      44             : 
      45             : ///////////////////////////////////////////////////////////////////////////////////////////////
      46             : // ModelRenderer implementation
      47             : 
      48           6 : void ModelRenderer::Init()
      49             : {
      50           6 : }
      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.GetSceneRenderer().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           0 : 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[CSceneRenderer::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             : 
     212             : // Submit one model.
     213           0 : void ShaderModelRenderer::Submit(int cullGroup, CModel* model)
     214             : {
     215           0 :     CModelRData* rdata = (CModelRData*)model->GetRenderData();
     216             : 
     217             :     // Ensure model data is valid
     218           0 :     const void* key = m->vertexRenderer.get();
     219           0 :     if (!rdata || rdata->GetKey() != key)
     220             :     {
     221           0 :         rdata = m->vertexRenderer->CreateModelData(key, model);
     222           0 :         model->SetRenderData(rdata);
     223           0 :         model->SetDirty(~0u);
     224             :     }
     225             : 
     226           0 :     m->submissions[cullGroup].push_back(model);
     227           0 : }
     228             : 
     229             : 
     230             : // Call update for all submitted models and enter the rendering phase
     231           0 : void ShaderModelRenderer::PrepareModels()
     232             : {
     233           0 :     for (int cullGroup = 0; cullGroup < CSceneRenderer::CULL_MAX; ++cullGroup)
     234             :     {
     235           0 :         for (CModel* model : m->submissions[cullGroup])
     236             :         {
     237           0 :             model->ValidatePosition();
     238             : 
     239           0 :             CModelRData* rdata = static_cast<CModelRData*>(model->GetRenderData());
     240           0 :             ENSURE(rdata->GetKey() == m->vertexRenderer.get());
     241             : 
     242           0 :             m->vertexRenderer->UpdateModelData(model, rdata, rdata->m_UpdateFlags);
     243           0 :             rdata->m_UpdateFlags = 0;
     244             :         }
     245             :     }
     246           0 : }
     247             : 
     248           0 : void ShaderModelRenderer::UploadModels(
     249             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
     250             : {
     251           0 :     for (int cullGroup = 0; cullGroup < CSceneRenderer::CULL_MAX; ++cullGroup)
     252             :     {
     253           0 :         for (CModel* model : m->submissions[cullGroup])
     254             :         {
     255           0 :             CModelRData* rdata = static_cast<CModelRData*>(model->GetRenderData());
     256           0 :             ENSURE(rdata->GetKey() == m->vertexRenderer.get());
     257             : 
     258           0 :             m->vertexRenderer->UploadModelData(deviceCommandContext, model, rdata);
     259             :         }
     260             :     }
     261           0 : }
     262             : 
     263             : // Clear the submissions list
     264           0 : void ShaderModelRenderer::EndFrame()
     265             : {
     266           0 :     for (int cullGroup = 0; cullGroup < CSceneRenderer::CULL_MAX; ++cullGroup)
     267           0 :         m->submissions[cullGroup].clear();
     268           0 : }
     269             : 
     270             : 
     271             : // Helper structs for ShaderModelRenderer::Render():
     272             : 
     273             : struct SMRSortByDistItem
     274             : {
     275             :     size_t techIdx;
     276             :     CModel* model;
     277             :     float dist;
     278             : };
     279             : 
     280             : struct SMRBatchModel
     281             : {
     282           0 :     bool operator()(CModel* a, CModel* b)
     283             :     {
     284           0 :         if (a->GetModelDef() < b->GetModelDef())
     285           0 :             return true;
     286           0 :         if (b->GetModelDef() < a->GetModelDef())
     287           0 :             return false;
     288             : 
     289           0 :         if (a->GetMaterial().GetDiffuseTexture() < b->GetMaterial().GetDiffuseTexture())
     290           0 :             return true;
     291           0 :         if (b->GetMaterial().GetDiffuseTexture() < a->GetMaterial().GetDiffuseTexture())
     292           0 :             return false;
     293             : 
     294           0 :         return a->GetMaterial().GetStaticUniforms() < b->GetMaterial().GetStaticUniforms();
     295             :     }
     296             : };
     297             : 
     298             : struct SMRCompareSortByDistItem
     299             : {
     300           0 :     bool operator()(const SMRSortByDistItem& a, const SMRSortByDistItem& b)
     301             :     {
     302             :         // Prefer items with greater distance, so we draw back-to-front
     303           0 :         return (a.dist > b.dist);
     304             : 
     305             :         // (Distances will almost always be distinct, so we don't need to bother
     306             :         // tie-breaking on modeldef/texture/etc)
     307             :     }
     308             : };
     309             : 
     310             : class SMRMaterialBucketKey
     311             : {
     312             : public:
     313           0 :     SMRMaterialBucketKey(CStrIntern effect, const CShaderDefines& defines)
     314           0 :         : effect(effect), defines(defines) { }
     315             : 
     316             :     SMRMaterialBucketKey(const SMRMaterialBucketKey& entity) = default;
     317             : 
     318             :     CStrIntern effect;
     319             :     CShaderDefines defines;
     320             : 
     321           0 :     bool operator==(const SMRMaterialBucketKey& b) const
     322             :     {
     323           0 :         return (effect == b.effect && defines == b.defines);
     324             :     }
     325             : 
     326             : private:
     327             :     SMRMaterialBucketKey& operator=(const SMRMaterialBucketKey&);
     328             : };
     329             : 
     330             : struct SMRMaterialBucketKeyHash
     331             : {
     332           0 :     size_t operator()(const SMRMaterialBucketKey& key) const
     333             :     {
     334           0 :         size_t hash = 0;
     335           0 :         hash_combine(hash, key.effect.GetHash());
     336           0 :         hash_combine(hash, key.defines.GetHash());
     337           0 :         return hash;
     338             :     }
     339             : };
     340             : 
     341           0 : struct SMRTechBucket
     342             : {
     343             :     CShaderTechniquePtr tech;
     344             :     CModel** models;
     345             :     size_t numModels;
     346             : 
     347             :     // Model list is stored as pointers, not as a std::vector,
     348             :     // so that sorting lists of this struct is fast
     349             : };
     350             : 
     351             : struct SMRCompareTechBucket
     352             : {
     353           0 :     bool operator()(const SMRTechBucket& a, const SMRTechBucket& b)
     354             :     {
     355           0 :         return a.tech < b.tech;
     356             :     }
     357             : };
     358             : 
     359           0 : void ShaderModelRenderer::Render(
     360             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
     361             :     const RenderModifierPtr& modifier, const CShaderDefines& context, int cullGroup, int flags)
     362             : {
     363           0 :     if (m->submissions[cullGroup].empty())
     364           0 :         return;
     365             : 
     366           0 :     CMatrix3D worldToCam;
     367           0 :     g_Renderer.GetSceneRenderer().GetViewCamera().GetOrientation().GetInverse(worldToCam);
     368             : 
     369             :     /*
     370             :      * Rendering approach:
     371             :      *
     372             :      * m->submissions contains the list of CModels to render.
     373             :      *
     374             :      * The data we need to render a model is:
     375             :      *  - CShaderTechnique
     376             :      *  - CTexture
     377             :      *  - CShaderUniforms
     378             :      *  - CModelDef (mesh data)
     379             :      *  - CModel (model instance data)
     380             :      *
     381             :      * For efficient rendering, we need to batch the draw calls to minimise state changes.
     382             :      * (Uniform and texture changes are assumed to be cheaper than binding new mesh data,
     383             :      * and shader changes are assumed to be most expensive.)
     384             :      * First, group all models that share a technique to render them together.
     385             :      * Within those groups, sub-group by CModelDef.
     386             :      * Within those sub-groups, sub-sub-group by CTexture.
     387             :      * Within those sub-sub-groups, sub-sub-sub-group by CShaderUniforms.
     388             :      *
     389             :      * Alpha-blended models have to be sorted by distance from camera,
     390             :      * then we can batch as long as the order is preserved.
     391             :      * Non-alpha-blended models can be arbitrarily reordered to maximise batching.
     392             :      *
     393             :      * For each model, the CShaderTechnique is derived from:
     394             :      *  - The current global 'context' defines
     395             :      *  - The CModel's material's defines
     396             :      *  - The CModel's material's shader effect name
     397             :      *
     398             :      * There are a smallish number of materials, and a smaller number of techniques.
     399             :      *
     400             :      * To minimise technique lookups, we first group models by material,
     401             :      * in 'materialBuckets' (a hash table).
     402             :      *
     403             :      * For each material bucket we then look up the appropriate shader technique.
     404             :      * If the technique requires sort-by-distance, the model is added to the
     405             :      * 'sortByDistItems' list with its computed distance.
     406             :      * Otherwise, the bucket's list of models is sorted by modeldef+texture+uniforms,
     407             :      * then the technique and model list is added to 'techBuckets'.
     408             :      *
     409             :      * 'techBuckets' is then sorted by technique, to improve batching when multiple
     410             :      * materials map onto the same technique.
     411             :      *
     412             :      * (Note that this isn't perfect batching: we don't sort across models in
     413             :      * multiple buckets that share a technique. In practice that shouldn't reduce
     414             :      * batching much (we rarely have one mesh used with multiple materials),
     415             :      * and it saves on copying and lets us sort smaller lists.)
     416             :      *
     417             :      * Extra tech buckets are added for the sorted-by-distance models without reordering.
     418             :      * Finally we render by looping over each tech bucket, then looping over the model
     419             :      * list in each, rebinding the GL state whenever it changes.
     420             :      */
     421             : 
     422             :     using Arena = Allocators::DynamicArena<256 * KiB>;
     423             : 
     424           0 :     Arena arena;
     425             :     using ModelListAllocator = ProxyAllocator<CModel*, Arena>;
     426             :     using ModelList_t = std::vector<CModel*, ModelListAllocator>;
     427             :     using MaterialBuckets_t = std::unordered_map<
     428             :         SMRMaterialBucketKey,
     429             :         ModelList_t,
     430             :         SMRMaterialBucketKeyHash,
     431             :         std::equal_to<SMRMaterialBucketKey>,
     432             :         ProxyAllocator<
     433             :             std::pair<const SMRMaterialBucketKey, ModelList_t>,
     434             :             Arena> >;
     435             : 
     436           0 :     MaterialBuckets_t materialBuckets((MaterialBuckets_t::allocator_type(arena)));
     437             : 
     438             :     {
     439           0 :         PROFILE3("bucketing by material");
     440             : 
     441           0 :         for (size_t i = 0; i < m->submissions[cullGroup].size(); ++i)
     442             :         {
     443           0 :             CModel* model = m->submissions[cullGroup][i];
     444           0 :             const CShaderDefines& defines = model->GetMaterial().GetShaderDefines();
     445           0 :             SMRMaterialBucketKey key(model->GetMaterial().GetShaderEffect(), defines);
     446             : 
     447           0 :             MaterialBuckets_t::iterator it = materialBuckets.find(key);
     448           0 :             if (it == materialBuckets.end())
     449             :             {
     450             :                 std::pair<MaterialBuckets_t::iterator, bool> inserted = materialBuckets.insert(
     451           0 :                     std::make_pair(key, ModelList_t(ModelList_t::allocator_type(arena))));
     452           0 :                 inserted.first->second.reserve(32);
     453           0 :                 inserted.first->second.push_back(model);
     454             :             }
     455             :             else
     456             :             {
     457           0 :                 it->second.push_back(model);
     458             :             }
     459             :         }
     460             :     }
     461             : 
     462             :     using SortByDistItemsAllocator = ProxyAllocator<SMRSortByDistItem, Arena>;
     463           0 :     std::vector<SMRSortByDistItem, SortByDistItemsAllocator> sortByDistItems((SortByDistItemsAllocator(arena)));
     464             : 
     465             :     using SortByTechItemsAllocator = ProxyAllocator<CShaderTechniquePtr, Arena>;
     466           0 :     std::vector<CShaderTechniquePtr, SortByTechItemsAllocator> sortByDistTechs((SortByTechItemsAllocator(arena)));
     467             :         // indexed by sortByDistItems[i].techIdx
     468             :         // (which stores indexes instead of CShaderTechniquePtr directly
     469             :         // to avoid the shared_ptr copy cost when sorting; maybe it'd be better
     470             :         // if we just stored raw CShaderTechnique* and assumed the shader manager
     471             :         // will keep it alive long enough)
     472             : 
     473             :     using TechBucketsAllocator =  ProxyAllocator<SMRTechBucket, Arena>;
     474           0 :     std::vector<SMRTechBucket, TechBucketsAllocator> techBuckets((TechBucketsAllocator(arena)));
     475             : 
     476             :     {
     477           0 :         PROFILE3("processing material buckets");
     478           0 :         for (MaterialBuckets_t::iterator it = materialBuckets.begin(); it != materialBuckets.end(); ++it)
     479             :         {
     480           0 :             CShaderDefines defines = context;
     481           0 :             defines.SetMany(it->first.defines);
     482           0 :             CShaderTechniquePtr tech = g_Renderer.GetShaderManager().LoadEffect(it->first.effect, defines);
     483             : 
     484             :             // Skip invalid techniques (e.g. from data file errors)
     485           0 :             if (!tech)
     486           0 :                 continue;
     487             : 
     488           0 :             if (tech->GetSortByDistance())
     489             :             {
     490             :                 // Add the tech into a vector so we can index it
     491             :                 // (There might be duplicates in this list, but that doesn't really matter)
     492           0 :                 if (sortByDistTechs.empty() || sortByDistTechs.back() != tech)
     493           0 :                     sortByDistTechs.push_back(tech);
     494           0 :                 size_t techIdx = sortByDistTechs.size() - 1;
     495             : 
     496             :                 // Add each model into sortByDistItems
     497           0 :                 for (size_t i = 0; i < it->second.size(); ++i)
     498             :                 {
     499             :                     SMRSortByDistItem itemWithDist;
     500           0 :                     itemWithDist.techIdx = techIdx;
     501             : 
     502           0 :                     CModel* model = it->second[i];
     503           0 :                     itemWithDist.model = model;
     504             : 
     505           0 :                     CVector3D modelpos = model->GetTransform().GetTranslation();
     506           0 :                     itemWithDist.dist = worldToCam.Transform(modelpos).Z;
     507             : 
     508           0 :                     sortByDistItems.push_back(itemWithDist);
     509             :                 }
     510             :             }
     511             :             else
     512             :             {
     513             :                 // Sort model list by modeldef+texture, for batching
     514             :                 // TODO: This only sorts by base texture. While this is an OK approximation
     515             :                 // for most cases (as related samplers are usually used together), it would be better
     516             :                 // to take all the samplers into account when sorting here.
     517           0 :                 std::sort(it->second.begin(), it->second.end(), SMRBatchModel());
     518             : 
     519             :                 // Add a tech bucket pointing at this model list
     520           0 :                 SMRTechBucket techBucket = { tech, &it->second[0], it->second.size() };
     521           0 :                 techBuckets.push_back(techBucket);
     522             :             }
     523             :         }
     524             :     }
     525             : 
     526             :     {
     527           0 :         PROFILE3("sorting tech buckets");
     528             :         // Sort by technique, for better batching
     529           0 :         std::sort(techBuckets.begin(), techBuckets.end(), SMRCompareTechBucket());
     530             :     }
     531             : 
     532             :     // List of models corresponding to sortByDistItems[i].model
     533             :     // (This exists primarily because techBuckets wants a CModel**;
     534             :     // we could avoid the cost of copying into this list by adding
     535             :     // a stride length into techBuckets and not requiring contiguous CModel*s)
     536           0 :     std::vector<CModel*, ModelListAllocator> sortByDistModels((ModelListAllocator(arena)));
     537             : 
     538           0 :     if (!sortByDistItems.empty())
     539             :     {
     540             :         {
     541           0 :             PROFILE3("sorting items by dist");
     542           0 :             std::sort(sortByDistItems.begin(), sortByDistItems.end(), SMRCompareSortByDistItem());
     543             :         }
     544             : 
     545             :         {
     546           0 :             PROFILE3("batching dist-sorted items");
     547             : 
     548           0 :             sortByDistModels.reserve(sortByDistItems.size());
     549             : 
     550             :             // Find runs of distance-sorted models that share a technique,
     551             :             // and create a new tech bucket for each run
     552             : 
     553           0 :             size_t start = 0; // start of current run
     554           0 :             size_t currentTechIdx = sortByDistItems[start].techIdx;
     555             : 
     556           0 :             for (size_t end = 0; end < sortByDistItems.size(); ++end)
     557             :             {
     558           0 :                 sortByDistModels.push_back(sortByDistItems[end].model);
     559             : 
     560           0 :                 size_t techIdx = sortByDistItems[end].techIdx;
     561           0 :                 if (techIdx != currentTechIdx)
     562             :                 {
     563             :                     // Start of a new run - push the old run into a new tech bucket
     564           0 :                     SMRTechBucket techBucket = { sortByDistTechs[currentTechIdx], &sortByDistModels[start], end - start };
     565           0 :                     techBuckets.push_back(techBucket);
     566           0 :                     start = end;
     567           0 :                     currentTechIdx = techIdx;
     568             :                 }
     569             :             }
     570             : 
     571             :             // Add the tech bucket for the final run
     572           0 :             SMRTechBucket techBucket = { sortByDistTechs[currentTechIdx], &sortByDistModels[start], sortByDistItems.size() - start };
     573           0 :             techBuckets.push_back(techBucket);
     574             :         }
     575             :     }
     576             : 
     577           0 :     const double time = g_Renderer.GetTimeManager().GetGlobalTime();
     578             : 
     579             :     {
     580           0 :         PROFILE3("rendering bucketed submissions");
     581             : 
     582           0 :         size_t idxTechStart = 0;
     583             : 
     584             :         // This vector keeps track of texture changes during rendering. It is kept outside the
     585             :         // loops to avoid excessive reallocations. The token allocation of 64 elements
     586             :         // should be plenty, though it is reallocated below (at a cost) if necessary.
     587             :         using TextureListAllocator = ProxyAllocator<CTexture*, Arena>;
     588           0 :         std::vector<CTexture*, TextureListAllocator> currentTexs((TextureListAllocator(arena)));
     589           0 :         currentTexs.reserve(64);
     590             : 
     591             :         // texBindings holds the identifier bindings in the shader, which can no longer be defined
     592             :         // statically in the ShaderRenderModifier class. texBindingNames uses interned strings to
     593             :         // keep track of when bindings need to be reevaluated.
     594             :         using BindingListAllocator = ProxyAllocator<int32_t, Arena>;
     595           0 :         std::vector<int32_t, BindingListAllocator> texBindings((BindingListAllocator(arena)));
     596           0 :         texBindings.reserve(64);
     597             : 
     598             :         using BindingNamesListAllocator = ProxyAllocator<CStrIntern, Arena>;
     599           0 :         std::vector<CStrIntern, BindingNamesListAllocator> texBindingNames((BindingNamesListAllocator(arena)));
     600           0 :         texBindingNames.reserve(64);
     601             : 
     602           0 :         while (idxTechStart < techBuckets.size())
     603             :         {
     604           0 :             CShaderTechniquePtr currentTech = techBuckets[idxTechStart].tech;
     605             : 
     606             :             // Find runs [idxTechStart, idxTechEnd) in techBuckets of the same technique
     607             :             size_t idxTechEnd;
     608           0 :             for (idxTechEnd = idxTechStart + 1; idxTechEnd < techBuckets.size(); ++idxTechEnd)
     609             :             {
     610           0 :                 if (techBuckets[idxTechEnd].tech != currentTech)
     611           0 :                     break;
     612             :             }
     613             : 
     614             :             // For each of the technique's passes, render all the models in this run
     615           0 :             for (int pass = 0; pass < currentTech->GetNumPasses(); ++pass)
     616             :             {
     617           0 :                 deviceCommandContext->SetGraphicsPipelineState(
     618           0 :                     currentTech->GetGraphicsPipelineState(pass));
     619           0 :                 deviceCommandContext->BeginPass();
     620             : 
     621           0 :                 Renderer::Backend::IShaderProgram* shader = currentTech->GetShader(pass);
     622             : 
     623           0 :                 modifier->BeginPass(deviceCommandContext, shader);
     624             : 
     625             :                 // TODO: Use a more generic approach to handle bound queries.
     626           0 :                 bool boundTime = false;
     627           0 :                 bool boundWaterTexture = false;
     628           0 :                 bool boundSkyCube = false;
     629             : 
     630             :                 // When the shader technique changes, textures need to be
     631             :                 // rebound, so ensure there are no remnants from the last pass.
     632             :                 // (the vector size is set to 0, but memory is not freed)
     633           0 :                 currentTexs.clear();
     634           0 :                 texBindings.clear();
     635           0 :                 texBindingNames.clear();
     636             : 
     637           0 :                 CModelDef* currentModeldef = NULL;
     638           0 :                 CShaderUniforms currentStaticUniforms;
     639             : 
     640           0 :                 for (size_t idx = idxTechStart; idx < idxTechEnd; ++idx)
     641             :                 {
     642           0 :                     CModel** models = techBuckets[idx].models;
     643           0 :                     size_t numModels = techBuckets[idx].numModels;
     644           0 :                     for (size_t i = 0; i < numModels; ++i)
     645             :                     {
     646           0 :                         CModel* model = models[i];
     647             : 
     648           0 :                         if (flags && !(model->GetFlags() & flags))
     649           0 :                             continue;
     650             : 
     651           0 :                         const CMaterial::SamplersVector& samplers = model->GetMaterial().GetSamplers();
     652           0 :                         size_t samplersNum = samplers.size();
     653             : 
     654             :                         // make sure the vectors are the right virtual sizes, and also
     655             :                         // reallocate if there are more samplers than expected.
     656           0 :                         if (currentTexs.size() != samplersNum)
     657             :                         {
     658           0 :                             currentTexs.resize(samplersNum, NULL);
     659           0 :                             texBindings.resize(samplersNum, -1);
     660           0 :                             texBindingNames.resize(samplersNum, CStrIntern());
     661             : 
     662             :                             // ensure they are definitely empty
     663           0 :                             std::fill(texBindings.begin(), texBindings.end(), -1);
     664           0 :                             std::fill(currentTexs.begin(), currentTexs.end(), nullptr);
     665           0 :                             std::fill(texBindingNames.begin(), texBindingNames.end(), CStrIntern());
     666             :                         }
     667             : 
     668             :                         // bind the samplers to the shader
     669           0 :                         for (size_t s = 0; s < samplersNum; ++s)
     670             :                         {
     671           0 :                             const CMaterial::TextureSampler& samp = samplers[s];
     672             : 
     673             :                             // check that the handles are current
     674             :                             // and reevaluate them if necessary
     675           0 :                             if (texBindingNames[s] != samp.Name || texBindings[s] < 0)
     676             :                             {
     677           0 :                                 texBindings[s] = shader->GetBindingSlot(samp.Name);
     678           0 :                                 texBindingNames[s] = samp.Name;
     679             :                             }
     680             : 
     681             :                             // same with the actual sampler bindings
     682           0 :                             CTexture* newTex = samp.Sampler.get();
     683           0 :                             if (texBindings[s] >= 0 && newTex != currentTexs[s])
     684             :                             {
     685           0 :                                 newTex->UploadBackendTextureIfNeeded(deviceCommandContext);
     686           0 :                                 deviceCommandContext->SetTexture(
     687           0 :                                     texBindings[s], newTex->GetBackendTexture());
     688           0 :                                 currentTexs[s] = newTex;
     689             :                             }
     690             :                         }
     691             : 
     692             :                         // Bind modeldef when it changes
     693           0 :                         CModelDef* newModeldef = model->GetModelDef().get();
     694           0 :                         if (newModeldef != currentModeldef)
     695             :                         {
     696           0 :                             currentModeldef = newModeldef;
     697           0 :                             m->vertexRenderer->PrepareModelDef(deviceCommandContext, *currentModeldef);
     698             :                         }
     699             : 
     700             :                         // Bind all uniforms when any change
     701           0 :                         CShaderUniforms newStaticUniforms = model->GetMaterial().GetStaticUniforms();
     702           0 :                         if (newStaticUniforms != currentStaticUniforms)
     703             :                         {
     704           0 :                             currentStaticUniforms = newStaticUniforms;
     705           0 :                             currentStaticUniforms.BindUniforms(deviceCommandContext, shader);
     706             :                         }
     707             : 
     708           0 :                         const CShaderRenderQueries& renderQueries = model->GetMaterial().GetRenderQueries();
     709             : 
     710           0 :                         for (size_t q = 0; q < renderQueries.GetSize(); ++q)
     711             :                         {
     712           0 :                             CShaderRenderQueries::RenderQuery rq = renderQueries.GetItem(q);
     713           0 :                             if (rq.first == RQUERY_TIME)
     714             :                             {
     715           0 :                                 if (!boundTime)
     716             :                                 {
     717           0 :                                     deviceCommandContext->SetUniform(
     718           0 :                                         shader->GetBindingSlot(rq.second), time);
     719           0 :                                     boundTime = true;
     720             :                                 }
     721             :                             }
     722           0 :                             else if (rq.first == RQUERY_WATER_TEX)
     723             :                             {
     724           0 :                                 if (!boundWaterTexture)
     725             :                                 {
     726           0 :                                     const double period = 1.6;
     727           0 :                                     const WaterManager& waterManager = g_Renderer.GetSceneRenderer().GetWaterManager();
     728           0 :                                     if (waterManager.m_RenderWater && waterManager.WillRenderFancyWater())
     729             :                                     {
     730           0 :                                         const CTexturePtr& waterTexture = waterManager.m_NormalMap[waterManager.GetCurrentTextureIndex(period)];
     731           0 :                                         waterTexture->UploadBackendTextureIfNeeded(deviceCommandContext);
     732           0 :                                         deviceCommandContext->SetTexture(
     733           0 :                                             shader->GetBindingSlot(str_waterTex),
     734           0 :                                             waterTexture->GetBackendTexture());
     735             :                                     }
     736             :                                     else
     737             :                                     {
     738           0 :                                         deviceCommandContext->SetTexture(
     739           0 :                                             shader->GetBindingSlot(str_waterTex),
     740           0 :                                             g_Renderer.GetTextureManager().GetErrorTexture()->GetBackendTexture());
     741             :                                     }
     742           0 :                                     boundWaterTexture = true;
     743             :                                 }
     744             :                             }
     745           0 :                             else if (rq.first == RQUERY_SKY_CUBE)
     746             :                             {
     747           0 :                                 if (!boundSkyCube)
     748             :                                 {
     749           0 :                                     deviceCommandContext->SetTexture(
     750           0 :                                         shader->GetBindingSlot(str_skyCube),
     751           0 :                                         g_Renderer.GetSceneRenderer().GetSkyManager().GetSkyCube());
     752           0 :                                     boundSkyCube = true;
     753             :                                 }
     754             :                             }
     755             :                         }
     756             : 
     757           0 :                         modifier->PrepareModel(deviceCommandContext, model);
     758             : 
     759           0 :                         CModelRData* rdata = static_cast<CModelRData*>(model->GetRenderData());
     760           0 :                         ENSURE(rdata->GetKey() == m->vertexRenderer.get());
     761             : 
     762           0 :                         m->vertexRenderer->RenderModel(deviceCommandContext, shader, model, rdata);
     763             :                     }
     764             :                 }
     765             : 
     766           0 :                 deviceCommandContext->EndPass();
     767             :             }
     768             : 
     769           0 :             idxTechStart = idxTechEnd;
     770             :         }
     771             :     }
     772           3 : }

Generated by: LCOV version 1.13