LCOV - code coverage report
Current view: top level - source/renderer - DecalRData.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 5 162 3.1 %
Date: 2023-01-19 00:18:29 Functions: 3 9 33.3 %

          Line data    Source code
       1             : /* Copyright (C) 2023 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 "DecalRData.h"
      21             : 
      22             : #include "graphics/Decal.h"
      23             : #include "graphics/Model.h"
      24             : #include "graphics/ShaderManager.h"
      25             : #include "graphics/Terrain.h"
      26             : #include "graphics/TextureManager.h"
      27             : #include "lib/allocators/DynamicArena.h"
      28             : #include "lib/allocators/STLAllocators.h"
      29             : #include "ps/CLogger.h"
      30             : #include "ps/CStrInternStatic.h"
      31             : #include "ps/Game.h"
      32             : #include "ps/Profile.h"
      33             : #include "renderer/Renderer.h"
      34             : #include "renderer/TerrainRenderer.h"
      35             : #include "simulation2/components/ICmpWaterManager.h"
      36             : #include "simulation2/Simulation2.h"
      37             : 
      38             : #include <algorithm>
      39             : 
      40             : // TODO: Currently each decal is a separate CDecalRData. We might want to use
      41             : // lots of decals for special effects like shadows, footprints, etc, in which
      42             : // case we should probably redesign this to batch them all together for more
      43             : // efficient rendering.
      44             : 
      45             : namespace
      46             : {
      47             : 
      48           0 : struct SDecalBatch
      49             : {
      50             :     CDecalRData* decal;
      51             :     CStrIntern shaderEffect;
      52             :     CShaderDefines shaderDefines;
      53             :     CVertexBuffer::VBChunk* vertices;
      54             :     CVertexBuffer::VBChunk* indices;
      55             : };
      56             : 
      57             : struct SDecalBatchComparator
      58             : {
      59           0 :     bool operator()(const SDecalBatch& lhs, const SDecalBatch& rhs) const
      60             :     {
      61           0 :         if (lhs.shaderEffect != rhs.shaderEffect)
      62           0 :             return lhs.shaderEffect < rhs.shaderEffect;
      63           0 :         if (lhs.shaderDefines != rhs.shaderDefines)
      64           0 :             return lhs.shaderDefines < rhs.shaderDefines;
      65           0 :         const CMaterial& lhsMaterial = lhs.decal->GetDecal()->m_Decal.m_Material;
      66           0 :         const CMaterial& rhsMaterial = rhs.decal->GetDecal()->m_Decal.m_Material;
      67           0 :         if (lhsMaterial.GetDiffuseTexture() != rhsMaterial.GetDiffuseTexture())
      68           0 :             return lhsMaterial.GetDiffuseTexture() < rhsMaterial.GetDiffuseTexture();
      69           0 :         if (lhs.vertices->m_Owner != rhs.vertices->m_Owner)
      70           0 :             return lhs.vertices->m_Owner < rhs.vertices->m_Owner;
      71           0 :         if (lhs.indices->m_Owner != rhs.indices->m_Owner)
      72           0 :             return lhs.indices->m_Owner < rhs.indices->m_Owner;
      73           0 :         return lhs.decal < rhs.decal;
      74             :     }
      75             : };
      76             : 
      77             : } // anonymous namespace
      78             : 
      79             : // static
      80           6 : Renderer::Backend::IVertexInputLayout* CDecalRData::GetVertexInputLayout()
      81             : {
      82           6 :     const uint32_t stride = sizeof(SDecalVertex);
      83           6 :     const std::array<Renderer::Backend::SVertexAttributeFormat, 3> attributes{{
      84             :         {Renderer::Backend::VertexAttributeStream::POSITION,
      85             :             Renderer::Backend::Format::R32G32B32_SFLOAT,
      86             :             offsetof(SDecalVertex, m_Position), stride,
      87             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
      88             :         {Renderer::Backend::VertexAttributeStream::NORMAL,
      89             :             Renderer::Backend::Format::R32G32B32_SFLOAT,
      90             :             offsetof(SDecalVertex, m_Normal), stride,
      91             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
      92             :         {Renderer::Backend::VertexAttributeStream::UV0,
      93             :             Renderer::Backend::Format::R32G32_SFLOAT,
      94             :             offsetof(SDecalVertex, m_UV), stride,
      95             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0}
      96             :     }};
      97           6 :     return g_Renderer.GetVertexInputLayout(attributes);
      98             : }
      99             : 
     100           0 : CDecalRData::CDecalRData(CModelDecal* decal, CSimulation2* simulation)
     101           0 :     : m_Decal(decal), m_Simulation(simulation)
     102             : {
     103           0 :     BuildVertexData();
     104           0 : }
     105             : 
     106             : CDecalRData::~CDecalRData() = default;
     107             : 
     108           0 : void CDecalRData::Update(CSimulation2* simulation)
     109             : {
     110           0 :     m_Simulation = simulation;
     111           0 :     if (m_UpdateFlags != 0)
     112             :     {
     113           0 :         BuildVertexData();
     114           0 :         m_UpdateFlags = 0;
     115             :     }
     116           0 : }
     117             : 
     118           0 : void CDecalRData::RenderDecals(
     119             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
     120             :     Renderer::Backend::IVertexInputLayout* vertexInputLayout,
     121             :     const std::vector<CDecalRData*>& decals, const CShaderDefines& context, ShadowMap* shadow)
     122             : {
     123           0 :     PROFILE3("render terrain decals");
     124           0 :     GPU_SCOPED_LABEL(deviceCommandContext, "Render terrain decals");
     125             : 
     126             :     using Arena = Allocators::DynamicArena<256 * KiB>;
     127             : 
     128           0 :     Arena arena;
     129             : 
     130             :     using Batches = std::vector<SDecalBatch, ProxyAllocator<SDecalBatch, Arena>>;
     131           0 :     Batches batches((Batches::allocator_type(arena)));
     132           0 :     batches.reserve(decals.size());
     133             : 
     134           0 :     CShaderDefines contextDecal = context;
     135           0 :     contextDecal.Add(str_DECAL, str_1);
     136             : 
     137           0 :     for (CDecalRData* decal : decals)
     138             :     {
     139           0 :         CMaterial& material = decal->m_Decal->m_Decal.m_Material;
     140             : 
     141           0 :         if (material.GetShaderEffect().empty())
     142             :         {
     143           0 :             LOGERROR("Terrain renderer failed to load shader effect.\n");
     144           0 :             continue;
     145             :         }
     146             : 
     147           0 :         if (material.GetSamplers().empty() || !decal->m_VBDecals || !decal->m_VBDecalsIndices)
     148           0 :             continue;
     149             : 
     150           0 :         SDecalBatch batch;
     151           0 :         batch.decal = decal;
     152           0 :         batch.shaderEffect = material.GetShaderEffect();
     153           0 :         batch.shaderDefines = material.GetShaderDefines();
     154           0 :         batch.vertices = decal->m_VBDecals.Get();
     155           0 :         batch.indices = decal->m_VBDecalsIndices.Get();
     156             : 
     157           0 :         batches.emplace_back(std::move(batch));
     158             :     }
     159             : 
     160           0 :     if (batches.empty())
     161           0 :         return;
     162             : 
     163           0 :     std::sort(batches.begin(), batches.end(), SDecalBatchComparator());
     164             : 
     165           0 :     CVertexBuffer* lastIB = nullptr;
     166           0 :     for (auto itTechBegin = batches.begin(), itTechEnd = batches.begin(); itTechBegin != batches.end(); itTechBegin = itTechEnd)
     167             :     {
     168           0 :         while (itTechEnd != batches.end() &&
     169           0 :             itTechBegin->shaderEffect == itTechEnd->shaderEffect &&
     170           0 :             itTechBegin->shaderDefines == itTechEnd->shaderDefines)
     171             :         {
     172           0 :             ++itTechEnd;
     173             :         }
     174             : 
     175           0 :         CShaderDefines defines = contextDecal;
     176           0 :         defines.SetMany(itTechBegin->shaderDefines);
     177             :         // TODO: move enabling blend to XML.
     178           0 :         CShaderTechniquePtr techBase = g_Renderer.GetShaderManager().LoadEffect(
     179           0 :             itTechBegin->shaderEffect == str_terrain_base ? str_terrain_decal : itTechBegin->shaderEffect, defines);
     180           0 :         if (!techBase)
     181             :         {
     182           0 :             LOGERROR("Terrain renderer failed to load shader effect (%s)\n",
     183             :                 itTechBegin->shaderEffect.c_str());
     184           0 :             continue;
     185             :         }
     186             : 
     187           0 :         const int numPasses = techBase->GetNumPasses();
     188           0 :         for (int pass = 0; pass < numPasses; ++pass)
     189             :         {
     190           0 :             deviceCommandContext->SetGraphicsPipelineState(
     191           0 :                 techBase->GetGraphicsPipelineState(pass));
     192           0 :             deviceCommandContext->BeginPass();
     193             : 
     194           0 :             Renderer::Backend::IShaderProgram* shader = techBase->GetShader(pass);
     195           0 :             TerrainRenderer::PrepareShader(deviceCommandContext, shader, shadow);
     196             : 
     197           0 :             CColor shadingColor(1.0f, 1.0f, 1.0f, 1.0f);
     198             :             const int32_t shadingColorBindingSlot =
     199           0 :                 shader->GetBindingSlot(str_shadingColor);
     200           0 :             deviceCommandContext->SetUniform(
     201           0 :                 shadingColorBindingSlot, shadingColor.AsFloatArray());
     202             : 
     203           0 :             CShaderUniforms currentStaticUniforms;
     204             : 
     205           0 :             CVertexBuffer* lastVB = nullptr;
     206           0 :             for (auto itDecal = itTechBegin; itDecal != itTechEnd; ++itDecal)
     207             :             {
     208           0 :                 SDecalBatch& batch = *itDecal;
     209           0 :                 CDecalRData* decal = batch.decal;
     210           0 :                 CMaterial& material = decal->m_Decal->m_Decal.m_Material;
     211             : 
     212           0 :                 const CMaterial::SamplersVector& samplers = material.GetSamplers();
     213           0 :                 for (const CMaterial::TextureSampler& sampler : samplers)
     214           0 :                     sampler.Sampler->UploadBackendTextureIfNeeded(deviceCommandContext);
     215           0 :                 for (const CMaterial::TextureSampler& sampler : samplers)
     216             :                 {
     217           0 :                     deviceCommandContext->SetTexture(
     218           0 :                         shader->GetBindingSlot(sampler.Name),
     219           0 :                         sampler.Sampler->GetBackendTexture());
     220             :                 }
     221             : 
     222           0 :                 if (currentStaticUniforms != material.GetStaticUniforms())
     223             :                 {
     224           0 :                     currentStaticUniforms = material.GetStaticUniforms();
     225           0 :                     material.GetStaticUniforms().BindUniforms(deviceCommandContext, shader);
     226             :                 }
     227             : 
     228             :                 // TODO: Need to handle floating decals correctly. In particular, we need
     229             :                 // to render non-floating before water and floating after water (to get
     230             :                 // the blending right), and we also need to apply the correct lighting in
     231             :                 // each case, which doesn't really seem possible with the current
     232             :                 // TerrainRenderer.
     233             :                 // Also, need to mark the decals as dirty when water height changes.
     234             : 
     235             :                 //  m_Decal->GetBounds().Render();
     236             : 
     237           0 :                 if (shadingColor != decal->m_Decal->GetShadingColor())
     238             :                 {
     239           0 :                     shadingColor = decal->m_Decal->GetShadingColor();
     240           0 :                     deviceCommandContext->SetUniform(
     241           0 :                         shadingColorBindingSlot, shadingColor.AsFloatArray());
     242             :                 }
     243             : 
     244           0 :                 if (lastVB != batch.vertices->m_Owner)
     245             :                 {
     246           0 :                     lastVB = batch.vertices->m_Owner;
     247           0 :                     ENSURE(!lastVB->GetBuffer()->IsDynamic());
     248             : 
     249           0 :                     deviceCommandContext->SetVertexInputLayout(vertexInputLayout);
     250             : 
     251           0 :                     deviceCommandContext->SetVertexBuffer(
     252           0 :                         0, batch.vertices->m_Owner->GetBuffer(), 0);
     253             :                 }
     254             : 
     255           0 :                 if (lastIB != batch.indices->m_Owner)
     256             :                 {
     257           0 :                     lastIB = batch.indices->m_Owner;
     258           0 :                     ENSURE(!lastIB->GetBuffer()->IsDynamic());
     259           0 :                     deviceCommandContext->SetIndexBuffer(batch.indices->m_Owner->GetBuffer());
     260             :                 }
     261             : 
     262           0 :                 deviceCommandContext->DrawIndexed(batch.indices->m_Index, batch.indices->m_Count, 0);
     263             : 
     264             :                 // bump stats
     265           0 :                 g_Renderer.m_Stats.m_DrawCalls++;
     266           0 :                 g_Renderer.m_Stats.m_TerrainTris += batch.indices->m_Count / 3;
     267             :             }
     268             : 
     269           0 :             deviceCommandContext->EndPass();
     270             :         }
     271             :     }
     272             : }
     273             : 
     274           0 : void CDecalRData::BuildVertexData()
     275             : {
     276           0 :     PROFILE("decal build");
     277             : 
     278           0 :     const SDecal& decal = m_Decal->m_Decal;
     279             : 
     280             :     // TODO: Currently this constructs an axis-aligned bounding rectangle around
     281             :     // the decal. It would be more efficient for rendering if we excluded tiles
     282             :     // that are outside the (non-axis-aligned) decal rectangle.
     283             : 
     284             :     ssize_t i0, j0, i1, j1;
     285           0 :     m_Decal->CalcVertexExtents(i0, j0, i1, j1);
     286             :     // Currently CalcVertexExtents might return empty rectangle, that means
     287             :     // we can't render it.
     288           0 :     if (i1 <= i0 || j1 <= j0)
     289             :     {
     290             :         // We have nothing to render.
     291           0 :         m_VBDecals.Reset();
     292           0 :         m_VBDecalsIndices.Reset();
     293           0 :         return;
     294             :     }
     295             : 
     296           0 :     CmpPtr<ICmpWaterManager> cmpWaterManager(*m_Simulation, SYSTEM_ENTITY);
     297             : 
     298           0 :     std::vector<SDecalVertex> vertices((i1 - i0 + 1) * (j1 - j0 + 1));
     299             : 
     300           0 :     for (ssize_t j = j0, idx = 0; j <= j1; ++j)
     301             :     {
     302           0 :         for (ssize_t i = i0; i <= i1; ++i, ++idx)
     303             :         {
     304           0 :             SDecalVertex& vertex = vertices[idx];
     305           0 :             m_Decal->m_Terrain->CalcPosition(i, j, vertex.m_Position);
     306             : 
     307           0 :             if (decal.m_Floating && cmpWaterManager)
     308             :             {
     309           0 :                 vertex.m_Position.Y = std::max(
     310             :                     vertex.m_Position.Y,
     311           0 :                     cmpWaterManager->GetExactWaterLevel(vertex.m_Position.X, vertex.m_Position.Z));
     312             :             }
     313             : 
     314           0 :             m_Decal->m_Terrain->CalcNormal(i, j, vertex.m_Normal);
     315             : 
     316             :             // Map from world space back into decal texture space.
     317           0 :             CVector3D inv = m_Decal->GetInvTransform().Transform(vertex.m_Position);
     318           0 :             vertex.m_UV.X = 0.5f + (inv.X - decal.m_OffsetX) / decal.m_SizeX;
     319             :             // Flip V to match our texture convention.
     320           0 :             vertex.m_UV.Y = 0.5f - (inv.Z - decal.m_OffsetZ) / decal.m_SizeZ;
     321             :         }
     322             :     }
     323             : 
     324           0 :     if (!m_VBDecals || m_VBDecals->m_Count != vertices.size())
     325             :     {
     326           0 :         m_VBDecals = g_VBMan.AllocateChunk(
     327             :             sizeof(SDecalVertex), vertices.size(),
     328           0 :             Renderer::Backend::IBuffer::Type::VERTEX, false);
     329             :     }
     330           0 :     m_VBDecals->m_Owner->UpdateChunkVertices(m_VBDecals.Get(), vertices.data());
     331             : 
     332           0 :     std::vector<u16> indices((i1 - i0) * (j1 - j0) * 6);
     333             : 
     334           0 :     const ssize_t w = i1 - i0 + 1;
     335           0 :     auto itIdx = indices.begin();
     336           0 :     const size_t base = m_VBDecals->m_Index;
     337           0 :     for (ssize_t dj = 0; dj < j1 - j0; ++dj)
     338             :     {
     339           0 :         for (ssize_t di = 0; di < i1 - i0; ++di)
     340             :         {
     341           0 :             const bool dir = m_Decal->m_Terrain->GetTriangulationDir(i0 + di, j0 + dj);
     342           0 :             if (dir)
     343             :             {
     344           0 :                 *itIdx++ = u16(((dj + 0) * w + (di + 0)) + base);
     345           0 :                 *itIdx++ = u16(((dj + 0) * w + (di + 1)) + base);
     346           0 :                 *itIdx++ = u16(((dj + 1) * w + (di + 0)) + base);
     347             : 
     348           0 :                 *itIdx++ = u16(((dj + 0) * w + (di + 1)) + base);
     349           0 :                 *itIdx++ = u16(((dj + 1) * w + (di + 1)) + base);
     350           0 :                 *itIdx++ = u16(((dj + 1) * w + (di + 0)) + base);
     351             :             }
     352             :             else
     353             :             {
     354           0 :                 *itIdx++ = u16(((dj + 0) * w + (di + 0)) + base);
     355           0 :                 *itIdx++ = u16(((dj + 0) * w + (di + 1)) + base);
     356           0 :                 *itIdx++ = u16(((dj + 1) * w + (di + 1)) + base);
     357             : 
     358           0 :                 *itIdx++ = u16(((dj + 1) * w + (di + 1)) + base);
     359           0 :                 *itIdx++ = u16(((dj + 1) * w + (di + 0)) + base);
     360           0 :                 *itIdx++ = u16(((dj + 0) * w + (di + 0)) + base);
     361             :             }
     362             :         }
     363             :     }
     364             : 
     365             :     // Construct vertex buffer.
     366           0 :     if (!m_VBDecalsIndices || m_VBDecalsIndices->m_Count != indices.size())
     367             :     {
     368           0 :         m_VBDecalsIndices = g_VBMan.AllocateChunk(
     369             :             sizeof(u16), indices.size(),
     370           0 :             Renderer::Backend::IBuffer::Type::INDEX, false);
     371             :     }
     372           0 :     m_VBDecalsIndices->m_Owner->UpdateChunkVertices(m_VBDecalsIndices.Get(), indices.data());
     373           3 : }

Generated by: LCOV version 1.13