Program Listing for File debugrenderstage.cpp

Return to documentation for file (/home/runner/work/Legion-Engine/Legion-Engine/legion/engine/rendering/pipeline/default/stages/debugrenderstage.cpp)

#include <rendering/pipeline/default/stages/debugrenderstage.hpp>

namespace legion::rendering
{
    async::spinlock DebugRenderStage::debugLinesLock;
    thread_local std::unordered_set<debug::debug_line_event>* DebugRenderStage::localLines;
    std::unordered_map<std::thread::id, std::unordered_set<debug::debug_line_event>*> DebugRenderStage::debugLines;

    void DebugRenderStage::startDebugDomain()
    {
        if (!localLines)
            localLines = new std::unordered_set<debug::debug_line_event>();
    }

    void DebugRenderStage::endDebugDomain()
    {
        if (!localLines) return;
            //localLines = new std::unordered_set<debug::debug_line_event>();*/
        size_type size = localLines->size();

        if (size == 0)
            return;

        std::thread::id id = std::this_thread::get_id();

        {
            std::lock_guard guard(debugLinesLock);

            if (debugLines[id])
            {
                for (auto& line : *(debugLines[id]))
                {
                    if (line.time > 0 && !localLines->count(line))
                        localLines->insert(line);
                }

                delete debugLines[id];
            }

            debugLines[id] = localLines;
            localLines = nullptr;
        }

        localLines = new std::unordered_set<debug::debug_line_event>();
        localLines->reserve(size);
    }

    void DebugRenderStage::drawDebugLine(events::event_base* event)
    {
        debug::debug_line_event* line = reinterpret_cast<debug::debug_line_event*>(event);
        if (localLines->count(*line))
            localLines->erase(*line);
        localLines->insert(*line);
    }

    void DebugRenderStage::setup(app::window& context)
    {
        scheduling::ProcessChain::subscribeToChainStart<&DebugRenderStage::startDebugDomain>();
        scheduling::ProcessChain::subscribeToChainEnd<&DebugRenderStage::endDebugDomain>();
        m_eventBus->bindToEventUnsafe(nameHash("debug_line"), delegate<void(events::event_base*)>::template create<DebugRenderStage, &DebugRenderStage::drawDebugLine>(this));
    }

