diff --git a/src/engine/engine.cc b/src/engine/engine.cc index d760062..ee36413 100644 --- a/src/engine/engine.cc +++ b/src/engine/engine.cc @@ -102,6 +102,8 @@ void Engine::Initialize() { thread_pool_.Initialize(); + imgui_backend_.Initialize(IsMobile(), GetRootPath()); + platform_->CreateMainWindow(); CreateRendererInternal(RendererType::kVulkan); @@ -119,8 +121,6 @@ void Engine::Initialize() { CreateRenderResources(); WaitForAsyncWork(); - imgui_backend_.Initialize(); - CHECK(game_->Initialize()) << "Failed to initialize the game."; } diff --git a/src/engine/imgui_backend.cc b/src/engine/imgui_backend.cc index c70939e..37fb18e 100644 --- a/src/engine/imgui_backend.cc +++ b/src/engine/imgui_backend.cc @@ -1,14 +1,10 @@ #include "engine/imgui_backend.h" #include "base/log.h" -#include "engine/asset/image.h" #include "engine/asset/shader_source.h" -#include "engine/engine.h" #include "engine/input_event.h" #include "engine/platform/asset_file.h" #include "engine/renderer/renderer.h" -#include "engine/renderer/shader.h" -#include "engine/renderer/texture.h" #include "third_party/imgui/imgui.h" using namespace base; @@ -19,11 +15,11 @@ namespace { const char vertex_description[] = "p2f;t2f;c4b"; } // namespace -ImguiBackend::ImguiBackend() : shader_{std::make_unique(nullptr)} {} +ImguiBackend::ImguiBackend() = default; ImguiBackend::~ImguiBackend() = default; -void ImguiBackend::Initialize() { +void ImguiBackend::Initialize(bool is_mobile, std::string root_path) { IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGui::GetIO().IniFilename = nullptr; @@ -33,12 +29,11 @@ void ImguiBackend::Initialize() { size_t buffer_size = 0; auto buffer = AssetFile::ReadWholeFile("engine/RobotoMono-Regular.ttf", - Engine::Get().GetRootPath().c_str(), - &buffer_size); + root_path.c_str(), &buffer_size); if (buffer) { ImFontConfig font_cfg = ImFontConfig(); font_cfg.FontDataOwnedByAtlas = false; - float size_pixels = Engine::Get().IsMobile() ? 64 : 32; + float size_pixels = is_mobile ? 64 : 32; ImGui::GetIO().Fonts->AddFontFromMemoryTTF(buffer.get(), (int)buffer_size, size_pixels, &font_cfg); ImGui::GetIO().Fonts->Build(); @@ -48,54 +43,45 @@ void ImguiBackend::Initialize() { // Arbitrary scale-up for mobile devices. // TODO: Put some effort into DPI awareness. - if (Engine::Get().IsMobile()) + if (is_mobile) ImGui::GetStyle().ScaleAllSizes(2.0f); - - Engine::Get().SetImageSource( - "imgui_atlas", - []() -> std::unique_ptr { - unsigned char* pixels; - int width, height; - ImGui::GetIO().Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); - LOG(0) << "Font atlas size: " << width << ", " << height; - auto image = std::make_unique(); - image->Create(width, height); - memcpy(image->GetBuffer(), pixels, width * height * 4); - return image; - }, - true); - Engine::Get().RefreshImage("imgui_atlas"); - ImGui::GetIO().Fonts->SetTexID( - (ImTextureID)(intptr_t)Engine::Get().AcquireTexture("imgui_atlas")); } void ImguiBackend::Shutdown() { + ImGui::DestroyContext(); for (auto& id : geometries_) renderer_->DestroyGeometry(id); geometries_.clear(); - ImGui::DestroyContext(); - shader_.reset(); + renderer_->DestroyTexture(font_atlas_); + renderer_->DestroyShader(shader_); } void ImguiBackend::CreateRenderResources(Renderer* renderer) { renderer_ = renderer; - shader_->SetRenderer(renderer); - geometries_.clear(); + + // Avoid flickering by using the geometries form the last frame if available. if (ImGui::GetCurrentContext() && ImGui::GetDrawData()) Render(); + // Create the shader. auto source = std::make_unique(); if (source->Load("engine/imgui.glsl")) { - VertexDescription vd; - if (!ParseVertexDescription(vertex_description, vd)) { - DLOG(0) << "Failed to parse vertex description."; - } else { - shader_->Create(std::move(source), vd, kPrimitive_Triangles, false); - } + shader_ = renderer_->CreateShader(std::move(source), vertex_description_, + kPrimitive_Triangles, false); } else { LOG(0) << "Could not create imgui shader."; } + + // Create a texture for the font atlas. + unsigned char* pixels; + int width, height; + ImGui::GetIO().Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); + LOG(0) << "Font atlas size: " << width << ", " << height; + font_atlas_ = renderer_->CreateTexture(); + renderer_->UpdateTexture(font_atlas_, width, height, ImageFormat::kRGBA32, + width * height * 4, pixels); + ImGui::GetIO().Fonts->SetTexID((ImTextureID)(intptr_t)font_atlas_); } std::unique_ptr ImguiBackend::OnInputEvent( @@ -133,6 +119,7 @@ void ImguiBackend::NewFrame(float delta_time) { void ImguiBackend::Render() { ImGui::Render(); + // Create a geometry for each draw list and upload the vertex data. ImDrawData* draw_data = ImGui::GetDrawData(); if ((int)geometries_.size() < draw_data->CmdListsCount) geometries_.resize(draw_data->CmdListsCount, 0); @@ -160,9 +147,9 @@ void ImguiBackend::Draw() { draw_data->DisplayPos.x + draw_data->DisplaySize.x, draw_data->DisplayPos.y + draw_data->DisplaySize.y, draw_data->DisplayPos.y); - shader_->Activate(); - shader_->SetUniform("projection", proj); - shader_->UploadUniforms(); + renderer_->ActivateShader(shader_); + renderer_->SetUniform(shader_, "projection", proj); + renderer_->UploadUniforms(shader_); for (int n = 0; n < draw_data->CmdListsCount; n++) { const ImDrawList* cmd_list = draw_data->CmdLists[n]; @@ -171,7 +158,8 @@ void ImguiBackend::Draw() { if (pcmd->ClipRect.z <= pcmd->ClipRect.x || pcmd->ClipRect.w <= pcmd->ClipRect.y) continue; - reinterpret_cast(pcmd->GetTexID())->Activate(0); + auto texture_id = reinterpret_cast(pcmd->GetTexID()); + renderer_->ActivateTexture(texture_id, 0); renderer_->SetScissor(int(pcmd->ClipRect.x), int(pcmd->ClipRect.y), int(pcmd->ClipRect.z - pcmd->ClipRect.x), int(pcmd->ClipRect.w - pcmd->ClipRect.y)); diff --git a/src/engine/imgui_backend.h b/src/engine/imgui_backend.h index 651f0b5..00a89cd 100644 --- a/src/engine/imgui_backend.h +++ b/src/engine/imgui_backend.h @@ -9,7 +9,6 @@ namespace eng { class InputEvent; -class Shader; class Renderer; class ImguiBackend { @@ -17,7 +16,7 @@ class ImguiBackend { ImguiBackend(); ~ImguiBackend(); - void Initialize(); + void Initialize(bool is_mobile, std::string root_path); void Shutdown(); void CreateRenderResources(Renderer* renderer); @@ -31,7 +30,8 @@ class ImguiBackend { private: VertexDescription vertex_description_; std::vector geometries_; - std::unique_ptr shader_; + uint64_t shader_ = 0; + uint64_t font_atlas_ = 0; Renderer* renderer_ = nullptr; }; diff --git a/src/engine/renderer/opengl/renderer_opengl.cc b/src/engine/renderer/opengl/renderer_opengl.cc index 5a55b90..9693f2b 100644 --- a/src/engine/renderer/opengl/renderer_opengl.cc +++ b/src/engine/renderer/opengl/renderer_opengl.cc @@ -251,56 +251,63 @@ uint64_t RendererOpenGL::CreateTexture() { void RendererOpenGL::UpdateTexture(uint64_t resource_id, std::unique_ptr image) { + UpdateTexture(resource_id, image->GetWidth(), image->GetHeight(), + image->GetFormat(), image->GetSize(), image->GetBuffer()); +} + +void RendererOpenGL::UpdateTexture(uint64_t resource_id, + int width, + int height, + ImageFormat format, + size_t data_size, + uint8_t* image_data) { auto it = textures_.find(resource_id); if (it == textures_.end()) return; glBindTexture(GL_TEXTURE_2D, it->second); - if (image->IsCompressed()) { - GLenum format = 0; - switch (image->GetFormat()) { + if (IsCompressedFormat(format)) { + GLenum gl_format = 0; + switch (format) { case ImageFormat::kDXT1: - format = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; + gl_format = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break; case ImageFormat::kDXT5: - format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + gl_format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break; case ImageFormat::kETC1: - format = GL_ETC1_RGB8_OES; + gl_format = GL_ETC1_RGB8_OES; break; #if defined(__ANDROID__) case ImageFormat::kATC: - format = GL_ATC_RGB_AMD; + gl_format = GL_ATC_RGB_AMD; break; case ImageFormat::kATCIA: - format = GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD; + gl_format = GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD; break; #endif default: - NOTREACHED() << "- Unhandled texure format: " - << ImageFormatToString(image->GetFormat()); + NOTREACHED() << "- Unhandled texture format: " + << ImageFormatToString(format); } - glCompressedTexImage2D(GL_TEXTURE_2D, 0, format, image->GetWidth(), - image->GetHeight(), 0, image->GetSize(), - image->GetBuffer()); + glCompressedTexImage2D(GL_TEXTURE_2D, 0, gl_format, width, height, 0, + data_size, image_data); // On some devices the first glCompressedTexImage2D call after context-lost // returns GL_INVALID_VALUE for some reason. GLenum err = glGetError(); if (err == GL_INVALID_VALUE) { - glCompressedTexImage2D(GL_TEXTURE_2D, 0, format, image->GetWidth(), - image->GetHeight(), 0, image->GetSize(), - image->GetBuffer()); + glCompressedTexImage2D(GL_TEXTURE_2D, 0, gl_format, width, height, 0, + data_size, image_data); err = glGetError(); } if (err != GL_NO_ERROR) LOG(0) << "GL ERROR after glCompressedTexImage2D: " << (int)err; } else { - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image->GetWidth(), - image->GetHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, - image->GetBuffer()); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, + GL_UNSIGNED_BYTE, image_data); } } diff --git a/src/engine/renderer/opengl/renderer_opengl.h b/src/engine/renderer/opengl/renderer_opengl.h index 18a9e48..8d2ee61 100644 --- a/src/engine/renderer/opengl/renderer_opengl.h +++ b/src/engine/renderer/opengl/renderer_opengl.h @@ -55,6 +55,12 @@ class RendererOpenGL final : public Renderer { uint64_t CreateTexture() final; void UpdateTexture(uint64_t resource_id, std::unique_ptr image) final; + void UpdateTexture(uint64_t resource_id, + int width, + int height, + ImageFormat format, + size_t data_size, + uint8_t* image_data) final; void DestroyTexture(uint64_t resource_id) final; void ActivateTexture(uint64_t resource_id, uint64_t texture_unit) final; diff --git a/src/engine/renderer/renderer.h b/src/engine/renderer/renderer.h index f42a69e..3959219 100644 --- a/src/engine/renderer/renderer.h +++ b/src/engine/renderer/renderer.h @@ -63,6 +63,12 @@ class Renderer { virtual uint64_t CreateTexture() = 0; virtual void UpdateTexture(uint64_t resource_id, std::unique_ptr image) = 0; + virtual void UpdateTexture(uint64_t resource_id, + int width, + int height, + ImageFormat format, + size_t data_size, + uint8_t* image_data) = 0; virtual void DestroyTexture(uint64_t resource_id) = 0; virtual void ActivateTexture(uint64_t resource_id, uint64_t texture_unit) = 0; diff --git a/src/engine/renderer/vulkan/renderer_vulkan.cc b/src/engine/renderer/vulkan/renderer_vulkan.cc index d3c1fd5..4d3922f 100644 --- a/src/engine/renderer/vulkan/renderer_vulkan.cc +++ b/src/engine/renderer/vulkan/renderer_vulkan.cc @@ -558,16 +558,27 @@ uint64_t RendererVulkan::CreateTexture() { void RendererVulkan::UpdateTexture(uint64_t resource_id, std::unique_ptr image) { + UpdateTexture(resource_id, image->GetWidth(), image->GetHeight(), + image->GetFormat(), image->GetSize(), image->GetBuffer()); + task_runner_.Delete(HERE, std::move(image)); + semaphore_.release(); +} + +void RendererVulkan::UpdateTexture(uint64_t resource_id, + int width, + int height, + ImageFormat format, + size_t data_size, + uint8_t* image_data) { auto it = textures_.find(resource_id); if (it == textures_.end()) return; VkImageLayout old_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - VkFormat format = GetImageFormat(image->GetFormat()); + VkFormat vk_format = GetImageFormat(format); if (it->second.view != VK_NULL_HANDLE && - (it->second.width != image->GetWidth() || - it->second.height != image->GetHeight())) { + (it->second.width != width || it->second.height != height)) { // Size mismatch. Recreate the texture. FreeImage(std::move(it->second.image), it->second.view, std::move(it->second.desc_set)); @@ -576,12 +587,12 @@ void RendererVulkan::UpdateTexture(uint64_t resource_id, if (it->second.view == VK_NULL_HANDLE) { AllocateImage(it->second.image, it->second.view, it->second.desc_set, - format, image->GetWidth(), image->GetHeight(), + vk_format, width, height, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VMA_MEMORY_USAGE_GPU_ONLY); old_layout = VK_IMAGE_LAYOUT_UNDEFINED; - it->second.width = image->GetWidth(); - it->second.height = image->GetHeight(); + it->second.width = width; + it->second.height = height; } task_runner_.PostTask( @@ -591,10 +602,9 @@ void RendererVulkan::UpdateTexture(uint64_t resource_id, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, VK_ACCESS_TRANSFER_WRITE_BIT, old_layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)); - task_runner_.PostTask( - HERE, std::bind(&RendererVulkan::UpdateImage, this, - std::get<0>(it->second.image), format, image->GetBuffer(), - image->GetWidth(), image->GetHeight())); + task_runner_.PostTask(HERE, std::bind(&RendererVulkan::UpdateImage, this, + std::get<0>(it->second.image), + vk_format, image_data, width, height)); task_runner_.PostTask( HERE, std::bind(&RendererVulkan::ImageMemoryBarrier, this, @@ -605,7 +615,6 @@ void RendererVulkan::UpdateTexture(uint64_t resource_id, 0, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)); - task_runner_.Delete(HERE, std::move(image)); semaphore_.release(); } diff --git a/src/engine/renderer/vulkan/renderer_vulkan.h b/src/engine/renderer/vulkan/renderer_vulkan.h index fe9f227..40bd713 100644 --- a/src/engine/renderer/vulkan/renderer_vulkan.h +++ b/src/engine/renderer/vulkan/renderer_vulkan.h @@ -55,6 +55,12 @@ class RendererVulkan final : public Renderer { uint64_t CreateTexture() final; void UpdateTexture(uint64_t resource_id, std::unique_ptr image) final; + void UpdateTexture(uint64_t resource_id, + int width, + int height, + ImageFormat format, + size_t data_size, + uint8_t* image_data) final; void DestroyTexture(uint64_t resource_id) final; void ActivateTexture(uint64_t resource_id, uint64_t texture_unit) final;