LCOV - code coverage report
Current view: top level - source/renderer - OverlayRenderer.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 69 395 17.5 %
Date: 2023-01-19 00:18:29 Functions: 12 42 28.6 %

          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 "OverlayRenderer.h"
      21             : 
      22             : #include "graphics/Camera.h"
      23             : #include "graphics/LOSTexture.h"
      24             : #include "graphics/Overlay.h"
      25             : #include "graphics/ShaderManager.h"
      26             : #include "graphics/Terrain.h"
      27             : #include "graphics/TextureManager.h"
      28             : #include "lib/hash.h"
      29             : #include "maths/MathUtil.h"
      30             : #include "maths/Quaternion.h"
      31             : #include "ps/CStrInternStatic.h"
      32             : #include "ps/Game.h"
      33             : #include "ps/Profile.h"
      34             : #include "renderer/backend/PipelineState.h"
      35             : #include "renderer/DebugRenderer.h"
      36             : #include "renderer/Renderer.h"
      37             : #include "renderer/SceneRenderer.h"
      38             : #include "renderer/TexturedLineRData.h"
      39             : #include "renderer/VertexArray.h"
      40             : #include "renderer/VertexBuffer.h"
      41             : #include "renderer/VertexBufferManager.h"
      42             : #include "simulation2/components/ICmpWaterManager.h"
      43             : #include "simulation2/Simulation2.h"
      44             : #include "simulation2/system/SimContext.h"
      45             : 
      46             : #include <unordered_map>
      47             : 
      48             : namespace
      49             : {
      50             : 
      51         120 : struct Shader
      52             : {
      53             :     CShaderTechniquePtr technique, techniqueWireframe;
      54             : 
      55           0 :     const CShaderTechniquePtr& GetTechnique() const
      56             :     {
      57           0 :         return g_Renderer.GetSceneRenderer().GetOverlayRenderMode() == WIREFRAME
      58           0 :             ? techniqueWireframe : technique;
      59             :     }
      60             : };
      61             : 
      62           0 : void AdjustOverlayGraphicsPipelineState(
      63             :     Renderer::Backend::SGraphicsPipelineStateDesc& pipelineStateDesc,
      64             :     const bool depthTestEnabled)
      65             : {
      66           0 :     pipelineStateDesc.depthStencilState.depthTestEnabled = depthTestEnabled;
      67           0 :     pipelineStateDesc.depthStencilState.depthWriteEnabled = false;
      68           0 :     pipelineStateDesc.blendState.enabled = true;
      69           0 :     pipelineStateDesc.blendState.srcColorBlendFactor = pipelineStateDesc.blendState.srcAlphaBlendFactor =
      70             :         Renderer::Backend::BlendFactor::SRC_ALPHA;
      71           0 :     pipelineStateDesc.blendState.dstColorBlendFactor = pipelineStateDesc.blendState.dstAlphaBlendFactor =
      72             :         Renderer::Backend::BlendFactor::ONE_MINUS_SRC_ALPHA;
      73           0 :     pipelineStateDesc.blendState.colorBlendOp = pipelineStateDesc.blendState.alphaBlendOp =
      74             :         Renderer::Backend::BlendOp::ADD;
      75           0 : }
      76             : 
      77          30 : Shader CreateShader(
      78             :     const CStrIntern name, const CShaderDefines& defines,
      79             :     const bool depthTestEnabled)
      80             : {
      81          30 :     Shader shader;
      82             : 
      83          60 :     shader.technique = g_Renderer.GetShaderManager().LoadEffect(
      84             :         name, defines,
      85           0 :         [depthTestEnabled](Renderer::Backend::SGraphicsPipelineStateDesc& pipelineStateDesc)
      86           0 :         {
      87           0 :             AdjustOverlayGraphicsPipelineState(pipelineStateDesc, depthTestEnabled);
      88          30 :         });
      89          60 :     shader.techniqueWireframe = g_Renderer.GetShaderManager().LoadEffect(
      90             :         name, defines,
      91           0 :         [depthTestEnabled](Renderer::Backend::SGraphicsPipelineStateDesc& pipelineStateDesc)
      92           0 :         {
      93           0 :             AdjustOverlayGraphicsPipelineState(pipelineStateDesc, depthTestEnabled);
      94           0 :             pipelineStateDesc.rasterizationState.polygonMode = Renderer::Backend::PolygonMode::LINE;
      95          30 :         });
      96             : 
      97          30 :     return shader;
      98             : }
      99             : 
     100             : /**
     101             :  * Key used to group quads into batches for more efficient rendering. Currently groups by the combination
     102             :  * of the main texture and the texture mask, to minimize texture swapping during rendering.
     103             :  */
     104           0 : struct QuadBatchKey
     105             : {
     106           0 :     QuadBatchKey (const CTexturePtr& texture, const CTexturePtr& textureMask)
     107           0 :         : m_Texture(texture), m_TextureMask(textureMask)
     108           0 :     { }
     109             : 
     110           0 :     bool operator==(const QuadBatchKey& other) const
     111             :     {
     112           0 :         return (m_Texture == other.m_Texture && m_TextureMask == other.m_TextureMask);
     113             :     }
     114             : 
     115             :     CTexturePtr m_Texture;
     116             :     CTexturePtr m_TextureMask;
     117             : };
     118             : 
     119             : struct QuadBatchHash
     120             : {
     121           0 :     std::size_t operator()(const QuadBatchKey& d) const
     122             :     {
     123           0 :         size_t seed = 0;
     124           0 :         hash_combine(seed, d.m_Texture);
     125           0 :         hash_combine(seed, d.m_TextureMask);
     126           0 :         return seed;
     127             :     }
     128             : };
     129             : 
     130             : /**
     131             :  * Holds information about a single quad rendering batch.
     132             :  */
     133           0 : class QuadBatchData : public CRenderData
     134             : {
     135             : public:
     136           0 :     QuadBatchData() : m_IndicesBase(0), m_NumRenderQuads(0) { }
     137             : 
     138             :     /// Holds the quad overlay structures requested to be rendered in this batch. Must be cleared
     139             :     /// after each frame.
     140             :     std::vector<SOverlayQuad*> m_Quads;
     141             : 
     142             :     /// Start index of this batch into the dedicated quad indices VertexArray (see OverlayInternals).
     143             :     size_t m_IndicesBase;
     144             :     /// Amount of quads to actually render in this batch. Potentially (although unlikely to be)
     145             :     /// different from m_Quads.size() due to restrictions on the total amount of quads that can be
     146             :     /// rendered. Must be reset after each frame.
     147             :     size_t m_NumRenderQuads;
     148             : };
     149             : 
     150             : } // anonymous namespace
     151             : 
     152             : struct OverlayRendererInternals
     153             : {
     154             :     using QuadBatchMap = std::unordered_map<QuadBatchKey, QuadBatchData, QuadBatchHash>;
     155             : 
     156             :     OverlayRendererInternals();
     157           6 :     ~OverlayRendererInternals() = default;
     158             : 
     159             :     Renderer::Backend::IDevice* device = nullptr;
     160             : 
     161             :     std::vector<SOverlayLine*> lines;
     162             :     std::vector<SOverlayTexturedLine*> texlines;
     163             :     std::vector<SOverlaySprite*> sprites;
     164             :     std::vector<SOverlayQuad*> quads;
     165             :     std::vector<SOverlaySphere*> spheres;
     166             : 
     167             :     QuadBatchMap quadBatchMap;
     168             : 
     169             :     // Dedicated vertex/index buffers for rendering all quads (to within the limits set by
     170             :     // MAX_QUAD_OVERLAYS).
     171             :     VertexArray quadVertices;
     172             :     VertexArray::Attribute quadAttributePos;
     173             :     VertexArray::Attribute quadAttributeColor;
     174             :     VertexArray::Attribute quadAttributeUV;
     175             :     VertexIndexArray quadIndices;
     176             : 
     177             :     // Maximum amount of quad overlays we support for rendering. This limit is set to be able to
     178             :     // render all quads from a single dedicated VB without having to reallocate it, which is much
     179             :     // faster in the typical case of rendering only a handful of quads. When modifying this value,
     180             :     // you must take care for the new amount of quads to fit in a single backend buffer (which is
     181             :     // not likely to be a problem).
     182             :     static const size_t MAX_QUAD_OVERLAYS = 1024;
     183             : 
     184             :     // Sets of commonly-(re)used shader defines.
     185             :     CShaderDefines defsOverlayLineNormal;
     186             :     CShaderDefines defsOverlayLineAlwaysVisible;
     187             :     CShaderDefines defsQuadOverlay;
     188             : 
     189             :     Shader shaderTexLineNormal;
     190             :     Shader shaderTexLineAlwaysVisible;
     191             :     Shader shaderQuadOverlay;
     192             :     Shader shaderForegroundOverlay;
     193             :     Shader shaderOverlaySolid;
     194             : 
     195             :     Renderer::Backend::IVertexInputLayout* quadVertexInputLayout = nullptr;
     196             :     Renderer::Backend::IVertexInputLayout* foregroundVertexInputLayout = nullptr;
     197             :     Renderer::Backend::IVertexInputLayout* sphereVertexInputLayout = nullptr;
     198             : 
     199             :     Renderer::Backend::IVertexInputLayout* texturedLineVertexInputLayout = nullptr;
     200             : 
     201             :     // Geometry for a unit sphere
     202             :     std::vector<float> sphereVertexes;
     203             :     std::vector<u16> sphereIndexes;
     204             :     void GenerateSphere();
     205             : 
     206             :     // Performs one-time setup. Called from CRenderer::Open, after graphics capabilities have
     207             :     // been detected. Note that no backend buffer must be created before this is called, since
     208             :     // the shader path and graphics capabilities are not guaranteed to be stable before this
     209             :     // point.
     210             :     void Initialize();
     211             : };
     212             : 
     213             : const float OverlayRenderer::OVERLAY_VOFFSET = 0.2f;
     214             : 
     215           6 : OverlayRendererInternals::OverlayRendererInternals()
     216             :     : quadVertices(Renderer::Backend::IBuffer::Type::VERTEX, true),
     217           6 :     quadIndices(false)
     218             : {
     219           6 :     quadAttributePos.format = Renderer::Backend::Format::R32G32B32_SFLOAT;
     220           6 :     quadVertices.AddAttribute(&quadAttributePos);
     221             : 
     222           6 :     quadAttributeColor.format = Renderer::Backend::Format::R8G8B8A8_UNORM;
     223           6 :     quadVertices.AddAttribute(&quadAttributeColor);
     224             : 
     225           6 :     quadAttributeUV.format = Renderer::Backend::Format::R32G32_SFLOAT;
     226           6 :     quadVertices.AddAttribute(&quadAttributeUV);
     227             : 
     228             :     // Note that we're reusing the textured overlay line shader for the quad overlay rendering. This
     229             :     // is because their code is almost identical; the only difference is that for the quad overlays
     230             :     // we want to use a vertex color stream as opposed to an objectColor uniform. To this end, the
     231             :     // shader has been set up to switch between the two behaviours based on the USE_OBJECTCOLOR define.
     232           6 :     defsOverlayLineNormal.Add(str_USE_OBJECTCOLOR, str_1);
     233           6 :     defsOverlayLineAlwaysVisible.Add(str_USE_OBJECTCOLOR, str_1);
     234           6 :     defsOverlayLineAlwaysVisible.Add(str_IGNORE_LOS, str_1);
     235           6 : }
     236             : 
     237           6 : void OverlayRendererInternals::Initialize()
     238             : {
     239             :     // Perform any initialization after graphics capabilities have been detected. Notably,
     240             :     // only at this point can we safely allocate backend buffer (in contrast to e.g. in the constructor),
     241             :     // because their creation depends on the shader path, which is not reliably set before this point.
     242             : 
     243           6 :     quadVertices.SetNumberOfVertices(MAX_QUAD_OVERLAYS * 4);
     244           6 :     quadVertices.Layout(); // allocate backing store
     245             : 
     246           6 :     quadIndices.SetNumberOfVertices(MAX_QUAD_OVERLAYS * 6);
     247           6 :     quadIndices.Layout(); // allocate backing store
     248             : 
     249             :     // Since the quads in the vertex array are independent and always consist of exactly 4 vertices per quad, the
     250             :     // indices are always the same; we can therefore fill in all the indices once and pretty much forget about
     251             :     // them. We then also no longer need its backing store, since we never change any indices afterwards.
     252           6 :     VertexArrayIterator<u16> index = quadIndices.GetIterator();
     253        6150 :     for (u16 i = 0; i < static_cast<u16>(MAX_QUAD_OVERLAYS); ++i)
     254             :     {
     255        6144 :         *index++ = i * 4 + 0;
     256        6144 :         *index++ = i * 4 + 1;
     257        6144 :         *index++ = i * 4 + 2;
     258        6144 :         *index++ = i * 4 + 2;
     259        6144 :         *index++ = i * 4 + 3;
     260        6144 :         *index++ = i * 4 + 0;
     261             :     }
     262           6 :     quadIndices.Upload();
     263           6 :     quadIndices.FreeBackingStore();
     264             : 
     265           6 :     shaderTexLineNormal =
     266          12 :         CreateShader(str_overlay_line, defsOverlayLineNormal, true);
     267           6 :     shaderTexLineAlwaysVisible =
     268          12 :         CreateShader(str_overlay_line, defsOverlayLineAlwaysVisible, true);
     269           6 :     shaderQuadOverlay =
     270          12 :         CreateShader(str_overlay_line, defsQuadOverlay, true);
     271           6 :     shaderForegroundOverlay =
     272          12 :         CreateShader(str_foreground_overlay, {}, false);
     273           6 :     shaderOverlaySolid =
     274          12 :         CreateShader(str_overlay_solid, {}, true);
     275             : 
     276           6 :     const uint32_t quadStride = quadVertices.GetStride();
     277           6 :     const std::array<Renderer::Backend::SVertexAttributeFormat, 3> quadAttributes{{
     278             :         {Renderer::Backend::VertexAttributeStream::POSITION,
     279          12 :             quadAttributePos.format, quadAttributePos.offset, quadStride,
     280             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
     281             :         {Renderer::Backend::VertexAttributeStream::COLOR,
     282          12 :             quadAttributeColor.format, quadAttributeColor.offset, quadStride,
     283             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
     284             :         {Renderer::Backend::VertexAttributeStream::UV0,
     285          12 :             quadAttributeUV.format, quadAttributeUV.offset, quadStride,
     286             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0}
     287          36 :     }};
     288           6 :     quadVertexInputLayout = g_Renderer.GetVertexInputLayout(quadAttributes);
     289             : 
     290           6 :     const std::array<Renderer::Backend::SVertexAttributeFormat, 2> foregroundAttributes{{
     291             :         {Renderer::Backend::VertexAttributeStream::POSITION,
     292             :             Renderer::Backend::Format::R32G32B32_SFLOAT, 0, sizeof(float) * 3,
     293             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
     294             :         {Renderer::Backend::VertexAttributeStream::UV0,
     295             :             Renderer::Backend::Format::R32G32_SFLOAT, 0, sizeof(float) * 2,
     296             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 1}
     297             :     }};
     298           6 :     foregroundVertexInputLayout = g_Renderer.GetVertexInputLayout(foregroundAttributes);
     299             : 
     300           6 :     const std::array<Renderer::Backend::SVertexAttributeFormat, 1> shpereAttributes{{
     301             :         {Renderer::Backend::VertexAttributeStream::POSITION,
     302             :             Renderer::Backend::Format::R32G32B32_SFLOAT, 0, sizeof(float) * 3,
     303             :             Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0}
     304             :     }};
     305           6 :     sphereVertexInputLayout = g_Renderer.GetVertexInputLayout(shpereAttributes);
     306             : 
     307           6 :     texturedLineVertexInputLayout = CTexturedLineRData::GetVertexInputLayout();
     308           6 : }
     309             : 
     310           6 : OverlayRenderer::OverlayRenderer()
     311             : {
     312           6 :     m = new OverlayRendererInternals();
     313           6 : }
     314             : 
     315          12 : OverlayRenderer::~OverlayRenderer()
     316             : {
     317           6 :     delete m;
     318           6 : }
     319             : 
     320           6 : void OverlayRenderer::Initialize()
     321             : {
     322           6 :     m->Initialize();
     323           6 : }
     324             : 
     325           0 : void OverlayRenderer::Submit(SOverlayLine* line)
     326             : {
     327           0 :     m->lines.push_back(line);
     328           0 : }
     329             : 
     330           0 : void OverlayRenderer::Submit(SOverlayTexturedLine* line)
     331             : {
     332             :     // Simplify the rest of the code by guaranteeing non-empty lines
     333           0 :     if (line->m_Coords.empty())
     334           0 :         return;
     335             : 
     336           0 :     m->texlines.push_back(line);
     337             : }
     338             : 
     339           0 : void OverlayRenderer::Submit(SOverlaySprite* overlay)
     340             : {
     341           0 :     m->sprites.push_back(overlay);
     342           0 : }
     343             : 
     344           0 : void OverlayRenderer::Submit(SOverlayQuad* overlay)
     345             : {
     346           0 :     m->quads.push_back(overlay);
     347           0 : }
     348             : 
     349           0 : void OverlayRenderer::Submit(SOverlaySphere* overlay)
     350             : {
     351           0 :     m->spheres.push_back(overlay);
     352           0 : }
     353             : 
     354           0 : void OverlayRenderer::EndFrame()
     355             : {
     356           0 :     m->lines.clear();
     357           0 :     m->texlines.clear();
     358           0 :     m->sprites.clear();
     359           0 :     m->quads.clear();
     360           0 :     m->spheres.clear();
     361             : 
     362             :     // this should leave the capacity unchanged, which is okay since it
     363             :     // won't be very large or very variable
     364             : 
     365             :     // Empty the batch rendering data structures, but keep their key mappings around for the next frames
     366           0 :     for (OverlayRendererInternals::QuadBatchMap::iterator it = m->quadBatchMap.begin(); it != m->quadBatchMap.end(); ++it)
     367             :     {
     368           0 :         QuadBatchData& quadBatchData = (it->second);
     369           0 :         quadBatchData.m_Quads.clear();
     370           0 :         quadBatchData.m_NumRenderQuads = 0;
     371           0 :         quadBatchData.m_IndicesBase = 0;
     372             :     }
     373           0 : }
     374             : 
     375           0 : void OverlayRenderer::PrepareForRendering()
     376             : {
     377           0 :     PROFILE3("prepare overlays");
     378             : 
     379             :     // This is where we should do something like sort the overlays by
     380             :     // color/sprite/etc for more efficient rendering
     381             : 
     382           0 :     for (size_t i = 0; i < m->texlines.size(); ++i)
     383             :     {
     384           0 :         SOverlayTexturedLine* line = m->texlines[i];
     385           0 :         if (!line->m_RenderData)
     386             :         {
     387           0 :             line->m_RenderData = std::make_shared<CTexturedLineRData>();
     388           0 :             line->m_RenderData->Update(*line);
     389             :             // We assume the overlay line will get replaced by the caller
     390             :             // if terrain changes, so we don't need to detect that here and
     391             :             // call Update again. Also we assume the caller won't change
     392             :             // any of the parameters after first submitting the line.
     393             :         }
     394             :     }
     395             : 
     396             :     // Group quad overlays by their texture/mask combination for efficient rendering
     397             :     // TODO: consider doing this directly in Submit()
     398           0 :     for (size_t i = 0; i < m->quads.size(); ++i)
     399             :     {
     400           0 :         SOverlayQuad* const quad = m->quads[i];
     401             : 
     402           0 :         QuadBatchKey textures(quad->m_Texture, quad->m_TextureMask);
     403           0 :         QuadBatchData& batchRenderData = m->quadBatchMap[textures]; // will create entry if it doesn't already exist
     404             : 
     405             :         // add overlay to list of quads
     406           0 :         batchRenderData.m_Quads.push_back(quad);
     407             :     }
     408             : 
     409           0 :     const CVector3D vOffset(0, OverlayRenderer::OVERLAY_VOFFSET, 0);
     410             : 
     411             :     // Write quad overlay vertices/indices to VA backing store
     412           0 :     VertexArrayIterator<CVector3D> vertexPos = m->quadAttributePos.GetIterator<CVector3D>();
     413           0 :     VertexArrayIterator<SColor4ub> vertexColor = m->quadAttributeColor.GetIterator<SColor4ub>();
     414           0 :     VertexArrayIterator<float[2]> vertexUV = m->quadAttributeUV.GetIterator<float[2]>();
     415             : 
     416           0 :     size_t indicesIdx = 0;
     417           0 :     size_t totalNumQuads = 0;
     418             : 
     419           0 :     for (OverlayRendererInternals::QuadBatchMap::iterator it = m->quadBatchMap.begin(); it != m->quadBatchMap.end(); ++it)
     420             :     {
     421           0 :         QuadBatchData& batchRenderData = (it->second);
     422           0 :         batchRenderData.m_NumRenderQuads = 0;
     423             : 
     424           0 :         if (batchRenderData.m_Quads.empty())
     425           0 :             continue;
     426             : 
     427             :         // Remember the current index into the (entire) indices array as our base offset for this batch
     428           0 :         batchRenderData.m_IndicesBase = indicesIdx;
     429             : 
     430             :         // points to the index where each iteration's vertices will be appended
     431           0 :         for (size_t i = 0; i < batchRenderData.m_Quads.size() && totalNumQuads < OverlayRendererInternals::MAX_QUAD_OVERLAYS; i++)
     432             :         {
     433           0 :             const SOverlayQuad* quad = batchRenderData.m_Quads[i];
     434             : 
     435           0 :             const SColor4ub quadColor = quad->m_Color.AsSColor4ub();
     436             : 
     437           0 :             *vertexPos++ = quad->m_Corners[0] + vOffset;
     438           0 :             *vertexPos++ = quad->m_Corners[1] + vOffset;
     439           0 :             *vertexPos++ = quad->m_Corners[2] + vOffset;
     440           0 :             *vertexPos++ = quad->m_Corners[3] + vOffset;
     441             : 
     442           0 :             (*vertexUV)[0] = 0;
     443           0 :             (*vertexUV)[1] = 0;
     444           0 :             ++vertexUV;
     445           0 :             (*vertexUV)[0] = 0;
     446           0 :             (*vertexUV)[1] = 1;
     447           0 :             ++vertexUV;
     448           0 :             (*vertexUV)[0] = 1;
     449           0 :             (*vertexUV)[1] = 1;
     450           0 :             ++vertexUV;
     451           0 :             (*vertexUV)[0] = 1;
     452           0 :             (*vertexUV)[1] = 0;
     453           0 :             ++vertexUV;
     454             : 
     455           0 :             *vertexColor++ = quadColor;
     456           0 :             *vertexColor++ = quadColor;
     457           0 :             *vertexColor++ = quadColor;
     458           0 :             *vertexColor++ = quadColor;
     459             : 
     460           0 :             indicesIdx += 6;
     461             : 
     462           0 :             totalNumQuads++;
     463           0 :             batchRenderData.m_NumRenderQuads++;
     464             :         }
     465             :     }
     466             : 
     467           0 :     m->quadVertices.Upload();
     468             :     // don't free the backing store! we'll overwrite it on the next frame to save a reallocation.
     469             : 
     470           0 :     m->quadVertices.PrepareForRendering();
     471           0 : }
     472             : 
     473           0 : void OverlayRenderer::Upload(
     474             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
     475             : {
     476           0 :     m->quadVertices.UploadIfNeeded(deviceCommandContext);
     477           0 :     m->quadIndices.UploadIfNeeded(deviceCommandContext);
     478           0 : }
     479             : 
     480           0 : void OverlayRenderer::RenderOverlaysBeforeWater(
     481             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
     482             : {
     483           0 :     PROFILE3_GPU("overlays (before)");
     484           0 :     GPU_SCOPED_LABEL(deviceCommandContext, "Render overlays before water");
     485             : 
     486           0 :     for (SOverlayLine* line : m->lines)
     487             :     {
     488           0 :         if (line->m_Coords.empty())
     489           0 :             continue;
     490             : 
     491           0 :         g_Renderer.GetDebugRenderer().DrawLine(line->m_Coords, line->m_Color, static_cast<float>(line->m_Thickness));
     492             :     }
     493           0 : }
     494             : 
     495           0 : void OverlayRenderer::RenderOverlaysAfterWater(
     496             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
     497             : {
     498           0 :     PROFILE3_GPU("overlays (after)");
     499           0 :     GPU_SCOPED_LABEL(deviceCommandContext, "Render overlays after water");
     500             : 
     501           0 :     RenderTexturedOverlayLines(deviceCommandContext);
     502           0 :     RenderQuadOverlays(deviceCommandContext);
     503           0 :     RenderSphereOverlays(deviceCommandContext);
     504           0 : }
     505             : 
     506           0 : void OverlayRenderer::RenderTexturedOverlayLines(Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
     507             : {
     508           0 :     if (m->texlines.empty())
     509           0 :         return;
     510             : 
     511           0 :     CLOSTexture& los = g_Renderer.GetSceneRenderer().GetScene().GetLOSTexture();
     512             : 
     513           0 :     if (m->shaderTexLineNormal.technique)
     514             :     {
     515           0 :         const CShaderTechniquePtr& shaderTechnique = m->shaderTexLineNormal.GetTechnique();
     516           0 :         deviceCommandContext->SetGraphicsPipelineState(
     517           0 :             shaderTechnique->GetGraphicsPipelineState());
     518           0 :         deviceCommandContext->BeginPass();
     519             : 
     520           0 :         Renderer::Backend::IShaderProgram* shaderTexLineNormal = shaderTechnique->GetShader();
     521             : 
     522           0 :         deviceCommandContext->SetTexture(
     523           0 :             shaderTexLineNormal->GetBindingSlot(str_losTex), los.GetTexture());
     524             : 
     525             :         const CMatrix3D transform =
     526           0 :             g_Renderer.GetSceneRenderer().GetViewCamera().GetViewProjection();
     527           0 :         deviceCommandContext->SetUniform(
     528           0 :             shaderTexLineNormal->GetBindingSlot(str_transform), transform.AsFloatArray());
     529           0 :         deviceCommandContext->SetUniform(
     530           0 :             shaderTexLineNormal->GetBindingSlot(str_losTransform),
     531           0 :             los.GetTextureMatrix()[0], los.GetTextureMatrix()[12]);
     532             : 
     533             :         // batch render only the non-always-visible overlay lines using the normal shader
     534           0 :         RenderTexturedOverlayLines(deviceCommandContext, shaderTexLineNormal, false);
     535             : 
     536           0 :         deviceCommandContext->EndPass();
     537             :     }
     538             : 
     539           0 :     if (m->shaderTexLineAlwaysVisible.technique)
     540             :     {
     541           0 :         const CShaderTechniquePtr& shaderTechnique = m->shaderTexLineAlwaysVisible.GetTechnique();
     542           0 :         deviceCommandContext->SetGraphicsPipelineState(
     543           0 :             shaderTechnique->GetGraphicsPipelineState());
     544           0 :         deviceCommandContext->BeginPass();
     545             : 
     546             :         Renderer::Backend::IShaderProgram* shaderTexLineAlwaysVisible =
     547           0 :             shaderTechnique->GetShader();
     548             : 
     549             :         // TODO: losTex and losTransform are unused in the always visible shader; see if these can be safely omitted
     550           0 :         deviceCommandContext->SetTexture(
     551           0 :             shaderTexLineAlwaysVisible->GetBindingSlot(str_losTex), los.GetTexture());
     552             : 
     553             :         const CMatrix3D transform =
     554           0 :             g_Renderer.GetSceneRenderer().GetViewCamera().GetViewProjection();
     555           0 :         deviceCommandContext->SetUniform(
     556           0 :             shaderTexLineAlwaysVisible->GetBindingSlot(str_transform), transform.AsFloatArray());
     557           0 :         deviceCommandContext->SetUniform(
     558           0 :             shaderTexLineAlwaysVisible->GetBindingSlot(str_losTransform),
     559           0 :             los.GetTextureMatrix()[0], los.GetTextureMatrix()[12]);
     560             : 
     561             :         // batch render only the always-visible overlay lines using the LoS-ignored shader
     562           0 :         RenderTexturedOverlayLines(deviceCommandContext, shaderTexLineAlwaysVisible, true);
     563             : 
     564           0 :         deviceCommandContext->EndPass();
     565             :     }
     566             : }
     567             : 
     568           0 : void OverlayRenderer::RenderTexturedOverlayLines(
     569             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
     570             :     Renderer::Backend::IShaderProgram* shader, bool alwaysVisible)
     571             : {
     572           0 :     for (size_t i = 0; i < m->texlines.size(); ++i)
     573             :     {
     574           0 :         SOverlayTexturedLine* line = m->texlines[i];
     575             : 
     576             :         // render only those lines matching the requested alwaysVisible status
     577           0 :         if (!line->m_RenderData || line->m_AlwaysVisible != alwaysVisible)
     578           0 :             continue;
     579             : 
     580           0 :         ENSURE(line->m_RenderData);
     581           0 :         line->m_RenderData->Render(
     582           0 :             deviceCommandContext, m->texturedLineVertexInputLayout, *line, shader);
     583             :     }
     584           0 : }
     585             : 
     586           0 : void OverlayRenderer::RenderQuadOverlays(
     587             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
     588             : {
     589           0 :     if (m->quadBatchMap.empty())
     590           0 :         return;
     591             : 
     592           0 :     if (!m->shaderQuadOverlay.technique)
     593           0 :         return;
     594             : 
     595           0 :     const CShaderTechniquePtr& shaderTechnique = m->shaderQuadOverlay.GetTechnique();
     596           0 :     deviceCommandContext->SetGraphicsPipelineState(
     597           0 :         shaderTechnique->GetGraphicsPipelineState());
     598           0 :     deviceCommandContext->BeginPass();
     599             : 
     600           0 :     Renderer::Backend::IShaderProgram* shader = shaderTechnique->GetShader();
     601             : 
     602           0 :     CLOSTexture& los = g_Renderer.GetSceneRenderer().GetScene().GetLOSTexture();
     603             : 
     604           0 :     deviceCommandContext->SetTexture(
     605           0 :         shader->GetBindingSlot(str_losTex), los.GetTexture());
     606           0 :     deviceCommandContext->SetUniform(
     607           0 :         shader->GetBindingSlot(str_losTransform),
     608           0 :         los.GetTextureMatrix()[0], los.GetTextureMatrix()[12]);
     609             : 
     610             :     const CMatrix3D transform =
     611           0 :         g_Renderer.GetSceneRenderer().GetViewCamera().GetViewProjection();
     612           0 :     deviceCommandContext->SetUniform(
     613           0 :         shader->GetBindingSlot(str_transform), transform.AsFloatArray());
     614             : 
     615           0 :     const uint32_t vertexStride = m->quadVertices.GetStride();
     616           0 :     const uint32_t firstVertexOffset = m->quadVertices.GetOffset() * vertexStride;
     617             : 
     618           0 :     deviceCommandContext->SetVertexInputLayout(m->quadVertexInputLayout);
     619             : 
     620           0 :     deviceCommandContext->SetVertexBuffer(
     621           0 :         0, m->quadVertices.GetBuffer(), firstVertexOffset);
     622           0 :     deviceCommandContext->SetIndexBuffer(m->quadIndices.GetBuffer());
     623             : 
     624           0 :     const int32_t baseTexBindingSlot = shader->GetBindingSlot(str_baseTex);
     625           0 :     const int32_t maskTexBindingSlot = shader->GetBindingSlot(str_maskTex);
     626             : 
     627           0 :     for (OverlayRendererInternals::QuadBatchMap::iterator it = m->quadBatchMap.begin(); it != m->quadBatchMap.end(); ++it)
     628             :     {
     629           0 :         QuadBatchData& batchRenderData = it->second;
     630           0 :         const size_t batchNumQuads = batchRenderData.m_NumRenderQuads;
     631             : 
     632           0 :         if (batchNumQuads == 0)
     633           0 :             continue;
     634             : 
     635           0 :         const QuadBatchKey& maskPair = it->first;
     636             : 
     637           0 :         maskPair.m_Texture->UploadBackendTextureIfNeeded(deviceCommandContext);
     638           0 :         maskPair.m_TextureMask->UploadBackendTextureIfNeeded(deviceCommandContext);
     639             : 
     640           0 :         deviceCommandContext->SetTexture(
     641           0 :             baseTexBindingSlot, maskPair.m_Texture->GetBackendTexture());
     642           0 :         deviceCommandContext->SetTexture(
     643           0 :             maskTexBindingSlot, maskPair.m_TextureMask->GetBackendTexture());
     644             : 
     645           0 :         deviceCommandContext->DrawIndexed(m->quadIndices.GetOffset() + batchRenderData.m_IndicesBase, batchNumQuads * 6, 0);
     646             : 
     647           0 :         g_Renderer.GetStats().m_DrawCalls++;
     648           0 :         g_Renderer.GetStats().m_OverlayTris += batchNumQuads*2;
     649             :     }
     650             : 
     651           0 :     deviceCommandContext->EndPass();
     652             : }
     653             : 
     654           0 : void OverlayRenderer::RenderForegroundOverlays(
     655             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
     656             :     const CCamera& viewCamera)
     657             : {
     658           0 :     PROFILE3_GPU("overlays (fg)");
     659           0 :     GPU_SCOPED_LABEL(deviceCommandContext, "Render foreground overlays");
     660             : 
     661           0 :     const CVector3D right = -viewCamera.GetOrientation().GetLeft();
     662           0 :     const CVector3D up = viewCamera.GetOrientation().GetUp();
     663             : 
     664           0 :     const CShaderTechniquePtr& shaderTechnique = m->shaderForegroundOverlay.GetTechnique();
     665           0 :     deviceCommandContext->SetGraphicsPipelineState(
     666           0 :         shaderTechnique->GetGraphicsPipelineState());
     667           0 :     deviceCommandContext->BeginPass();
     668             : 
     669           0 :     Renderer::Backend::IShaderProgram* shader = shaderTechnique->GetShader();
     670             : 
     671             :     const CMatrix3D transform =
     672           0 :         g_Renderer.GetSceneRenderer().GetViewCamera().GetViewProjection();
     673           0 :     deviceCommandContext->SetUniform(
     674           0 :         shader->GetBindingSlot(str_transform), transform.AsFloatArray());
     675             : 
     676             :     const CVector2D uvs[6] =
     677             :     {
     678             :         {0.0f, 1.0f},
     679             :         {1.0f, 1.0f},
     680             :         {1.0f, 0.0f},
     681             :         {0.0f, 1.0f},
     682             :         {1.0f, 0.0f},
     683             :         {0.0f, 0.0f},
     684           0 :     };
     685             : 
     686           0 :     deviceCommandContext->SetVertexInputLayout(m->foregroundVertexInputLayout);
     687             : 
     688           0 :     deviceCommandContext->SetVertexBufferData(
     689           0 :         1, &uvs[0], std::size(uvs) * sizeof(uvs[0]));
     690             : 
     691           0 :     const int32_t baseTexBindingSlot = shader->GetBindingSlot(str_baseTex);
     692           0 :     const int32_t colorMulBindingSlot = shader->GetBindingSlot(str_colorMul);
     693             : 
     694           0 :     for (size_t i = 0; i < m->sprites.size(); ++i)
     695             :     {
     696           0 :         SOverlaySprite* sprite = m->sprites[i];
     697           0 :         if (!i || sprite->m_Texture != m->sprites[i - 1]->m_Texture)
     698             :         {
     699           0 :             sprite->m_Texture->UploadBackendTextureIfNeeded(deviceCommandContext);
     700           0 :             deviceCommandContext->SetTexture(
     701           0 :                 baseTexBindingSlot, sprite->m_Texture->GetBackendTexture());
     702             :         }
     703             : 
     704           0 :         deviceCommandContext->SetUniform(
     705           0 :             colorMulBindingSlot, sprite->m_Color.AsFloatArray());
     706             : 
     707             :         const CVector3D position[6] =
     708             :         {
     709           0 :             sprite->m_Position + right*sprite->m_X0 + up*sprite->m_Y0,
     710           0 :             sprite->m_Position + right*sprite->m_X1 + up*sprite->m_Y0,
     711           0 :             sprite->m_Position + right*sprite->m_X1 + up*sprite->m_Y1,
     712           0 :             sprite->m_Position + right*sprite->m_X0 + up*sprite->m_Y0,
     713           0 :             sprite->m_Position + right*sprite->m_X1 + up*sprite->m_Y1,
     714           0 :             sprite->m_Position + right*sprite->m_X0 + up*sprite->m_Y1
     715           0 :         };
     716             : 
     717           0 :         deviceCommandContext->SetVertexBufferData(
     718           0 :             0, &position[0].X, std::size(position) * sizeof(position[0]));
     719             : 
     720           0 :         deviceCommandContext->Draw(0, 6);
     721             : 
     722           0 :         g_Renderer.GetStats().m_DrawCalls++;
     723           0 :         g_Renderer.GetStats().m_OverlayTris += 2;
     724             :     }
     725             : 
     726           0 :     deviceCommandContext->EndPass();
     727           0 : }
     728             : 
     729           0 : static void TessellateSphereFace(const CVector3D& a, u16 ai,
     730             :                                  const CVector3D& b, u16 bi,
     731             :                                  const CVector3D& c, u16 ci,
     732             :                                  std::vector<float>& vertexes, std::vector<u16>& indexes, int level)
     733             : {
     734           0 :     if (level == 0)
     735             :     {
     736           0 :         indexes.push_back(ai);
     737           0 :         indexes.push_back(bi);
     738           0 :         indexes.push_back(ci);
     739             :     }
     740             :     else
     741             :     {
     742           0 :         CVector3D d = (a + b).Normalized();
     743           0 :         CVector3D e = (b + c).Normalized();
     744           0 :         CVector3D f = (c + a).Normalized();
     745           0 :         int di = vertexes.size() / 3; vertexes.push_back(d.X); vertexes.push_back(d.Y); vertexes.push_back(d.Z);
     746           0 :         int ei = vertexes.size() / 3; vertexes.push_back(e.X); vertexes.push_back(e.Y); vertexes.push_back(e.Z);
     747           0 :         int fi = vertexes.size() / 3; vertexes.push_back(f.X); vertexes.push_back(f.Y); vertexes.push_back(f.Z);
     748           0 :         TessellateSphereFace(a,ai, d,di, f,fi, vertexes, indexes, level-1);
     749           0 :         TessellateSphereFace(d,di, b,bi, e,ei, vertexes, indexes, level-1);
     750           0 :         TessellateSphereFace(f,fi, e,ei, c,ci, vertexes, indexes, level-1);
     751           0 :         TessellateSphereFace(d,di, e,ei, f,fi, vertexes, indexes, level-1);
     752             :     }
     753           0 : }
     754             : 
     755           0 : static void TessellateSphere(std::vector<float>& vertexes, std::vector<u16>& indexes, int level)
     756             : {
     757             :     /* Start with a tetrahedron, then tessellate */
     758           0 :     float s = sqrtf(0.5f);
     759             : #define VERT(a,b,c) vertexes.push_back(a); vertexes.push_back(b); vertexes.push_back(c);
     760           0 :     VERT(-s,  0, -s);
     761           0 :     VERT( s,  0, -s);
     762           0 :     VERT( s,  0,  s);
     763           0 :     VERT(-s,  0,  s);
     764           0 :     VERT( 0, -1,  0);
     765           0 :     VERT( 0,  1,  0);
     766             : #define FACE(a,b,c) \
     767             :     TessellateSphereFace( \
     768             :         CVector3D(vertexes[a*3], vertexes[a*3+1], vertexes[a*3+2]), a, \
     769             :         CVector3D(vertexes[b*3], vertexes[b*3+1], vertexes[b*3+2]), b, \
     770             :         CVector3D(vertexes[c*3], vertexes[c*3+1], vertexes[c*3+2]), c, \
     771             :         vertexes, indexes, level);
     772           0 :     FACE(0,4,1);
     773           0 :     FACE(1,4,2);
     774           0 :     FACE(2,4,3);
     775           0 :     FACE(3,4,0);
     776           0 :     FACE(1,5,0);
     777           0 :     FACE(2,5,1);
     778           0 :     FACE(3,5,2);
     779           0 :     FACE(0,5,3);
     780             : #undef FACE
     781             : #undef VERT
     782           0 : }
     783             : 
     784           0 : void OverlayRendererInternals::GenerateSphere()
     785             : {
     786           0 :     if (sphereVertexes.empty())
     787           0 :         TessellateSphere(sphereVertexes, sphereIndexes, 3);
     788           0 : }
     789             : 
     790           0 : void OverlayRenderer::RenderSphereOverlays(
     791             :     Renderer::Backend::IDeviceCommandContext* deviceCommandContext)
     792             : {
     793           0 :     PROFILE3_GPU("overlays (spheres)");
     794             : 
     795           0 :     if (m->spheres.empty() || m->shaderOverlaySolid.technique)
     796           0 :         return;
     797             : 
     798           0 :     const CShaderTechniquePtr& shaderTechnique = m->shaderOverlaySolid.GetTechnique();
     799           0 :     deviceCommandContext->SetGraphicsPipelineState(
     800           0 :         shaderTechnique->GetGraphicsPipelineState());
     801           0 :     deviceCommandContext->BeginPass();
     802             : 
     803           0 :     Renderer::Backend::IShaderProgram* shader = shaderTechnique->GetShader();
     804             : 
     805             :     const CMatrix3D transform =
     806           0 :         g_Renderer.GetSceneRenderer().GetViewCamera().GetViewProjection();
     807           0 :     deviceCommandContext->SetUniform(
     808           0 :         shader->GetBindingSlot(str_transform), transform.AsFloatArray());
     809             : 
     810           0 :     m->GenerateSphere();
     811             : 
     812           0 :     deviceCommandContext->SetVertexInputLayout(m->sphereVertexInputLayout);
     813             : 
     814           0 :     deviceCommandContext->SetVertexBufferData(
     815           0 :         0, m->sphereVertexes.data(), m->sphereVertexes.size() * sizeof(m->sphereVertexes[0]));
     816           0 :     deviceCommandContext->SetIndexBufferData(
     817           0 :         m->sphereIndexes.data(), m->sphereIndexes.size() * sizeof(m->sphereIndexes[0]));
     818             : 
     819           0 :     for (const SOverlaySphere* sphere : m->spheres)
     820             :     {
     821             :         const CVector4D instancingTransform{
     822           0 :             sphere->m_Center.X, sphere->m_Center.Y, sphere->m_Center.Z, sphere->m_Radius};
     823             : 
     824           0 :         deviceCommandContext->SetUniform(
     825           0 :             shader->GetBindingSlot(str_instancingTransform),
     826           0 :             instancingTransform.AsFloatArray());
     827             : 
     828           0 :         deviceCommandContext->SetUniform(
     829           0 :             shader->GetBindingSlot(str_color), sphere->m_Color.AsFloatArray());
     830             : 
     831           0 :         deviceCommandContext->DrawIndexed(0, m->sphereIndexes.size(), 0);
     832             : 
     833           0 :         g_Renderer.GetStats().m_DrawCalls++;
     834           0 :         g_Renderer.GetStats().m_OverlayTris = m->sphereIndexes.size()/3;
     835             :     }
     836             : 
     837           0 :     deviceCommandContext->EndPass();
     838           3 : }

Generated by: LCOV version 1.13