From 278e80f682804b64da4ea4bd0965f1f9e9c09660 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Sat, 6 Oct 2018 01:33:55 +0700 Subject: [PATCH] vertex and index declarations --- headers/enduro2d/core/_all.hpp | 1 + headers/enduro2d/core/_core.hpp | 1 - headers/enduro2d/core/render.hpp | 234 +++++- headers/enduro2d/core/render.inl | 74 ++ samples/sources/sample_00/sample_00.cpp | 95 ++- sources/enduro2d/core/render.cpp | 199 +++++ .../core/render_impl/render_opengl.cpp | 735 ++++++++++++------ untests/sources/untests_core/render.cpp | 78 ++ 8 files changed, 1083 insertions(+), 334 deletions(-) create mode 100644 headers/enduro2d/core/render.inl diff --git a/headers/enduro2d/core/_all.hpp b/headers/enduro2d/core/_all.hpp index 42b5b1a7..728bff56 100644 --- a/headers/enduro2d/core/_all.hpp +++ b/headers/enduro2d/core/_all.hpp @@ -11,5 +11,6 @@ #include "debug.hpp" #include "input.hpp" #include "render.hpp" +#include "render.inl" #include "vfs.hpp" #include "window.hpp" diff --git a/headers/enduro2d/core/_core.hpp b/headers/enduro2d/core/_core.hpp index 134b6fe2..11dfd236 100644 --- a/headers/enduro2d/core/_core.hpp +++ b/headers/enduro2d/core/_core.hpp @@ -16,7 +16,6 @@ namespace e2d class mouse; class keyboard; class input; - class vertex; class shader; class texture; class index_buffer; diff --git a/headers/enduro2d/core/render.hpp b/headers/enduro2d/core/render.hpp index d1866a1e..22140a88 100644 --- a/headers/enduro2d/core/render.hpp +++ b/headers/enduro2d/core/render.hpp @@ -4,6 +4,8 @@ * Copyright (C) 2018 Matvey Cherevko ******************************************************************************/ +#ifndef E2D_INCLUDE_GUARD_64EC1BED88C24F57851A315A761E9D48 +#define E2D_INCLUDE_GUARD_64EC1BED88C24F57851A315A761E9D48 #pragma once #include "_core.hpp" @@ -11,16 +13,141 @@ namespace e2d { // - // vertex + // index_declaration // - class vertex { + class index_declaration final { public: - v3f pos; - v2f uv[2]; - color32 color; + enum class index_type : u8 { + unsigned_byte, + unsigned_short + }; + + class index_info final { + public: + index_type type = index_type::unsigned_short; + public: + index_info() noexcept = default; + ~index_info() noexcept = default; + + index_info(const index_info&) noexcept = default; + index_info& operator=(const index_info&) noexcept = default; + + explicit index_info(index_type type) noexcept; + }; + public: + index_declaration() noexcept; + ~index_declaration() noexcept; + + index_declaration(const index_declaration&) noexcept = default; + index_declaration& operator=(const index_declaration&) noexcept = default; + + explicit index_declaration(index_type index_type) noexcept; + + const index_info& index() const noexcept; + std::size_t index_size() const noexcept; + private: + index_info index_; }; + bool operator==( + const index_declaration& l, + const index_declaration& r) noexcept; + bool operator!=( + const index_declaration& l, + const index_declaration& r) noexcept; + bool operator==( + const index_declaration::index_info& l, + const index_declaration::index_info& r) noexcept; + bool operator!=( + const index_declaration::index_info& l, + const index_declaration::index_info& r) noexcept; + + // + // vertex_declaration + // + + class vertex_declaration final { + public: + constexpr static std::size_t max_attribute_name = 128; + constexpr static std::size_t max_attribute_count = 16; + + enum class attribute_type : u8 { + signed_byte, + unsigned_byte, + signed_short, + unsigned_short, + floating_point + }; + + class attribute_info final { + public: + char name[max_attribute_name] = {0}; + std::size_t rows = 0; + std::size_t columns = 0; + std::size_t stride = 0; + attribute_type type = attribute_type::floating_point; + bool normalized = false; + public: + attribute_info() noexcept = default; + ~attribute_info() noexcept = default; + + attribute_info(const attribute_info&) noexcept = default; + attribute_info& operator=(const attribute_info&) noexcept = default; + + attribute_info( + str_view name, + std::size_t rows, + std::size_t columns, + std::size_t stride, + attribute_type type, + bool normalized); + + std::size_t row_size() const noexcept; + }; + public: + vertex_declaration() noexcept; + ~vertex_declaration() noexcept; + + vertex_declaration(const vertex_declaration&) noexcept = default; + vertex_declaration& operator=(const vertex_declaration&) noexcept = default; + + template < typename T > + vertex_declaration& add_attribute(str_view name) noexcept; + vertex_declaration& normalized() noexcept; + + vertex_declaration& skip_bytes( + std::size_t bytes) noexcept; + + vertex_declaration& add_attribute( + str_view name, + std::size_t rows, + std::size_t columns, + attribute_type type, + bool normalized) noexcept; + + const attribute_info& attribute(std::size_t i) const noexcept; + std::size_t attribute_count() const noexcept; + std::size_t vertex_size() const noexcept; + private: + array attributes_; + std::size_t attribute_count_ = 0; + std::size_t vertex_size_ = 0; + }; + + bool operator==( + const vertex_declaration& l, + const vertex_declaration& r) noexcept; + bool operator!=( + const vertex_declaration& l, + const vertex_declaration& r) noexcept; + bool operator==( + const vertex_declaration::attribute_info& l, + const vertex_declaration::attribute_info& r) noexcept; + bool operator!=( + const vertex_declaration::attribute_info& l, + const vertex_declaration::attribute_info& r) noexcept; + // // shader // @@ -31,9 +158,9 @@ namespace e2d class internal_state; using internal_state_uptr = std::unique_ptr; public: - enum class uniform_type { - i, - f, + enum class uniform_type : u8 { + signed_integer, + floating_point, v2i, v3i, @@ -47,9 +174,10 @@ namespace e2d m3f, m4f, - s2d + sampler_2d, + sampler_cube }; - enum class attribute_type { + enum class attribute_type : u8 { f, v2f, @@ -61,8 +189,11 @@ namespace e2d m4f }; public: - shader(internal_state_uptr); + explicit shader(internal_state_uptr); ~shader() noexcept; + const vertex_declaration& decl() const noexcept; + void set_uniform(str_view name, i32 value) const noexcept; + void set_uniform(str_view name, f32 value) const noexcept; private: internal_state_uptr state_; }; @@ -78,17 +209,17 @@ namespace e2d class internal_state; using internal_state_uptr = std::unique_ptr; public: - enum class wrap { + enum class wrap : u8 { clamp, repeat, mirror }; - enum class filter { + enum class filter : u8 { linear, nearest }; public: - texture(internal_state_uptr); + explicit texture(internal_state_uptr); ~texture() noexcept; void set_wrap(wrap u, wrap v) noexcept; @@ -108,16 +239,19 @@ namespace e2d class internal_state; using internal_state_uptr = std::unique_ptr; public: - enum class usage { + enum class usage : u8 { static_draw, stream_draw, dynamic_draw }; public: - index_buffer(internal_state_uptr); + explicit index_buffer(internal_state_uptr); ~index_buffer() noexcept; - void update(const u16* indices, std::size_t count, std::size_t offset) noexcept; - std::size_t count() const noexcept; + void update(const buffer& indices, std::size_t offset) noexcept; + const index_declaration& decl() const noexcept; + usage buffer_usage() const noexcept; + std::size_t buffer_size() const noexcept; + std::size_t index_count() const noexcept; private: internal_state_uptr state_; }; @@ -133,16 +267,19 @@ namespace e2d class internal_state; using internal_state_uptr = std::unique_ptr; public: - enum class usage { + enum class usage : u8{ static_draw, stream_draw, dynamic_draw }; public: - vertex_buffer(internal_state_uptr); + explicit vertex_buffer(internal_state_uptr); ~vertex_buffer() noexcept; - void update(const vertex* vertices, std::size_t count, std::size_t offset) noexcept; - std::size_t count() const noexcept; + void update(const buffer& vertices, std::size_t offset) noexcept; + const vertex_declaration& decl() const noexcept; + usage buffer_usage() const noexcept; + std::size_t buffer_size() const noexcept; + std::size_t vertex_count() const noexcept; private: internal_state_uptr state_; }; @@ -154,18 +291,20 @@ namespace e2d class render final : public module { public: - enum class state { + enum class state : u8 { blend, cull_face, depth_test, stencil_test }; - enum class cull_face { + + enum class cull_face : u8 { front, back, front_back }; - enum class blend_func { + + enum class blend_func : u8 { zero, one, src_color, @@ -182,12 +321,14 @@ namespace e2d one_minus_constant_alpha, src_alpha_saturate }; - enum class blend_equation { + + enum class blend_equation : u8 { add, subtract, reverse_subtract }; - enum class depth_func { + + enum class depth_func : u8 { never, less, lequal, @@ -197,7 +338,8 @@ namespace e2d notequal, always }; - enum class stencil_func { + + enum class stencil_func : u8 { never, less, lequal, @@ -207,7 +349,8 @@ namespace e2d notequal, always }; - enum class stencil_op { + + enum class stencil_op : u8 { keep, zero, replace, @@ -217,13 +360,20 @@ namespace e2d decr_wrap, invert }; + + enum class topology : u8 { + triangles, + triangles_fan, + triangles_strip + }; public: render(debug& d, window& w); - ~render() noexcept; + ~render() noexcept final; shader_ptr create_shader( input_stream_uptr vertex, - input_stream_uptr fragment); + input_stream_uptr fragment, + const vertex_declaration& decl); texture_ptr create_texture( const image& image); @@ -233,22 +383,27 @@ namespace e2d image_data_format format); index_buffer_ptr create_index_buffer( - const u16* indices, - std::size_t count, - index_buffer::usage usage); + const buffer& indices, + const index_declaration& decl, + enum index_buffer::usage usage); vertex_buffer_ptr create_vertex_buffer( - const vertex* vertices, - std::size_t count, - vertex_buffer::usage usage); + const buffer& vertices, + const vertex_declaration& decl, + enum vertex_buffer::usage usage); - void clear(bool color, bool depth, bool stencil) noexcept; + void clear( + bool color, + bool depth, + bool stencil) noexcept; void draw( + topology tp, const shader_ptr& ps, const index_buffer_ptr& ib, const vertex_buffer_ptr& vb) noexcept; + void set_model(const m4f& model) noexcept; void set_view(const m4f& view) noexcept; void set_projection(const m4f& projection) noexcept; void set_viewport(u32 x, u32 y, u32 w, u32 h) noexcept; @@ -278,3 +433,6 @@ namespace e2d std::unique_ptr state_; }; } + +#include "render.inl" +#endif diff --git a/headers/enduro2d/core/render.inl b/headers/enduro2d/core/render.inl new file mode 100644 index 00000000..83f9dbd0 --- /dev/null +++ b/headers/enduro2d/core/render.inl @@ -0,0 +1,74 @@ +/******************************************************************************* + * This file is part of the "Enduro2D" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2018 Matvey Cherevko + ******************************************************************************/ + +#ifndef E2D_INCLUDE_GUARD_8247225BE32B4135BDAECEEE88535A86 +#define E2D_INCLUDE_GUARD_8247225BE32B4135BDAECEEE88535A86 +#pragma once + +#include "_core.hpp" +#include "render.hpp" + +namespace e2d +{ + template < typename T > + vertex_declaration& vertex_declaration::add_attribute(str_view name) noexcept { + E2D_UNUSED(name); + static_assert(sizeof(T) == 0, "not implemented for this type"); + } + + #define DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(t, rows, columns, type)\ + template <>\ + inline vertex_declaration& vertex_declaration::add_attribute(str_view name) noexcept {\ + return add_attribute(name, (rows), (columns), attribute_type::type, false);\ + } + + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(i8, 1, 1, signed_byte) + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(vec2, 1, 2, signed_byte) + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(vec3, 1, 3, signed_byte) + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(vec4, 1, 4, signed_byte) + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(mat2, 2, 2, signed_byte) + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(mat3, 3, 3, signed_byte) + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(mat4, 4, 4, signed_byte) + + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(u8, 1, 1, unsigned_byte) + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(vec2, 1, 2, unsigned_byte) + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(vec3, 1, 3, unsigned_byte) + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(vec4, 1, 4, unsigned_byte) + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(mat2, 2, 2, unsigned_byte) + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(mat3, 3, 3, unsigned_byte) + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(mat4, 4, 4, unsigned_byte) + + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(i16, 1, 1, signed_short) + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(vec2, 1, 2, signed_short) + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(vec3, 1, 3, signed_short) + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(vec4, 1, 4, signed_short) + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(mat2, 2, 2, signed_short) + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(mat3, 3, 3, signed_short) + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(mat4, 4, 4, signed_short) + + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(u16, 1, 1, unsigned_short) + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(vec2, 1, 2, unsigned_short) + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(vec3, 1, 3, unsigned_short) + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(vec4, 1, 4, unsigned_short) + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(mat2, 2, 2, unsigned_short) + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(mat3, 3, 3, unsigned_short) + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(mat4, 4, 4, unsigned_short) + + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(f32, 1, 1, floating_point) + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(vec2, 1, 2, floating_point) + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(vec3, 1, 3, floating_point) + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(vec4, 1, 4, floating_point) + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(mat2, 2, 2, floating_point) + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(mat3, 3, 3, floating_point) + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(mat4, 4, 4, floating_point) + + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(color, 1, 4, floating_point) + DEFINE_ADD_ATTRIBUTE_SPECIALIZATION(color32, 1, 4, unsigned_byte) + + #undef DEFINE_ADD_ATTRIBUTE_SPECIALIZATION +} + +#endif diff --git a/samples/sources/sample_00/sample_00.cpp b/samples/sources/sample_00/sample_00.cpp index 3e49c348..3826886f 100644 --- a/samples/sources/sample_00/sample_00.cpp +++ b/samples/sources/sample_00/sample_00.cpp @@ -10,69 +10,88 @@ using namespace e2d; namespace { const char* vs_source_cstr = - "#version 120 \n" - " \n" - "attribute vec3 in_pos; \n" - "attribute vec2 in_uv0; \n" - "attribute vec2 in_uv1; \n" - "attribute vec4 in_color; \n" - " \n" - "varying vec2 uv0; \n" - "varying vec2 uv1; \n" - "varying vec4 color; \n" - " \n" - "void main(){ \n" - " uv0 = in_uv0; \n" - " uv1 = in_uv1; \n" - " color = in_color; \n" - " gl_Position = vec4(in_pos, 1.0); \n" + "#version 120 \n" + " \n" + "attribute vec3 a_position; \n" + "attribute vec4 a_color; \n" + " \n" + "uniform float u_time = 0.0; \n" + " \n" + "varying vec4 color; \n" + " \n" + "void main(){ \n" + " color = a_color; \n" + " \n" + " float s = 0.7 + 0.3 * (cos(u_time * 0.003) + 1); \n" + " gl_Position = vec4(a_position * s, 1.0); \n" "}"; const char* fs_source_cstr = - "#version 120 \n" - " \n" - "varying vec2 uv0; \n" - "varying vec2 uv1; \n" - "varying vec4 color; \n" - " \n" - "void main(){ \n" - " gl_FragColor = color; \n" + "#version 120 \n" + " \n" + "varying vec4 color; \n" + " \n" + "void main(){ \n" + " gl_FragColor = color; \n" "}"; - u16 indices[] = { + struct vertex { + v3f position; + color32 color; + }; + + u8 indices[] = { 0, 1, 2, 2, 1, 3}; const vertex vertices[] = { - {{-0.5f, 0.5f, 0.0f}, {{0.f, 0.f}, {0.f, 0.f}}, {0xFF, 0x00, 0x00, 0xFF}}, - {{-0.5f, -0.5f, 0.0f}, {{0.f, 0.f}, {0.f, 0.f}}, {0x00, 0xFF, 0x00, 0xFF}}, - {{ 0.5f, 0.5f, 0.0f}, {{0.f, 0.f}, {0.f, 0.f}}, {0x00, 0x00, 0xFF, 0xFF}}, - {{ 0.5f, -0.5f, 0.0f}, {{0.f, 0.f}, {0.f, 0.f}}, {0xFF, 0xFF, 0xFF, 0xFF}}}; + {{-0.5f, 0.5f, 0.0f}, color32::red()}, + {{-0.5f, -0.5f, 0.0f}, color32::green()}, + {{ 0.5f, 0.5f, 0.0f}, color32::blue()}, + {{ 0.5f, -0.5f, 0.0f}, color32::white()}}; } int e2d_main() { - modules::initialize(); + modules::initialize() + .register_sink(); modules::initialize(); - modules::initialize(v2u{640, 480}, "Enduro2D", false); + modules::initialize(v2u{640, 480}, "Enduro2D", false) + .register_event_listener(the()); modules::initialize(the(), the()); - the().register_sink(); - the().register_event_listener(the()); + auto index_decl = index_declaration( + index_declaration::index_type::unsigned_byte); + + auto vertex_decl = vertex_declaration() + .add_attribute("a_position") + .add_attribute("a_color").normalized(); const auto ps = the().create_shader( make_memory_stream(buffer(vs_source_cstr, std::strlen(vs_source_cstr))), - make_memory_stream(buffer(fs_source_cstr, std::strlen(fs_source_cstr)))); + make_memory_stream(buffer(fs_source_cstr, std::strlen(fs_source_cstr))), + vertex_decl); const auto ib = the().create_index_buffer( - indices, E2D_COUNTOF(indices), index_buffer::usage::static_draw); + buffer(indices, E2D_COUNTOF(indices) * sizeof(indices[0])), + index_decl, + index_buffer::usage::static_draw); const auto vb = the().create_vertex_buffer( - vertices, E2D_COUNTOF(vertices), vertex_buffer::usage::static_draw); + buffer(vertices, E2D_COUNTOF(vertices) * sizeof(vertices[0])), + vertex_decl, + vertex_buffer::usage::static_draw); + + const auto begin_time = time::now_ms(); const keyboard& k = the().keyboard(); while ( !the().should_close() && !k.is_key_just_released(keyboard_key::escape) ) { - the().set_clear_color({1.f, 0.4f, 0.f}); + the().set_clear_color({1.f, 0.4f, 0.f, 1.f}); the().clear(true, true, true); - the().draw(ps, ib, vb); + + f32 t = (time::now_ms() - begin_time).cast_to().value; + ps->set_uniform("u_time", t); + { + the().draw(render::topology::triangles, ps, ib, vb); + } 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 83c0bd1c..f2d1850b 100644 --- a/sources/enduro2d/core/render.cpp +++ b/sources/enduro2d/core/render.cpp @@ -5,3 +5,202 @@ ******************************************************************************/ #include "render_impl/render.hpp" + +namespace +{ + using namespace e2d; + + std::size_t index_element_size(index_declaration::index_type it) noexcept { + #define DEFINE_CASE(x,y) case index_declaration::index_type::x: return y; + switch ( it ) { + DEFINE_CASE(unsigned_byte, sizeof(u8)); + DEFINE_CASE(unsigned_short, sizeof(u16)); + default: + E2D_ASSERT_MSG(false, "unexpected index type"); + break; + } + #undef DEFINE_CASE + } + + std::size_t attribute_element_size(vertex_declaration::attribute_type at) noexcept { + #define DEFINE_CASE(x,y) case vertex_declaration::attribute_type::x: return y; + switch ( at ) { + DEFINE_CASE(signed_byte, sizeof(u8)); + DEFINE_CASE(unsigned_byte, sizeof(u8)); + DEFINE_CASE(signed_short, sizeof(u16)); + DEFINE_CASE(unsigned_short, sizeof(u16)); + DEFINE_CASE(floating_point, sizeof(u32)); + default: + E2D_ASSERT_MSG(false, "unexpected attribute type"); + break; + } + #undef DEFINE_CASE + } +} + +namespace e2d +{ + // + // index_declaration::index_info + // + + index_declaration::index_info::index_info(index_type ntype) noexcept + : type(ntype) {} + + // + // index_declaration + // + + index_declaration::index_declaration() noexcept = default; + index_declaration::~index_declaration() noexcept = default; + + index_declaration::index_declaration(index_type type) noexcept + : index_(type) {} + + const index_declaration::index_info& index_declaration::index() const noexcept { + return index_; + } + + std::size_t index_declaration::index_size() const noexcept { + return index_element_size(index_.type); + } + + bool operator==(const index_declaration& l, const index_declaration& r) noexcept { + return l.index_size() == r.index_size() + && l.index() == r.index(); + } + + bool operator!=(const index_declaration& l, const index_declaration& r) noexcept { + return !(l == r); + } + + bool operator==( + const index_declaration::index_info& l, + const index_declaration::index_info& r) noexcept + { + return l.type == r.type; + } + + bool operator!=( + const index_declaration::index_info& l, + const index_declaration::index_info& r) noexcept + { + return !(l == r); + } + + // + // vertex_declaration::attribute_info + // + + vertex_declaration::attribute_info::attribute_info( + str_view nname, + std::size_t nrows, + std::size_t ncolumns, + std::size_t nstride, + attribute_type ntype, + bool nnormalized) + : rows(nrows) + , columns(ncolumns) + , stride(nstride) + , type(ntype) + , normalized(nnormalized) { + bool name_format_success = strings::format_nothrow( + name, E2D_COUNTOF(name), nullptr, "%0", nname); + E2D_UNUSED(name_format_success); + E2D_ASSERT(name_format_success); + } + + std::size_t vertex_declaration::attribute_info::row_size() const noexcept { + return attribute_element_size(type) * columns; + } + + // + // vertex_declaration + // + + vertex_declaration::vertex_declaration() noexcept = default; + vertex_declaration::~vertex_declaration() noexcept = default; + + vertex_declaration& vertex_declaration::normalized() noexcept { + E2D_ASSERT(attribute_count_ > 0); + attributes_[attribute_count_ - 1].normalized = true; + return *this; + } + + vertex_declaration& vertex_declaration::skip_bytes(std::size_t bytes) noexcept { + vertex_size_ += bytes; + return *this; + } + + vertex_declaration& vertex_declaration::add_attribute( + str_view name, + std::size_t rows, + std::size_t columns, + attribute_type type, + bool normalized) noexcept + { + E2D_ASSERT(attribute_count_ < attributes_.size()); + const std::size_t stride = vertex_size_; + attributes_[attribute_count_] = attribute_info( + name, + rows, + columns, + stride, + type, + normalized); + vertex_size_ += attribute_element_size(type) * rows * columns; + ++attribute_count_; + return *this; + } + + const vertex_declaration::attribute_info& vertex_declaration::attribute(std::size_t i) const noexcept { + E2D_ASSERT(i < attribute_count_); + return attributes_[i]; + } + + std::size_t vertex_declaration::attribute_count() const noexcept { + return attribute_count_; + } + + std::size_t vertex_declaration::vertex_size() const noexcept { + return vertex_size_; + } + + bool operator==(const vertex_declaration& l, const vertex_declaration& r) noexcept { + if ( l.vertex_size() != r.vertex_size() ) { + return false; + } + if ( l.attribute_count() != r.attribute_count() ) { + return false; + } + for ( std::size_t i = 0, e = l.attribute_count(); i < e; ++i ) { + if ( l.attribute(i) != r.attribute(i) ) { + return false; + } + } + return true; + } + + bool operator!=(const vertex_declaration& l, const vertex_declaration& r) noexcept { + return !(l == r); + } + + bool operator==( + const vertex_declaration::attribute_info& l, + const vertex_declaration::attribute_info& r) noexcept + { + return !std::strcmp(l.name, r.name) + && l.rows == r.rows + && l.columns == r.columns + && l.stride == r.stride + && l.type == r.type + && l.normalized == r.normalized; + } + + bool operator!=( + const vertex_declaration::attribute_info& l, + const vertex_declaration::attribute_info& r) noexcept + { + return !(l == r); + } +} diff --git a/sources/enduro2d/core/render_impl/render_opengl.cpp b/sources/enduro2d/core/render_impl/render_opengl.cpp index 2363ef85..d98e3195 100644 --- a/sources/enduro2d/core/render_impl/render_opengl.cpp +++ b/sources/enduro2d/core/render_impl/render_opengl.cpp @@ -16,11 +16,38 @@ namespace { using namespace e2d; + GLenum convert_index_type(index_declaration::index_type it) noexcept { + #define DEFINE_CASE(x,y) case index_declaration::index_type::x: return y; + switch ( it ) { + DEFINE_CASE(unsigned_byte, GL_UNSIGNED_BYTE); + DEFINE_CASE(unsigned_short, GL_UNSIGNED_SHORT); + default: + E2D_ASSERT_MSG(false, "unexpected index type"); + return 0; + } + #undef DEFINE_CASE + } + + GLenum convert_attribute_type(vertex_declaration::attribute_type at) noexcept { + #define DEFINE_CASE(x,y) case vertex_declaration::attribute_type::x: return y; + switch ( at ) { + DEFINE_CASE(signed_byte, GL_BYTE); + DEFINE_CASE(unsigned_byte, GL_UNSIGNED_BYTE); + DEFINE_CASE(signed_short, GL_SHORT); + DEFINE_CASE(unsigned_short, GL_UNSIGNED_SHORT); + DEFINE_CASE(floating_point, GL_FLOAT); + default: + E2D_ASSERT_MSG(false, "unexpected attribute type"); + return 0; + } + #undef DEFINE_CASE + } + GLint convert_uniform_type(shader::uniform_type ut) noexcept { #define DEFINE_CASE(x,y) case shader::uniform_type::x: return y; switch ( ut ) { - DEFINE_CASE(i, GL_INT); - DEFINE_CASE(f, GL_FLOAT); + DEFINE_CASE(signed_integer, GL_INT); + DEFINE_CASE(floating_point, GL_FLOAT); DEFINE_CASE(v2i, GL_INT_VEC2); DEFINE_CASE(v3i, GL_INT_VEC3); @@ -34,7 +61,8 @@ namespace DEFINE_CASE(m3f, GL_FLOAT_MAT3); DEFINE_CASE(m4f, GL_FLOAT_MAT4); - DEFINE_CASE(s2d, GL_SAMPLER_2D); + DEFINE_CASE(sampler_2d, GL_SAMPLER_2D); + DEFINE_CASE(sampler_cube, GL_SAMPLER_CUBE); default: E2D_ASSERT_MSG(false, "unexpected uniform type"); return 0; @@ -42,9 +70,9 @@ namespace #undef DEFINE_CASE } - GLint convert_attribute_type(shader::attribute_type ut) noexcept { + GLint convert_attribute_type(shader::attribute_type at) noexcept { #define DEFINE_CASE(x,y) case shader::attribute_type::x: return y; - switch ( ut ) { + switch ( at ) { DEFINE_CASE(f, GL_FLOAT); DEFINE_CASE(v2f, GL_FLOAT_VEC2); @@ -231,6 +259,19 @@ namespace #undef DEFINE_CASE } + GLenum convert_topology(render::topology t) noexcept { + #define DEFINE_CASE(x,y) case render::topology::x: return y; + switch ( t ) { + DEFINE_CASE(triangles, GL_TRIANGLES); + DEFINE_CASE(triangles_fan, GL_TRIANGLE_FAN); + DEFINE_CASE(triangles_strip, GL_TRIANGLE_STRIP); + default: + E2D_ASSERT_MSG(false, "unexpected topology"); + return 0; + } + #undef DEFINE_CASE + } + const char* glsl_type_to_cstr(GLenum t) noexcept { #define DEFINE_CASE(x) case x: return #x switch ( t ) { @@ -250,6 +291,7 @@ namespace DEFINE_CASE(GL_FLOAT_MAT4); DEFINE_CASE(GL_SAMPLER_2D); + DEFINE_CASE(GL_SAMPLER_CUBE); default: return "GL_UNKNOWN"; } @@ -270,6 +312,11 @@ namespace #undef DEFINE_CASE } + void gl_get_string(GLenum name, const char** str) noexcept { + E2D_ASSERT(str); + *str = reinterpret_cast(glGetString(name)); + } + void gl_create_shader_wrapper(GLenum type, GLuint* id) noexcept { E2D_ASSERT(id); *id = glCreateShader(type); @@ -293,7 +340,7 @@ namespace #define GL_FLUSH_ERRORS(dbg)\ for ( GLenum err = glGetError(); err != GL_NO_ERROR; err = glGetError() ) {\ E2D_ASSERT_MSG(false, "RENDER: GL_FLUSH_ERRORS()");\ - dbg.error("RENDER: GL_FLUSH_ERRORS():\n"\ + (dbg).error("RENDER: GL_FLUSH_ERRORS():\n"\ "--> File: %0\n"\ "--> Line: %1\n"\ "--> Code: %2",\ @@ -306,7 +353,7 @@ namespace code;\ for ( GLenum err = glGetError(); err != GL_NO_ERROR; err = glGetError() ) {\ E2D_ASSERT_MSG(false, #code);\ - dbg.error(\ + (dbg).error(\ "RENDER: GL_CHECK(%0):\n"\ "--> File: %1\n"\ "--> Line: %2\n"\ @@ -337,7 +384,7 @@ namespace E2D_ASSERT(!id || glIsBuffer(id)); } public: - gl_buffer_id(debug& debug) noexcept + explicit gl_buffer_id(debug& debug) noexcept : debug_(debug) {} ~gl_buffer_id() noexcept { @@ -390,7 +437,7 @@ namespace E2D_ASSERT(!id || glIsShader(id)); } public: - gl_shader_id(debug& debug) noexcept + explicit gl_shader_id(debug& debug) noexcept : debug_(debug) {} ~gl_shader_id() noexcept { @@ -440,7 +487,7 @@ namespace E2D_ASSERT(!id || glIsProgram(id)); } public: - gl_program_id(debug& debug) noexcept + explicit gl_program_id(debug& debug) noexcept : debug_(debug) {} ~gl_program_id() noexcept { @@ -494,7 +541,7 @@ namespace E2D_ASSERT(!id || glIsTexture(id)); } public: - gl_texture_id(debug& debug) noexcept + explicit gl_texture_id(debug& debug) noexcept : debug_(debug) {} ~gl_texture_id() noexcept { @@ -526,7 +573,129 @@ namespace GLuint id_ = 0; }; - gl_shader_id compile_shader(debug& debug, const str& source, GLenum type) noexcept { + void trace_opengl_info(debug& debug) noexcept { + const char* vendor = nullptr; + GL_CHECK_CODE(debug, gl_get_string(GL_VENDOR, &vendor)); + const char* renderer = nullptr; + GL_CHECK_CODE(debug, gl_get_string(GL_RENDERER, &renderer)); + const char* version = nullptr; + GL_CHECK_CODE(debug, gl_get_string(GL_VERSION, &version)); + const char* language = nullptr; + GL_CHECK_CODE(debug, gl_get_string(GL_SHADING_LANGUAGE_VERSION, &language)); + const char* extensions = nullptr; + GL_CHECK_CODE(debug, gl_get_string(GL_EXTENSIONS, &extensions)); + const auto cstr_or_undefined = [](const char* cstr) noexcept { + return (cstr && *cstr) + ? cstr + : "(undefined)"; + }; + debug.trace("RENDER: opengl info:\n" + "--> VENDOR: %0\n" + "--> RENDERER: %1\n" + "--> VERSION: %2\n" + "--> LANGUAGE: %3\n" + "--> EXTENSIONS: %4", + cstr_or_undefined(vendor), + cstr_or_undefined(renderer), + cstr_or_undefined(version), + cstr_or_undefined(language), + cstr_or_undefined(extensions)); + } + + void trace_current_program_info(debug& debug, GLuint program) noexcept { + E2D_ASSERT(glIsProgram(program)); + + GLint uniforms = 0; + GLint attributes = 0; + GL_CHECK_CODE(debug, glGetProgramiv( + program, GL_ACTIVE_UNIFORMS, &uniforms)); + GL_CHECK_CODE(debug, glGetProgramiv( + program, GL_ACTIVE_ATTRIBUTES, &attributes)); + + debug.trace("RENDER: Program info:\n" + "--> active uniforms: %0\n" + "--> active attributes: %1", + uniforms, + attributes); + + GLint uniform_max_len = 0; + GLint attribute_max_len = 0; + GL_CHECK_CODE(debug, glGetProgramiv( + program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &uniform_max_len)); + GL_CHECK_CODE(debug, glGetProgramiv( + program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &attribute_max_len)); + + GLint max_name_len = math::max(uniform_max_len, attribute_max_len); + GLchar* name_buffer = static_cast(E2D_ALLOCA( + sizeof(GLchar) * math::numeric_cast(max_name_len))); + E2D_ASSERT(name_buffer); + + for ( GLuint i = 0; i < math::numeric_cast(uniforms); ++i ) { + GLint size = 0; + GLenum type = 0; + GL_CHECK_CODE(debug, glGetActiveUniform( + program, i, max_name_len, + nullptr, &size, &type, name_buffer)); + GLint location = 0; + GL_CHECK_CODE(debug, gl_get_uniform_location( + program, name_buffer, &location)); + debug.trace( + "uniform: %0, size: %1, type: %2, location: %3", + name_buffer, size, glsl_type_to_cstr(type), location); + } + + for ( GLuint i = 0; i < math::numeric_cast(attributes); ++i ) { + GLint size = 0; + GLenum type = 0; + GL_CHECK_CODE(debug, glGetActiveAttrib( + program, i, max_name_len, + nullptr, &size, &type, name_buffer)); + GLint location = 0; + GL_CHECK_CODE(debug, gl_get_attribute_location( + program, name_buffer, &location)); + debug.trace( + "attribute: %0, size: %1, type: %2, location: %3", + name_buffer, size, glsl_type_to_cstr(type), location); + } + } + + bool process_shader_compilation_result(debug& debug, GLuint shader) noexcept { + E2D_ASSERT(glIsShader(shader)); + GLint success = GL_FALSE; + GL_CHECK_CODE(debug, glGetShaderiv(shader, GL_COMPILE_STATUS, &success)); + GLint log_len = 0; + GL_CHECK_CODE(debug, glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_len)); + if ( log_len > 0 ) { + GLchar* log_buffer = static_cast(E2D_ALLOCA( + sizeof(GLchar) * math::numeric_cast(log_len))); + GL_CHECK_CODE(debug, glGetShaderInfoLog( + shader, log_len, nullptr, log_buffer)); + debug.log(success ? debug::level::warning : debug::level::error, + "RENDER: shader compilation info:\n--> %0", log_buffer); + } + return success == GL_TRUE; + } + + bool process_program_linking_result(debug& debug, GLuint program) noexcept { + E2D_ASSERT(glIsProgram(program)); + GLint success = GL_FALSE; + GL_CHECK_CODE(debug, glGetProgramiv(program, GL_LINK_STATUS, &success)); + GLint log_len = 0; + GL_CHECK_CODE(debug, glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_len)); + if ( log_len > 0 ) { + GLchar* log_buffer = static_cast(E2D_ALLOCA( + sizeof(GLchar) * math::numeric_cast(log_len))); + GL_CHECK_CODE(debug, glGetProgramInfoLog( + program, log_len, nullptr, log_buffer)); + debug.log(success ? debug::level::warning : debug::level::error, + "RENDER: program linking info:\n--> %0", log_buffer); + } + return success == GL_TRUE; + } + + gl_shader_id compile_shader( + debug& debug, const str& source, GLenum type) noexcept + { gl_shader_id id = gl_shader_id::create(debug, type); if ( id.empty() ) { return id; @@ -534,22 +703,32 @@ namespace const char* source_cstr = source.c_str(); GL_CHECK_CODE(debug, glShaderSource(*id, 1, &source_cstr, nullptr)); GL_CHECK_CODE(debug, glCompileShader(*id)); - GLint success = GL_FALSE; - GL_CHECK_CODE(debug, glGetShaderiv(*id, GL_COMPILE_STATUS, &success)); - GLint log_len = 0; - GL_CHECK_CODE(debug, glGetShaderiv(*id, GL_INFO_LOG_LENGTH, &log_len)); - if ( log_len > 0 ) { - vector log_buffer(math::numeric_cast(log_len) + 1, '\0'); - GL_CHECK_CODE(debug, glGetShaderInfoLog(*id, log_len, nullptr, log_buffer.data())); - debug.log(success ? debug::level::trace : debug::level::error, - "RENDER: shader compilation info:\n--> %0", log_buffer.data()); - } - return success + return process_shader_compilation_result(debug, *id) ? std::move(id) : gl_shader_id(debug); } - gl_program_id link_program(debug& debug, gl_shader_id vs, gl_shader_id fs) noexcept { + bool rebind_program_attributes( + debug& debug, GLuint program, const vertex_declaration& decl) noexcept + { + E2D_ASSERT(glIsProgram(program)); + GLuint location = 0; + for ( std::size_t i = 0, e = decl.attribute_count(); i < e; ++i ) { + const vertex_declaration::attribute_info& ai = decl.attribute(i); + GL_CHECK_CODE(debug, glBindAttribLocation( + program, location, ai.name)); + location += math::numeric_cast(ai.rows); + } + //TODO(BlackMat): add attribute type checks + return true; + } + + gl_program_id link_program( + debug& debug, + gl_shader_id vs, + gl_shader_id fs, + const vertex_declaration& decl) noexcept + { E2D_ASSERT(!vs.empty() && !fs.empty()); gl_program_id id = gl_program_id::create(debug); if ( id.empty() ) { @@ -557,93 +736,15 @@ namespace } GL_CHECK_CODE(debug, glAttachShader(*id, *vs)); GL_CHECK_CODE(debug, glAttachShader(*id, *fs)); - - GL_CHECK_CODE(debug, glBindAttribLocation(*id, 0, "in_pos")); - GL_CHECK_CODE(debug, glBindAttribLocation(*id, 1, "in_uv0")); - GL_CHECK_CODE(debug, glBindAttribLocation(*id, 2, "in_uv1")); - GL_CHECK_CODE(debug, glBindAttribLocation(*id, 3, "in_color")); - + if ( !rebind_program_attributes(debug, *id, decl) ) { + return gl_program_id(debug); + } GL_CHECK_CODE(debug, glLinkProgram(*id)); - GLint success = GL_FALSE; - GL_CHECK_CODE(debug, glGetProgramiv(*id, GL_LINK_STATUS, &success)); - GLint log_len = 0; - GL_CHECK_CODE(debug, glGetProgramiv(*id, GL_INFO_LOG_LENGTH, &log_len)); - if ( log_len > 0 ) { - vector log_buffer(math::numeric_cast(log_len) + 1, '\0'); - GL_CHECK_CODE(debug, glGetProgramInfoLog(*id, log_len, nullptr, log_buffer.data())); - debug.log(success ? debug::level::trace : debug::level::error, - "RENDER: program linking info:\n--> %0", log_buffer.data()); + if ( !process_program_linking_result(debug, *id) ) { + return gl_program_id(debug); } - if ( success ) { - GLint uniforms = 0; - GLint attributes = 0; - GL_CHECK_CODE(debug, glGetProgramiv(*id, GL_ACTIVE_UNIFORMS, &uniforms)); - GL_CHECK_CODE(debug, glGetProgramiv(*id, GL_ACTIVE_ATTRIBUTES, &attributes)); - - GLint uniform_max_len = 0; - GLint attribute_max_len = 0; - GL_CHECK_CODE(debug, glGetProgramiv(*id, GL_ACTIVE_UNIFORM_MAX_LENGTH, &uniform_max_len)); - GL_CHECK_CODE(debug, glGetProgramiv(*id, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &attribute_max_len)); - - debug.trace("Shader info:\n" - "--> uniforms: %0\n" - "--> attributes: %1\n" - "--> uniform_max_len: %2\n" - "--> attribute_max_len: %3", - uniforms, - attributes, - uniform_max_len, - attribute_max_len); - - vector name_buffer( - math::numeric_cast( - math::max(uniform_max_len, attribute_max_len)), '\0'); - - for ( GLuint i = 0; i < math::numeric_cast(uniforms); ++i ) { - GLint size = 0; - GLenum type = 0; - GL_CHECK_CODE(debug, glGetActiveUniform( - *id, - i, - math::numeric_cast(name_buffer.size()), - nullptr, - &size, - &type, - name_buffer.data())); - - GLint location = 0; - GL_CHECK_CODE(debug, gl_get_uniform_location( - *id, name_buffer.data(), &location)); - - debug.trace( - "uniform: %0, size: %1, type: %2, location: %3", - name_buffer.data(), size, glsl_type_to_cstr(type), location); - } - - for ( GLuint i = 0; i < math::numeric_cast(attributes); ++i ) { - GLint size = 0; - GLenum type = 0; - GL_CHECK_CODE(debug, glGetActiveAttrib( - *id, - i, - math::numeric_cast(name_buffer.size()), - nullptr, - &size, - &type, - name_buffer.data())); - - GLint location = 0; - GL_CHECK_CODE(debug, gl_get_attribute_location( - *id, name_buffer.data(), &location)); - - debug.trace( - "attribute: %0, size: %1, type: %2, location: %3", - name_buffer.data(), size, glsl_type_to_cstr(type), location); - } - } - return success - ? std::move(id) - : gl_program_id(debug); + trace_current_program_info(debug, *id); + return id; } bool validate_program(debug& debug, const gl_program_id& ps) noexcept { @@ -654,13 +755,47 @@ namespace GLint log_len = 0; GL_CHECK_CODE(debug, glGetProgramiv(*ps, GL_INFO_LOG_LENGTH, &log_len)); if ( log_len > 0 ) { - vector log_buffer(math::numeric_cast(log_len) + 1, '\0'); - GL_CHECK_CODE(debug, glGetProgramInfoLog(*ps, log_len, nullptr, log_buffer.data())); - debug.log(success ? debug::level::trace : debug::level::error, - "RENDER: program validation info:\n--> %0", log_buffer.data()); + GLchar* log_buffer = static_cast(E2D_ALLOCA( + sizeof(GLchar) * math::numeric_cast(log_len))); + GL_CHECK_CODE(debug, glGetProgramInfoLog(*ps, log_len, nullptr, log_buffer)); + debug.log(success ? debug::level::warning : debug::level::error, + "RENDER: program validation info:\n--> %0", log_buffer); } return success == GL_TRUE; } + + void bind_vertex_declaration(debug& debug, const vertex_declaration& decl) noexcept { + GLuint location = 0; + for ( std::size_t i = 0; i < decl.attribute_count(); ++i ) { + const vertex_declaration::attribute_info& ai = decl.attribute(i); + const GLuint rows = math::numeric_cast(ai.rows); + for ( GLuint row = 0; row < rows; ++row ) { + GL_CHECK_CODE(debug, glEnableVertexAttribArray( + location + row)); + GL_CHECK_CODE(debug, glVertexAttribPointer( + location + row, + math::numeric_cast(ai.columns), + convert_attribute_type(ai.type), + ai.normalized ? GL_TRUE : GL_FALSE, + math::numeric_cast(decl.vertex_size()), + reinterpret_cast(ai.stride + row * ai.row_size()))); + } + location += rows; + } + } + + void unbind_vertex_declaration(debug& debug, const vertex_declaration& decl) noexcept { + GLuint location = 0; + for ( std::size_t i = 0; i < decl.attribute_count(); ++i ) { + const vertex_declaration::attribute_info& ai = decl.attribute(i); + const GLuint rows = math::numeric_cast(ai.rows); + for ( GLuint row = 0; row < rows; ++row ) { + GL_CHECK_CODE(debug, glDisableVertexAttribArray( + location + row)); + } + location += rows; + } + } } namespace e2d @@ -671,13 +806,18 @@ namespace e2d class shader::internal_state final : private e2d::noncopyable { public: - debug& debug; - gl_program_id id; + debug& debug_; + gl_program_id id_; + vertex_declaration decl_; public: - internal_state(class debug& debug, gl_program_id nid) - : debug(debug) - , id(std::move(nid)) { - E2D_ASSERT(!id.empty()); + internal_state( + debug& debug, + gl_program_id id, + const vertex_declaration& decl) + : debug_(debug) + , id_(std::move(id)) + , decl_(decl) { + E2D_ASSERT(!id_.empty()); } ~internal_state() noexcept = default; }; @@ -688,13 +828,13 @@ namespace e2d class texture::internal_state final : private e2d::noncopyable { public: - debug& debug; - gl_texture_id id; + debug& debug_; + gl_texture_id id_; public: - internal_state(class debug& debug, gl_texture_id nid) - : debug(debug) - , id(std::move(nid)) { - E2D_ASSERT(!id.empty()); + internal_state(debug& debug, gl_texture_id id) + : debug_(debug) + , id_(std::move(id)) { + E2D_ASSERT(!id_.empty()); } ~internal_state() noexcept = default; }; @@ -705,15 +845,24 @@ namespace e2d class index_buffer::internal_state final : private e2d::noncopyable { public: - debug& debug; - gl_buffer_id id; - std::size_t count = 0; + debug& debug_; + gl_buffer_id id_; + std::size_t size_ = 0; + index_declaration decl_; + usage usage_; public: - internal_state(class debug& debug, gl_buffer_id nid, std::size_t ncount) - : debug(debug) - , id(std::move(nid)) - , count(ncount) { - E2D_ASSERT(!id.empty()); + internal_state( + debug& debug, + gl_buffer_id id, + std::size_t size, + const index_declaration& decl, + usage usage) + : debug_(debug) + , id_(std::move(id)) + , size_(size) + , decl_(decl) + , usage_(usage) { + E2D_ASSERT(!id_.empty()); } ~internal_state() noexcept = default; }; @@ -724,15 +873,24 @@ namespace e2d class vertex_buffer::internal_state final : private e2d::noncopyable { public: - debug& debug; - gl_buffer_id id; - std::size_t count = 0; + debug& debug_; + gl_buffer_id id_; + std::size_t size_ = 0; + vertex_declaration decl_; + usage usage_; public: - internal_state(class debug& debug, gl_buffer_id nid, std::size_t ncount) - : debug(debug) - , id(std::move(nid)) - , count(ncount) { - E2D_ASSERT(!id.empty()); + internal_state( + debug& debug, + gl_buffer_id id, + std::size_t size, + const vertex_declaration& decl, + usage usage) + : debug_(debug) + , id_(std::move(id)) + , size_(size) + , decl_(decl) + , usage_(usage) { + E2D_ASSERT(!id_.empty()); } ~internal_state() noexcept = default; }; @@ -743,14 +901,15 @@ namespace e2d class render::internal_state final : private e2d::noncopyable { public: - debug& debug; - window& window; - m4f view; - m4f projection; + debug& debug_; + window& window_; + m4f model_; + m4f view_; + m4f projection_; public: - internal_state(class debug& debug, class window& window) - : debug(debug) - , window(window) {} + internal_state(debug& debug, window& window) + : debug_(debug) + , window_(window) {} ~internal_state() noexcept = default; }; @@ -762,6 +921,40 @@ namespace e2d : state_(std::move(state)) {} shader::~shader() noexcept = default; + const vertex_declaration& shader::decl() const noexcept { + return state_->decl_; + } + + void shader::set_uniform(str_view name, i32 value) const noexcept { + const std::size_t name_buffer_size = name.length() + 1; + GLchar* name_buffer = static_cast(E2D_ALLOCA(name_buffer_size)); + strings::format_nothrow(name_buffer, name_buffer_size, nullptr, "%0", name); + GLint location = 0; + GL_CHECK_CODE(state_->debug_, gl_get_uniform_location( + *state_->id_, name_buffer, &location)); + if ( location != -1 ) { + GL_CHECK_CODE(state_->debug_, glUseProgram(*state_->id_)); + GL_CHECK_CODE(state_->debug_, glUniform1i( + location, math::numeric_cast(value))); + GL_CHECK_CODE(state_->debug_, glUseProgram(0)); + } + } + + void shader::set_uniform(str_view name, f32 value) const noexcept { + const std::size_t name_buffer_size = name.length() + 1; + GLchar* name_buffer = static_cast(E2D_ALLOCA(name_buffer_size)); + strings::format_nothrow(name_buffer, name_buffer_size, nullptr, "%0", name); + GLint location = 0; + GL_CHECK_CODE(state_->debug_, gl_get_uniform_location( + *state_->id_, name_buffer, &location)); + if ( location != -1 ) { + GL_CHECK_CODE(state_->debug_, glUseProgram(*state_->id_)); + GL_CHECK_CODE(state_->debug_, glUniform1f( + location, math::numeric_cast(value))); + GL_CHECK_CODE(state_->debug_, glUseProgram(0)); + } + } + // // texture // @@ -771,24 +964,24 @@ namespace e2d texture::~texture() noexcept = default; void texture::set_wrap(wrap u, wrap v) noexcept { - GL_CHECK_CODE(state_->debug, glBindTexture( - GL_TEXTURE_2D, *state_->id)); - GL_CHECK_CODE(state_->debug, glTexParameteri( + GL_CHECK_CODE(state_->debug_, glBindTexture( + GL_TEXTURE_2D, *state_->id_)); + GL_CHECK_CODE(state_->debug_, glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, convert_wrap(u))); - GL_CHECK_CODE(state_->debug, glTexParameteri( + GL_CHECK_CODE(state_->debug_, glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, convert_wrap(v))); - GL_CHECK_CODE(state_->debug, glBindTexture( + GL_CHECK_CODE(state_->debug_, glBindTexture( GL_TEXTURE_2D, 0)); } void texture::set_filter(filter min, filter mag) noexcept { - GL_CHECK_CODE(state_->debug, glBindTexture( - GL_TEXTURE_2D, *state_->id)); - GL_CHECK_CODE(state_->debug, glTexParameteri( + GL_CHECK_CODE(state_->debug_, glBindTexture( + GL_TEXTURE_2D, *state_->id_)); + GL_CHECK_CODE(state_->debug_, glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, convert_filter(min))); - GL_CHECK_CODE(state_->debug, glTexParameteri( + GL_CHECK_CODE(state_->debug_, glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, convert_filter(mag))); - GL_CHECK_CODE(state_->debug, glBindTexture( + GL_CHECK_CODE(state_->debug_, glBindTexture( GL_TEXTURE_2D, 0)); } @@ -800,22 +993,36 @@ namespace e2d : state_(std::move(state)) {} index_buffer::~index_buffer() noexcept = default; - void index_buffer::update( - const u16* indices, std::size_t count, std::size_t offset) noexcept - { - GL_CHECK_CODE(state_->debug, glBindBuffer( - GL_ELEMENT_ARRAY_BUFFER, *state_->id)); - GL_CHECK_CODE(state_->debug, glBufferSubData( + void index_buffer::update(const buffer& indices, std::size_t offset) noexcept { + const std::size_t buffer_offset = offset * state_->decl_.index_size(); + E2D_ASSERT(indices.size() + buffer_offset <= state_->size_); + E2D_ASSERT(indices.size() % state_->decl_.index_size() == 0); + GL_CHECK_CODE(state_->debug_, glBindBuffer( + GL_ELEMENT_ARRAY_BUFFER, *state_->id_)); + GL_CHECK_CODE(state_->debug_, glBufferSubData( GL_ELEMENT_ARRAY_BUFFER, - math::numeric_cast(sizeof(u16) * offset), - math::numeric_cast(sizeof(u16) * count), - indices)); - GL_CHECK_CODE(state_->debug, glBindBuffer( + math::numeric_cast(buffer_offset), + math::numeric_cast(indices.size()), + indices.data())); + GL_CHECK_CODE(state_->debug_, glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0)); } - std::size_t index_buffer::count() const noexcept { - return state_->count; + const index_declaration& index_buffer::decl() const noexcept { + return state_->decl_; + } + + index_buffer::usage index_buffer::buffer_usage() const noexcept { + return state_->usage_; + } + + std::size_t index_buffer::buffer_size() const noexcept { + return state_->size_; + } + + std::size_t index_buffer::index_count() const noexcept { + E2D_ASSERT(state_->size_ % state_->decl_.index_size() == 0); + return state_->size_ / state_->decl_.index_size(); } // @@ -826,54 +1033,76 @@ namespace e2d : state_(std::move(state)) {} vertex_buffer::~vertex_buffer() noexcept = default; - void vertex_buffer::update( - const vertex* vertices, std::size_t count, std::size_t offset) noexcept - { - GL_CHECK_CODE(state_->debug, glBindBuffer( + void vertex_buffer::update(const buffer& vertices, std::size_t offset) noexcept { + const std::size_t buffer_offset = offset * state_->decl_.vertex_size(); + E2D_ASSERT(vertices.size() + buffer_offset <= state_->size_); + E2D_ASSERT(vertices.size() % state_->decl_.vertex_size() == 0); + GL_CHECK_CODE(state_->debug_, glBindBuffer( GL_ARRAY_BUFFER, - *state_->id)); - GL_CHECK_CODE(state_->debug, glBufferSubData( + *state_->id_)); + GL_CHECK_CODE(state_->debug_, glBufferSubData( GL_ARRAY_BUFFER, - math::numeric_cast(sizeof(vertex) * offset), - math::numeric_cast(sizeof(vertex) * count), - vertices)); - GL_CHECK_CODE(state_->debug, glBindBuffer( + math::numeric_cast(buffer_offset), + math::numeric_cast(vertices.size()), + vertices.data())); + GL_CHECK_CODE(state_->debug_, glBindBuffer( GL_ARRAY_BUFFER, 0)); } - std::size_t vertex_buffer::count() const noexcept { - return state_->count; + const vertex_declaration& vertex_buffer::decl() const noexcept { + return state_->decl_; + } + + vertex_buffer::usage vertex_buffer::buffer_usage() const noexcept { + return state_->usage_; + } + + std::size_t vertex_buffer::buffer_size() const noexcept { + return state_->size_; + } + + std::size_t vertex_buffer::vertex_count() const noexcept { + E2D_ASSERT(state_->size_ % state_->decl_.vertex_size() == 0); + return state_->size_ / state_->decl_.vertex_size(); } // // render // - render::render(debug& d, window& w) - : state_(new internal_state(d, w)) {} + render::render(debug& debug, window& window) + : state_(new internal_state(debug, window)) { + trace_opengl_info(debug); + } render::~render() noexcept = default; - shader_ptr render::create_shader(input_stream_uptr vertex, input_stream_uptr fragment) { + shader_ptr render::create_shader( + input_stream_uptr vertex, + input_stream_uptr fragment, + const vertex_declaration& decl) + { str vertex_str; gl_shader_id vs = streams::try_read_tail(vertex_str, vertex) - ? compile_shader(state_->debug, vertex_str.c_str(), GL_VERTEX_SHADER) - : gl_shader_id(state_->debug); + ? compile_shader(state_->debug_, vertex_str, GL_VERTEX_SHADER) + : gl_shader_id(state_->debug_); if ( vs.empty() ) { return nullptr; } str fragment_str; gl_shader_id fs = streams::try_read_tail(fragment_str, fragment) - ? compile_shader(state_->debug, fragment_str.c_str(), GL_FRAGMENT_SHADER) - : gl_shader_id(state_->debug); + ? compile_shader(state_->debug_, fragment_str, GL_FRAGMENT_SHADER) + : gl_shader_id(state_->debug_); if ( fs.empty() ) { return nullptr; } - gl_program_id ps = link_program(state_->debug, std::move(vs), std::move(fs)); - if ( ps.empty() || !validate_program(state_->debug, ps) ) { + gl_program_id ps = link_program( + state_->debug_, std::move(vs), std::move(fs), decl); + if ( ps.empty() || !validate_program(state_->debug_, ps) ) { return nullptr; } return std::make_shared( - std::make_unique(state_->debug, std::move(ps))); + std::make_unique( + state_->debug_, std::move(ps), decl)); } texture_ptr render::create_texture(const image& image) { @@ -887,43 +1116,49 @@ namespace e2d } index_buffer_ptr render::create_index_buffer( - const u16* indices, std::size_t count, index_buffer::usage usage) + const buffer& indices, + const index_declaration& decl, + index_buffer::usage usage) { gl_buffer_id id = gl_buffer_id::create_and_bind( - state_->debug, GL_ELEMENT_ARRAY_BUFFER); + state_->debug_, GL_ELEMENT_ARRAY_BUFFER); if ( id.empty() ) { return nullptr; } - GL_CHECK_CODE(state_->debug, glBufferData( + E2D_ASSERT(indices.size() % decl.index_size() == 0); + GL_CHECK_CODE(state_->debug_, glBufferData( GL_ELEMENT_ARRAY_BUFFER, - math::numeric_cast(sizeof(u16) * count), - indices, + math::numeric_cast(indices.size()), + indices.data(), convert_usage(usage))); - GL_CHECK_CODE(state_->debug, glBindBuffer( + GL_CHECK_CODE(state_->debug_, glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0)); return std::make_shared( std::make_unique( - state_->debug, std::move(id), count)); + state_->debug_, std::move(id), indices.size(), decl, usage)); } vertex_buffer_ptr render::create_vertex_buffer( - const vertex* vertices, std::size_t count, vertex_buffer::usage usage) + const buffer& vertices, + const vertex_declaration& decl, + vertex_buffer::usage usage) { gl_buffer_id id = gl_buffer_id::create_and_bind( - state_->debug, GL_ARRAY_BUFFER); + state_->debug_, GL_ARRAY_BUFFER); if ( id.empty() ) { return nullptr; } - GL_CHECK_CODE(state_->debug, glBufferData( + E2D_ASSERT(vertices.size() % decl.vertex_size() == 0); + GL_CHECK_CODE(state_->debug_, glBufferData( GL_ARRAY_BUFFER, - math::numeric_cast(sizeof(vertex) * count), - vertices, + math::numeric_cast(vertices.size()), + vertices.data(), convert_usage(usage))); - GL_CHECK_CODE(state_->debug, glBindBuffer( + GL_CHECK_CODE(state_->debug_, glBindBuffer( GL_ARRAY_BUFFER, 0)); return std::make_shared( std::make_unique( - state_->debug, std::move(id), count)); + state_->debug_, std::move(id), vertices.size(), decl, usage)); } void render::clear(bool color, bool depth, bool stencil) noexcept { @@ -937,64 +1172,50 @@ namespace e2d if ( stencil ) { mask |= GL_STENCIL_BUFFER_BIT; } - GL_CHECK_CODE(state_->debug, glClear(mask)); + GL_CHECK_CODE(state_->debug_, glClear(mask)); } void render::draw( + topology tp, const shader_ptr& ps, const index_buffer_ptr& ib, const vertex_buffer_ptr& vb) noexcept { E2D_ASSERT(ps && ib && vb); + E2D_ASSERT(ps->decl() == vb->decl()); - GL_CHECK_CODE(state_->debug, glUseProgram(*ps->state_->id)); - GL_CHECK_CODE(state_->debug, glEnableVertexAttribArray(0)); - GL_CHECK_CODE(state_->debug, glEnableVertexAttribArray(1)); - GL_CHECK_CODE(state_->debug, glEnableVertexAttribArray(2)); - GL_CHECK_CODE(state_->debug, glEnableVertexAttribArray(3)); + const index_declaration& id = ib->decl(); + const vertex_declaration& vd = vb->decl(); - GL_CHECK_CODE(state_->debug, glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, *ib->state_->id)); - GL_CHECK_CODE(state_->debug, glBindBuffer(GL_ARRAY_BUFFER, *vb->state_->id)); + GL_CHECK_CODE(state_->debug_, glUseProgram(*ps->state_->id_)); + GL_CHECK_CODE(state_->debug_, glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, *ib->state_->id_)); + GL_CHECK_CODE(state_->debug_, glBindBuffer(GL_ARRAY_BUFFER, *vb->state_->id_)); + bind_vertex_declaration(state_->debug_, vd); - GL_CHECK_CODE(state_->debug, glVertexAttribPointer( - 0, 3, GL_FLOAT, GL_FALSE, - sizeof(vertex), reinterpret_cast(offsetof(vertex, pos)))); - - GL_CHECK_CODE(state_->debug, glVertexAttribPointer( - 1, 2, GL_FLOAT, GL_FALSE, - sizeof(vertex), reinterpret_cast(offsetof(vertex, uv[0])))); - - GL_CHECK_CODE(state_->debug, glVertexAttribPointer( - 2, 2, GL_FLOAT, GL_FALSE, - sizeof(vertex), reinterpret_cast(offsetof(vertex, uv[1])))); - - GL_CHECK_CODE(state_->debug, glVertexAttribPointer( - 3, 4, GL_UNSIGNED_BYTE, GL_TRUE, - sizeof(vertex), reinterpret_cast(offsetof(vertex, color)))); - - GL_CHECK_CODE(state_->debug, glDrawElements( - GL_TRIANGLES, - math::numeric_cast(ib->count()), - GL_UNSIGNED_SHORT, + GL_CHECK_CODE(state_->debug_, glDrawElements( + convert_topology(tp), + math::numeric_cast(ib->index_count()), + convert_index_type(id.index().type), nullptr)); - GL_CHECK_CODE(state_->debug, glDisableVertexAttribArray(3)); - GL_CHECK_CODE(state_->debug, glDisableVertexAttribArray(2)); - GL_CHECK_CODE(state_->debug, glDisableVertexAttribArray(1)); - GL_CHECK_CODE(state_->debug, glDisableVertexAttribArray(0)); - GL_CHECK_CODE(state_->debug, glUseProgram(0)); + unbind_vertex_declaration(state_->debug_, vd); + GL_CHECK_CODE(state_->debug_, glUseProgram(0)); + } + + void render::set_model(const m4f& model) noexcept { + state_->model_ = model; } void render::set_view(const m4f& view) noexcept { - state_->view = view; + state_->view_ = view; } void render::set_projection(const m4f& projection) noexcept { - state_->projection = projection; + state_->projection_ = projection; } void render::set_viewport(u32 x, u32 y, u32 w, u32 h) noexcept { - GL_CHECK_CODE(state_->debug, glViewport( + GL_CHECK_CODE(state_->debug_, glViewport( math::numeric_cast(x), math::numeric_cast(y), math::numeric_cast(w), @@ -1002,20 +1223,20 @@ namespace e2d } void render::enable_state(state state) noexcept { - GL_CHECK_CODE(state_->debug, glEnable(convert_state(state))); + GL_CHECK_CODE(state_->debug_, glEnable(convert_state(state))); } void render::disable_state(state state) noexcept { - GL_CHECK_CODE(state_->debug, glDisable(convert_state(state))); + GL_CHECK_CODE(state_->debug_, glDisable(convert_state(state))); } void render::set_blend_func(blend_func src, blend_func dst) noexcept { - GL_CHECK_CODE(state_->debug, glBlendFunc( + GL_CHECK_CODE(state_->debug_, glBlendFunc( convert_blend_func(src), convert_blend_func(dst))); } void render::set_blend_color(const color& color) noexcept { - GL_CHECK_CODE(state_->debug, glBlendColor( + GL_CHECK_CODE(state_->debug_, glBlendColor( math::numeric_cast(color.r), math::numeric_cast(color.g), math::numeric_cast(color.b), @@ -1023,58 +1244,58 @@ namespace e2d } void render::set_blend_equation(blend_equation blend_equation) noexcept { - GL_CHECK_CODE(state_->debug, glBlendEquation( + GL_CHECK_CODE(state_->debug_, glBlendEquation( convert_blend_equation(blend_equation))); } void render::set_cull_face(cull_face cull_face) noexcept { - GL_CHECK_CODE(state_->debug, glCullFace( + GL_CHECK_CODE(state_->debug_, glCullFace( convert_cull_face(cull_face))); } void render::set_depth_func(depth_func depth_func) noexcept { - GL_CHECK_CODE(state_->debug, glDepthFunc( + GL_CHECK_CODE(state_->debug_, glDepthFunc( convert_depth_func(depth_func))); } void render::set_depth_mask(bool yesno) noexcept { - GL_CHECK_CODE(state_->debug, glDepthMask( + GL_CHECK_CODE(state_->debug_, glDepthMask( yesno ? GL_TRUE : GL_FALSE)); } void render::set_clear_depth(f32 value) noexcept { - GL_CHECK_CODE(state_->debug, glClearDepth( + GL_CHECK_CODE(state_->debug_, glClearDepth( math::numeric_cast(value))); } void render::set_stencil_func(stencil_func stencil_func, u32 ref, u32 mask) noexcept { - GL_CHECK_CODE(state_->debug, glStencilFunc( + GL_CHECK_CODE(state_->debug_, glStencilFunc( convert_stencil_func(stencil_func), math::numeric_cast(ref), math::numeric_cast(mask))); } void render::set_stencil_mask(u32 mask) noexcept { - GL_CHECK_CODE(state_->debug, glStencilMask( + GL_CHECK_CODE(state_->debug_, glStencilMask( math::numeric_cast(mask))); } void render::set_stencil_op( stencil_op fail, stencil_op zfail, stencil_op zpass) noexcept { - GL_CHECK_CODE(state_->debug, glStencilOp( + GL_CHECK_CODE(state_->debug_, glStencilOp( convert_stencil_op(fail), convert_stencil_op(zfail), convert_stencil_op(zpass))); } void render::set_clear_stencil(u32 value) noexcept { - GL_CHECK_CODE(state_->debug, glClearStencil( + GL_CHECK_CODE(state_->debug_, glClearStencil( math::numeric_cast(value))); } void render::set_clear_color(const color& color) noexcept { - GL_CHECK_CODE(state_->debug, glClearColor( + GL_CHECK_CODE(state_->debug_, glClearColor( math::numeric_cast(color.r), math::numeric_cast(color.g), math::numeric_cast(color.b), @@ -1082,7 +1303,7 @@ namespace e2d } void render::set_color_mask(bool r, bool g, bool b, bool a) { - GL_CHECK_CODE(state_->debug, glColorMask( + GL_CHECK_CODE(state_->debug_, glColorMask( r ? GL_TRUE : GL_FALSE, g ? GL_TRUE : GL_FALSE, b ? GL_TRUE : GL_FALSE, diff --git a/untests/sources/untests_core/render.cpp b/untests/sources/untests_core/render.cpp index 036d817d..1eac9857 100644 --- a/untests/sources/untests_core/render.cpp +++ b/untests/sources/untests_core/render.cpp @@ -8,4 +8,82 @@ using namespace e2d; TEST_CASE("render"){ + SECTION("index_declaration"){ + index_declaration id; + REQUIRE(id.index().type == index_declaration::index_type::unsigned_short); + REQUIRE(id.index_size() == 2); + + index_declaration id2(index_declaration::index_type::unsigned_short); + REQUIRE(id2.index().type == index_declaration::index_type::unsigned_short); + REQUIRE(id2.index_size() == 2); + + index_declaration id3(index_declaration::index_type::unsigned_byte); + REQUIRE(id3.index().type == index_declaration::index_type::unsigned_byte); + REQUIRE(id3.index_size() == 1); + + REQUIRE(id == id2); + REQUIRE_FALSE(id == id3); + + REQUIRE_FALSE(id != id2); + REQUIRE(id != id3); + + index_declaration id4 = id; + REQUIRE(id4 == id2); + id4 = id3; + REQUIRE(id4 != id2); + REQUIRE(id4 == id3); + } + SECTION("vertex_declaration"){ + vertex_declaration vd; + REQUIRE(vd.attribute_count() == 0); + REQUIRE(vd.vertex_size() == 0); + + REQUIRE(&vd == &vd.add_attribute("hello")); + REQUIRE(vd.attribute_count() == 1); + REQUIRE(vd.vertex_size() == 8); + + vertex_declaration::attribute_info ai = vd.attribute(0); + REQUIRE(!std::strcmp(ai.name, "hello")); + REQUIRE(ai.columns == 2); + REQUIRE(ai.stride == 0); + REQUIRE(ai.type == vertex_declaration::attribute_type::floating_point); + REQUIRE_FALSE(ai.normalized); + + REQUIRE(&vd == &vd.skip_bytes(4)); + REQUIRE(vd.vertex_size() == 12); + + REQUIRE(&vd == &vd.add_attribute("world").normalized()); + REQUIRE(vd.attribute_count() == 2); + REQUIRE(vd.vertex_size() == 14); + + vertex_declaration::attribute_info ai2 = vd.attribute(1); + REQUIRE(!std::strcmp(ai2.name, "world")); + REQUIRE(ai2.columns == 1); + REQUIRE(ai2.stride == 12); + REQUIRE(ai2.type == vertex_declaration::attribute_type::unsigned_short); + REQUIRE(ai2.normalized); + + auto vd2 = vertex_declaration() + .add_attribute("hello", 1, 2, vertex_declaration::attribute_type::floating_point, false) + .skip_bytes(4) + .add_attribute("world", 1, 1, vertex_declaration::attribute_type::unsigned_short, true); + REQUIRE(vd == vd2); + REQUIRE_FALSE(vd != vd2); + + auto vd3 = vertex_declaration() + .add_attribute("hello") + .add_attribute("world"); + REQUIRE_FALSE(vd == vd3); + REQUIRE(vd != vd3); + + REQUIRE(vd3.vertex_size() == 16); + vd3.skip_bytes(4); + REQUIRE(vd3.vertex_size() == 20); + + vertex_declaration vd4 = vd2; + REQUIRE(vd4 == vd); + vd4 = vd3; + REQUIRE(vd4 != vd); + REQUIRE(vd4 == vd3); + } }