diff --git a/src/Cool/Exporter/ExporterU.cpp b/src/Cool/Exporter/ExporterU.cpp index 316d2dd07..cf9c48293 100644 --- a/src/Cool/Exporter/ExporterU.cpp +++ b/src/Cool/Exporter/ExporterU.cpp @@ -5,10 +5,10 @@ namespace Cool::ExporterU { -void export_image(img::Size size, Time time, Time delta_time, Polaroid polaroid, std::filesystem::path const& file_path) +void export_image(img::Size size, Time time, Time delta_time, Polaroid const& polaroid, std::filesystem::path const& file_path) { - polaroid.render(time, delta_time, size); - ImageU::save_png(file_path, polaroid.render_target.download_pixels()); + polaroid.render(size, time, delta_time); + ImageU::save_png(file_path, polaroid.texture().download_pixels()); } } // namespace Cool::ExporterU diff --git a/src/Cool/Exporter/ExporterU.h b/src/Cool/Exporter/ExporterU.h index 80c4016cc..e2d0e5603 100644 --- a/src/Cool/Exporter/ExporterU.h +++ b/src/Cool/Exporter/ExporterU.h @@ -9,6 +9,6 @@ namespace Cool::ExporterU { * * @param file_path The name of the file that you want to create */ -void export_image(img::Size size, Time time, Time delta_time, Polaroid polaroid, std::filesystem::path const& file_path); +void export_image(img::Size size, Time time, Time delta_time, Polaroid const& polaroid, std::filesystem::path const& file_path); } // namespace Cool::ExporterU diff --git a/src/Cool/Exporter/VideoExportProcess.cpp b/src/Cool/Exporter/VideoExportProcess.cpp index fd23e3d8c..9e3bb9b59 100644 --- a/src/Cool/Exporter/VideoExportProcess.cpp +++ b/src/Cool/Exporter/VideoExportProcess.cpp @@ -24,7 +24,7 @@ VideoExportProcess::VideoExportProcess(VideoExportParams const& params, TimeSpee _thread_pool.start(); } -bool VideoExportProcess::update(Polaroid polaroid) +bool VideoExportProcess::update(Polaroid const& polaroid) { if (_should_stop_asap || _nb_frames_which_finished_exporting.load() == _total_nb_of_frames_in_sequence) return true; // The export is finished @@ -100,13 +100,13 @@ void VideoExportProcess::imgui(std::function const& extra_widgets) extra_widgets(); } -void VideoExportProcess::export_frame(Polaroid polaroid, std::filesystem::path const& file_path) +void VideoExportProcess::export_frame(Polaroid const& polaroid, std::filesystem::path const& file_path) { - polaroid.render(_clock.time(), _clock.delta_time(), _size); + polaroid.render(_size, _clock.time(), _clock.delta_time()); _thread_pool.push_job(ImageExportJob{ file_path, - polaroid.render_target.download_pixels(), + polaroid.texture().download_pixels(), _average_export_time, _average_export_time_mutex, _nb_frames_which_finished_exporting diff --git a/src/Cool/Exporter/VideoExportProcess.h b/src/Cool/Exporter/VideoExportProcess.h index f8f98ffdb..7f5e9e4dc 100644 --- a/src/Cool/Exporter/VideoExportProcess.h +++ b/src/Cool/Exporter/VideoExportProcess.h @@ -12,7 +12,7 @@ namespace Cool { class VideoExportProcess { public: VideoExportProcess(VideoExportParams const& params, TimeSpeed time_speed, std::filesystem::path const& folder_path, img::Size size); - auto update(Polaroid polaroid) -> bool; + auto update(Polaroid const& polaroid) -> bool; void imgui(std::function const& extra_widgets); auto clock() const -> Clock const& { return _clock; } @@ -20,7 +20,7 @@ class VideoExportProcess { private: auto estimated_remaining_time() -> Time; void update_time_estimate(); - void export_frame(Polaroid polaroid, std::filesystem::path const& file_path); + void export_frame(Polaroid const& polaroid, std::filesystem::path const& file_path); private: std::filesystem::path _folder_path; diff --git a/src/Cool/Exporter/internal/Polaroid.h b/src/Cool/Exporter/internal/Polaroid.h index 66e2ec60f..8ae10c420 100644 --- a/src/Cool/Exporter/internal/Polaroid.h +++ b/src/Cool/Exporter/internal/Polaroid.h @@ -5,20 +5,10 @@ namespace Cool { struct Polaroid { - // The target that we will render to - RenderTarget& render_target; + // The texture that we will render to + std::function texture; // The function that renders the desired image - std::function render_fn; - - void render(Time time, Time delta_time) - { - render_fn(render_target, time, delta_time); - } - void render(Time time, Time delta_time, img::Size size) - { - render_target.set_size(size); - render(time, delta_time); - } + std::function render; }; } // namespace Cool \ No newline at end of file diff --git a/src/Cool/Gpu/OpenGL/Texture.h b/src/Cool/Gpu/OpenGL/Texture.h index b081ed5ff..d34618c66 100644 --- a/src/Cool/Gpu/OpenGL/Texture.h +++ b/src/Cool/Gpu/OpenGL/Texture.h @@ -1,6 +1,7 @@ #if defined(COOL_OPENGL) #pragma once #include +#include "TextureRef.hpp" #include "glpp-extended/src/Texture2D.h" #include "img/src/Image.h" #include "img/src/Size.h" diff --git a/src/Cool/Gpu/OpenGL/TextureRef.cpp b/src/Cool/Gpu/OpenGL/TextureRef.cpp new file mode 100644 index 000000000..71506ba6d --- /dev/null +++ b/src/Cool/Gpu/OpenGL/TextureRef.cpp @@ -0,0 +1,29 @@ +#include "TextureRef.hpp" +#include "Cool/Gpu/OpenGL/copy_tex_pipeline.hpp" +#include "Cool/Gpu/RenderTarget.h" +#include "glpp/glpp.hpp" + +namespace Cool { + +auto TextureRef::download_pixels() const -> img::Image +{ + static auto rt = RenderTarget{}; + rt.set_size(size); + std::unique_ptr data{new uint8_t[4 * width() * height()]}; + rt.render([&]() { + // TODO(WebGPU) We shouldn't need to run a shader to get the texture on a framebuffer and then download its pixels + Cool::copy_tex_pipeline().shader()->bind(); + Cool::copy_tex_pipeline().shader()->set_uniform_texture("tex_to_copy", id); + glDisable(GL_BLEND); + Cool::copy_tex_pipeline().draw(); + glEnable(GL_BLEND); + glReadPixels(0, 0, static_cast(width()), static_cast(height()), GL_RGBA, GL_UNSIGNED_BYTE, data.get()); + }); + return img::Image{ + img::Size{width(), height()}, + 4, + data.release() + }; +} + +} // namespace Cool \ No newline at end of file diff --git a/src/Cool/Gpu/OpenGL/TextureRef.hpp b/src/Cool/Gpu/OpenGL/TextureRef.hpp new file mode 100644 index 000000000..8e04d3a45 --- /dev/null +++ b/src/Cool/Gpu/OpenGL/TextureRef.hpp @@ -0,0 +1,17 @@ +#pragma once + +namespace Cool { + +struct TextureRef { + GLuint id{}; + img::Size size{}; + + auto width() const { return size.width(); } + auto height() const { return size.height(); } + + [[nodiscard]] auto imgui_texture_id() const -> ImTextureID { return reinterpret_cast(static_cast(id)); } + + auto download_pixels() const -> img::Image; +}; + +} // namespace Cool \ No newline at end of file diff --git a/src/Cool/Gpu/OpenGL/copy_tex_pipeline.cpp b/src/Cool/Gpu/OpenGL/copy_tex_pipeline.cpp new file mode 100644 index 000000000..107bdf1f4 --- /dev/null +++ b/src/Cool/Gpu/OpenGL/copy_tex_pipeline.cpp @@ -0,0 +1,38 @@ +#include "copy_tex_pipeline.hpp" + +namespace Cool { + +static auto make_copy_tex_pipeline() -> Cool::OpenGL::FullscreenPipeline +{ + auto instance = Cool::OpenGL::FullscreenPipeline{}; + auto const err = instance.compile(R"GLSL( +#version 410 +#include "_COOL_RES_/shaders/shader-utils.glsl" +out vec4 out_Color; +uniform sampler2D tex_to_copy; +void main() +{ + out_Color = texture(tex_to_copy, _uv); +} + )GLSL"); + err.send_error_if_any( + [&](std::string const& message) { + return Cool::Message{ + .category = "Internal", + .message = "Failed to create shader to copy texture:\n" + message, + .severity = Cool::MessageSeverity::Error, + }; + }, + Cool::Log::ToUser::console() + ); + + return instance; +} + +auto copy_tex_pipeline() -> Cool::OpenGL::FullscreenPipeline& +{ + static auto instance = make_copy_tex_pipeline(); + return instance; +} + +} // namespace Cool \ No newline at end of file diff --git a/src/Cool/Gpu/OpenGL/copy_tex_pipeline.hpp b/src/Cool/Gpu/OpenGL/copy_tex_pipeline.hpp new file mode 100644 index 000000000..59dc9052a --- /dev/null +++ b/src/Cool/Gpu/OpenGL/copy_tex_pipeline.hpp @@ -0,0 +1,8 @@ +#pragma once +#include "FullscreenPipeline.h" + +namespace Cool { + +auto copy_tex_pipeline() -> Cool::OpenGL::FullscreenPipeline&; + +} \ No newline at end of file diff --git a/src/Cool/Gpu/internal/RenderTarget_Base.h b/src/Cool/Gpu/internal/RenderTarget_Base.h index f943f0e91..afac91f1e 100644 --- a/src/Cool/Gpu/internal/RenderTarget_Base.h +++ b/src/Cool/Gpu/internal/RenderTarget_Base.h @@ -1,7 +1,7 @@ #pragma once - #include #include "../RenderTargetInfo.h" +#include "Cool/Gpu/OpenGL/TextureRef.hpp" namespace Cool { @@ -11,6 +11,7 @@ class RenderTarget_Base { void render(typename RenderTarget_Impl::RenderFuncType render_fn); img::Image download_pixels() const { return _impl.download_pixels(); } ImTextureID imgui_texture_id() const { return _impl.imgui_texture_id(); } + auto texture_ref() const -> TextureRef { return _impl.texture_ref(); } RenderTargetInfo info() const { return _impl.info(); } img::Size current_size() const { return _impl.size(); } img::Size desired_size() const { return _desired_size; } diff --git a/src/Cool/Gpu/internal/RenderTarget_ImplOpenGL.cpp b/src/Cool/Gpu/internal/RenderTarget_ImplOpenGL.cpp index 3a438ffd0..b5d6cd432 100644 --- a/src/Cool/Gpu/internal/RenderTarget_ImplOpenGL.cpp +++ b/src/Cool/Gpu/internal/RenderTarget_ImplOpenGL.cpp @@ -11,9 +11,9 @@ RenderTarget_ImplOpenGL::RenderTarget_ImplOpenGL(img::Size size) void RenderTarget_ImplOpenGL::render(RenderFuncType render_fn) { - _texture.bind(); + _texture_fb.bind(); render_fn(); - _texture.unbind(); + _texture_fb.unbind(); } RenderTargetInfo RenderTarget_ImplOpenGL::info() const @@ -21,24 +21,27 @@ RenderTargetInfo RenderTarget_ImplOpenGL::info() const return RenderTargetInfo{ .viewport = { .size = size(), - .bottom_left_corner = {0, 0}}}; + .bottom_left_corner = {0, 0} + } + }; } void RenderTarget_ImplOpenGL::resize(img::Size size) { - _texture.setSize(size); + _texture_fb.setSize(size); } img::Image RenderTarget_ImplOpenGL::download_pixels() const { - _texture.bind(); + _texture_fb.bind(); std::unique_ptr data{new uint8_t[4 * width() * height()]}; glReadPixels(0, 0, static_cast(width()), static_cast(height()), GL_RGBA, GL_UNSIGNED_BYTE, data.get()); - _texture.unbind(); + _texture_fb.unbind(); return img::Image{ img::Size{width(), height()}, 4, - data.release()}; + data.release() + }; } } // namespace Cool diff --git a/src/Cool/Gpu/internal/RenderTarget_ImplOpenGL.h b/src/Cool/Gpu/internal/RenderTarget_ImplOpenGL.h index feb6bcf1b..fec358665 100644 --- a/src/Cool/Gpu/internal/RenderTarget_ImplOpenGL.h +++ b/src/Cool/Gpu/internal/RenderTarget_ImplOpenGL.h @@ -1,7 +1,7 @@ #pragma once #if defined(COOL_OPENGL) - #include +#include "../OpenGL/Texture.h" #include "../OpenGL/TextureFB.h" #include "../RenderTargetInfo.h" @@ -14,19 +14,20 @@ class RenderTarget_ImplOpenGL { void render(RenderFuncType render_fn); RenderTargetInfo info() const; - img::Size::DataType width() const { return _texture.width(); } - img::Size::DataType height() const { return _texture.height(); } - img::Size size() const { return _texture.size(); } + img::Size::DataType width() const { return _texture_fb.width(); } + img::Size::DataType height() const { return _texture_fb.height(); } + img::Size size() const { return _texture_fb.size(); } img::Image download_pixels() const; - GLuint texture_id() const { return _texture.textureID(); } - ImTextureID imgui_texture_id() const { return reinterpret_cast(static_cast(_texture.textureID())); } // Double-cast to fix a warning : first we convert to the correct size (uint32_t -> uint64_t) then from integral type to pointer type (uint64_t -> ImTextureID) + GLuint texture_id() const { return _texture_fb.textureID(); } + auto texture_ref() const -> TextureRef { return TextureRef{texture_id(), size()}; } + ImTextureID imgui_texture_id() const { return reinterpret_cast(static_cast(_texture_fb.textureID())); } // Double-cast to fix a warning : first we convert to the correct size (uint32_t -> uint64_t) then from integral type to pointer type (uint64_t -> ImTextureID) void imgui_window(); void resize(img::Size size); private: - TextureFB _texture; + TextureFB _texture_fb; }; } // namespace Cool diff --git a/src/Cool/View/ForwardingOrRenderView.h b/src/Cool/View/ForwardingOrTextureView.hpp similarity index 73% rename from src/Cool/View/ForwardingOrRenderView.h rename to src/Cool/View/ForwardingOrTextureView.hpp index b6d1fe1bc..384b8b4ed 100644 --- a/src/Cool/View/ForwardingOrRenderView.h +++ b/src/Cool/View/ForwardingOrTextureView.hpp @@ -1,22 +1,22 @@ #pragma once -#include "RenderView.h" +#include "TextureView.hpp" namespace Cool { -class ForwardingOrRenderView : public RenderView { +class ForwardingOrTextureView : public TextureView { public: - ForwardingOrRenderView(View const& forwarded_view, ViewCreationParams const& p) - : RenderView{p} + ForwardingOrTextureView(View const& forwarded_view, ViewCreationParams const& p) + : TextureView{p} , _forwarded_view{forwarded_view} {} private: auto get_image_texture_id() const -> ImTextureID override { return _forwarded_view.is_open() ? _forwarded_view.get_image_texture_id() - : RenderView::get_image_texture_id(); } + : TextureView::get_image_texture_id(); } auto get_image_size() const -> img::Size override { return _forwarded_view.is_open() ? _forwarded_view.get_image_size() - : RenderView::get_image_size(); } + : TextureView::get_image_size(); } private: View const& _forwarded_view; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members) diff --git a/src/Cool/View/RenderView.cpp b/src/Cool/View/RenderView.cpp deleted file mode 100644 index c7902fc77..000000000 --- a/src/Cool/View/RenderView.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "RenderView.h" - -namespace Cool { - -void RenderView::update_size(ImageSizeConstraint const& constraint) -{ - if (!window_size().has_value()) - return; - _render_target.set_size(constraint.applied_to(*window_size())); -} - -auto RenderView::get_image_texture_id() const -> ImTextureID -{ - return _render_target.imgui_texture_id(); -} -auto RenderView::get_image_size() const -> img::Size -{ - return _render_target.current_size(); -} - -} // namespace Cool \ No newline at end of file diff --git a/src/Cool/View/RenderView.h b/src/Cool/View/RenderView.h deleted file mode 100644 index 8ab2d883f..000000000 --- a/src/Cool/View/RenderView.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once -#include -#include -#include "View.h" - -namespace Cool { - -/// A View that uses a RenderTarget as its image. -/// You can render on that render target however you want. -class RenderView : public View { -public: - using View::View; - - void update_size(ImageSizeConstraint const&); - - auto render_target() -> RenderTarget& { return _render_target; } - auto render_target() const -> RenderTarget const& { return _render_target; } - -protected: - auto get_image_texture_id() const -> ImTextureID override; - auto get_image_size() const -> img::Size override; - -private: - RenderTarget _render_target; -}; - -} // namespace Cool diff --git a/src/Cool/View/TextureView.cpp b/src/Cool/View/TextureView.cpp new file mode 100644 index 000000000..26ba2a4c1 --- /dev/null +++ b/src/Cool/View/TextureView.cpp @@ -0,0 +1,21 @@ +#include "TextureView.hpp" + +namespace Cool { + +auto TextureView::desired_image_size(ImageSizeConstraint const& constraint) const -> img::Size +{ + if (!window_size().has_value()) + return {}; + return constraint.applied_to(*window_size()); +} + +auto TextureView::get_image_texture_id() const -> ImTextureID +{ + return _texture.imgui_texture_id(); +} +auto TextureView::get_image_size() const -> img::Size +{ + return _texture.size; +} + +} // namespace Cool \ No newline at end of file diff --git a/src/Cool/View/TextureView.hpp b/src/Cool/View/TextureView.hpp new file mode 100644 index 000000000..28419398c --- /dev/null +++ b/src/Cool/View/TextureView.hpp @@ -0,0 +1,24 @@ +#pragma once +#include +#include +#include "View.h" + +namespace Cool { + +/// A View that displays a given texture. +class TextureView : public View { +public: + using View::View; + + void set_texture(TextureRef texture) { _texture = texture; } + auto desired_image_size(ImageSizeConstraint const&) const -> img::Size; + +protected: + auto get_image_texture_id() const -> ImTextureID override; + auto get_image_size() const -> img::Size override; + +private: + TextureRef _texture{}; +}; + +} // namespace Cool diff --git a/src/Cool/View/View.h b/src/Cool/View/View.h index 6e70f33ae..2e7787ebc 100644 --- a/src/Cool/View/View.h +++ b/src/Cool/View/View.h @@ -78,7 +78,7 @@ class View { auto is_open() const -> bool { return _is_open; } private: /// Child classes need to implement these functions in order for us to display their image in the View. - friend class ForwardingOrRenderView; + friend class ForwardingOrTextureView; virtual auto get_image_texture_id() const -> ImTextureID = 0; virtual auto get_image_size() const -> img::Size = 0;