From bf98ea188638606de86e23fa672a974aa4158b76 Mon Sep 17 00:00:00 2001 From: WillisMedwell Date: Thu, 29 Feb 2024 13:22:37 +1100 Subject: [PATCH] Improved: Rendering API & Added: FontBatchRenderer --- code/Demos/src/Main.cpp | 131 +++++----- code/Engine/include/App/App.hpp | 7 +- code/Engine/include/App/AppRenderer.hpp | 34 +-- code/Engine/include/Config.hpp | 3 +- code/Engine/include/Core/DebugOpRecorder.hpp | 53 ++++ code/Engine/include/Core/IndexBuffer.hpp | 13 +- code/Engine/include/Core/OpenglContext.hpp | 3 + code/Engine/include/Core/Renderer.hpp | 14 -- code/Engine/include/Core/Shader.hpp | 2 + code/Engine/include/Core/VertexArray.hpp | 47 +++- code/Engine/include/Core/VertexBuffer.hpp | 2 + code/Engine/include/Renderer/FontRenderer.hpp | 231 ++++++++++++++++++ code/Engine/include/Renderer/Renderer.hpp | 17 +- .../include/Renderer/ResourceHandle.hpp | 12 + .../include/Renderer/ResourceManager.hpp | 113 +++++++++ code/Engine/src/Core/Framebuffer.cpp | 29 +-- code/Engine/src/Core/IndexBuffer.cpp | 24 +- code/Engine/src/Core/OpenglContext.cpp | 6 + code/Engine/src/Core/Shader.cpp | 51 ++-- code/Engine/src/Core/Texture.cpp | 60 +++-- code/Engine/src/Core/VertexArray.cpp | 48 ++-- code/Engine/src/Core/VertexBuffer.cpp | 9 + code/Engine/src/Media/Font.cpp | 2 +- code/Engine/src/Renderer/FontRenderer.cpp | 200 +++++++++++++++ code/Test/include/Integration/BasicApps.hpp | 70 +++--- code/build-web.bat | 2 +- 26 files changed, 925 insertions(+), 258 deletions(-) create mode 100644 code/Engine/include/Core/DebugOpRecorder.hpp delete mode 100644 code/Engine/include/Core/Renderer.hpp create mode 100644 code/Engine/include/Renderer/FontRenderer.hpp create mode 100644 code/Engine/include/Renderer/ResourceHandle.hpp create mode 100644 code/Engine/include/Renderer/ResourceManager.hpp create mode 100644 code/Engine/src/Renderer/FontRenderer.cpp diff --git a/code/Demos/src/Main.cpp b/code/Demos/src/Main.cpp index 05d5667..21ca77e 100644 --- a/code/Demos/src/Main.cpp +++ b/code/Demos/src/Main.cpp @@ -4,6 +4,7 @@ #include #include +#include #include using namespace std::literals; @@ -338,37 +339,14 @@ struct SpinningTeapotLogic { struct FontData { std::chrono::steady_clock::time_point start_time; - glm::vec4 background_colour = { 0, 0, 0, 1.0f }; - Media::Font font; - Cameras::StationaryPerspective camera { glm::vec3(0, 0, -10), glm::normalize(glm::vec3(0, 0, 1)) }; + glm::vec4 background_colour = { 1.0f, 0, 1.0f, 1.0f }; - AppRenderer::ShaderId s_id; - AppRenderer::IndexBufferId ib_id; - AppRenderer::VertexBufferId vb_id; - AppRenderer::VertexArrayId va_id; - AppRenderer::TextureId t_id; - - constexpr static std::string_view VERT = - "precision highp float; " - "uniform mat4 u_mvp;" - "layout(location = 0) in vec2 l_pos;" - "layout(location = 1) in vec2 l_uv;" - "out vec2 uv;" - "void main() {" - " gl_Position = u_mvp * vec4(l_pos, -1.0, 1.0);" - " uv = l_uv;" - "}"sv; - constexpr static std::string_view FRAG = - "precision highp float; " - "uniform sampler2D u_texture;" - "out vec4 FragColor;" - "in vec2 uv;" - "void main() {" - " vec2 uv_flipped = vec2(uv.x, 1 - uv.y);" - " FragColor = vec4(texture(u_texture, uv_flipped).rrr, 1);" - "}"sv; + Media::Font font {}; + Media::FontAtlas font_atlas {}; - Media::FontAtlas font_atlas = {}; + Renderer::ResourceManager resource_manager {}; + Renderer::FontRenderer font_renderer {}; + Renderer::FontBatchRenderer font_batch_renderer {}; }; struct FontLogic { void init(AppRenderer& renderer, entt::registry& ecs, FontData& data) { @@ -379,53 +357,72 @@ struct FontLogic { data.font_atlas.init(data.font, 100).on_error(print_then_quit); data.font_atlas.image.save_to_disk("FontAtlasGeneration.png").on_error(print_then_quit); - - data.s_id = renderer.add_shader(data.VERT, data.FRAG).on_error(print_then_quit).value(); - data.ib_id = renderer.add_index_buffer().on_error(print_then_quit).value(); - data.vb_id = renderer.add_vertex_buffer().on_error(print_then_quit).value(); - data.va_id = renderer.add_vertex_array(Model::Vertex2D::VBL {}, data.vb_id).on_error(print_then_quit).value(); - data.t_id = renderer.add_texture(data.font_atlas.image).on_error(print_then_quit).value(); + //data.font_renderer.init(data.resource_manager, data.font_atlas); + data.font_batch_renderer.init(data.resource_manager, data.font_atlas); } void update(float dt, const Core::InputManager& input, AppState& state, entt::registry& ecs, FontData& data) { - - auto duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - data.start_time); - if (duration > std::chrono::seconds(1)) { - state.should_close = true; - } } void draw(AppRenderer& renderer, entt::registry& ecs, FontData& data) { - renderer.screen_frame_buffer.clear(data.background_colour); - - // auto pm = data.camera.projection_matrix(renderer.window_width, renderer.window_height); - auto pm = Cameras::Orthographic::projection_matrix(renderer.window_width, renderer.window_height); - renderer.screen_frame_buffer.bind(); - renderer.screen_frame_buffer.clear(); + renderer.screen_frame_buffer.clear(data.background_colour); renderer.screen_frame_buffer.resize(renderer.window_width, renderer.window_height); - Core::IndexBuffer& ib = renderer.index_buffers[data.ib_id.id]; - Core::VertexBuffer& vb = renderer.vertex_buffers[data.vb_id.id]; - Core::VertexArray& va = renderer.vertex_arrays[data.va_id.id]; - Core::Shader& s = renderer.shaders[data.s_id.id]; - Core::Texture& t = renderer.textures[data.t_id.id]; - - const static auto [verts, indis] = Media::FontMeshGenerator::generate_static_mesh("hello there", 100, { 50, 50 }, data.font_atlas); - - s.bind(); - - s.set_uniform("u_mvp", pm).on_error(print_then_quit); - s.set_uniform("u_texture", static_cast(t.bind().value())).on_error(print_then_quit); - - va.bind(); - ib.bind(); - vb.bind(); - ib.load_indices(indis); - vb.load_vertices(verts); - - glDrawElements(GL_TRIANGLES, ib.get_count(), GL_UNSIGNED_INT, (void*)0); + data.font_batch_renderer.begin_batch({ + .resource_manager = data.resource_manager, + .screen_dimensions = glm::vec2 { renderer.window_width, renderer.window_height }, + .font_colour = { 0, 0, 0, 1 }, + }); + + data.font_batch_renderer.push_to_batch("hi there", { 0, 0 }, 50); + + data.font_batch_renderer.push_to_batch("this is a lot of freaking text on the screen. lah blah blah", { 0, 25 }, 25); + data.font_batch_renderer.push_to_batch("this is a lot of freaking text on the screen. blh blah blah", { 0, 50 }, 25); + data.font_batch_renderer.push_to_batch("this is a lot of freaking text on the screen. bah blah blah", { 0, 75 }, 25); + data.font_batch_renderer.push_to_batch("this is a lot of freaking text on the screen. blah bah blah", { 0, 100 }, 25); + data.font_batch_renderer.push_to_batch("this is a lot of freaking text on the screen. blah blah blah", { 0, 125 }, 25); + data.font_batch_renderer.push_to_batch("this is a lot of freaking text on the screen. blah blah bah", { 0, 150 }, 25); + data.font_batch_renderer.push_to_batch("this is a lot of freaking text on the screen. blah blah bla", { 0, 175 }, 25); + // Note that the last four calls are similar to the first four but with slightly different y-positions. + // Ensure this is intentional and not a duplication error. If they are indeed different messages or required duplications, convert them similarly: + data.font_batch_renderer.push_to_batch("this is a lot of freaking text on the screen. lah blah blah", { 0, 40 }, 25); + data.font_batch_renderer.push_to_batch("this is a lot of freaking text on the screen. blh blah blah", { 0, 60 }, 25); + data.font_batch_renderer.push_to_batch("this is a lot of freaking text on the screen. bah blah blah", { 0, 80 }, 25); + data.font_batch_renderer.push_to_batch("this is a lot of freaking text on the screen. blah bah blah", { 0, 110 }, 25); + data.font_batch_renderer.push_to_batch("this is a lot of freaking text on the screen. blah blah blah", { 0, 130 }, 25); + data.font_batch_renderer.push_to_batch("this is a lot of freaking text on the screen. blah blah bah", { 0, 160 }, 25); + data.font_batch_renderer.push_to_batch("this is a lot of freaking text on the screen. blah blah bla", { 0, 180 }, 25); + + data.font_batch_renderer.end_batch(); + + // data.font_renderer.add_to_draw_list("some text", pos); + // data.font_renderer.add_to_draw_list("some other text", pos2); + // data.font_renderer.draw("some other text", pos2); + + // data.font_renderer.begin_batch(); + // + // data.font_renderer.push(); + // data.font_renderer.push(); + // data.font_renderer.push(); + // data.font_renderer.push(); + + // data.font_renderer.end_batch(); + + // data.font_renderer.draw(data.resource_manager, glm::vec2 { renderer.window_width, renderer.window_height }, "this is a lot of freaking text on the screen. lah blah blah", 25, { 0, 25 }, { 0, 0, 0, 1 }); + // data.font_renderer.draw(data.resource_manager, glm::vec2 { renderer.window_width, renderer.window_height }, "this is a lot of freaking text on the screen. blh blah blah", 25, { 0, 50 }, { 0, 0, 0, 1 }); + // data.font_renderer.draw(data.resource_manager, glm::vec2 { renderer.window_width, renderer.window_height }, "this is a lot of freaking text on the screen. bah blah blah", 25, { 0, 75 }, { 0, 0, 0, 1 }); + // data.font_renderer.draw(data.resource_manager, glm::vec2 { renderer.window_width, renderer.window_height }, "this is a lot of freaking text on the screen. blah bah blah", 25, { 0, 100 }, { 0, 0, 0, 1 }); + // data.font_renderer.draw(data.resource_manager, glm::vec2 { renderer.window_width, renderer.window_height }, "this is a lot of freaking text on the screen. blah blah blah", 25, { 0, 125 }, { 0, 0, 0, 1 }); + // data.font_renderer.draw(data.resource_manager, glm::vec2 { renderer.window_width, renderer.window_height }, "this is a lot of freaking text on the screen. blah blah bah", 25, { 0, 150 }, { 0, 0, 0, 1 }); + // data.font_renderer.draw(data.resource_manager, glm::vec2 { renderer.window_width, renderer.window_height }, "this is a lot of freaking text on the screen. blah blah bla", 25, { 0, 175 }, { 0, 0, 0, 1 }); + // data.font_renderer.draw(data.resource_manager, glm::vec2 { renderer.window_width, renderer.window_height }, "this is a lot of freaking text on the screen. lah blah blah", 25, { 0, 40 }, { 0, 0, 0, 1 }); + // data.font_renderer.draw(data.resource_manager, glm::vec2 { renderer.window_width, renderer.window_height }, "this is a lot of freaking text on the screen. blh blah blah", 25, { 0, 60 }, { 0, 0, 0, 1 }); + // data.font_renderer.draw(data.resource_manager, glm::vec2 { renderer.window_width, renderer.window_height }, "this is a lot of freaking text on the screen. bah blah blah", 25, { 0, 80 }, { 0, 0, 0, 1 }); + // data.font_renderer.draw(data.resource_manager, glm::vec2 { renderer.window_width, renderer.window_height }, "this is a lot of freaking text on the screen. blah bah blah", 25, { 0, 110 }, { 0, 0, 0, 1 }); + // data.font_renderer.draw(data.resource_manager, glm::vec2 { renderer.window_width, renderer.window_height }, "this is a lot of freaking text on the screen. blah blah blah", 25, { 0, 130 }, { 0, 0, 0, 1 }); + // data.font_renderer.draw(data.resource_manager, glm::vec2 { renderer.window_width, renderer.window_height }, "this is a lot of freaking text on the screen. blah blah bah", 25, { 0, 160 }, { 0, 0, 0, 1 }); + // data.font_renderer.draw(data.resource_manager, glm::vec2 { renderer.window_width, renderer.window_height }, "this is a lot of freaking text on the screen. blah blah bla", 25, { 0, 180 }, { 0, 0, 0, 1 }); } void stop() { - } }; diff --git a/code/Engine/include/App/App.hpp b/code/Engine/include/App/App.hpp index e643eb4..ab9c753 100644 --- a/code/Engine/include/App/App.hpp +++ b/code/Engine/include/App/App.hpp @@ -11,10 +11,11 @@ #include "Cameras/Cameras.hpp" #include "Profiler/Profiler.hpp" -#include "Core/Renderer.hpp" +#include "Core/Core.hpp" #include "Core/Input.hpp" -#include "AppRenderer.hpp" + +#include "App/AppRenderer.hpp" #include #include @@ -23,6 +24,8 @@ struct AppState { bool should_close = false; }; + + template concept HasValidAppLogic = requires(T t, double dt, AppState& state, AppData& data, AppRenderer& renderer, const Core::InputManager& input, entt::registry& ecs) { { diff --git a/code/Engine/include/App/AppRenderer.hpp b/code/Engine/include/App/AppRenderer.hpp index a36942a..61adadd 100644 --- a/code/Engine/include/App/AppRenderer.hpp +++ b/code/Engine/include/App/AppRenderer.hpp @@ -29,7 +29,7 @@ class AppRenderer Utily::StaticVector index_buffers; Utily::StaticVector vertex_arrays; Utily::StaticVector textures; - + Core::ScreenFrameBuffer screen_frame_buffer; float window_width; @@ -40,40 +40,22 @@ class AppRenderer [[nodiscard]] auto add_index_buffer() noexcept -> Utily::Result; template - [[nodiscard]] auto add_vertex_array(Core::VertexBufferLayout vertex_buffer_layout, VertexBufferId vb_id) -> Utily::Result { + [[nodiscard]] auto add_vertex_array(Core::VertexBufferLayout vertex_buffer_layout, VertexBufferId vb_id, IndexBufferId ib_id) -> Utily::Result { auto id = vertex_arrays.size(); vertex_arrays.emplace_back(); Core::VertexArray& va = vertex_arrays[id]; - auto result = va.init(); + Core::VertexBuffer& vb = vertex_buffers[vb_id.id]; + Core::IndexBuffer& ib = index_buffers[ib_id.id]; + + auto result = va.init(vertex_buffer_layout, vb, ib); if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { if (result.has_error()) { return result.error(); } } - - va.bind(); - vertex_buffers[vb_id.id].bind(); - - constexpr static auto layout = vertex_buffer_layout.get_layout(); - /*constexpr static*/ auto stride = vertex_buffer_layout.get_stride(); - - uint32_t offset = 0; - for (size_t i = 0; i < layout.size(); i++) { - const auto& element = layout[i]; - -#if defined(CONFIG_TARGET_NATIVE) - glEnableVertexArrayAttrib(va.get_id().value(), i); - glVertexAttribPointer(i, element.count, element.type, element.normalised, stride, reinterpret_cast(offset)); -#elif defined(CONFIG_TARGET_WEB) - glEnableVertexAttribArray(i); - glVertexAttribPointer(i, element.count, element.type, element.normalised, stride, reinterpret_cast(offset)); -#endif - - offset += element.type_size; - } - return 0; - } + return { static_cast(id) }; + } [[nodiscard]] auto add_texture(Media::Image& image) noexcept -> Utily::Result; diff --git a/code/Engine/include/Config.hpp b/code/Engine/include/Config.hpp index f3ed194..7130336 100644 --- a/code/Engine/include/Config.hpp +++ b/code/Engine/include/Config.hpp @@ -29,8 +29,9 @@ namespace Config { // false == ensure the gpu texture is reading valid cpu image data. Enables fencing as default. constexpr static bool SKIP_IMAGE_TEXTURE_FENCING = false; - constexpr static bool SKIP_PROFILE = false; + + constexpr static bool ENABLE_VSYNC = false; } #if defined(EMSCRIPTEN) diff --git a/code/Engine/include/Core/DebugOpRecorder.hpp b/code/Engine/include/Core/DebugOpRecorder.hpp new file mode 100644 index 0000000..d68442e --- /dev/null +++ b/code/Engine/include/Core/DebugOpRecorder.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include "Config.hpp" +#include +#include +#include + +namespace Core { + class DebugOpRecorder + { + public: + static inline auto instance() -> DebugOpRecorder& { + static DebugOpRecorder dor {}; + return dor; + } + + inline void clear() { + if constexpr (Config::DEBUG_LEVEL == Config::DebugInfo::all) { + _ops.clear(); + } + } + + inline void push(std::string_view type [[maybe_unused]], std::string_view details [[maybe_unused]]) { + if constexpr (Config::DEBUG_LEVEL == Config::DebugInfo::all) { + try { + _ops.emplace_back(type, details); + } catch (const std::exception& e) { + std::cerr << e.what() << '\n'; + } + } + } + + inline auto get_formatted_ops() { + std::string res {}; + if constexpr (Config::DEBUG_LEVEL == Config::DebugInfo::all) { + for (auto [core_type, details] : _ops) { + res += " - "; + res += core_type; + res += ": \t"; + res += details; + res += "\n"; + } + } + return res; + } + + private: + using Ops = std::vector>; + + Ops _ops; + DebugOpRecorder() = default; + }; +} \ No newline at end of file diff --git a/code/Engine/include/Core/IndexBuffer.hpp b/code/Engine/include/Core/IndexBuffer.hpp index 60820e4..98f024c 100644 --- a/code/Engine/include/Core/IndexBuffer.hpp +++ b/code/Engine/include/Core/IndexBuffer.hpp @@ -3,7 +3,10 @@ #include #include "Config.hpp" +#include "Model/Types.hpp" #include "Profiler/Profiler.hpp" +#include "Core/DebugOpRecorder.hpp" + namespace Core { class IndexBuffer @@ -25,16 +28,12 @@ namespace Core { && std::same_as, uint32_t> && std::ranges::sized_range void load_indices(const Range& indices) noexcept { - Profiler::Timer timer("Core::IndexBuffer::load_indices", {"rendering"}); + Core::DebugOpRecorder::instance().push("Core::IndexBuffer", "load_indices()"); + Profiler::Timer timer("Core::IndexBuffer::load_indices", { "rendering" }); this->bind(); - size_t size_in_bytes = indices.size() * sizeof(uint32_t); -#if defined(CONFIG_TARGET_NATIVE) + size_t size_in_bytes = indices.size() * sizeof(Model::Index); glBufferData(GL_ELEMENT_ARRAY_BUFFER, size_in_bytes, &(*indices.begin()), GL_DYNAMIC_DRAW); -#elif defined(CONFIG_TARGET_WEB) - // for some reason, GL_DYNAMIC_DRAW has a very small buffer capacity. - glBufferData(GL_ELEMENT_ARRAY_BUFFER, size_in_bytes, &(*indices.begin()), GL_STATIC_DRAW); -#endif _count = indices.size(); } diff --git a/code/Engine/include/Core/OpenglContext.hpp b/code/Engine/include/Core/OpenglContext.hpp index e3313fe..0a46991 100644 --- a/code/Engine/include/Core/OpenglContext.hpp +++ b/code/Engine/include/Core/OpenglContext.hpp @@ -7,6 +7,9 @@ #include +#include "Core/DebugOpRecorder.hpp" + + namespace Core { class OpenglContext { diff --git a/code/Engine/include/Core/Renderer.hpp b/code/Engine/include/Core/Renderer.hpp deleted file mode 100644 index a242777..0000000 --- a/code/Engine/include/Core/Renderer.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include "Core/OpenglContext.hpp" -#include "Core/IndexBuffer.hpp" -#include "Core/VertexBufferLayout.hpp" -#include "Core/VertexBuffer.hpp" -#include "Core/VertexArray.hpp" -#include "Core/Shader.hpp" -#include "Core/FrameBuffer.hpp" -#include "Core/Texture.hpp" - -namespace Core { - // TODO generate both vertex and index buffer at the same time. -} \ No newline at end of file diff --git a/code/Engine/include/Core/Shader.hpp b/code/Engine/include/Core/Shader.hpp index 7c944f4..e55383c 100644 --- a/code/Engine/include/Core/Shader.hpp +++ b/code/Engine/include/Core/Shader.hpp @@ -25,8 +25,10 @@ namespace Core { auto set_uniform(std::string_view uniform, int32_t value) noexcept -> Utily::Result; auto set_uniform(std::string_view uniform, float value) noexcept -> Utily::Result; auto set_uniform(std::string_view uniform, const glm::vec3& value) noexcept -> Utily::Result; + auto set_uniform(std::string_view uniform, const glm::vec4& value) noexcept -> Utily::Result; auto set_uniform(std::string_view uniform, const glm::mat4& value) noexcept -> Utily::Result; + ~Shader(); private: diff --git a/code/Engine/include/Core/VertexArray.hpp b/code/Engine/include/Core/VertexArray.hpp index bfe45ac..e516383 100644 --- a/code/Engine/include/Core/VertexArray.hpp +++ b/code/Engine/include/Core/VertexArray.hpp @@ -3,19 +3,62 @@ #include #include - +#include "Config.hpp" +#include "Core/IndexBuffer.hpp" +#include "Core/VertexBuffer.hpp" +#include "Core/VertexBufferLayout.hpp" +#include "Core/DebugOpRecorder.hpp" #include namespace Core { class VertexArray { + constexpr static uint32_t INVALID_ARRAY_OBJECT_ID = 0; + public: VertexArray() = default; VertexArray(const VertexArray&) = delete; VertexArray(VertexArray&& other) noexcept; - [[nodiscard]] auto init() noexcept -> Utily::Result; + template + [[nodiscard]] auto init(Core::VertexBufferLayout vbl, Core::VertexBuffer& vb, Core::IndexBuffer& ib) noexcept -> Utily::Result { + Core::DebugOpRecorder::instance().push("Core::VertexArray", "init()"); + + // generation + if (_id) { + return Utily::Error { "Trying to override in-use Vertex Array Object" }; + } + _id = INVALID_ARRAY_OBJECT_ID; + glGenVertexArrays(1, &_id.value()); + if (_id.value() == INVALID_ARRAY_OBJECT_ID) { + _id = std::nullopt; + return Utily::Error { "Failed to create Vertex Array Object. glGenVertexArrays failed." }; + } + // bind associated ib, and vb + + + ib.bind(); + vb.bind(); + this->bind(); + + constexpr static auto layout = vbl.get_layout(); + constexpr static auto stride = vbl.get_stride(); + + uint32_t offset = 0; + for (size_t i = 0; i < layout.size(); i++) { + const auto& element = layout[i]; +#if defined(CONFIG_TARGET_NATIVE) + glEnableVertexArrayAttrib(get_id().value(), i); +#elif defined(CONFIG_TARGET_WEB) + glEnableVertexAttribArray(i); +#endif + glVertexAttribPointer(i, element.count, element.type, element.normalised, stride, reinterpret_cast(offset)); + offset += element.type_size; + } + return {}; + } + void stop() noexcept; void bind() noexcept; diff --git a/code/Engine/include/Core/VertexBuffer.hpp b/code/Engine/include/Core/VertexBuffer.hpp index 289326d..ee0e84d 100644 --- a/code/Engine/include/Core/VertexBuffer.hpp +++ b/code/Engine/include/Core/VertexBuffer.hpp @@ -8,6 +8,7 @@ #include #include "Profiler/Profiler.hpp" +#include "Core/DebugOpRecorder.hpp" #include "Config.hpp" @@ -33,6 +34,7 @@ namespace Core { && std::ranges::sized_range void load_vertices(const Range& vertices) noexcept { Profiler::Timer timer("Core::VertexBuffer::load_vertices", {"rendering"}); + Core::DebugOpRecorder::instance().push("Core::VertexBuffer", "load_vertices()"); this->bind(); using Underlying = std::ranges::range_value_t; diff --git a/code/Engine/include/Renderer/FontRenderer.hpp b/code/Engine/include/Renderer/FontRenderer.hpp new file mode 100644 index 0000000..c23e9b9 --- /dev/null +++ b/code/Engine/include/Renderer/FontRenderer.hpp @@ -0,0 +1,231 @@ +#pragma once + +#include "Core/Core.hpp" +#include "Media/Media.hpp" +#include "Renderer/ResourceManager.hpp" + +namespace Renderer { + class FontRenderer + { + private: + Renderer::ResourceHandle _s; + Renderer::ResourceHandle _t; + Renderer::ResourceHandle _vb; + Renderer::ResourceHandle _ib; + Renderer::ResourceHandle _va; + + struct FontVertex { + glm::vec2 position; + glm::vec2 uv_coord; + + using VBL = Core::VertexBufferLayout; + }; + + size_t _loaded_text_hash = 0; + size_t _loaded_text_size = 0; + std::vector _vertices; + std::vector _indices; + + glm::vec2 _glyph_dimensions = { 0, 0 }; + glm::vec2 _atlas_dimensions = { 0, 0 }; + + void ensure_buffers_have_capacity_for(const size_t N, Core::IndexBuffer& ib); + void load_text_into_vb(const std::string_view& text, Core::VertexBuffer& vb); + bool is_init() const noexcept; + auto uv_coord_of_char(char a) const noexcept -> Media::FontAtlas::UvCoord; + + public: + void init(ResourceManager& resource_manager, Media::FontAtlas& font_atlas); + void stop(ResourceManager& resource_manager); + void draw(ResourceManager& resource_manager, glm::vec2 screen_width, std::string_view text, float char_size_px, glm::vec2 bottom_left, glm::vec4 colour = { 1, 1, 1, 1 }); + }; + + class FontBatchRenderer + { + private: + constexpr static std::string_view SHADER_VERT_SRC = + "precision highp float;\n" + + "layout(location = 0) in vec2 l_pos;\n" + "layout(location = 1) in vec2 l_uv;\n" + + "out vec2 uv;\n" + + "void main() {\n" + " gl_Position = vec4(l_pos, 0, 1.0);\n" + " uv = l_uv;\n" + "}"; + constexpr static std::string_view SHADER_FRAG_SRC = + "precision highp float;\n" + + "uniform sampler2D u_texture;\n" + "uniform vec4 u_colour;\n" + + "in vec2 uv;\n" + "out vec4 FragColor;\n" + + "void main() {\n" + " vec2 uv_flipped = vec2(uv.x, 1.0f - uv.y);\n" + " float r = texture(u_texture, uv_flipped).r;" + " if(r > 0.1f) {\n" + " FragColor = vec4(u_colour.rgb, r * u_colour.a);\n" + " } else {\n" + " FragColor = vec4(0,0,0,0);" + " }\n" + "}"; + + struct Vertex { + glm::vec2 position; + glm::vec2 uv_coord; + using VBL = Core::VertexBufferLayout; + }; + + void load_text_into_vb(const std::string_view& text, glm::vec2 bottom_left, float height_px) { + int v = static_cast(_current_batch_vertices.size()); + _current_batch_vertices.resize(_current_batch_vertices.size() + text.size() * 4); + + Media::FontAtlas font_atlas; + font_atlas.glyph_width = _glyph_dimensions.x; + font_atlas.glyph_height = _glyph_dimensions.y; + font_atlas.columns = _atlas_dimensions.x; + font_atlas.rows = _atlas_dimensions.y; + + const float glyph_ratio = _glyph_dimensions.x / _glyph_dimensions.y; + + for (int t = 0; t < text.size(); ++t, v += 4) { + Vertex* vertices = _current_batch_vertices.data() + v; + + const auto uv = font_atlas.uv_coord_of_char(text[t]); + + // translate it by screen coords + const float translated_min_x = glyph_ratio * t * height_px + bottom_left.x; + const float translated_max_x = (glyph_ratio * t + glyph_ratio) * height_px + bottom_left.x; + const float translated_min_y = bottom_left.y; + const float translated_max_y = height_px + bottom_left.y; + + // scale it to screen coords [-1, 1] + const float actual_min_x = translated_min_x / _current_batch_config->screen_dimensions.x * 2 - 1; + const float actual_max_x = translated_max_x / _current_batch_config->screen_dimensions.x * 2 - 1; + const float actual_min_y = translated_min_y / _current_batch_config->screen_dimensions.y * 2 - 1; + const float actual_max_y = translated_max_y / _current_batch_config->screen_dimensions.y * 2 - 1; + + vertices[0] = { + .position = { actual_min_x, actual_min_y }, + .uv_coord = { uv.min_x, uv.min_y } + }; + vertices[1] = { + .position = { actual_max_x, actual_min_y }, + .uv_coord = { uv.max_x, uv.min_y } + }; + vertices[2] = { + .position = { actual_max_x, actual_max_y }, + .uv_coord = { uv.max_x, uv.max_y } + }; + vertices[3] = { + .position = { actual_min_x, actual_max_y }, + .uv_coord = { uv.min_x, uv.max_y } + }; + } + } + + public: + struct BatchConfig { + ResourceManager& resource_manager; + glm::vec2 screen_dimensions; + glm::vec4 font_colour; + }; + + void init(ResourceManager& resource_manager, Media::FontAtlas& font_atlas) { + auto [s_handle, shader] = resource_manager.create_and_init_resource(SHADER_VERT_SRC, SHADER_FRAG_SRC); + auto [t_handle, texture] = resource_manager.create_and_init_resource(); + auto [vb_handle, vertex_buffer] = resource_manager.create_and_init_resource(); + auto [ib_handle, index_buffer] = resource_manager.create_and_init_resource(); + auto [va_handle, vertex_array] = resource_manager.create_and_init_resource(Vertex::VBL {}, vertex_buffer, index_buffer); + + _s = s_handle; + _t = t_handle; + _vb = vb_handle; + _ib = ib_handle; + _va = va_handle; + + texture.upload_image(font_atlas.image); //.on_error(Panic {}); + + _glyph_dimensions = { font_atlas.glyph_width, font_atlas.glyph_height }; + _atlas_dimensions = { font_atlas.columns, font_atlas.rows }; + } + + void begin_batch(BatchConfig&& batch_config) { + assert(!_current_batch_config); + _current_batch_config.emplace(std::move(batch_config)); + } + void push_to_batch(std::string_view text, glm::vec2 bottom_left, float height_px) { + Profiler::Timer timer("FontBatchRenderer::push_to_batch()", {}); + + assert(_current_batch_config); + load_text_into_vb(text, bottom_left, height_px); + } + void end_batch() { + Profiler::Timer timer("FontBatchRenderer::end_batch()", {}); + assert(_current_batch_config); + if (_current_batch_vertices.size() == 0) { + assert(false && "redundant batching"); + return; + } + + // bind everything + auto [s, t, va, vb, ib] = _current_batch_config->resource_manager.get_resources(_s, _t, _va, _vb, _ib); + const int32_t texture_slot = t.bind().value(); + s.bind(); + va.bind(); + ib.bind(); + vb.bind(); + + s.set_uniform("u_texture", texture_slot); + s.set_uniform("u_colour", _current_batch_config->font_colour); + + // load vertices + vb.load_vertices(_current_batch_vertices); + + // ensure ib has enough capacity/indices for vb + if (ib.get_count() < _current_batch_vertices.size() / 4 * 6) { + std::vector indices; + indices.resize(_current_batch_vertices.size() / (size_t)4 * (size_t)6, 0); + + for (int v = 0, i = 0; i < indices.size(); i += 6, v += 4) { + indices[i + 0] = v + 0; + indices[i + 1] = v + 1; + indices[i + 2] = v + 2; + indices[i + 3] = v + 2; + indices[i + 4] = v + 3; + indices[i + 5] = v + 0; + } + assert(indices.size()); + ib.load_indices(indices); + } + + // gldraw indices = vb.vertices / 4 * 6 + { + Profiler::Timer draw_timer("glDrawElements()", {}); + glDisable(GL_DEPTH_TEST); + glDrawElements(GL_TRIANGLES, _current_batch_vertices.size() / 4 * 6, GL_UNSIGNED_INT, (void*)0); + glEnable(GL_DEPTH_TEST); + } + + _current_batch_config = std::nullopt; + _current_batch_vertices.resize(0); + } + + private: + std::optional _current_batch_config = std::nullopt; + std::vector _current_batch_vertices = {}; + + glm::vec2 _glyph_dimensions = { 0, 0 }; + glm::vec2 _atlas_dimensions = { 0, 0 }; + + Renderer::ResourceHandle _s; + Renderer::ResourceHandle _t; + Renderer::ResourceHandle _vb; + Renderer::ResourceHandle _ib; + Renderer::ResourceHandle _va; + }; +} \ No newline at end of file diff --git a/code/Engine/include/Renderer/Renderer.hpp b/code/Engine/include/Renderer/Renderer.hpp index 7b9637e..88da04c 100644 --- a/code/Engine/include/Renderer/Renderer.hpp +++ b/code/Engine/include/Renderer/Renderer.hpp @@ -1 +1,16 @@ -#pragma once \ No newline at end of file +#pragma once + +#include "Core/Core.hpp" + +namespace Renderer { + + template + struct ResourceHandle; + + class ResourceManager; + class FontRenderer; +} + +#include "Renderer/ResourceHandle.hpp" +#include "Renderer/ResourceManager.hpp" +#include "Renderer/FontRenderer.hpp" diff --git a/code/Engine/include/Renderer/ResourceHandle.hpp b/code/Engine/include/Renderer/ResourceHandle.hpp new file mode 100644 index 0000000..7b5f8f3 --- /dev/null +++ b/code/Engine/include/Renderer/ResourceHandle.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include "Core/Core.hpp" +#include + +namespace Renderer { + template + struct ResourceHandle { + std::optional id = std::nullopt; + size_t resource_owner_id = 0; + }; +} \ No newline at end of file diff --git a/code/Engine/include/Renderer/ResourceManager.hpp b/code/Engine/include/Renderer/ResourceManager.hpp new file mode 100644 index 0000000..22e1911 --- /dev/null +++ b/code/Engine/include/Renderer/ResourceManager.hpp @@ -0,0 +1,113 @@ +#pragma once + +#include "Renderer/ResourceHandle.hpp" + +#include +#include +#include + +namespace Renderer { + + template + concept CanBeInitWithArgs = requires(T t, Args&&... args) { + t.init(std::forward(args)...); + }; + + class ResourceManager + { + private: + constexpr static size_t MAX_S = 32; + constexpr static size_t MAX_T = 32; + constexpr static size_t MAX_VB = 32; + constexpr static size_t MAX_VA = 32; + constexpr static size_t MAX_IB = 32; + +#if 0 + Utily::StaticVector _shaders; + Utily::StaticVector _textures; + Utily::StaticVector _vertex_arrays; + Utily::StaticVector _index_buffers; + Utily::StaticVector _vertex_buffers; +#else + std::vector _shaders; + std::vector _textures; + std::vector _vertex_arrays; + std::vector _index_buffers; + std::vector _vertex_buffers; +#endif + + size_t owner_id; + + template + inline constexpr auto& get_resource_buffer() { + if constexpr (std::same_as) { + return _shaders; + } else if constexpr (std::same_as) { + return _textures; + } else if constexpr (std::same_as) { + return _vertex_arrays; + } else if constexpr (std::same_as) { + return _vertex_buffers; + } else if constexpr (std::same_as) { + return _index_buffers; + } else { + throw std::runtime_error("That resource is not mapped."); + } + } + + public: + template + [[nodiscard]] inline auto create_and_init_resource(Args&&... args) -> std::tuple, T&> { + auto& resource_buffer = get_resource_buffer(); + ResourceHandle handle { resource_buffer.size(), this->owner_id }; + + resource_buffer.emplace_back(); + + T& resource = resource_buffer[handle.id.value()]; + + static_assert(CanBeInitWithArgs, "The Args must be params for the method T::init()"); + resource.init(std::forward(args)...) + .on_error([](auto& error) { + throw std::runtime_error(std::string(error.what())); + }); + + return std::tuple, T&>(handle, resource); + } + + template + [[nodiscard]] inline auto get_resource(ResourceHandle handle) -> T& { + auto& resource_buffer = get_resource_buffer(); + assert(handle.resource_owner_id == this->owner_id); + if (handle.id) { + return resource_buffer[handle.id.value()]; + } else { + throw std::runtime_error("Invalid resource handle"); + } + } + + template + [[nodiscard]] inline auto get_resources(ResourceHandle... handles) -> std::tuple { + return std::tuple(get_resource(handles)...); + } + + template + inline void free_resource(ResourceHandle handle) { + auto& resource_buffer = get_resource_buffer(); + assert(handle.resource_owner_id == this->owner_id); + if (handle.id) { + resource_buffer[handle.id.value()].stop(); + } + } + + template + inline void free_resources(ResourceHandle... handles) { + (..., free_resource(handles)); + } + + ResourceManager() { + static std::mt19937 rng(std::random_device {}()); + static std::uniform_int_distribution dist { 0, std::numeric_limits::max() }; + owner_id = dist(rng); + } + }; +} \ No newline at end of file diff --git a/code/Engine/src/Core/Framebuffer.cpp b/code/Engine/src/Core/Framebuffer.cpp index c7eac33..05fd7cf 100644 --- a/code/Engine/src/Core/Framebuffer.cpp +++ b/code/Engine/src/Core/Framebuffer.cpp @@ -1,14 +1,12 @@ #include "Core/FrameBuffer.hpp" +#include "Profiler/Profiler.hpp" #include #include -#include "Profiler/Profiler.hpp" namespace Core { constexpr static uint32_t INVALID_BUFFER_ID = 0; - static FrameBuffer* last_bound = nullptr; - struct ColourAttachment { std::optional id = std::nullopt; bool in_use = false; @@ -42,7 +40,6 @@ namespace Core { FrameBuffer::FrameBuffer(FrameBuffer&& other) noexcept : _id(std::exchange(other._id, std::nullopt)) , _colour_attachment_index(std::exchange(other._id, std::nullopt)) { - last_bound = nullptr; } auto FrameBuffer::init(uint32_t width, uint32_t height) noexcept -> Utily::Result { @@ -91,10 +88,6 @@ namespace Core { && _colour_attachment_index.value() < colour_attachments.size()) { colour_attachments[*_colour_attachment_index].in_use = false; } - - if (last_bound == this) { - last_bound = this; - } } FrameBuffer::~FrameBuffer() noexcept { @@ -108,10 +101,8 @@ namespace Core { assert(false); } } - if (last_bound != this) { - glBindFramebuffer(GL_FRAMEBUFFER, _id.value_or(INVALID_BUFFER_ID)); - last_bound = this; - } + + glBindFramebuffer(GL_FRAMEBUFFER, _id.value_or(INVALID_BUFFER_ID)); } void FrameBuffer::unbind() noexcept { @@ -124,30 +115,24 @@ namespace Core { } } - if (last_bound != nullptr) { - glBindBuffer(GL_FRAMEBUFFER, 0); - last_bound = nullptr; - } + glBindBuffer(GL_FRAMEBUFFER, 0); } uint32_t ScreenFrameBuffer::width = 0; uint32_t ScreenFrameBuffer::height = 0; void ScreenFrameBuffer::clear(glm::vec4 colour) noexcept { - Profiler::Timer timer("Core::ScreenFrameBuffer::clear()", {"rendering"}); + Profiler::Timer timer("Core::ScreenFrameBuffer::clear()", { "rendering" }); ScreenFrameBuffer::bind(); glClearColor(colour.x, colour.y, colour.z, colour.w); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } void ScreenFrameBuffer::bind() noexcept { - if (last_bound != nullptr) { - glBindBuffer(GL_FRAMEBUFFER, 0); - last_bound = nullptr; - } + glBindFramebuffer(GL_FRAMEBUFFER, 0); } void ScreenFrameBuffer::resize(uint32_t screen_width, uint32_t screen_height) noexcept { if (screen_width != ScreenFrameBuffer::width || screen_height != ScreenFrameBuffer::height) { - Profiler::Timer timer("Core::ScreenFrameBuffer::resize()", {"rendering"}); + Profiler::Timer timer("Core::ScreenFrameBuffer::resize()", { "rendering" }); ScreenFrameBuffer::bind(); glViewport(0, 0, screen_width, screen_height); ScreenFrameBuffer::width = screen_width; diff --git a/code/Engine/src/Core/IndexBuffer.cpp b/code/Engine/src/Core/IndexBuffer.cpp index 2128adb..9dc1cd8 100644 --- a/code/Engine/src/Core/IndexBuffer.cpp +++ b/code/Engine/src/Core/IndexBuffer.cpp @@ -4,15 +4,15 @@ namespace Core { constexpr static uint32_t INVALID_INDEX_BUFFER_ID = 0; - static IndexBuffer* last_bound_ib = nullptr; IndexBuffer::IndexBuffer(IndexBuffer&& other) noexcept : _id(std::exchange(other._id, std::nullopt)) , _count(std::exchange(other._count, 0)) { - last_bound_ib = nullptr; } auto IndexBuffer::init() noexcept -> Utily::Result { + Core::DebugOpRecorder::instance().push("Core::IndexBuffer", "init()"); + if (_id.has_value()) { return Utily::Error { "Trying to override in-use index buffer" }; } @@ -27,30 +27,30 @@ namespace Core { } void IndexBuffer::stop() noexcept { + Core::DebugOpRecorder::instance().push("Core::IndexBuffer", "stop()"); + if (_id.value_or(INVALID_INDEX_BUFFER_ID) != INVALID_INDEX_BUFFER_ID) { glDeleteBuffers(1, &_id.value()); } _id = std::nullopt; _count = 0; - if (last_bound_ib == this) { - last_bound_ib = nullptr; - } } void IndexBuffer::bind() noexcept { + Core::DebugOpRecorder::instance().push("Core::IndexBuffer", "bind()"); + if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { if (_id.value_or(INVALID_INDEX_BUFFER_ID) == INVALID_INDEX_BUFFER_ID) { std::cerr << "Trying to unbind invalid vertex buffer."; assert(false); } } - if (last_bound_ib != this) { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _id.value_or(INVALID_INDEX_BUFFER_ID)); - last_bound_ib = this; - } + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _id.value_or(INVALID_INDEX_BUFFER_ID)); } void IndexBuffer::unbind() noexcept { + Core::DebugOpRecorder::instance().push("Core::IndexBuffer", "unbind()"); + if constexpr (Config::SKIP_UNBINDING) { return; } else if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { @@ -59,11 +59,7 @@ namespace Core { assert(false); } } - - if (last_bound_ib != nullptr) { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - last_bound_ib = nullptr; - } + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } } \ No newline at end of file diff --git a/code/Engine/src/Core/OpenglContext.cpp b/code/Engine/src/Core/OpenglContext.cpp index a1ea365..928d59d 100644 --- a/code/Engine/src/Core/OpenglContext.cpp +++ b/code/Engine/src/Core/OpenglContext.cpp @@ -98,6 +98,8 @@ static void GLAPIENTRY openglDebugCallback( << "\n" << std::endl; + std::cout << Core::DebugOpRecorder::instance().get_formatted_ops() << std::endl; + if (severity == GL_DEBUG_SEVERITY_HIGH) { assert(false); exit(EXIT_FAILURE); @@ -148,6 +150,9 @@ namespace Core { window_width = width; window_height = height; glfwMakeContextCurrent(*_window); + if constexpr (Config::ENABLE_VSYNC) { + glfwSwapInterval(1); + } } #elif defined(CONFIG_TARGET_WEB) if (g_window) { @@ -192,6 +197,7 @@ namespace Core { } if constexpr (Config::DEBUG_LEVEL == Config::DebugInfo::all) { if (GLEW_ARB_debug_output) { + Core::DebugOpRecorder::instance().clear(); glEnable(GL_DEBUG_OUTPUT); glDebugMessageCallback(openglDebugCallback, nullptr); } diff --git a/code/Engine/src/Core/Shader.cpp b/code/Engine/src/Core/Shader.cpp index 8ec266f..6f5bb5a 100644 --- a/code/Engine/src/Core/Shader.cpp +++ b/code/Engine/src/Core/Shader.cpp @@ -1,24 +1,23 @@ -#include "Config.hpp" #include "Core/Shader.hpp" -#include "Profiler/Profiler.hpp" +#include "Config.hpp" +#include "Core/DebugOpRecorder.hpp" +#include "Profiler/Profiler.hpp" #include using namespace std::literals; namespace Core { - - static Shader* last_bound_s = nullptr; - Shader::Shader(Shader&& other) : _program_id(std::exchange(other._program_id, std::nullopt)) , _cached_uniforms(std::move(other._cached_uniforms)) { - last_bound_s = nullptr; } auto Shader::compile_shader(Type type, const std::string_view& source) -> Utily::Result { - Profiler::Timer timer("Core::Shader::compile_shader()", {"rendering"}); + Profiler::Timer timer("Core::Shader::compile_shader()", { "rendering" }); + Core::DebugOpRecorder::instance().push("Core::Shader", "compile_shader()"); + constexpr static auto shader_verison = #if defined(CONFIG_TARGET_NATIVE) "#version 330 core \n"sv; @@ -60,7 +59,8 @@ namespace Core { } auto Shader::init(const std::string_view& vert, const std::string_view& frag) -> Utily::Result { - Profiler::Timer timer("Core::Shader::init()", {"rendering"}); + Core::DebugOpRecorder::instance().push("Core::Shader", "init()"); + Profiler::Timer timer("Core::Shader::init()", { "rendering" }); if (_program_id) { return Utily::Error { "Trying to override in-use shader" }; } @@ -79,7 +79,7 @@ namespace Core { } { - Profiler::Timer timer("glLinkProgram()", {"rendering"}); + Profiler::Timer timer("glLinkProgram()", { "rendering" }); glAttachShader(_program_id.value(), vr.value()); glAttachShader(_program_id.value(), fr.value()); glLinkProgram(_program_id.value()); @@ -117,18 +117,19 @@ namespace Core { // cache it so we dont query the GPU over already bound stuff. void Shader::bind() noexcept { + Core::DebugOpRecorder::instance().push("Core::Shader", "bind()"); + if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { if (!_program_id.has_value()) { std::cerr << "Trying to use invalid program"; assert(_program_id.has_value()); } } - if (last_bound_s != this) { - last_bound_s = this; - } glUseProgram(_program_id.value()); } void Shader::unbind() noexcept { + Core::DebugOpRecorder::instance().push("Core::Shader", "unbind()"); + if constexpr (Config::SKIP_UNBINDING) { return; } else if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { @@ -138,24 +139,22 @@ namespace Core { } } - if (last_bound_s != this && last_bound_s != nullptr) { - glUseProgram(0); - last_bound_s = nullptr; - } + glUseProgram(0); } void Shader::stop() { + Core::DebugOpRecorder::instance().push("Core::Shader", "stop()"); + if (_program_id) { _cached_uniforms.clear(); glDeleteProgram(*_program_id); _program_id = std::nullopt; } - if (last_bound_s == this) { - last_bound_s = nullptr; - } } // Assumes shader is bound already. auto Shader::get_uniform(const std::string_view uniform) noexcept -> Utily::Result { + Core::DebugOpRecorder::instance().push("Core::Shader", "get_uniform()"); + size_t uniform_hash = std::hash {}(uniform); Uniform& ul = _cached_uniforms[uniform_hash]; @@ -169,6 +168,7 @@ namespace Core { } auto Shader::set_uniform(std::string_view uniform, int32_t value) noexcept -> Utily::Result { + Core::DebugOpRecorder::instance().push("Core::Shader", "get_uniform()"); bind(); auto maybe_uniform = get_uniform(uniform); if (maybe_uniform.has_error()) { @@ -178,6 +178,7 @@ namespace Core { return {}; } auto Shader::set_uniform(std::string_view uniform, float value) noexcept -> Utily::Result { + Core::DebugOpRecorder::instance().push("Core::Shader", "get_uniform()"); bind(); auto maybe_uniform = get_uniform(uniform); if (maybe_uniform.has_error()) { @@ -187,6 +188,7 @@ namespace Core { return {}; } auto Shader::set_uniform(std::string_view uniform, const glm::vec3& value) noexcept -> Utily::Result { + Core::DebugOpRecorder::instance().push("Core::Shader", "get_uniform()"); bind(); auto maybe_uniform = get_uniform(uniform); if (maybe_uniform.has_error()) { @@ -195,7 +197,18 @@ namespace Core { glUniform3f(maybe_uniform.value().location, value.x, value.y, value.z); return {}; } + auto Shader::set_uniform(std::string_view uniform, const glm::vec4& value) noexcept -> Utily::Result { + Core::DebugOpRecorder::instance().push("Core::Shader", "get_uniform()"); + bind(); + auto maybe_uniform = get_uniform(uniform); + if (maybe_uniform.has_error()) { + return maybe_uniform.error(); + } + glUniform4f(maybe_uniform.value().location, value.x, value.y, value.z, value.w); + return {}; + } auto Shader::set_uniform(std::string_view uniform, const glm::mat4& value) noexcept -> Utily::Result { + Core::DebugOpRecorder::instance().push("Core::Shader", "get_uniform()"); bind(); auto maybe_uniform = get_uniform(uniform); if (maybe_uniform.has_error()) { diff --git a/code/Engine/src/Core/Texture.cpp b/code/Engine/src/Core/Texture.cpp index fb7a5b5..6763b88 100644 --- a/code/Engine/src/Core/Texture.cpp +++ b/code/Engine/src/Core/Texture.cpp @@ -1,44 +1,57 @@ #include "Core/Texture.hpp" -#include "Profiler/Profiler.hpp" #include "Config.hpp" +#include "Core/DebugOpRecorder.hpp" +#include "Profiler/Profiler.hpp" #include #include #include - namespace Core { struct TextureUnit { Texture* texture = nullptr; bool immutable = false; }; - static Utily::StaticVector texture_units = {}; + auto texture_units() -> Utily::StaticVector& + { + static Utily::StaticVector texture_units = {}; + return texture_units; + } + + + Texture::Texture(Texture&& other) + : _height(std::exchange(other._height, 0)) + , _width(std::exchange(other._width, 0)) + , _id(std::exchange(other._id, std::nullopt)) + , _texture_unit_index(std::exchange(other._texture_unit_index, std::nullopt)) {} auto getUsableTextureUnit() noexcept -> Utily::Result, Utily::Error> { - if (!texture_units.size()) { + if (!texture_units().size()) { // TODO collect analytics about how many texture slots. int32_t max_texture_units; glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &max_texture_units); - texture_units.resize(static_cast(max_texture_units)); - return std::tuple { 0, &texture_units[0] }; + texture_units().resize(static_cast(max_texture_units)); + return std::tuple { 0, &texture_units()[0] }; } auto isUsableUnit = [](TextureUnit& tu) { return tu.texture == nullptr || tu.immutable == false; }; - auto iter = std::ranges::find_if(texture_units, isUsableUnit); + auto iter = std::ranges::find_if(texture_units(), isUsableUnit); - if (iter == texture_units.end()) [[unlikely]] { + if (iter == texture_units().end()) [[unlikely]] { return Utily::Error { "Ran out of usable texture units." }; } - return std::tuple { std::distance(texture_units.begin(), iter), &(*iter) }; + return std::tuple { std::distance(texture_units().begin(), iter), &(*iter) }; } constexpr static uint32_t INVALID_TEXTURE_ID = 0; auto Texture::init() noexcept -> Utily::Result { + Core::DebugOpRecorder::instance().push("Core::Texture", "init()"); + if (_id) { return Utily::Error { "Trying to override in-use Texture" }; } @@ -56,8 +69,8 @@ namespace Core { Filter filter, bool offload_image_on_success) noexcept -> Utily::Result { - - Profiler::Timer timer("Core::Texture::upload_image()", {"rendering"}); + Core::DebugOpRecorder::instance().push("Core::Texture", "upload_image()"); + Profiler::Timer timer("Core::Texture::upload_image()", { "rendering" }); if (!_id) { if (auto ir = init(); ir.has_error()) { @@ -74,7 +87,6 @@ namespace Core { if (auto br = bind(); br.has_error()) { return br.error(); } - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (int32_t)filter); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (int32_t)filter); @@ -102,7 +114,7 @@ namespace Core { } auto Texture::bind(bool locked) noexcept -> Utily::Result { - + Core::DebugOpRecorder::instance().push("Core::Texture", "bind()"); if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { if (_id.value_or(INVALID_TEXTURE_ID) == INVALID_TEXTURE_ID) { return Utily::Error { "Trying to bind an texture that has not been initialised." }; @@ -110,9 +122,9 @@ namespace Core { } if (_texture_unit_index) { - assert(_texture_unit_index.value() < texture_units.size()); - if (texture_units[_texture_unit_index.value()].texture == this) { - texture_units[_texture_unit_index.value()].immutable = locked; + assert(_texture_unit_index.value() < texture_units().size()); + if (texture_units()[_texture_unit_index.value()].texture == this) { + texture_units()[_texture_unit_index.value()].immutable = locked; return _texture_unit_index.value(); } _texture_unit_index = std::nullopt; @@ -133,20 +145,22 @@ namespace Core { return static_cast(index); } void Texture::unbind() noexcept { - if (!texture_units.size()) { + Core::DebugOpRecorder::instance().push("Core::Texture", "unbind()"); + + if (!texture_units().size()) { return; } if constexpr (Config::SKIP_UNBINDING) { - if (texture_units[_texture_unit_index.value_or(0)].texture == this) { - texture_units[_texture_unit_index.value_or(0)].immutable = false; + if (texture_units()[_texture_unit_index.value_or(0)].texture == this) { + texture_units()[_texture_unit_index.value_or(0)].immutable = false; } return; } - if (texture_units[_texture_unit_index.value_or(0)].texture == this) { - texture_units[_texture_unit_index.value_or(0)].texture = nullptr; - texture_units[_texture_unit_index.value_or(0)].immutable = false; + if (texture_units()[_texture_unit_index.value_or(0)].texture == this) { + texture_units()[_texture_unit_index.value_or(0)].texture = nullptr; + texture_units()[_texture_unit_index.value_or(0)].immutable = false; glActiveTexture(GL_TEXTURE0 + _texture_unit_index.value_or(0)); glBindTexture(GL_TEXTURE_2D, 0); _texture_unit_index = std::nullopt; @@ -154,6 +168,8 @@ namespace Core { } void Texture::stop() noexcept { + Core::DebugOpRecorder::instance().push("Core::Texture", "stop()"); + if (_id) { unbind(); glDeleteTextures(1, &_id.value()); diff --git a/code/Engine/src/Core/VertexArray.cpp b/code/Engine/src/Core/VertexArray.cpp index 507c715..8af07b7 100644 --- a/code/Engine/src/Core/VertexArray.cpp +++ b/code/Engine/src/Core/VertexArray.cpp @@ -3,50 +3,48 @@ #include "Config.hpp" namespace Core { - constexpr static uint32_t INVALID_ARRAY_OBJECT_ID = 0; - static VertexArray* last_bound_va = nullptr; VertexArray::VertexArray(VertexArray&& other) noexcept : _id(std::exchange(other._id, std::nullopt)) { - last_bound_va = nullptr; } - auto VertexArray::init() noexcept -> Utily::Result { - if (_id) { - return Utily::Error { "Trying to override in-use Vertex Array Object" }; - } - _id = INVALID_ARRAY_OBJECT_ID; - glGenVertexArrays(1, &_id.value()); - if (_id.value() == INVALID_ARRAY_OBJECT_ID) { - _id = std::nullopt; - return Utily::Error { "Failed to create Vertex Array Object. glGenVertexArrays failed." }; - } - return {}; - } + // auto VertexArray::init() noexcept -> Utily::Result { + // if (_id) { + // return Utily::Error { "Trying to override in-use Vertex Array Object" }; + // } + // _id = INVALID_ARRAY_OBJECT_ID; + // glGenVertexArrays(1, &_id.value()); + // if (_id.value() == INVALID_ARRAY_OBJECT_ID) { + // _id = std::nullopt; + // return Utily::Error { "Failed to create Vertex Array Object. glGenVertexArrays failed." }; + // } + // return {}; + // } void VertexArray::stop() noexcept { + Core::DebugOpRecorder::instance().push("Core::VertexArray", "stop()"); + if (_id.value_or(INVALID_ARRAY_OBJECT_ID) == INVALID_ARRAY_OBJECT_ID) { glDeleteVertexArrays(1, &_id.value()); } _id = std::nullopt; - if (last_bound_va == this) { - last_bound_va = nullptr; - } } void VertexArray::bind() noexcept { + Core::DebugOpRecorder::instance().push("Core::VertexArray", "bind()"); + if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { if (_id.value_or(INVALID_ARRAY_OBJECT_ID) == INVALID_ARRAY_OBJECT_ID) { std::cerr << "Trying to bind invalid vertex array object."; assert(false); } } - if (last_bound_va != this) { - glBindVertexArray(_id.value_or(INVALID_ARRAY_OBJECT_ID)); - last_bound_va = this; - } + + glBindVertexArray(_id.value_or(INVALID_ARRAY_OBJECT_ID)); } void VertexArray::unbind() noexcept { + Core::DebugOpRecorder::instance().push("Core::VertexArray", "unbind()"); + if constexpr (Config::SKIP_UNBINDING) { return; } else if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { @@ -55,11 +53,7 @@ namespace Core { assert(false); } } - - if (last_bound_va != nullptr) { - glBindVertexArray(0); - last_bound_va = nullptr; - } + glBindVertexArray(0); } } \ No newline at end of file diff --git a/code/Engine/src/Core/VertexBuffer.cpp b/code/Engine/src/Core/VertexBuffer.cpp index 60e5b40..61add4f 100644 --- a/code/Engine/src/Core/VertexBuffer.cpp +++ b/code/Engine/src/Core/VertexBuffer.cpp @@ -1,4 +1,5 @@ #include "Core/VertexBuffer.hpp" +#include "Core/DebugOpRecorder.hpp" #include @@ -12,6 +13,8 @@ namespace Core { } auto VertexBuffer::init() noexcept -> Utily::Result { + Core::DebugOpRecorder::instance().push("Core::VertexBuffer", "init()"); + if (_id) { return Utily::Error { "Trying to override in-use vertex buffer" }; } @@ -25,6 +28,8 @@ namespace Core { } void VertexBuffer::stop() noexcept { + Core::DebugOpRecorder::instance().push("Core::VertexBuffer", "stop()"); + if (_id.value_or(INVALID_VERTEX_BUFFER_ID) != INVALID_VERTEX_BUFFER_ID) { glDeleteBuffers(1, &_id.value()); } @@ -36,6 +41,8 @@ namespace Core { } void VertexBuffer::bind() noexcept { + Core::DebugOpRecorder::instance().push("Core::VertexBuffer", "bind()"); + if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { if (_id.value_or(INVALID_VERTEX_BUFFER_ID) == INVALID_VERTEX_BUFFER_ID) { std::cerr << "Trying to bind invalid vertex buffer."; @@ -48,6 +55,8 @@ namespace Core { } } void VertexBuffer::unbind() noexcept { + Core::DebugOpRecorder::instance().push("Core::VertexBuffer", "unbind()"); + if constexpr (Config::SKIP_UNBINDING) { return; } else if constexpr (Config::DEBUG_LEVEL != Config::DebugInfo::none) { diff --git a/code/Engine/src/Media/Font.cpp b/code/Engine/src/Media/Font.cpp index 51b077b..1e1a613 100644 --- a/code/Engine/src/Media/Font.cpp +++ b/code/Engine/src/Media/Font.cpp @@ -106,7 +106,7 @@ namespace Media { GlyphInfo(const FT_GlyphSlot& slot) : width(slot->bitmap.width) - , height(slot->bitmap.rows + slot->bitmap_top) + , height(slot->bitmap.rows + 1) , spanline(slot->bitmap_top) , left_padding(slot->bitmap_left) { } diff --git a/code/Engine/src/Renderer/FontRenderer.cpp b/code/Engine/src/Renderer/FontRenderer.cpp new file mode 100644 index 0000000..f605697 --- /dev/null +++ b/code/Engine/src/Renderer/FontRenderer.cpp @@ -0,0 +1,200 @@ +#pragma once +#include "Renderer/FontRenderer.hpp" + +#include "Cameras/Orthographic.hpp" +#include "Core/Core.hpp" +#include "Media/Media.hpp" + +#include +#include + +namespace Renderer { + constexpr static std::string_view SHADER_VERT_SRC = + "precision highp float;\n" + + "layout(location = 0) in vec2 l_pos;\n" + "layout(location = 1) in vec2 l_uv;\n" + + "uniform mat4 u_mvp;\n" + + "out vec2 uv;\n" + + "void main() {\n" + " gl_Position = u_mvp * vec4(l_pos, 0, 1.0);\n" + " uv = l_uv;\n" + "}"; + constexpr static std::string_view SHADER_FRAG_SRC = + "precision highp float;\n" + + "uniform sampler2D u_texture;\n" + "uniform vec4 u_colour;\n" + + "in vec2 uv;\n" + "out vec4 FragColor;\n" + + "void main() {\n" + " vec2 uv_flipped = vec2(uv.x, 1.0f - uv.y);\n" + " float r = texture(u_texture, uv_flipped).r;" + " if(r > 0.1f) {\n" + " FragColor = vec4(u_colour.rgb, r * u_colour.a);\n" + " } else {\n" + " FragColor = vec4(0,0,0,0);" + " }\n" + "}"; + + void FontRenderer::ensure_buffers_have_capacity_for(const size_t N, Core::IndexBuffer& ib) { + Profiler::Timer timer("FontRenderer::ensure_buffers_have_capacity_for()", {}); + + if (_vertices.size() >= (N * 4) || _indices.size() >= (N * 6)) { + return; + } + + _vertices.resize(N * 4); + _indices.resize(N * 6); + + const float glyph_ratio = _glyph_dimensions.x / _glyph_dimensions.y; + constexpr float min_y = 0; + constexpr float max_y = 1; + + for (int i = 0, v = 0; i < _vertices.size(); ++v, i += 4) { + auto vertices = std::span { _vertices.data() + i, 4 }; + + const float min_x = glyph_ratio * v; + const float max_x = min_x + glyph_ratio; + + vertices[0].position = { min_x, min_y }; + vertices[1].position = { max_x, min_y }; + vertices[2].position = { max_x, max_y }; + vertices[3].position = { min_x, max_y }; + } + + for (int i = 0, v = 0; i < _indices.size(); v += 4, i += 6) { + auto indices = _indices.data() + i; + indices[0] = v + 0; + indices[1] = v + 1; + indices[2] = v + 2; + indices[3] = v + 2; + indices[4] = v + 3; + indices[5] = v + 0; + } + ib.load_indices(_indices); + } + + auto FontRenderer::uv_coord_of_char(char a) const noexcept -> Media::FontAtlas::UvCoord { + constexpr auto drawble_chars = Media::FontAtlasConstants::DRAWABLE_CHARS; + + assert(drawble_chars.front() <= a && a <= drawble_chars.back() && "Must be a printable character"); + + const auto i = static_cast(a - drawble_chars.front()); + + const float r = static_cast(i / _atlas_dimensions.x); + const float c = static_cast(i % static_cast(_atlas_dimensions.x)); + + return { + .min_x = c / static_cast(_atlas_dimensions.x), + .max_x = (c + 1) / static_cast(_atlas_dimensions.x), + .min_y = 1 - (r + 1) / static_cast(_atlas_dimensions.y), + .max_y = 1 - r / static_cast(_atlas_dimensions.y), + }; + } + + void FontRenderer::load_text_into_vb(const std::string_view& text, Core::VertexBuffer& vb) { + Profiler::Timer timer("FontRenderer::load_text_into_vb()", {}); + const auto text_hash = std::hash {}(text); + const auto text_size = text.size(); + + if (text_hash == _loaded_text_hash) { + return; + } + + Media::FontAtlas fa {}; + + fa.glyph_width = _glyph_dimensions.x; + fa.glyph_height = _glyph_dimensions.y; + fa.columns = _atlas_dimensions.x; + fa.rows = _atlas_dimensions.y; + + for (int i = 0, v = 0; i < _vertices.size() && v < text.size(); ++v, i += 4) { + auto vertices = std::span { _vertices.data() + i, 4 }; + const auto uv = fa.uv_coord_of_char(text[v]); + + vertices[0].uv_coord = { uv.min_x, uv.min_y }; + vertices[1].uv_coord = { uv.max_x, uv.min_y }; + vertices[2].uv_coord = { uv.max_x, uv.max_y }; + vertices[3].uv_coord = { uv.min_x, uv.max_y }; + } + vb.load_vertices(std::span { _vertices.begin(), _vertices.begin() + (text.size() * 4) }); + } + + bool FontRenderer::is_init() const noexcept { + return _glyph_dimensions.x > 0 && _glyph_dimensions.y > 0 + && _atlas_dimensions.x > 0 && _atlas_dimensions.y > 0; + } + + struct Panic { + void operator()(Utily::Error& error) { + throw std::runtime_error(std::string(error.what())); + } + }; + + void FontRenderer::init(ResourceManager& resource_manager, Media::FontAtlas& font_atlas) { + + auto [s_handle, shader] = resource_manager.create_and_init_resource(SHADER_VERT_SRC, SHADER_FRAG_SRC); + auto [t_handle, texture] = resource_manager.create_and_init_resource(); + auto [vb_handle, vertex_buffer] = resource_manager.create_and_init_resource(); + auto [ib_handle, index_buffer] = resource_manager.create_and_init_resource(); + auto [va_handle, vertex_array] = resource_manager.create_and_init_resource(FontVertex::VBL {}, vertex_buffer, index_buffer); + + _s = s_handle; + _t = t_handle; + _vb = vb_handle; + _ib = ib_handle; + _va = va_handle; + + texture.upload_image(font_atlas.image).on_error(Panic {}); + + _glyph_dimensions = { font_atlas.glyph_width, font_atlas.glyph_height }; + _atlas_dimensions = { font_atlas.columns, font_atlas.rows }; + } + + + + void FontRenderer::stop(ResourceManager& resource_manager) { + resource_manager.free_resources(_s, _t, _va, _ib, _vb); + } + + void FontRenderer::draw(ResourceManager& resource_manager, glm::vec2 screen_dimensions, std::string_view text, float char_size_px, glm::vec2 bottom_left, glm::vec4 colour) { + Profiler::Timer timer("FontRenderer::draw()", {}); + assert(is_init()); + + auto [s, t, va, vb, ib] = resource_manager.get_resources(_s, _t, _va, _vb, _ib); + + ensure_buffers_have_capacity_for(text.size(), ib); + load_text_into_vb(text, vb); + + const int32_t texture_slot = t.bind().on_error(Panic {}).value(); + s.bind(); + va.bind(); + ib.bind(); + vb.bind(); + + const auto proj_mat = Cameras::Orthographic::projection_matrix(screen_dimensions.x, screen_dimensions.y); + + auto mat = glm::mat4(1.0f); + mat = glm::translate(mat, glm::vec3(bottom_left.x / screen_dimensions.x * 2 - 1, bottom_left.y / screen_dimensions.y * 2 - 1, 0)); + mat = glm::scale(mat, glm::vec3(char_size_px / screen_dimensions.x * 2, char_size_px / screen_dimensions.y * 2, 0.0f)); + + s.set_uniform("u_texture", texture_slot).on_error(Panic {}); + s.set_uniform("u_mvp", mat).on_error(Panic {}); + s.set_uniform("u_colour", colour).on_error(Panic {}); + + assert(text.size() * 6 <= ib.get_count()); + + { + Profiler::Timer draw_timer("glDrawElements()", {}); + glDisable(GL_DEPTH_TEST); + glDrawElements(GL_TRIANGLES, text.size() * 6, GL_UNSIGNED_INT, (void*)0); + glEnable(GL_DEPTH_TEST); + } + } +} \ No newline at end of file diff --git a/code/Test/include/Integration/BasicApps.hpp b/code/Test/include/Integration/BasicApps.hpp index 63a2b57..26e1359 100644 --- a/code/Test/include/Integration/BasicApps.hpp +++ b/code/Test/include/Integration/BasicApps.hpp @@ -5,6 +5,8 @@ #include #include +#include + #include using namespace std::literals; @@ -49,6 +51,8 @@ struct SpinningSquareData { constexpr static auto TRIANGLE_VERTICES = std::to_array({ 0.5f, 0.5f, 0.0f, 0.5f, -0.5f, 0.0f, -0.5f, -0.5f, 0.0f, -0.5f, 0.5f, 0.0f }); constexpr static auto TRIANGLE_INDICES = std::to_array({ 0, 1, 3, 1, 2, 3 }); + Renderer::ResourceManager resource_manager; + AppRenderer::ShaderId s_id; AppRenderer::IndexBufferId ib_id; AppRenderer::VertexBufferId vb_id; @@ -97,11 +101,15 @@ struct SpinningSquareLogic { data.ib_id = id; }); - renderer.add_vertex_array(Core::VertexBufferLayout {}, data.vb_id) + renderer.add_vertex_array(Core::VertexBufferLayout {}, data.vb_id, data.ib_id) .on_error(Utily::ErrorHandler::print_then_quit) .on_value([&](auto& id) { data.va_id = id; }); + + auto [vb_h, vb] = data.resource_manager.create_and_init_resource(); + auto [ib_h, ib] = data.resource_manager.create_and_init_resource(); + auto [va_h, va] = data.resource_manager.create_and_init_resource(Model::Vertex::VBL {}, vb, ib); } void update(double dt, const Core::InputManager& input, AppState& state, entt::registry& ecs, SpinningSquareData& data) { @@ -148,6 +156,12 @@ struct SpinningTeapotData { entt::entity teapot; Cameras::StationaryPerspective camera { glm::vec3(0, 1, -1), glm::normalize(glm::vec3(0, -0.25f, 0.5f)) }; + Renderer::ResourceManager resource_manager; + Renderer::ResourceHandle s_h; + Renderer::ResourceHandle vb_h; + Renderer::ResourceHandle ib_h; + Renderer::ResourceHandle va_h; + AppRenderer::ShaderId s_id; AppRenderer::IndexBufferId ib_id; AppRenderer::VertexBufferId vb_id; @@ -186,7 +200,7 @@ struct SpinningTeapotLogic { auto teapot_source = Utily::FileReader::load_entire_file("assets/teapot.obj") .on_error(print_pause_quit) .value(); - + auto teapot_model = std::move(Model::decode_as_static_model(teapot_source, { '.', 'o', 'b', 'j' }) .on_error(print_pause_quit) .value()); @@ -196,26 +210,16 @@ struct SpinningTeapotLogic { ecs.emplace(data.teapot, glm::vec3 { 0, -1, 1 }, glm::vec3(0.5f)); ecs.emplace(data.teapot, glm::vec3 { 0, 1, 0 }, 0.0, 1.0); - renderer.add_shader(data.VERT, data.FRAG) - .on_error(Utily::ErrorHandler::print_then_quit) - .on_value([&](auto& id) { - data.s_id = id; - }); - renderer.add_vertex_buffer() - .on_error(Utily::ErrorHandler::print_then_quit) - .on_value([&](auto& id) { - data.vb_id = id; - }); - renderer.add_index_buffer() - .on_error(Utily::ErrorHandler::print_then_quit) - .on_value([&](auto& id) { - data.ib_id = id; - }); - renderer.add_vertex_array(Model::Vertex::VBL {}, data.vb_id) - .on_error(Utily::ErrorHandler::print_then_quit) - .on_value([&](auto& id) { - data.va_id = id; - }); + auto [s_h, s] = data.resource_manager.create_and_init_resource(data.VERT, data.FRAG); + auto [vb_h, vb] = data.resource_manager.create_and_init_resource(); + auto [ib_h, ib] = data.resource_manager.create_and_init_resource(); + auto [va_h, va] = data.resource_manager.create_and_init_resource(Model::Vertex::VBL {}, vb, ib); + + data.s_h = s_h; + data.vb_h = vb_h; + data.va_h = va_h; + data.ib_h = ib_h; + data.start_time = std::chrono::high_resolution_clock::now(); } void update(double dt, const Core::InputManager& input, AppState& state, entt::registry& ecs, SpinningTeapotData& data) { @@ -243,19 +247,16 @@ struct SpinningTeapotLogic { renderer.screen_frame_buffer.clear(); renderer.screen_frame_buffer.resize(renderer.window_width, renderer.window_height); - Core::IndexBuffer& ib = renderer.index_buffers[data.ib_id.id]; - Core::VertexBuffer& vb = renderer.vertex_buffers[data.vb_id.id]; - Core::VertexArray& va = renderer.vertex_arrays[data.va_id.id]; - Core::Shader& s = renderer.shaders[data.s_id.id]; + auto [s, va, vb, ib] = data.resource_manager.get_resources(data.s_h, data.va_h, data.vb_h, data.ib_h); s.bind(); s.set_uniform("u_mvp", mvp); va.bind(); - ib.bind(); vb.bind(); - ib.load_indices(model.indices); vb.load_vertices(model.vertices); + ib.bind(); + ib.load_indices(model.indices); glDrawElements(GL_TRIANGLES, ib.get_count(), GL_UNSIGNED_INT, (void*)0); } @@ -275,6 +276,9 @@ struct FontData { AppRenderer::VertexArrayId va_id; AppRenderer::TextureId t_id; + Renderer::ResourceManager resource_manager; + Renderer::FontRenderer font_renderer; + constexpr static std::string_view VERT = "precision highp float; " "uniform mat4 u_mvp;" @@ -312,10 +316,12 @@ struct FontLogic { data.s_id = renderer.add_shader(data.VERT, data.FRAG).on_error(report_error).value(); data.ib_id = renderer.add_index_buffer().on_error(report_error).value(); data.vb_id = renderer.add_vertex_buffer().on_error(report_error).value(); - data.va_id = renderer.add_vertex_array(Model::Vertex2D::VBL {}, data.vb_id).on_error(report_error).value(); + data.va_id = renderer.add_vertex_array(Model::Vertex2D::VBL {}, data.vb_id, data.ib_id).on_error(report_error).value(); data.t_id = renderer.add_texture(data.font_atlas.image).on_error(report_error).value(); data.start_time = std::chrono::high_resolution_clock::now(); + + data.font_renderer.init(data.resource_manager, data.font_atlas); } void update(float dt, const Core::InputManager& input, AppState& state, entt::registry& ecs, FontData& data) { auto duration = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - data.start_time); @@ -337,7 +343,9 @@ struct FontLogic { Core::Shader& s = renderer.shaders[data.s_id.id]; Core::Texture& t = renderer.textures[data.t_id.id]; - const static auto [verts, indis] = Media::FontMeshGenerator::generate_static_mesh("hello there", 100, { 50, 50 }, data.font_atlas); + const static auto [verts, indis] = Media::FontMeshGenerator::generate_static_mesh("hello there", 50, { 50, 50 }, data.font_atlas); + + data.font_renderer.draw(data.resource_manager, glm::vec2 { renderer.window_width, renderer.window_height }, "string2", 50, { 0, 0 }); s.bind(); @@ -395,8 +403,6 @@ void run_font_renderer() { st_app.poll_events(); st_app.update(); st_app.render(); - - }, 0, 1); diff --git a/code/build-web.bat b/code/build-web.bat index 63d8ed9..d0c1ed9 100644 --- a/code/build-web.bat +++ b/code/build-web.bat @@ -1,7 +1,7 @@ set VCPKG_PATH=C:/apps/vcpkg/vcpkg/ set EMSDK=C:/apps/emscripten/emsdk/ set EMSCRIPTEN=C:/apps/emscripten/emsdk/upstream/emscripten/ -set BUILD_TYPE=Release +set BUILD_TYPE=Debug if not exist "build-web\" ( mkdir build-web