Archived
1
Fork 0
This repository has been archived on 2025-04-12. You can view files and clone it, but cannot push or open issues or pull requests.
prism/engine/renderer/src/imguipass.cpp

237 lines
9.7 KiB
C++
Executable file

#include "imguipass.hpp"
#include <imgui.h>
#include "engine.hpp"
#include "gfx.hpp"
#include "gfx_commandbuffer.hpp"
#include "log.hpp"
#include "assertions.hpp"
#include "renderer.hpp"
void ImGuiPass::initialize() {
ImGuiIO& io = ImGui::GetIO();
io.BackendRendererName = "GFX";
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset;
io.BackendFlags |= ImGuiBackendFlags_RendererHasViewports;
load_font("OpenSans-Regular.ttf");
create_font_texture();
}
void ImGuiPass::create_render_target_resources(RenderTarget& target) {
if(pipeline == nullptr) {
GFXGraphicsPipelineCreateInfo createInfo;
createInfo.label = "ImGui";
createInfo.shaders.vertex_src = ShaderSource(prism::path("imgui.vert"));
createInfo.shaders.fragment_src = ShaderSource(prism::path("imgui.frag"));
GFXVertexInput vertexInput = {};
vertexInput.stride = sizeof(ImDrawVert);
createInfo.vertex_input.inputs.push_back(vertexInput);
GFXVertexAttribute positionAttribute = {};
positionAttribute.format = GFXVertexFormat::FLOAT2;
positionAttribute.offset = offsetof(ImDrawVert, pos);
createInfo.vertex_input.attributes.push_back(positionAttribute);
GFXVertexAttribute uvAttribute = {};
uvAttribute.location = 1;
uvAttribute.format = GFXVertexFormat::FLOAT2;
uvAttribute.offset = offsetof(ImDrawVert, uv);
createInfo.vertex_input.attributes.push_back(uvAttribute);
GFXVertexAttribute colAttribute = {};
colAttribute.location = 2;
colAttribute.format = GFXVertexFormat::UNORM4;
colAttribute.offset = offsetof(ImDrawVert, col);
createInfo.vertex_input.attributes.push_back(colAttribute);
createInfo.blending.enable_blending = true;
createInfo.blending.src_rgb = GFXBlendFactor::SrcAlpha;
createInfo.blending.src_alpha= GFXBlendFactor::SrcAlpha;
createInfo.blending.dst_rgb = GFXBlendFactor::OneMinusSrcAlpha;
createInfo.blending.dst_alpha = GFXBlendFactor::OneMinusSrcAlpha;
createInfo.shader_input.push_constants = {
{sizeof(Matrix4x4), 0}
};
createInfo.shader_input.bindings = {
{1, GFXBindingType::PushConstant},
{2, GFXBindingType::Texture}
};
pipeline = engine->get_gfx()->create_graphics_pipeline(createInfo);
}
}
void ImGuiPass::render_post(GFXCommandBuffer* command_buffer, RenderTarget& target, const platform::window_ptr index) {
ImDrawData* draw_data = nullptr;
if(!(ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable)) {
draw_data = ImGui::GetDrawData();
} else {
auto viewport = ImGui::FindViewportByPlatformHandle(index);
if(viewport != nullptr)
draw_data = viewport->DrawData;
}
if(draw_data == nullptr)
return;
const int framebuffer_width = static_cast<int>(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
const int framebuffer_height = static_cast<int>(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
if (framebuffer_width <= 0 || framebuffer_height <= 0)
return;
command_buffer->set_graphics_pipeline(pipeline);
if(draw_data->TotalVtxCount > 0)
update_buffers(target, *draw_data);
const Matrix4x4 projection = prism::orthographic(draw_data->DisplayPos.x,
draw_data->DisplayPos.x + draw_data->DisplaySize.x,
draw_data->DisplayPos.y + draw_data->DisplaySize.y,
draw_data->DisplayPos.y,
0.0f,
1.0f);
command_buffer->set_push_constant(&projection, sizeof(Matrix4x4));
const ImVec2 clip_offset = draw_data->DisplayPos;
const ImVec2 clip_scale = draw_data->FramebufferScale;
int vertex_offset = 0;
int index_offset = 0;
for(int n = 0; n < draw_data->CmdListsCount; n++) {
const ImDrawList* cmd_list = draw_data->CmdLists[n];
for(int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) {
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if(pcmd->TextureId != nullptr)
command_buffer->bind_texture((GFXTexture*)pcmd->TextureId, 2);
if(pcmd->UserCallback != nullptr) {
pcmd->UserCallback(cmd_list, pcmd);
} else {
ImVec2 clip_min((pcmd->ClipRect.x - clip_offset.x) * clip_scale.x, (pcmd->ClipRect.y - clip_offset.y) * clip_scale.y);
ImVec2 clip_max((pcmd->ClipRect.z - clip_offset.x) * clip_scale.x, (pcmd->ClipRect.w - clip_offset.y) * clip_scale.y);
// Clamp to viewport as vkCmdSetScissor() won't accept values that are off bounds
if (clip_min.x < 0.0f) { clip_min.x = 0.0f; }
if (clip_min.y < 0.0f) { clip_min.y = 0.0f; }
if (clip_max.x > framebuffer_width) { clip_max.x = (float)framebuffer_width; }
if (clip_max.y > framebuffer_height) { clip_max.y = (float)framebuffer_height; }
if (clip_max.x < clip_min.x || clip_max.y < clip_min.y)
continue;
// Apply scissor/clipping rectangle
prism::Rectangle scissor;
scissor.offset.x = (int32_t)(clip_min.x);
scissor.offset.y = (int32_t)(clip_min.y);
scissor.extent.width = (uint32_t)(clip_max.x - clip_min.x);
scissor.extent.height = (uint32_t)(clip_max.y - clip_min.y);
command_buffer->set_scissor(scissor);
command_buffer->set_vertex_buffer(target.vertex_buffer[target.current_frame], 0, 0);
command_buffer->set_index_buffer(target.index_buffer[target.current_frame], sizeof(ImDrawIdx) == 2 ? IndexType::UINT16 : IndexType::UINT32);
command_buffer->draw_indexed(pcmd->ElemCount,
(index_offset + pcmd->IdxOffset),
(vertex_offset + pcmd->VtxOffset), 0);
}
}
index_offset += cmd_list->IdxBuffer.Size;
vertex_offset += cmd_list->VtxBuffer.Size;
}
}
void ImGuiPass::load_font(const std::string_view filename) {
Expects(!filename.empty());
ImGuiIO& io = ImGui::GetIO();
if(io.Fonts->Fonts.empty()) {
auto file = prism::open_file(prism::app_domain / filename);
if(file != std::nullopt) {
font_file = std::make_unique<prism::file>(std::move(file.value()));
font_file->read_all();
io.Fonts->AddFontFromMemoryTTF(font_file->cast_data<unsigned char>(), font_file->size(), 15.0f * platform::get_monitor_dpi());
ImGui::GetIO().FontGlobalScale = 1.0f / platform::get_monitor_dpi();
} else {
prism::log("Failed to load font file for imgui!");
return;
}
}
}
void ImGuiPass::create_font_texture() {
ImGuiIO& io = ImGui::GetIO();
unsigned char* pixels = nullptr;
int width = -1, height = -1;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
GFXTextureCreateInfo createInfo = {};
createInfo.label = "ImGui Font";
createInfo.width = width;
createInfo.height = height;
createInfo.format = GFXPixelFormat::RGBA8_UNORM;
createInfo.usage = GFXTextureUsage::Sampled | GFXTextureUsage::TransferDst;
font_texture = engine->get_gfx()->create_texture(createInfo);
engine->get_gfx()->copy_texture(font_texture, pixels, width * height * 4);
io.Fonts->TexID = reinterpret_cast<void*>(font_texture);
}
void ImGuiPass::update_buffers(RenderTarget& target, const ImDrawData& draw_data) {
const int new_vertex_size = draw_data.TotalVtxCount * sizeof(ImDrawVert);
const int new_index_size = draw_data.TotalIdxCount * sizeof(ImDrawIdx);
Expects(new_vertex_size > 0);
Expects(new_index_size > 0);
if(target.vertex_buffer[target.current_frame] == nullptr || target.current_vertex_size[target.current_frame] < new_vertex_size) {
target.vertex_buffer[target.current_frame] = engine->get_gfx()->create_buffer(nullptr, new_vertex_size, true, GFXBufferUsage::Vertex);
target.current_vertex_size[target.current_frame] = new_vertex_size;
}
if(target.index_buffer[target.current_frame] == nullptr || target.current_index_size[target.current_frame] < new_index_size) {
target.index_buffer[target.current_frame] = engine->get_gfx()->create_buffer(nullptr, new_index_size, true, GFXBufferUsage::Index);
target.current_index_size[target.current_frame] = new_index_size;
}
// todo: dont map every frame
auto vtx_map = engine->get_gfx()->get_buffer_contents(target.vertex_buffer[target.current_frame]);
auto idx_map = engine->get_gfx()->get_buffer_contents(target.index_buffer[target.current_frame]);
if(vtx_map == nullptr || idx_map == nullptr)
return;
auto vtx_dst = (ImDrawVert*)vtx_map;
auto idx_dst = (ImDrawIdx*)idx_map;
for(int i = 0; i < draw_data.CmdListsCount; i++) {
const ImDrawList* cmd_list = draw_data.CmdLists[i];
memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert));
memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx));
vtx_dst += cmd_list->VtxBuffer.Size;
idx_dst += cmd_list->IdxBuffer.Size;
}
engine->get_gfx()->release_buffer_contents(target.vertex_buffer[target.current_frame], vtx_map);
engine->get_gfx()->release_buffer_contents(target.index_buffer[target.current_frame], idx_map);
}