From 8d5758c2d77902c5f031412ef4c7c39cb2bfd08a Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Thu, 25 Oct 2018 18:49:32 +0700 Subject: [PATCH] render command_block --- headers/enduro2d/core/render.hpp | 135 ++++++++++-- headers/enduro2d/core/render.inl | 53 +++++ samples/sources/sample_00/sample_00.cpp | 9 +- samples/sources/sample_01/sample_01.cpp | 9 +- samples/sources/sample_02/sample_02.cpp | 53 +++-- sources/enduro2d/core/render.cpp | 203 +++++++++++++++++- .../core/render_impl/render_opengl.cpp | 94 ++++---- 7 files changed, 452 insertions(+), 104 deletions(-) diff --git a/headers/enduro2d/core/render.hpp b/headers/enduro2d/core/render.hpp index bb5224cd..2fcedfc5 100644 --- a/headers/enduro2d/core/render.hpp +++ b/headers/enduro2d/core/render.hpp @@ -182,7 +182,7 @@ namespace e2d attribute_type type, bool normalized) noexcept; - const attribute_info& attribute(std::size_t i) const noexcept; + const attribute_info& attribute(std::size_t index) const noexcept; std::size_t attribute_count() const noexcept; std::size_t bytes_per_vertex() const noexcept; private: @@ -708,6 +708,118 @@ namespace e2d topology topology_ = topology::triangles; u8 _pad[7] = {0}; }; + + class swap_command final { + public: + swap_command() = default; + swap_command(bool vsync); + swap_command& vsync(bool value) noexcept; + bool& vsync() noexcept; + bool vsync() const noexcept; + private: + bool vsync_ = true; + }; + + class draw_command final { + public: + draw_command() = delete; + draw_command(const material& mat, const geometry& geo) noexcept; + + draw_command& material_ref(const material& value) noexcept; + draw_command& geometry_ref(const geometry& value) noexcept; + + const material& material_ref() const noexcept; + const geometry& geometry_ref() const noexcept; + + draw_command& properties(const property_block& value); + property_block& properties() noexcept; + const property_block& properties() const noexcept; + private: + const material* material_ = nullptr; + const geometry* geometry_ = nullptr; + property_block properties_; + }; + + class clear_command final { + public: + enum class buffer : u8 { + color = (1 << 0), + depth = (1 << 1), + stencil = (1 << 2), + color_depth = color | depth, + color_stencil = color | stencil, + depth_stencil = depth | stencil, + color_depth_stencil = color | depth | stencil + }; + public: + clear_command() = default; + clear_command(buffer clear_buffer) noexcept; + + clear_command& color_value(const color& value) noexcept; + clear_command& depth_value(f32 value) noexcept; + clear_command& stencil_value(u8 value) noexcept; + + color& color_value() noexcept; + f32& depth_value() noexcept; + u8& stencil_value() noexcept; + + const color& color_value() const noexcept; + f32 depth_value() const noexcept; + u8 stencil_value() const noexcept; + + buffer& clear_buffer() noexcept; + buffer clear_buffer() const noexcept; + private: + color color_value_ = color::clear(); + f32 depth_value_ = 1.f; + u8 stencil_value_ = 0; + buffer clear_buffer_ = buffer::color_depth_stencil; + u8 _pad[2] = {0}; + }; + + class viewport_command final { + public: + viewport_command() = default; + viewport_command(const b2u& rect) noexcept; + viewport_command& rect(const b2u& value) noexcept; + b2u& rect() noexcept; + const b2u& rect() const noexcept; + private: + b2u rect_; + }; + + class render_target_command final { + public: + render_target_command() = default; + render_target_command(const render_target_ptr& rt) noexcept; + render_target_command& render_target(const render_target_ptr& value) noexcept; + render_target_ptr& render_target() noexcept; + const render_target_ptr& render_target() const noexcept; + private: + render_target_ptr render_target_; + }; + + using command_value = stdex::variant< + swap_command, + draw_command, + clear_command, + viewport_command, + render_target_command>; + + template < std::size_t N > + class command_block final { + public: + command_block() = default; + + command_block& clear() noexcept; + command_block& add_command(command_value&& value); + command_block& add_command(const command_value& value); + const command_value& command(std::size_t index) const noexcept; + std::size_t command_count() const noexcept; + private: + array commands_; + std::size_t command_count_ = 0; + }; public: render(debug& d, window& w); ~render() noexcept final; @@ -746,20 +858,15 @@ namespace e2d const pixel_declaration& depth_decl, render_target::external_texture external_texture); - void draw( - const material& mat, - const geometry& geo); + template < std::size_t N > + render& execute(const command_block& commands); + render& execute(const command_value& command); - void draw( - const material& mat, - const geometry& geo, - const property_block& props); - - render& clear_depth_buffer(f32 value) noexcept; - render& clear_stencil_buffer(u8 value) noexcept; - render& clear_color_buffer(const color& value) noexcept; - render& set_viewport(const b2u& rect) noexcept; - render& set_render_target(const render_target_ptr& rt) noexcept; + render& execute(const swap_command& command); + render& execute(const draw_command& command); + render& execute(const clear_command& command); + render& execute(const viewport_command& command); + render& execute(const render_target_command& command); private: class internal_state; std::unique_ptr state_; diff --git a/headers/enduro2d/core/render.inl b/headers/enduro2d/core/render.inl index 3078cbdb..bbe834ea 100644 --- a/headers/enduro2d/core/render.inl +++ b/headers/enduro2d/core/render.inl @@ -106,6 +106,59 @@ namespace e2d stdex::invoke(f, p.first, p.second); } } + + // + // render::command_block + // + + template < std::size_t N > + render::command_block& render::command_block::clear() noexcept { + commands_.clear(); + return *this; + } + + template < std::size_t N > + render::command_block& render::command_block::add_command(command_value&& value) { + E2D_ASSERT(command_count_ < commands_.size()); + commands_[command_count_] = std::move(value); + ++command_count_; + return *this; + } + + template < std::size_t N > + render::command_block& render::command_block::add_command(const command_value& value) { + E2D_ASSERT(command_count_ < commands_.size()); + commands_[command_count_] = value; + ++command_count_; + return *this; + } + + template < std::size_t N > + const render::command_value& render::command_block::command(std::size_t index) const noexcept { + E2D_ASSERT(index < command_count_); + return commands_[index]; + } + + template < std::size_t N > + std::size_t render::command_block::command_count() const noexcept { + return command_count_; + } + + // + // render + // + + template < std::size_t N > + render& render::execute(const command_block& commands) { + E2D_ASSERT( + std::this_thread::get_id() == + modules::main_thread()); + + for ( std::size_t i = 0; i < commands.command_count(); ++i ) { + execute(commands.command(i)); + } + return *this; + } } #endif diff --git a/samples/sources/sample_00/sample_00.cpp b/samples/sources/sample_00/sample_00.cpp index 7f3c07a4..74cb1030 100644 --- a/samples/sources/sample_00/sample_00.cpp +++ b/samples/sources/sample_00/sample_00.cpp @@ -180,11 +180,10 @@ int e2d_main() { .property("u_time", game_time) .property("u_MVP", projection); - the() - .clear_depth_buffer(1.f) - .clear_stencil_buffer(0) - .clear_color_buffer({1.f, 0.4f, 0.f, 1.f}) - .draw(material, geometry); + the().execute(render::command_block<64>() + .add_command(render::clear_command() + .color_value({1.f, 0.4f, 0.f, 1.f})) + .add_command(render::draw_command(material, geometry))); the().swap_buffers(true); the().frame_tick(); diff --git a/samples/sources/sample_01/sample_01.cpp b/samples/sources/sample_01/sample_01.cpp index 353cff4c..85067c62 100644 --- a/samples/sources/sample_01/sample_01.cpp +++ b/samples/sources/sample_01/sample_01.cpp @@ -247,11 +247,10 @@ int e2d_main() { .property("u_time", game_time) .property("u_MVP", MVP); - the() - .clear_depth_buffer(1.f) - .clear_stencil_buffer(0) - .clear_color_buffer({1.f, 0.4f, 0.f, 1.f}) - .draw(material, geometry); + the().execute(render::command_block<64>() + .add_command(render::clear_command() + .color_value({1.f, 0.4f, 0.f, 1.f})) + .add_command(render::draw_command(material, geometry))); the().swap_buffers(true); the().frame_tick(); diff --git a/samples/sources/sample_02/sample_02.cpp b/samples/sources/sample_02/sample_02.cpp index 0e31a6e5..7f3030eb 100644 --- a/samples/sources/sample_02/sample_02.cpp +++ b/samples/sources/sample_02/sample_02.cpp @@ -205,37 +205,32 @@ int e2d_main() { .property("u_time", game_time) .property("u_MVP", MVP); - material.properties() - .sampler("u_texture", render::sampler_state() - .texture(texture) - .min_filter(render::sampler_min_filter::linear) - .mag_filter(render::sampler_mag_filter::linear)); + the().execute(render::command_block<64>() + .add_command(render::render_target_command(rt)) + .add_command(render::viewport_command(rt->size())) + .add_command(render::clear_command() + .color_value({0.f, 0.4f, 0.f, 1.f})) + .add_command(render::draw_command(material, geometry) + .properties(render::property_block() + .sampler("u_texture", render::sampler_state() + .texture(texture) + .min_filter(render::sampler_min_filter::linear) + .mag_filter(render::sampler_mag_filter::linear)))) + .add_command(render::render_target_command(nullptr)) + .add_command(render::viewport_command(the().real_size())) + .add_command(render::clear_command() + .color_value({1.f, 0.4f, 0.f, 1.f})) + .add_command(render::draw_command(material, geometry) + .properties(render::property_block() + .sampler("u_texture", render::sampler_state() + .texture(rt->color()) + .min_filter(render::sampler_min_filter::linear) + .mag_filter(render::sampler_mag_filter::linear)))) + .add_command(render::swap_command(true))); - the() - .set_render_target(rt) - .set_viewport(rt->size()) - .clear_depth_buffer(1.f) - .clear_stencil_buffer(0) - .clear_color_buffer({0.f, 0.4f, 0.f, 1.f}) - .draw(material, geometry); + std::this_thread::sleep_for( + time::to_chrono(make_milliseconds(10))); - material.properties() - .sampler("u_texture", render::sampler_state() - .texture(rt->color()) - .min_filter(render::sampler_min_filter::linear) - .mag_filter(render::sampler_mag_filter::linear)); - - the() - .set_render_target(nullptr) - .set_viewport(the().real_size()) - .clear_depth_buffer(1.f) - .clear_stencil_buffer(0) - .clear_color_buffer({1.f, 0.4f, 0.f, 1.f}) - .draw(material, geometry); - - std::this_thread::sleep_for(time::to_chrono(make_milliseconds(10))); - - the().swap_buffers(true); the().frame_tick(); window::poll_events(); } diff --git a/sources/enduro2d/core/render.cpp b/sources/enduro2d/core/render.cpp index e2aa1afd..fd60eebf 100644 --- a/sources/enduro2d/core/render.cpp +++ b/sources/enduro2d/core/render.cpp @@ -75,6 +75,34 @@ namespace } #undef DEFINE_CASE } + + class command_value_visitor final : private noncopyable { + public: + command_value_visitor(render& render) noexcept + : render_(render) {} + + void operator()(const render::swap_command& command) const { + render_.execute(command); + } + + void operator()(const render::draw_command& command) const { + render_.execute(command); + } + + void operator()(const render::clear_command& command) const { + render_.execute(command); + } + + void operator()(const render::viewport_command& command) const { + render_.execute(command); + } + + void operator()(const render::render_target_command& command) const { + render_.execute(command); + } + private: + render& render_; + }; } namespace e2d @@ -199,9 +227,9 @@ namespace e2d return *this; } - const vertex_declaration::attribute_info& vertex_declaration::attribute(std::size_t i) const noexcept { - E2D_ASSERT(i < attribute_count_); - return attributes_[i]; + const vertex_declaration::attribute_info& vertex_declaration::attribute(std::size_t index) const noexcept { + E2D_ASSERT(index < attribute_count_); + return attributes_[index]; } std::size_t vertex_declaration::attribute_count() const noexcept { @@ -828,4 +856,173 @@ namespace e2d E2D_ASSERT(index < vertices_count_); return vertices_[index]; } + + // + // swap_command + // + + render::swap_command::swap_command(bool vsync) + : vsync_(vsync) {} + + render::swap_command& render::swap_command::vsync(bool value) noexcept { + vsync_ = value; + return *this; + } + + bool& render::swap_command::vsync() noexcept { + return vsync_; + } + + bool render::swap_command::vsync() const noexcept { + return vsync_; + } + + // + // draw_command + // + + render::draw_command::draw_command(const material& mat, const geometry& geo) noexcept + : material_(&mat) + , geometry_(&geo) {} + + render::draw_command& render::draw_command::material_ref(const material& value) noexcept { + material_ = &value; + return *this; + } + + render::draw_command& render::draw_command::geometry_ref(const geometry& value) noexcept { + geometry_ = &value; + return *this; + } + + const render::material& render::draw_command::material_ref() const noexcept { + E2D_ASSERT(material_); + return *material_; + } + + const render::geometry& render::draw_command::geometry_ref() const noexcept { + E2D_ASSERT(geometry_); + return *geometry_; + } + + render::draw_command& render::draw_command::properties(const property_block& value) { + properties_ = value; + return *this; + } + + render::property_block& render::draw_command::properties() noexcept { + return properties_; + } + + const render::property_block& render::draw_command::properties() const noexcept { + return properties_; + } + + // + // clear_command + // + + render::clear_command::clear_command(buffer clear_buffer) noexcept + : clear_buffer_(clear_buffer) {} + + render::clear_command& render::clear_command::color_value(const color& value) noexcept { + color_value_ = value; + return *this; + } + + render::clear_command& render::clear_command::depth_value(f32 value) noexcept { + depth_value_ = value; + return *this; + } + + render::clear_command& render::clear_command::stencil_value(u8 value) noexcept { + stencil_value_ = value; + return *this; + } + + color& render::clear_command::color_value() noexcept { + return color_value_; + } + + f32& render::clear_command::depth_value() noexcept { + return depth_value_; + } + + u8& render::clear_command::stencil_value() noexcept { + return stencil_value_; + } + + const color& render::clear_command::color_value() const noexcept { + return color_value_; + } + + f32 render::clear_command::depth_value() const noexcept { + return depth_value_; + } + + u8 render::clear_command::stencil_value() const noexcept { + return stencil_value_; + } + + render::clear_command::buffer& render::clear_command::clear_buffer() noexcept { + return clear_buffer_; + } + + render::clear_command::buffer render::clear_command::clear_buffer() const noexcept { + return clear_buffer_; + } + + // + // viewport_command + // + + render::viewport_command::viewport_command(const b2u& rect) noexcept + : rect_(rect) {} + + render::viewport_command& render::viewport_command::rect(const b2u& value) noexcept { + rect_ = value; + return *this; + } + + b2u& render::viewport_command::rect() noexcept { + return rect_; + } + + const b2u& render::viewport_command::rect() const noexcept { + return rect_; + } + + // + // render_target_command + // + + render::render_target_command::render_target_command(const render_target_ptr& rt) noexcept + : render_target_(rt) {} + + render::render_target_command& render::render_target_command::render_target(const render_target_ptr& value) noexcept { + render_target_ = value; + return *this; + } + + render_target_ptr& render::render_target_command::render_target() noexcept { + return render_target_; + } + + const render_target_ptr& render::render_target_command::render_target() const noexcept { + return render_target_; + } + + // + // render + // + + render& render::execute(const command_value& command) { + E2D_ASSERT( + std::this_thread::get_id() == + modules::main_thread()); + + E2D_ASSERT(!command.valueless_by_exception()); + stdex::visit(command_value_visitor(*this), command); + return *this; + } } diff --git a/sources/enduro2d/core/render_impl/render_opengl.cpp b/sources/enduro2d/core/render_impl/render_opengl.cpp index f9869a75..456f2637 100644 --- a/sources/enduro2d/core/render_impl/render_opengl.cpp +++ b/sources/enduro2d/core/render_impl/render_opengl.cpp @@ -17,7 +17,7 @@ namespace class property_block_value_visitor : private noncopyable { public: - property_block_value_visitor(debug& debug, uniform_info ui) + property_block_value_visitor(debug& debug, uniform_info ui) noexcept : debug_(debug) , ui_(std::move(ui)) {} @@ -768,26 +768,24 @@ namespace e2d std::move(depth_rb))); } - void render::draw( - const material& mat, - const geometry& geo) - { + render& render::execute(const swap_command& command) { E2D_ASSERT( std::this_thread::get_id() == modules::main_thread()); - draw(mat, geo, property_block()); + state_->wnd().swap_buffers(command.vsync()); + return *this; } - void render::draw( - const material& mat, - const geometry& geo, - const property_block& props) - { + render& render::execute(const draw_command& command) { E2D_ASSERT( std::this_thread::get_id() == modules::main_thread()); + const material& mat = command.material_ref(); + const geometry& geo = command.geometry_ref(); + const property_block& props = command.properties(); + for ( std::size_t i = 0; i < mat.pass_count(); ++i ) { const pass_state& pass = mat.pass(i); const property_block& main_props = main_property_cache() @@ -802,59 +800,59 @@ namespace e2d }); }); } - } - - render& render::clear_depth_buffer(f32 value) noexcept { - E2D_ASSERT( - std::this_thread::get_id() == - modules::main_thread()); - - const render_target_ptr& rt = state_->render_target(); - if ( !rt || rt->state().depth() || !rt->state().depth_rb().empty() ) { - GL_CHECK_CODE(state_->dbg(), glClearDepth( - math::numeric_cast(math::saturate(value)))); - GL_CHECK_CODE(state_->dbg(), glClear(GL_DEPTH_BUFFER_BIT)); - } return *this; } - render& render::clear_stencil_buffer(u8 value) noexcept { + render& render::execute(const clear_command& command) { E2D_ASSERT( std::this_thread::get_id() == modules::main_thread()); - const render_target_ptr& rt = state_->render_target(); - if ( !rt || rt->state().depth() || !rt->state().depth_rb().empty() ) { - GL_CHECK_CODE(state_->dbg(), glClearStencil( - math::numeric_cast(value))); - GL_CHECK_CODE(state_->dbg(), glClear(GL_STENCIL_BUFFER_BIT)); - } - return *this; - } - - render& render::clear_color_buffer(const color& value) noexcept { - E2D_ASSERT( - std::this_thread::get_id() == - modules::main_thread()); + bool clear_color = + math::enum_to_number(command.clear_buffer()) + & math::enum_to_number(clear_command::buffer::color); + bool clear_depth = + math::enum_to_number(command.clear_buffer()) + & math::enum_to_number(clear_command::buffer::depth); + bool clear_stencil = + math::enum_to_number(command.clear_buffer()) + & math::enum_to_number(clear_command::buffer::stencil); const render_target_ptr& rt = state_->render_target(); - if ( !rt || rt->state().color() || !rt->state().color_rb().empty() ) { + bool has_color = !rt || rt->state().color() || !rt->state().color_rb().empty(); + bool has_depth = !rt || rt->state().depth() || !rt->state().depth_rb().empty(); + + GLbitfield clear_mask = 0; + if ( has_color && clear_color ) { + clear_mask |= GL_COLOR_BUFFER_BIT; GL_CHECK_CODE(state_->dbg(), glClearColor( - math::numeric_cast(math::saturate(value.r)), - math::numeric_cast(math::saturate(value.g)), - math::numeric_cast(math::saturate(value.b)), - math::numeric_cast(math::saturate(value.a)))); - GL_CHECK_CODE(state_->dbg(), glClear(GL_COLOR_BUFFER_BIT)); + math::numeric_cast(math::saturate(command.color_value().r)), + math::numeric_cast(math::saturate(command.color_value().g)), + math::numeric_cast(math::saturate(command.color_value().b)), + math::numeric_cast(math::saturate(command.color_value().a)))); } + if ( has_depth ) { + if ( clear_depth ) { + clear_mask |= GL_DEPTH_BUFFER_BIT; + GL_CHECK_CODE(state_->dbg(), glClearDepth( + math::numeric_cast(math::saturate(command.depth_value())))); + } + if ( clear_stencil ) { + clear_mask |= GL_STENCIL_BUFFER_BIT; + GL_CHECK_CODE(state_->dbg(), glClearStencil( + math::numeric_cast(command.stencil_value()))); + } + } + GL_CHECK_CODE(state_->dbg(), glClear(clear_mask)); return *this; } - render& render::set_viewport(const b2u& rect) noexcept { + render& render::execute(const viewport_command& command) { E2D_ASSERT( std::this_thread::get_id() == modules::main_thread()); - const b2u vp = make_minmax_rect(rect); + const b2u vp = make_minmax_rect(command.rect()); GL_CHECK_CODE(state_->dbg(), glViewport( math::numeric_cast(vp.position.x), math::numeric_cast(vp.position.y), @@ -863,12 +861,12 @@ namespace e2d return *this; } - render& render::set_render_target(const render_target_ptr& rt) noexcept { + render& render::execute(const render_target_command& command) { E2D_ASSERT( std::this_thread::get_id() == modules::main_thread()); - state_->set_render_target(rt); + state_->set_render_target(command.render_target()); return *this; } }