diff --git a/headers/enduro2d/core/render.hpp b/headers/enduro2d/core/render.hpp index 186fdd76..34820a83 100644 --- a/headers/enduro2d/core/render.hpp +++ b/headers/enduro2d/core/render.hpp @@ -49,6 +49,8 @@ namespace e2d depth32, depth24_stencil8, + g8, + ga8, rgb8, rgba8, @@ -82,6 +84,7 @@ namespace e2d bool is_stencil() const noexcept; bool is_compressed() const noexcept; std::size_t bits_per_pixel() const noexcept; + v2u compressed_block_size() const noexcept; private: pixel_type type_ = pixel_type::rgba8; }; @@ -264,7 +267,6 @@ namespace e2d explicit index_buffer(internal_state_uptr); ~index_buffer() noexcept; public: - void update(buffer_view indices, std::size_t offset) noexcept; std::size_t buffer_size() const noexcept; std::size_t index_count() const noexcept; const index_declaration& decl() const noexcept; @@ -291,7 +293,6 @@ namespace e2d explicit vertex_buffer(internal_state_uptr); ~vertex_buffer() noexcept; public: - void update(buffer_view vertices, std::size_t offset) noexcept; std::size_t buffer_size() const noexcept; std::size_t vertex_count() const noexcept; const vertex_declaration& decl() const noexcept; @@ -940,6 +941,26 @@ namespace e2d render& execute(const target_command& command); render& execute(const viewport_command& command); + render& update_buffer( + const index_buffer_ptr& ibuffer, + buffer_view indices, + std::size_t offset); + + render& update_buffer( + const vertex_buffer_ptr& vbuffer, + buffer_view vertices, + std::size_t offset); + + render& update_texture( + const texture_ptr& tex, + const image& img, + v2u offset); + + render& update_texture( + const texture_ptr& tex, + buffer_view pixels, + const b2u& region); + const device_caps& device_capabilities() const noexcept; bool is_pixel_supported(const pixel_declaration& decl) const noexcept; bool is_index_supported(const index_declaration& decl) const noexcept; diff --git a/headers/enduro2d/utils/buffer.hpp b/headers/enduro2d/utils/buffer.hpp index 775c3200..beab74d5 100644 --- a/headers/enduro2d/utils/buffer.hpp +++ b/headers/enduro2d/utils/buffer.hpp @@ -11,6 +11,18 @@ namespace e2d { class buffer final { + public: + using value_type = u8; + + using pointer = u8*; + using const_pointer = const u8*; + using reference = u8&; + using const_reference = const u8&; + + using iterator = pointer; + using const_iterator = const_pointer; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; public: buffer() = default; @@ -23,6 +35,22 @@ namespace e2d explicit buffer(std::size_t size); buffer(const void* src, std::size_t size); + iterator begin() noexcept; + const_iterator begin() const noexcept; + const_iterator cbegin() const noexcept; + + iterator end() noexcept; + const_iterator end() const noexcept; + const_iterator cend() const noexcept; + + reverse_iterator rbegin() noexcept; + const_reverse_iterator rbegin() const noexcept; + const_reverse_iterator crbegin() const noexcept; + + reverse_iterator rend() noexcept; + const_reverse_iterator rend() const noexcept; + const_reverse_iterator crend() const noexcept; + buffer& fill(u8 ch) noexcept; buffer& resize(std::size_t nsize); diff --git a/headers/enduro2d/utils/buffer_view.hpp b/headers/enduro2d/utils/buffer_view.hpp index a9f814f5..78a2eff0 100644 --- a/headers/enduro2d/utils/buffer_view.hpp +++ b/headers/enduro2d/utils/buffer_view.hpp @@ -30,6 +30,9 @@ namespace e2d template < typename T, std::size_t N > buffer_view(const std::array& buffer) noexcept; + template < typename T, std::size_t N > + buffer_view(const T (&buffer)[N]) noexcept; + const void* data() const noexcept; std::size_t size() const noexcept; bool empty() const noexcept; @@ -56,4 +59,9 @@ namespace e2d buffer_view::buffer_view(const std::array& buffer) noexcept : data_(buffer.data()) , size_(buffer.size() * sizeof(T)) {} + + template < typename T, std::size_t N > + buffer_view::buffer_view(const T (&buffer)[N]) noexcept + : data_(&buffer[0]) + , size_(std::size(buffer) * sizeof(T)) {} } diff --git a/sources/enduro2d/core/dbgui.cpp b/sources/enduro2d/core/dbgui.cpp index f4d80a1a..7b2d7dd7 100644 --- a/sources/enduro2d/core/dbgui.cpp +++ b/sources/enduro2d/core/dbgui.cpp @@ -303,7 +303,7 @@ namespace e2d void update_index_buffer(buffer_view indices) { if ( index_buffer_ && index_buffer_->buffer_size() >= indices.size() ) { - index_buffer_->update(indices, 0); + render_.update_buffer(index_buffer_, indices, 0u); return; } @@ -329,7 +329,7 @@ namespace e2d void update_vertex_buffer(buffer_view vertices) { if ( vertex_buffer_ && vertex_buffer_->buffer_size() >= vertices.size() ) { - vertex_buffer_->update(vertices, 0); + render_.update_buffer(vertex_buffer_, vertices, 0u); return; } diff --git a/sources/enduro2d/core/render.cpp b/sources/enduro2d/core/render.cpp index 88b48466..8a79b5ad 100644 --- a/sources/enduro2d/core/render.cpp +++ b/sources/enduro2d/core/render.cpp @@ -18,32 +18,35 @@ namespace bool stencil; pixel_declaration::pixel_type type; bool compressed; + v2u block_size; }; const pixel_type_description pixel_type_descriptions[] = { - {"depth16", 16, false, true, false, pixel_declaration::pixel_type::depth16, false}, - {"depth24", 24, false, true, false, pixel_declaration::pixel_type::depth24, false}, - {"depth32", 32, false, true, false, pixel_declaration::pixel_type::depth32, false}, - {"depth24_stencil8", 32, false, true, true, pixel_declaration::pixel_type::depth24_stencil8, false}, + {"depth16", 16, false, true, false, pixel_declaration::pixel_type::depth16, false, v2u(1)}, + {"depth24", 24, false, true, false, pixel_declaration::pixel_type::depth24, false, v2u(1)}, + {"depth32", 32, false, true, false, pixel_declaration::pixel_type::depth32, false, v2u(1)}, + {"depth24_stencil8", 32, false, true, true, pixel_declaration::pixel_type::depth24_stencil8, false, v2u(1)}, + + {"g8", 8, true, false, false, pixel_declaration::pixel_type::g8, false, v2u(1)}, + {"ga8", 16, true, false, false, pixel_declaration::pixel_type::ga8, false, v2u(1)}, + {"rgb8", 24, true, false, false, pixel_declaration::pixel_type::rgb8, false, v2u(1)}, + {"rgba8", 32, true, false, false, pixel_declaration::pixel_type::rgba8, false, v2u(1)}, - {"rgb8", 24, true, false, false, pixel_declaration::pixel_type::rgb8, false}, - {"rgba8", 32, true, false, false, pixel_declaration::pixel_type::rgba8, false}, + {"rgb_dxt1", 4, true, false, false, pixel_declaration::pixel_type::rgb_dxt1, true, v2u(4,4)}, + {"rgba_dxt1", 4, true, false, false, pixel_declaration::pixel_type::rgba_dxt1, true, v2u(4,4)}, + {"rgba_dxt3", 8, true, false, false, pixel_declaration::pixel_type::rgba_dxt3, true, v2u(4,4)}, + {"rgba_dxt5", 8, true, false, false, pixel_declaration::pixel_type::rgba_dxt5, true, v2u(4,4)}, - {"rgb_dxt1", 4, true, false, false, pixel_declaration::pixel_type::rgb_dxt1, true}, - {"rgba_dxt1", 4, true, false, false, pixel_declaration::pixel_type::rgba_dxt1, true}, - {"rgba_dxt3", 8, true, false, false, pixel_declaration::pixel_type::rgba_dxt3, true}, - {"rgba_dxt5", 8, true, false, false, pixel_declaration::pixel_type::rgba_dxt5, true}, + {"rgb_pvrtc2", 2, true, false, false, pixel_declaration::pixel_type::rgb_pvrtc2, true, v2u(8,4)}, + {"rgb_pvrtc4", 4, true, false, false, pixel_declaration::pixel_type::rgb_pvrtc4, true, v2u(4,4)}, + {"rgba_pvrtc2", 2, true, false, false, pixel_declaration::pixel_type::rgba_pvrtc2, true, v2u(8,4)}, + {"rgba_pvrtc4", 4, true, false, false, pixel_declaration::pixel_type::rgba_pvrtc4, true, v2u(4,4)}, - {"rgb_pvrtc2", 2, true, false, false, pixel_declaration::pixel_type::rgb_pvrtc2, true}, - {"rgb_pvrtc4", 4, true, false, false, pixel_declaration::pixel_type::rgb_pvrtc4, true}, - {"rgba_pvrtc2", 2, true, false, false, pixel_declaration::pixel_type::rgba_pvrtc2, true}, - {"rgba_pvrtc4", 4, true, false, false, pixel_declaration::pixel_type::rgba_pvrtc4, true}, + {"rgba_pvrtc2", 2, true, false, false, pixel_declaration::pixel_type::rgba_pvrtc2, true, v2u(8,4)}, + {"rgba_pvrtc4", 4, true, false, false, pixel_declaration::pixel_type::rgba_pvrtc4, true, v2u(4,4)}, - {"rgba_pvrtc2", 2, true, false, false, pixel_declaration::pixel_type::rgba_pvrtc2, true}, - {"rgba_pvrtc4", 4, true, false, false, pixel_declaration::pixel_type::rgba_pvrtc4, true}, - - {"rgba_pvrtc2_v2", 2, true, false, false, pixel_declaration::pixel_type::rgba_pvrtc2_v2, true}, - {"rgba_pvrtc4_v2", 4, true, false, false, pixel_declaration::pixel_type::rgba_pvrtc4_v2, true} + {"rgba_pvrtc2_v2", 2, true, false, false, pixel_declaration::pixel_type::rgba_pvrtc2_v2, true, v2u(8,4)}, + {"rgba_pvrtc4_v2", 4, true, false, false, pixel_declaration::pixel_type::rgba_pvrtc4_v2, true, v2u(4,4)} }; const pixel_type_description& get_pixel_type_description(pixel_declaration::pixel_type type) noexcept { @@ -160,6 +163,10 @@ namespace e2d std::size_t pixel_declaration::bits_per_pixel() const noexcept { return get_pixel_type_description(type_).bits_per_pixel; } + + v2u pixel_declaration::compressed_block_size() const noexcept { + return get_pixel_type_description(type_).block_size; + } bool operator==(const pixel_declaration& l, const pixel_declaration& r) noexcept { return l.type() == r.type(); diff --git a/sources/enduro2d/core/render_impl/render_none.cpp b/sources/enduro2d/core/render_impl/render_none.cpp index 99494db5..e561a51c 100644 --- a/sources/enduro2d/core/render_impl/render_none.cpp +++ b/sources/enduro2d/core/render_impl/render_none.cpp @@ -121,10 +121,6 @@ namespace e2d : state_(std::move(state)) {} index_buffer::~index_buffer() noexcept = default; - void index_buffer::update(buffer_view indices, std::size_t offset) noexcept { - E2D_UNUSED(indices, offset); - } - std::size_t index_buffer::buffer_size() const noexcept { return 0u; } @@ -141,10 +137,6 @@ namespace e2d : state_(std::move(state)) {} vertex_buffer::~vertex_buffer() noexcept = default; - void vertex_buffer::update(buffer_view vertices, std::size_t offset) noexcept { - E2D_UNUSED(vertices, offset); - } - std::size_t vertex_buffer::buffer_size() const noexcept { return 0u; } @@ -258,6 +250,42 @@ namespace e2d E2D_UNUSED(command); return *this; } + + render& render::update_buffer( + const index_buffer_ptr& ibuffer, + buffer_view indices, + std::size_t offset) + { + E2D_UNUSED(ibuffer, indices, offset); + return *this; + } + + render& render::update_buffer( + const vertex_buffer_ptr& vbuffer, + buffer_view vertices, + std::size_t offset) + { + E2D_UNUSED(vbuffer, vertices, offset); + return *this; + } + + render& render::update_texture( + const texture_ptr& tex, + const image& img, + v2u offset) + { + E2D_UNUSED(tex, img, offset); + return *this; + } + + render& render::update_texture( + const texture_ptr& tex, + buffer_view pixels, + const b2u& region) + { + E2D_UNUSED(tex, pixels, region); + return *this; + } const render::device_caps& render::device_capabilities() const noexcept { static device_caps caps; diff --git a/sources/enduro2d/core/render_impl/render_opengl.cpp b/sources/enduro2d/core/render_impl/render_opengl.cpp index 3a4e4715..8d003ea0 100644 --- a/sources/enduro2d/core/render_impl/render_opengl.cpp +++ b/sources/enduro2d/core/render_impl/render_opengl.cpp @@ -355,20 +355,6 @@ namespace e2d } index_buffer::~index_buffer() noexcept = default; - void index_buffer::update(buffer_view indices, std::size_t offset) noexcept { - const std::size_t buffer_offset = offset * state_->decl().bytes_per_index(); - E2D_ASSERT(indices.size() + buffer_offset <= state_->size()); - E2D_ASSERT(indices.size() % state_->decl().bytes_per_index() == 0); - opengl::with_gl_bind_buffer(state_->dbg(), state_->id(), - [this, &indices, &buffer_offset]() noexcept { - GL_CHECK_CODE(state_->dbg(), glBufferSubData( - state_->id().target(), - math::numeric_cast(buffer_offset), - math::numeric_cast(indices.size()), - indices.data())); - }); - } - std::size_t index_buffer::buffer_size() const noexcept { return state_->size(); } @@ -396,20 +382,6 @@ namespace e2d } vertex_buffer::~vertex_buffer() noexcept = default; - void vertex_buffer::update(buffer_view vertices, std::size_t offset) noexcept { - const std::size_t buffer_offset = offset * state_->decl().bytes_per_vertex(); - E2D_ASSERT(vertices.size() + buffer_offset <= state_->size()); - E2D_ASSERT(vertices.size() % state_->decl().bytes_per_vertex() == 0); - opengl::with_gl_bind_buffer(state_->dbg(), state_->id(), - [this, &vertices, &buffer_offset]() noexcept { - GL_CHECK_CODE(state_->dbg(), glBufferSubData( - state_->id().target(), - math::numeric_cast(buffer_offset), - math::numeric_cast(vertices.size()), - vertices.data())); - }); - } - std::size_t vertex_buffer::buffer_size() const noexcept { return state_->size(); } @@ -1000,6 +972,119 @@ namespace e2d return *this; } + + render& render::update_buffer( + const index_buffer_ptr& ibuffer, + buffer_view indices, + std::size_t offset) + { + E2D_ASSERT(is_in_main_thread()); + E2D_ASSERT(ibuffer); + const std::size_t buffer_offset = offset * ibuffer->state().decl().bytes_per_index(); + E2D_ASSERT(indices.size() + buffer_offset <= ibuffer->state().size()); + E2D_ASSERT(indices.size() % ibuffer->state().decl().bytes_per_index() == 0); + opengl::with_gl_bind_buffer(ibuffer->state().dbg(), ibuffer->state().id(), + [&ibuffer, &indices, &buffer_offset]() noexcept { + GL_CHECK_CODE(ibuffer->state().dbg(), glBufferSubData( + ibuffer->state().id().target(), + math::numeric_cast(buffer_offset), + math::numeric_cast(indices.size()), + indices.data())); + }); + return *this; + } + + render& render::update_buffer( + const vertex_buffer_ptr& vbuffer, + buffer_view vertices, + std::size_t offset) + { + E2D_ASSERT(is_in_main_thread()); + E2D_ASSERT(vbuffer); + const std::size_t buffer_offset = offset * vbuffer->state().decl().bytes_per_vertex(); + E2D_ASSERT(vertices.size() + buffer_offset <= vbuffer->state().size()); + E2D_ASSERT(vertices.size() % vbuffer->state().decl().bytes_per_vertex() == 0); + opengl::with_gl_bind_buffer(vbuffer->state().dbg(), vbuffer->state().id(), + [&vbuffer, &vertices, &buffer_offset]() noexcept { + GL_CHECK_CODE(vbuffer->state().dbg(), glBufferSubData( + vbuffer->state().id().target(), + math::numeric_cast(buffer_offset), + math::numeric_cast(vertices.size()), + vertices.data())); + }); + return *this; + } + + render& render::update_texture( + const texture_ptr& tex, + const image& img, + v2u offset) + { + E2D_ASSERT(is_in_main_thread()); + E2D_ASSERT(tex); + + const pixel_declaration decl = + convert_image_data_format_to_pixel_declaration(img.format()); + if ( tex->decl() != decl ) { + state_->dbg().error("RENDER: Failed to update texture:\n" + "--> Info: incompatible pixel formats\n" + "--> Texture format: %0\n" + "--> Image format: %1", + pixel_declaration::pixel_type_to_cstr(tex->decl().type()), + pixel_declaration::pixel_type_to_cstr(decl.type())); + throw bad_render_operation(); + } + + return update_texture(tex, img.data(), b2u(offset, img.size())); + } + + render& render::update_texture( + const texture_ptr& tex, + buffer_view pixels, + const b2u& region) + { + E2D_ASSERT(is_in_main_thread()); + E2D_ASSERT(tex); + E2D_ASSERT(region.position.x < tex->size().x && region.position.y < tex->size().y); + E2D_ASSERT(region.position.x + region.size.x <= tex->size().x); + E2D_ASSERT(region.position.y + region.size.y <= tex->size().y); + E2D_ASSERT(pixels.size() == region.size.y * ((region.size.x * tex->decl().bits_per_pixel()) / 8u)); + + if ( tex->decl().is_compressed() ) { + const v2u block_size = tex->decl().compressed_block_size(); + E2D_ASSERT(region.position.x % block_size.x == 0 && region.position.y % block_size.y == 0); + E2D_ASSERT(region.size.x % block_size.x == 0 && region.size.y % block_size.y == 0); + opengl::with_gl_bind_texture(state_->dbg(), tex->state().id(), + [&tex, &pixels, ®ion]() noexcept { + GL_CHECK_CODE(tex->state().dbg(), glCompressedTexSubImage2D( + tex->state().id().target(), + 0, + math::numeric_cast(region.position.x), + math::numeric_cast(region.position.y), + math::numeric_cast(region.size.x), + math::numeric_cast(region.size.y), + convert_pixel_type_to_internal_format_e(tex->state().decl().type()), + math::numeric_cast(pixels.size()), + pixels.data())); + }); + } else { + opengl::with_gl_bind_texture(state_->dbg(), tex->state().id(), + [&tex, &pixels, ®ion]() noexcept { + GL_CHECK_CODE(tex->state().dbg(), glTexSubImage2D( + tex->state().id().target(), + 0, + math::numeric_cast(region.position.x), + math::numeric_cast(region.position.y), + math::numeric_cast(region.size.x), + math::numeric_cast(region.size.y), + convert_pixel_type_to_external_format(tex->state().decl().type()), + convert_pixel_type_to_external_data_type(tex->state().decl().type()), + pixels.data())); + }); + } + + return *this; + } const render::device_caps& render::device_capabilities() const noexcept { E2D_ASSERT(is_in_main_thread()); @@ -1018,6 +1103,8 @@ namespace e2d case pixel_declaration::pixel_type::depth24_stencil8: return GLEW_OES_packed_depth_stencil || GLEW_EXT_packed_depth_stencil; + case pixel_declaration::pixel_type::g8: + case pixel_declaration::pixel_type::ga8: case pixel_declaration::pixel_type::rgb8: case pixel_declaration::pixel_type::rgba8: return true; diff --git a/sources/enduro2d/core/render_impl/render_opengl_base.cpp b/sources/enduro2d/core/render_impl/render_opengl_base.cpp index 18ec09ff..d5a15088 100644 --- a/sources/enduro2d/core/render_impl/render_opengl_base.cpp +++ b/sources/enduro2d/core/render_impl/render_opengl_base.cpp @@ -723,6 +723,8 @@ namespace e2d::opengl DEFINE_CASE(depth24, GL_DEPTH_COMPONENT); DEFINE_CASE(depth32, GL_DEPTH_COMPONENT); DEFINE_CASE(depth24_stencil8, GL_DEPTH_STENCIL); + DEFINE_CASE(g8, GL_ALPHA); + DEFINE_CASE(ga8, GL_LUMINANCE_ALPHA); DEFINE_CASE(rgb8, GL_RGB); DEFINE_CASE(rgba8, GL_RGBA); default: @@ -739,6 +741,8 @@ namespace e2d::opengl DEFINE_CASE(depth24, GL_UNSIGNED_INT); DEFINE_CASE(depth32, GL_UNSIGNED_INT); DEFINE_CASE(depth24_stencil8, GL_UNSIGNED_INT_24_8); + DEFINE_CASE(g8, GL_UNSIGNED_BYTE); + DEFINE_CASE(ga8, GL_UNSIGNED_BYTE); DEFINE_CASE(rgb8, GL_UNSIGNED_BYTE); DEFINE_CASE(rgba8, GL_UNSIGNED_BYTE); default: @@ -755,7 +759,9 @@ namespace e2d::opengl DEFINE_CASE(depth24, GL_DEPTH_COMPONENT24); DEFINE_CASE(depth32, GL_DEPTH_COMPONENT32); DEFINE_CASE(depth24_stencil8, GL_DEPTH24_STENCIL8); - + + DEFINE_CASE(g8, GL_ALPHA); + DEFINE_CASE(ga8, GL_LUMINANCE_ALPHA); DEFINE_CASE(rgb8, GL_RGB); DEFINE_CASE(rgba8, GL_RGBA); @@ -786,8 +792,8 @@ namespace e2d::opengl pixel_declaration convert_image_data_format_to_pixel_declaration(image_data_format f) noexcept { #define DEFINE_CASE(x,y) case image_data_format::x: return pixel_declaration::pixel_type::y switch ( f ) { - DEFINE_CASE(g8, rgb8); - DEFINE_CASE(ga8, rgba8); + DEFINE_CASE(g8, g8); + DEFINE_CASE(ga8, ga8); DEFINE_CASE(rgb8, rgb8); DEFINE_CASE(rgba8, rgba8); diff --git a/sources/enduro2d/core/render_impl/render_opengl_base.hpp b/sources/enduro2d/core/render_impl/render_opengl_base.hpp index f7062e84..9340025b 100644 --- a/sources/enduro2d/core/render_impl/render_opengl_base.hpp +++ b/sources/enduro2d/core/render_impl/render_opengl_base.hpp @@ -17,7 +17,6 @@ #if defined(E2D_BUILD_MODE) && E2D_BUILD_MODE == E2D_BUILD_MODE_DEBUG # define GL_FLUSH_ERRORS(dbg)\ for ( GLenum err = glGetError(); err != GL_NO_ERROR; err = glGetError() ) {\ - E2D_ASSERT_MSG(false, "RENDER: GL_FLUSH_ERRORS()");\ (dbg).log(err == GL_OUT_OF_MEMORY\ ? debug::level::fatal\ : debug::level::error,\ @@ -26,13 +25,13 @@ "--> Line: %1\n"\ "--> Code: %2",\ __FILE__, __LINE__, e2d::opengl::gl_error_code_to_cstr(err));\ + E2D_ASSERT_MSG(false, "RENDER: GL_FLUSH_ERRORS()");\ if ( err == GL_OUT_OF_MEMORY ) std::terminate();\ } # define GL_CHECK_CODE(dbg, code)\ GL_FLUSH_ERRORS(dbg);\ code;\ for ( GLenum err = glGetError(); err != GL_NO_ERROR; err = glGetError() ) {\ - E2D_ASSERT_MSG(false, #code);\ (dbg).log(err == GL_OUT_OF_MEMORY\ ? debug::level::fatal\ : debug::level::error,\ @@ -41,6 +40,7 @@ "--> Line: %2\n"\ "--> Code: %3",\ #code, __FILE__, __LINE__, e2d::opengl::gl_error_code_to_cstr(err));\ + E2D_ASSERT_MSG(false, #code);\ if ( err == GL_OUT_OF_MEMORY ) std::terminate();\ } #else diff --git a/sources/enduro2d/core/render_impl/render_opengl_impl.cpp b/sources/enduro2d/core/render_impl/render_opengl_impl.cpp index 0baaf463..008f2a6b 100644 --- a/sources/enduro2d/core/render_impl/render_opengl_impl.cpp +++ b/sources/enduro2d/core/render_impl/render_opengl_impl.cpp @@ -217,9 +217,9 @@ namespace e2d GL_CHECK_CODE(debug_, glPixelStorei(GL_PACK_ALIGNMENT, 1)); GL_CHECK_CODE(debug_, glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); - set_states(state_block_); - set_shader_program(shader_program_); - set_render_target(render_target_); + reset_states(); + reset_shader_program(); + reset_render_target(); } debug& render::internal_state::dbg() const noexcept { @@ -237,21 +237,90 @@ namespace e2d const render_target_ptr& render::internal_state::render_target() const noexcept { return render_target_; } - - render::internal_state& render::internal_state::set_states(const state_block& sb) noexcept { - set_depth_state(sb.depth()); - set_stencil_state(sb.stencil()); - set_culling_state(sb.culling()); - set_blending_state(sb.blending()); - set_capabilities_state(sb.capabilities()); + + render::internal_state& render::internal_state::reset_states() noexcept { + set_depth_state_(state_block_.depth()); + set_stencil_state_(state_block_.stencil()); + set_culling_state_(state_block_.culling()); + set_blending_state_(state_block_.blending()); + set_capabilities_state_(state_block_.capabilities()); return *this; } - render::internal_state& render::internal_state::set_depth_state(const depth_state& ds) noexcept { - if ( ds == state_block_.depth() ) { + render::internal_state& render::internal_state::set_states(const state_block& sb) noexcept { + if ( sb.depth() != state_block_.depth() ) { + set_depth_state_(sb.depth()); + state_block_.depth(sb.depth()); + } + + if ( sb.stencil() != state_block_.stencil() ) { + set_stencil_state_(sb.stencil()); + state_block_.stencil(sb.stencil()); + } + + if ( sb.culling() != state_block_.culling() ) { + set_culling_state_(sb.culling()); + state_block_.culling(sb.culling()); + } + + if ( sb.blending() != state_block_.blending() ) { + set_blending_state_(sb.blending()); + state_block_.blending(sb.blending()); + } + + if ( sb.capabilities() != state_block_.capabilities() ) { + set_capabilities_state_(sb.capabilities()); + state_block_.capabilities(sb.capabilities()); + } + + return *this; + } + + render::internal_state& render::internal_state::reset_shader_program() noexcept { + const gl_program_id& sp_id = shader_program_ + ? shader_program_->state().id() + : default_sp_; + GL_CHECK_CODE(debug_, glUseProgram(*sp_id)); + return *this; + } + + render::internal_state& render::internal_state::set_shader_program(const shader_ptr& sp) noexcept { + if ( sp == shader_program_ ) { return *this; } + const gl_program_id& sp_id = sp + ? sp->state().id() + : default_sp_; + GL_CHECK_CODE(debug_, glUseProgram(*sp_id)); + + shader_program_ = sp; + return *this; + } + + render::internal_state& render::internal_state::reset_render_target() noexcept { + const gl_framebuffer_id& rt_id = render_target_ + ? render_target_->state().id() + : default_fb_; + GL_CHECK_CODE(debug_, glBindFramebuffer(rt_id.target(), *rt_id)); + return *this; + } + + render::internal_state& render::internal_state::set_render_target(const render_target_ptr& rt) noexcept { + if ( rt == render_target_ ) { + return *this; + } + + const gl_framebuffer_id& rt_id = rt + ? rt->state().id() + : default_fb_; + GL_CHECK_CODE(debug_, glBindFramebuffer(rt_id.target(), *rt_id)); + + render_target_ = rt; + return *this; + } + + void render::internal_state::set_depth_state_(const depth_state& ds) noexcept { GL_CHECK_CODE(debug_, glDepthRange( math::numeric_cast(math::saturate(ds.range_near())), math::numeric_cast(math::saturate(ds.range_far())))); @@ -259,16 +328,9 @@ namespace e2d ds.write() ? GL_TRUE : GL_FALSE)); GL_CHECK_CODE(debug_, glDepthFunc( convert_compare_func(ds.func()))); - - state_block_.depth(ds); - return *this; } - render::internal_state& render::internal_state::set_stencil_state(const stencil_state& ss) noexcept { - if ( ss == state_block_.stencil() ) { - return *this; - } - + void render::internal_state::set_stencil_state_(const stencil_state& ss) noexcept { GL_CHECK_CODE(debug_, glStencilMask( math::numeric_cast(ss.write()))); GL_CHECK_CODE(debug_, glStencilFunc( @@ -279,30 +341,16 @@ namespace e2d convert_stencil_op(ss.sfail()), convert_stencil_op(ss.zfail()), convert_stencil_op(ss.pass()))); - - state_block_.stencil(ss); - return *this; } - render::internal_state& render::internal_state::set_culling_state(const culling_state& cs) noexcept { - if ( cs == state_block_.culling() ) { - return *this; - } - + void render::internal_state::set_culling_state_(const culling_state& cs) noexcept { GL_CHECK_CODE(debug_, glFrontFace( convert_culling_mode(cs.mode()))); GL_CHECK_CODE(debug_, glCullFace( convert_culling_face(cs.face()))); - - state_block_.culling(cs); - return *this; } - render::internal_state& render::internal_state::set_blending_state(const blending_state& bs) noexcept { - if ( bs == state_block_.blending() ) { - return *this; - } - + void render::internal_state::set_blending_state_(const blending_state& bs) noexcept { GL_CHECK_CODE(debug_, glBlendColor( math::numeric_cast(math::saturate(bs.constant_color().r)), math::numeric_cast(math::saturate(bs.constant_color().g)), @@ -321,12 +369,9 @@ namespace e2d (utils::enum_to_underlying(bs.color_mask()) & utils::enum_to_underlying(blending_color_mask::g)) != 0, (utils::enum_to_underlying(bs.color_mask()) & utils::enum_to_underlying(blending_color_mask::b)) != 0, (utils::enum_to_underlying(bs.color_mask()) & utils::enum_to_underlying(blending_color_mask::a)) != 0)); - - state_block_.blending(bs); - return *this; } - render::internal_state& render::internal_state::set_capabilities_state(const capabilities_state& cs) noexcept { + void render::internal_state::set_capabilities_state_(const capabilities_state& cs) noexcept { const auto enable_or_disable = [](GLenum cap, bool enable) noexcept { if ( enable ) { glEnable(cap); @@ -335,45 +380,10 @@ namespace e2d } }; - if ( cs == state_block_.capabilities() ) { - return *this; - } - GL_CHECK_CODE(debug_, enable_or_disable(GL_CULL_FACE, cs.culling())); GL_CHECK_CODE(debug_, enable_or_disable(GL_BLEND, cs.blending())); GL_CHECK_CODE(debug_, enable_or_disable(GL_DEPTH_TEST, cs.depth_test())); GL_CHECK_CODE(debug_, enable_or_disable(GL_STENCIL_TEST, cs.stencil_test())); - - state_block_.capabilities(cs); - return *this; - } - - render::internal_state& render::internal_state::set_shader_program(const shader_ptr& sp) noexcept { - if ( sp == shader_program_ ) { - return *this; - } - - const gl_program_id& sp_id = sp - ? sp->state().id() - : default_sp_; - GL_CHECK_CODE(debug_, glUseProgram(*sp_id)); - - shader_program_ = sp; - return *this; - } - - render::internal_state& render::internal_state::set_render_target(const render_target_ptr& rt) noexcept { - if ( rt == render_target_ ) { - return *this; - } - - const gl_framebuffer_id& rt_id = rt - ? rt->state().id() - : default_fb_; - GL_CHECK_CODE(debug_, glBindFramebuffer(rt_id.target(), *rt_id)); - - render_target_ = rt; - return *this; } } diff --git a/sources/enduro2d/core/render_impl/render_opengl_impl.hpp b/sources/enduro2d/core/render_impl/render_opengl_impl.hpp index d097ec04..8889f5cf 100644 --- a/sources/enduro2d/core/render_impl/render_opengl_impl.hpp +++ b/sources/enduro2d/core/render_impl/render_opengl_impl.hpp @@ -176,15 +176,20 @@ namespace e2d const device_caps& device_capabilities() const noexcept; const render_target_ptr& render_target() const noexcept; public: + internal_state& reset_states() noexcept; internal_state& set_states(const state_block& sb) noexcept; - internal_state& set_depth_state(const depth_state& ds) noexcept; - internal_state& set_stencil_state(const stencil_state& ss) noexcept; - internal_state& set_culling_state(const culling_state& cs) noexcept; - internal_state& set_blending_state(const blending_state& bs) noexcept; - internal_state& set_capabilities_state(const capabilities_state& cs) noexcept; - + + internal_state& reset_shader_program() noexcept; internal_state& set_shader_program(const shader_ptr& sp) noexcept; + + internal_state& reset_render_target() noexcept; internal_state& set_render_target(const render_target_ptr& rt) noexcept; + private: + void set_depth_state_(const depth_state& ds) noexcept; + void set_stencil_state_(const stencil_state& ss) noexcept; + void set_culling_state_(const culling_state& cs) noexcept; + void set_blending_state_(const blending_state& bs) noexcept; + void set_capabilities_state_(const capabilities_state& cs) noexcept; private: debug& debug_; window& window_; diff --git a/sources/enduro2d/high/systems/render_system_impl/render_system_batcher.hpp b/sources/enduro2d/high/systems/render_system_impl/render_system_batcher.hpp index 4f65ce22..9f664553 100644 --- a/sources/enduro2d/high/systems/render_system_impl/render_system_batcher.hpp +++ b/sources/enduro2d/high/systems/render_system_impl/render_system_batcher.hpp @@ -203,7 +203,7 @@ namespace e2d::render_system_impl void batcher::update_index_buffer_() { const std::size_t min_ib_size = indices_.size() * sizeof(indices_[0]); if ( index_buffer_ && index_buffer_->buffer_size() >= min_ib_size ) { - index_buffer_->update(indices_, 0u); + render_.update_buffer(index_buffer_, indices_, 0u); } else { const std::size_t new_ib_size = calculate_new_buffer_size( sizeof(Index), @@ -230,7 +230,7 @@ namespace e2d::render_system_impl void batcher::update_vertex_buffer_() { const std::size_t min_vb_size = vertices_.size() * sizeof(vertices_[0]); if ( vertex_buffer_ && vertex_buffer_->buffer_size() >= min_vb_size ) { - vertex_buffer_->update(vertices_, 0u); + render_.update_buffer(vertex_buffer_, vertices_, 0u); } else { const std::size_t new_vb_size = calculate_new_buffer_size( sizeof(Vertex), diff --git a/sources/enduro2d/utils/buffer.cpp b/sources/enduro2d/utils/buffer.cpp index 09687519..0d774b85 100644 --- a/sources/enduro2d/utils/buffer.cpp +++ b/sources/enduro2d/utils/buffer.cpp @@ -32,6 +32,54 @@ namespace e2d assign(src, size); } + buffer::iterator buffer::begin() noexcept { + return data_.get(); + } + + buffer::const_iterator buffer::begin() const noexcept { + return data_.get(); + } + + buffer::const_iterator buffer::cbegin() const noexcept { + return data_.get(); + } + + buffer::iterator buffer::end() noexcept { + return data_.get() + size_; + } + + buffer::const_iterator buffer::end() const noexcept { + return data_.get() + size_; + } + + buffer::const_iterator buffer::cend() const noexcept { + return data_.get() + size_; + } + + buffer::reverse_iterator buffer::rbegin() noexcept { + return reverse_iterator(end()); + } + + buffer::const_reverse_iterator buffer::rbegin() const noexcept { + return const_reverse_iterator(end()); + } + + buffer::const_reverse_iterator buffer::crbegin() const noexcept { + return const_reverse_iterator(end()); + } + + buffer::reverse_iterator buffer::rend() noexcept { + return reverse_iterator(begin()); + } + + buffer::const_reverse_iterator buffer::rend() const noexcept { + return const_reverse_iterator(begin()); + } + + buffer::const_reverse_iterator buffer::crend() const noexcept { + return const_reverse_iterator(begin()); + } + buffer& buffer::fill(u8 ch) noexcept { if ( data_ && size_) { std::memset(data_.get(), ch, size_); diff --git a/untests/sources/untests_core/render.cpp b/untests/sources/untests_core/render.cpp index a685e444..07c93118 100644 --- a/untests/sources/untests_core/render.cpp +++ b/untests/sources/untests_core/render.cpp @@ -7,7 +7,26 @@ #include "_core.hpp" using namespace e2d; +namespace +{ + class safe_engine_initializer final : private noncopyable { + public: + safe_engine_initializer() { + modules::initialize(0, nullptr, + engine::parameters("renderer_untests", "enduro2d") + .without_audio(true) + .without_graphics(true)); + } + + ~safe_engine_initializer() noexcept { + modules::shutdown(); + } + }; +} + TEST_CASE("render"){ + safe_engine_initializer initializer; + SECTION("sampler_state"){ { const auto ss = render::sampler_state(); @@ -169,4 +188,94 @@ TEST_CASE("render"){ REQUIRE(vd4 != vd); REQUIRE(vd4 == vd3); } + SECTION("update_texture"){ + if ( modules::is_initialized() ) { + render& r = the(); + { + texture_ptr tex = r.create_texture(v2u(128,128), pixel_declaration::pixel_type::rgba8); + REQUIRE(tex != nullptr); + + buffer src; + src.resize(((tex->size().x * tex->decl().bits_per_pixel()) / 8u) * tex->size().y); + for ( auto& c : src ) { + c = rand() % 255; + } + REQUIRE_NOTHROW(r.update_texture(tex, src, b2u(0, 0, 128, 128))); + } + { + texture_ptr tex = r.create_texture(v2u(128,128), pixel_declaration::pixel_type::g8); + REQUIRE(tex != nullptr); + + buffer src; + src.resize(((tex->size().x * tex->decl().bits_per_pixel()) / 8u) * tex->size().y); + for ( auto& c : src ) { + c = rand() % 255; + } + REQUIRE_NOTHROW(r.update_texture(tex, src, b2u(0, 0, 128, 128))); + } + { + texture_ptr tex = r.create_texture(v2u(128,128), pixel_declaration::pixel_type::rgb8); + REQUIRE(tex != nullptr); + + buffer src; + src.resize(((tex->size().x * tex->decl().bits_per_pixel()) / 8u) * tex->size().y); + for ( auto& c : src ) { + c = rand() % 255; + } + REQUIRE_NOTHROW(r.update_texture(tex, src, b2u(0, 0, 128, 128))); + } + { + texture_ptr tex = r.create_texture(v2u(57,31), pixel_declaration::pixel_type::rgba8); + REQUIRE(tex != nullptr); + + buffer src; + src.resize(((tex->size().x * tex->decl().bits_per_pixel()) / 8u) * tex->size().y); + for ( auto& c : src ) { + c = rand() % 255; + } + REQUIRE_NOTHROW(r.update_texture(tex, src, b2u(0, 0, 57, 31))); + } + { + texture_ptr tex = r.create_texture(v2u(128,128), pixel_declaration::pixel_type::rgba8); + REQUIRE(tex != nullptr); + + buffer src; + src.resize(((31 * tex->decl().bits_per_pixel()) / 8u) * 44); + for ( auto& c : src ) { + c = rand() % 255; + } + REQUIRE_NOTHROW(r.update_texture(tex, src, b2u(22, 17, 31, 44))); + } + { + texture_ptr tex = r.create_texture(v2u(128,128), pixel_declaration::pixel_type::rgba8); + REQUIRE(tex != nullptr); + + buffer src; + src.resize(((31 * tex->decl().bits_per_pixel()) / 8u) * 44); + for ( auto& c : src ) { + c = rand() % 255; + } + + image img(v2u(31, 44), image_data_format::ga8, src); + REQUIRE_THROWS_AS( + r.update_texture(tex, img, v2u(11,27)), + bad_render_operation); + } + + //if ( r.device_capabilities().dxt5_compression_supported ) // TODO: wait for android branch + { + str resources; + REQUIRE(filesystem::extract_predef_path( + resources, + filesystem::predef_path::resources)); + + image src; + REQUIRE(images::try_load_image(src, make_read_file(path::combine(resources, "bin/images/ship_rgba.dds")))); + + texture_ptr tex = r.create_texture(src.size(), pixel_declaration::pixel_type::rgba_dxt5); + REQUIRE(tex != nullptr); + REQUIRE_NOTHROW(r.update_texture(tex, src, v2u(0,0))); + } + } + } } diff --git a/untests/sources/untests_utils/buffer.cpp b/untests/sources/untests_utils/buffer.cpp index 80287aa3..899317e7 100644 --- a/untests/sources/untests_utils/buffer.cpp +++ b/untests/sources/untests_utils/buffer.cpp @@ -170,6 +170,22 @@ TEST_CASE("buffer") { } #endif } + { + const u8 data[] = {1,2,3}; + buffer b(data, std::size(data) * sizeof(data[0])); + + const auto con_str = [](std::string acc, u8 ch){ + return acc + std::to_string(ch); + }; + + REQUIRE(std::accumulate(b.begin(), b.end(), std::string(), con_str) == "123"); + REQUIRE(std::accumulate(std::as_const(b).begin(), std::as_const(b).end(), std::string(), con_str) == "123"); + REQUIRE(std::accumulate(b.cbegin(), b.cend(), std::string(), con_str) == "123"); + + REQUIRE(std::accumulate(b.rbegin(), b.rend(), std::string(), con_str) == "321"); + REQUIRE(std::accumulate(std::as_const(b).rbegin(), std::as_const(b).rend(), std::string(), con_str) == "321"); + REQUIRE(std::accumulate(b.crbegin(), b.crend(), std::string(), con_str) == "321"); + } } TEST_CASE("buffer_view") {