targets.cpp

cross platform rendering playground

src/editor/targets.cpp

5.4 KB
#ifdef ENABLE_EDITOR
#define IMGUI_DEFINE_MATH_OPERATORS
#include "editor.h"

#include <imgui.h>

#include <cstring>
#include <string>
#include <vector>

#include "quick_submit.h"
#include "renderer.h"

namespace editor {

const char *format_name(rhi::ImageFormat fmt) {
    switch (fmt) {
    case rhi::ImageFormat::R8_UNORM:
        return "R8_UNORM";
    case rhi::ImageFormat::RGBA8_UNORM:
        return "RGBA8_UNORM";
    case rhi::ImageFormat::RGBA8_SRGB:
        return "RGBA8_SRGB";
    case rhi::ImageFormat::BGRA8_UNORM:
        return "BGRA8_UNORM";
    case rhi::ImageFormat::D32_FLOAT:
        return "D32_FLOAT";
    case rhi::ImageFormat::RGBA16_FLOAT:
        return "RGBA16_FLOAT";
    case rhi::ImageFormat::RGBA32_FLOAT:
        return "RGBA32_FLOAT";
    case rhi::ImageFormat::BC7_UNORM:
        return "BC7_UNORM";
    case rhi::ImageFormat::BC7_SRGB:
        return "BC7_SRGB";
    case rhi::ImageFormat::ASTC6X6_UNORM:
        return "ASTC6X6_UNORM";
    case rhi::ImageFormat::ASTC6X6_SRGB:
        return "ASTC6X6_SRGB";
    case rhi::ImageFormat::R16_FLOAT:
        return "R16_FLOAT";
    case rhi::ImageFormat::R32_FLOAT:
        return "R32_FLOAT";
    case rhi::ImageFormat::R10G10B10A2_UNORM:
        return "R10G10B10A2_UNORM";
    case rhi::ImageFormat::RG16_FLOAT:
        return "RG16_FLOAT";
    default:
        return "Other";
    }
}

struct SourceEntry {
    std::string name;
    std::string format_name;
    rhi::Image *img = nullptr;
    // false = render target (display / ddgi / cascades), true = scene texture.
    bool is_texture = false;
};

// persists across frames; re-resolved against the catalog each frame so
// pointers stay valid across scene reloads.
std::vector<SourceEntry> PINNED;
bool AUTO_PINNED = false;

bool is_pinned(const std::string &name) {
    for (const auto &p : PINNED) {
        if (p.name == name) {
            return true;
        }
    }
    return false;
}

void build_catalog(RenderData &rd, std::vector<SourceEntry> &out) {
    out.clear();
    out.push_back({"Main Color", format_name(rd.display.desc.format), &rd.display, false});
    out.push_back({"DDGI Irradiance", format_name(rd.irr_atlas.desc.format), &rd.irr_atlas, false});
    out.push_back({"DDGI Distance", format_name(rd.dis_atlas.desc.format), &rd.dis_atlas, false});

    for (u32 i = 0; i < Light::CASCADE_COUNT; ++i) {
        std::string name = "Shadow Cascade " + std::to_string(i);
        out.push_back({name, format_name(rd.cascade[i].desc.format), &rd.cascade[i], false});
    }

    // Scene textures (deduped by GPU handle; multiple materials may share a load).
    u32 tex_idx = 0;
    for (auto &[img, view] : rd.loaded_textures) {
        (void)view;
        if (!rhi::valid(img)) {
            continue;
        }
        out.push_back({"Tex " + std::to_string(tex_idx), format_name(img.desc.format), &img, true});
        ++tex_idx;
    }
}

void draw_texture_viewer_window(rhi::Device &device, Editor &editor, RenderData &rd) {
    if (!ImGui::Begin("Texture Viewer", nullptr, PANEL_FLAGS)) {
        ImGui::End();
        return;
    }

    std::vector<SourceEntry> sources;
    build_catalog(rd, sources);

    if (!AUTO_PINNED && !sources.empty()) {
        AUTO_PINNED = true;
        PINNED.push_back(sources[0]);
    }

    for (auto &p : PINNED) {
        const SourceEntry *match = nullptr;
        for (const auto &e : sources) {
            if (e.name == p.name) {
                match = &e;
                break;
            }
        }
        p.img = match ? match->img : nullptr;
        p.format_name = match ? match->format_name : std::string();
    }

    ImGui::TextUnformatted("Sources:");
    ImGui::SameLine();
    ImGui::SetNextItemWidth(150);
    if (ImGui::BeginCombo("##add_source", "Add...")) {
        const char *last_section = nullptr;
        for (const auto &e : sources) {
            if (is_pinned(e.name)) {
                continue;
            }
            const char *section = e.is_texture ? "Scene Textures" : "Render Targets";
            if (last_section == nullptr || strcmp(last_section, section) != 0) {
                last_section = section;
                ImGui::TextUnformatted(section);
            }
            if (ImGui::Selectable(e.name.c_str())) {
                PINNED.push_back(e);
            }
        }
        ImGui::EndCombo();
    }

    std::vector<usize> to_remove;
    if (ImGui::BeginTabBar("##tv_tabs", ImGuiTabBarFlags_None)) {
        for (usize i = 0; i < PINNED.size(); ++i) {
            SourceEntry &pin = PINNED[i];
            bool open_tab = true;
            if (ImGui::BeginTabItem(pin.name.c_str(), &open_tab)) {
                if (pin.img && rhi::valid(*pin.img)) {
                    TextureViewerDesc tv{};
                    tv.image = pin.img;
                    tv.sampler = &editor.viewport_sampler;
                    tv.name = pin.name.c_str();
                    tv.format_name = pin.format_name.c_str();
                    draw_texture_inspector(device, *editor.qs, pin.name.c_str(), tv);
                } else {
                    ImGui::TextUnformatted("Source unavailable");
                }
                ImGui::EndTabItem();
            }
            if (!open_tab) {
                to_remove.push_back(i);
            }
        }
        ImGui::EndTabBar();
    }

    for (usize i = to_remove.size(); i-- > 0;) {
        PINNED.erase(PINNED.begin() + (i32)to_remove[i]);
    }

    ImGui::End();
}

} // namespace editor

#endif // ENABLE_EDITOR