    void DebugRenderStage::render(app::window& context, camera& cam, const camera::camera_input& camInput, time::span deltaTime)
    {
        using namespace legion::core::fs::literals;

        std::vector<debug::debug_line_event> lines;

        {
            std::lock_guard guard(debugLinesLock);
            if (debugLines.size() == 0)
                return;

            std::vector<debug::debug_line_event> toRemove;
            for (auto& [threadId, domain] : debugLines)
            {
                lines.insert(lines.end(), domain->begin(), domain->end());

                for (auto& line : (*domain))
                {
                    if (line.time == 0)
                        continue;

                    line.timeBuffer += deltaTime;

                    if (line.timeBuffer >= line.time)
                        toRemove.push_back(line);
                }

                for (auto line : toRemove)
                    domain->erase(line);
            }
        }

        static id_type mainId = nameHash("main");
        auto fbo = getFramebuffer(mainId);
        if (!fbo)
        {
            log::error("Main frame buffer is missing.");
            abort();
            return;
        }

        app::context_guard guard(context);
        if (!guard.contextIsValid())
        {
            abort();
            return;
        }

        static material_handle debugMaterial = MaterialCache::create_material("debug", "assets://shaders/debug.shs"_view);
        static app::gl_id vertexBuffer = -1;
        static size_type vertexBufferSize = 0;
        static app::gl_id colorBuffer = -1;
        static size_type colorBufferSize = 0;
        static app::gl_id ignoreDepthBuffer = -1;
        static size_type ignoreDepthBufferSize = 0;
        static app::gl_id vao = -1;

        if (debugMaterial == invalid_material_handle)
            return;

        if (vertexBuffer == -1)
            glGenBuffers(1, &vertexBuffer);

        if (colorBuffer == -1)
            glGenBuffers(1, &colorBuffer);

        if (ignoreDepthBuffer == -1)
            glGenBuffers(1, &ignoreDepthBuffer);

        if (vao == -1)
            glGenVertexArrays(1, &vao);

        static std::unordered_map<float, std::tuple<std::vector<uint>, std::vector<math::color>, std::vector<math::vec3>>> lineBatches;
        for (auto& [width, data] : lineBatches)
        {
            auto& [ignoreDepths, colors, vertices] = data;
            ignoreDepths.clear();
            colors.clear();
            vertices.clear();
        }

        for (auto& line : lines)
        {
            auto& [ignoreDepths, colors, vertices] = lineBatches[line.width];
            ignoreDepths.push_back(line.ignoreDepth);
            ignoreDepths.push_back(line.ignoreDepth);
            colors.push_back(line.color);
            colors.push_back(line.color);
            vertices.push_back(line.start);
            vertices.push_back(line.end);
        }

        auto [valid, message] = fbo->verify();
        if (!valid)
        {
            log::error("Main frame buffer isn't complete: {}", message);
            abort();
            return;
        }

        fbo->bind();

        debugMaterial.bind();

        glEnable(GL_LINE_SMOOTH);
        glBindVertexArray(vao);

        for (auto& [width, lineData] : lineBatches)
        {
            auto& [ignoreDepths, colors, vertices] = lineData;

            glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);

            size_type vertexCount = vertices.size();
            if (vertexCount > vertexBufferSize)
            {
                glBufferData(GL_ARRAY_BUFFER, vertexCount * sizeof(math::vec3), 0, GL_DYNAMIC_DRAW);
                vertexBufferSize = vertexCount;
            }

            glBufferSubData(GL_ARRAY_BUFFER, 0, vertexCount * sizeof(math::vec3), vertices.data());
            glEnableVertexAttribArray(SV_POSITION);
            glVertexAttribPointer(SV_POSITION, 3, GL_FLOAT, GL_FALSE, 0, 0);

            glBindBuffer(GL_ARRAY_BUFFER, colorBuffer);

            size_type colorCount = colors.size();
            if (colorCount > colorBufferSize)
            {
                glBufferData(GL_ARRAY_BUFFER, colorCount * sizeof(math::color), 0, GL_DYNAMIC_DRAW);
                colorBufferSize = colorCount;
            }

            glBufferSubData(GL_ARRAY_BUFFER, 0, colorCount * sizeof(math::color), colors.data());

            auto colorAttrib = debugMaterial.get_attribute("color");

            if (colorAttrib == invalid_attribute)
            {
                glBindBuffer(GL_ARRAY_BUFFER, 0);
                break;
            }

            colorAttrib.set_attribute_pointer(4, GL_FLOAT, GL_FALSE, 0, 0);

            glBindBuffer(GL_ARRAY_BUFFER, ignoreDepthBuffer);

            size_type ignoreDepthCount = ignoreDepths.size();
            if (ignoreDepthCount > ignoreDepthBufferSize)
            {
                glBufferData(GL_ARRAY_BUFFER, ignoreDepthCount * sizeof(uint), 0, GL_DYNAMIC_DRAW);
                ignoreDepthBufferSize = ignoreDepthCount;
            }

            glBufferSubData(GL_ARRAY_BUFFER, 0, ignoreDepthCount * sizeof(uint), ignoreDepths.data());

            auto ignoreDepthAttrib = debugMaterial.get_attribute("ignoreDepth");

            if (ignoreDepthAttrib == invalid_attribute)
            {
                glBindBuffer(GL_ARRAY_BUFFER, 0);
                break;
            }

            ignoreDepthAttrib.set_attribute_pointer(1, GL_UNSIGNED_INT, GL_FALSE, 0, 0);

            glUniformMatrix4fv(SV_VIEW, 1, false, math::value_ptr(camInput.view));
            glUniformMatrix4fv(SV_PROJECT, 1, false, math::value_ptr(camInput.proj));

            glLineWidth(width + 1);

            glDrawArraysInstanced(GL_LINES, 0, vertices.size(), colors.size());

            ignoreDepthAttrib.disable_attribute_pointer();
            colorAttrib.disable_attribute_pointer();
            glBindBuffer(GL_ARRAY_BUFFER, 0);
        }

        glBindVertexArray(0);

        glDisable(GL_LINE_SMOOTH);

        debugMaterial.release();

        fbo->release();
    }

    priority_type DebugRenderStage::priority()
    {
        return post_fx_priority + 1;
    }

}