From 3038c6bb0e44c6d5b73dfd28ed641ceeb6a019b3 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Wed, 3 Oct 2018 20:27:46 +0700 Subject: [PATCH] GL_CHECK_CODE. index and vertex buffers. hardcoded vertex wip. --- headers/enduro2d/core/_core.hpp | 3 + headers/enduro2d/core/render.hpp | 90 +- samples/sources/sample_00/sample_00.cpp | 55 +- sources/enduro2d/core/render_impl/render.hpp | 1 + .../core/render_impl/render_opengl.cpp | 787 ++++++++++++++---- 5 files changed, 774 insertions(+), 162 deletions(-) diff --git a/headers/enduro2d/core/_core.hpp b/headers/enduro2d/core/_core.hpp index c61814ba..134b6fe2 100644 --- a/headers/enduro2d/core/_core.hpp +++ b/headers/enduro2d/core/_core.hpp @@ -16,8 +16,11 @@ namespace e2d class mouse; class keyboard; class input; + class vertex; class shader; class texture; + class index_buffer; + class vertex_buffer; class render; class vfs; class window; diff --git a/headers/enduro2d/core/render.hpp b/headers/enduro2d/core/render.hpp index e3df7c87..d1866a1e 100644 --- a/headers/enduro2d/core/render.hpp +++ b/headers/enduro2d/core/render.hpp @@ -10,6 +10,17 @@ namespace e2d { + // + // vertex + // + + class vertex { + public: + v3f pos; + v2f uv[2]; + color32 color; + }; + // // shader // @@ -82,14 +93,61 @@ namespace e2d void set_wrap(wrap u, wrap v) noexcept; void set_filter(filter min, filter mag) noexcept; - - const v2u& native_size() const noexcept; - const v2u& original_size() const noexcept; private: internal_state_uptr state_; }; using texture_ptr = std::shared_ptr; + // + // index buffer + // + + class index_buffer final : private noncopyable { + private: + friend class render; + class internal_state; + using internal_state_uptr = std::unique_ptr; + public: + enum class usage { + static_draw, + stream_draw, + dynamic_draw + }; + public: + 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; + private: + internal_state_uptr state_; + }; + using index_buffer_ptr = std::shared_ptr; + + // + // vertex buffer + // + + class vertex_buffer final : private noncopyable { + private: + friend class render; + class internal_state; + using internal_state_uptr = std::unique_ptr; + public: + enum class usage { + static_draw, + stream_draw, + dynamic_draw + }; + public: + 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; + private: + internal_state_uptr state_; + }; + using vertex_buffer_ptr = std::shared_ptr; + // // render // @@ -160,18 +218,37 @@ namespace e2d invert }; public: - render(debug& debug); + render(debug& d, window& w); ~render() noexcept; shader_ptr create_shader( input_stream_uptr vertex, input_stream_uptr fragment); - texture_ptr create_texture(const image& image); - texture_ptr create_texture(const v2u& size, image_data_format format); + texture_ptr create_texture( + const image& image); + + texture_ptr create_texture( + const v2u& size, + image_data_format format); + + index_buffer_ptr create_index_buffer( + const u16* indices, + std::size_t count, + index_buffer::usage usage); + + vertex_buffer_ptr create_vertex_buffer( + const vertex* vertices, + std::size_t count, + vertex_buffer::usage usage); void clear(bool color, bool depth, bool stencil) noexcept; + void draw( + const shader_ptr& ps, + const index_buffer_ptr& ib, + const vertex_buffer_ptr& vb) 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; @@ -197,7 +274,6 @@ namespace e2d void set_clear_color(const color& color) noexcept; void set_color_mask(bool r, bool g, bool b, bool a); private: - debug& debug_; class internal_state; std::unique_ptr state_; }; diff --git a/samples/sources/sample_00/sample_00.cpp b/samples/sources/sample_00/sample_00.cpp index 5860cde1..3e49c348 100644 --- a/samples/sources/sample_00/sample_00.cpp +++ b/samples/sources/sample_00/sample_00.cpp @@ -7,19 +7,72 @@ #include "../common.hpp" 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" + "}"; + + 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" + "}"; + + u16 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}}}; +} + int e2d_main() { modules::initialize(); modules::initialize(); - modules::initialize(the()); modules::initialize(v2u{640, 480}, "Enduro2D", false); + modules::initialize(the(), the()); the().register_sink(); the().register_event_listener(the()); + 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)))); + + const auto ib = the().create_index_buffer( + indices, E2D_COUNTOF(indices), index_buffer::usage::static_draw); + + const auto vb = the().create_vertex_buffer( + vertices, E2D_COUNTOF(vertices), vertex_buffer::usage::static_draw); + 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().clear(true, true, true); + the().draw(ps, ib, vb); the().swap_buffers(true); the().frame_tick(); window::poll_events(); diff --git a/sources/enduro2d/core/render_impl/render.hpp b/sources/enduro2d/core/render_impl/render.hpp index da72cc9f..43bf337c 100644 --- a/sources/enduro2d/core/render_impl/render.hpp +++ b/sources/enduro2d/core/render_impl/render.hpp @@ -7,6 +7,7 @@ #pragma once #include +#include #include #define E2D_RENDER_MODE_NONE 1 diff --git a/sources/enduro2d/core/render_impl/render_opengl.cpp b/sources/enduro2d/core/render_impl/render_opengl.cpp index b0b1c8f5..2363ef85 100644 --- a/sources/enduro2d/core/render_impl/render_opengl.cpp +++ b/sources/enduro2d/core/render_impl/render_opengl.cpp @@ -86,6 +86,32 @@ namespace #undef DEFINE_CASE } + GLenum convert_usage(index_buffer::usage u) noexcept { + #define DEFINE_CASE(x,y) case index_buffer::usage::x: return y; + switch ( u ) { + DEFINE_CASE(static_draw, GL_STATIC_DRAW); + DEFINE_CASE(stream_draw, GL_STREAM_DRAW); + DEFINE_CASE(dynamic_draw, GL_DYNAMIC_DRAW); + default: + E2D_ASSERT_MSG(false, "unexpected index buffer usage"); + return 0; + } + #undef DEFINE_CASE + } + + GLenum convert_usage(vertex_buffer::usage u) noexcept { + #define DEFINE_CASE(x,y) case vertex_buffer::usage::x: return y; + switch ( u ) { + DEFINE_CASE(static_draw, GL_STATIC_DRAW); + DEFINE_CASE(stream_draw, GL_STREAM_DRAW); + DEFINE_CASE(dynamic_draw, GL_DYNAMIC_DRAW); + default: + E2D_ASSERT_MSG(false, "unexpected vertex buffer usage"); + return 0; + } + #undef DEFINE_CASE + } + GLenum convert_state(render::state s) noexcept { #define DEFINE_CASE(x,y) case render::state::x: return y; switch ( s ) { @@ -205,32 +231,132 @@ namespace #undef DEFINE_CASE } - class gl_shader_id final : private noncopyable { + const char* glsl_type_to_cstr(GLenum t) noexcept { + #define DEFINE_CASE(x) case x: return #x + switch ( t ) { + DEFINE_CASE(GL_INT); + DEFINE_CASE(GL_FLOAT); + + DEFINE_CASE(GL_INT_VEC2); + DEFINE_CASE(GL_INT_VEC3); + DEFINE_CASE(GL_INT_VEC4); + + DEFINE_CASE(GL_FLOAT_VEC2); + DEFINE_CASE(GL_FLOAT_VEC3); + DEFINE_CASE(GL_FLOAT_VEC4); + + DEFINE_CASE(GL_FLOAT_MAT2); + DEFINE_CASE(GL_FLOAT_MAT3); + DEFINE_CASE(GL_FLOAT_MAT4); + + DEFINE_CASE(GL_SAMPLER_2D); + default: + return "GL_UNKNOWN"; + } + #undef DEFINE_CASE + } + + const char* gl_error_code_to_cstr(GLenum e) noexcept { + #define DEFINE_CASE(x) case x: return #x; + switch ( e ) { + DEFINE_CASE(GL_INVALID_ENUM); + DEFINE_CASE(GL_INVALID_VALUE); + DEFINE_CASE(GL_INVALID_OPERATION); + DEFINE_CASE(GL_INVALID_FRAMEBUFFER_OPERATION); + DEFINE_CASE(GL_OUT_OF_MEMORY); + default: + return "GL_UNKNOWN"; + } + #undef DEFINE_CASE + } + + void gl_create_shader_wrapper(GLenum type, GLuint* id) noexcept { + E2D_ASSERT(id); + *id = glCreateShader(type); + } + + void gl_create_program_wrapper(GLuint* id) noexcept { + E2D_ASSERT(id); + *id = glCreateProgram(); + } + + void gl_get_uniform_location(GLuint program, const GLchar* name, GLint* loc) noexcept { + E2D_ASSERT(loc); + *loc = glGetUniformLocation(program, name); + } + + void gl_get_attribute_location(GLuint program, const GLchar* name, GLint* loc) noexcept { + E2D_ASSERT(loc); + *loc = glGetAttribLocation(program, name); + } + + #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"\ + "--> File: %0\n"\ + "--> Line: %1\n"\ + "--> Code: %2",\ + __FILE__, __LINE__, gl_error_code_to_cstr(err));\ + if ( err == GL_OUT_OF_MEMORY ) throw std::bad_alloc();\ + } + + #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.error(\ + "RENDER: GL_CHECK(%0):\n"\ + "--> File: %1\n"\ + "--> Line: %2\n"\ + "--> Code: %3",\ + #code, __FILE__, __LINE__, gl_error_code_to_cstr(err));\ + if ( err == GL_OUT_OF_MEMORY ) throw std::bad_alloc();\ + } + + class gl_buffer_id final : private noncopyable { public: - gl_shader_id() noexcept - : id_(0) {} - - gl_shader_id(GLuint id) noexcept - : id_(id) { - E2D_ASSERT(glIsShader(id)); + static gl_buffer_id create_and_bind(debug& debug, GLenum target) { + E2D_ASSERT( + target == GL_ARRAY_BUFFER || + target == GL_ELEMENT_ARRAY_BUFFER); + GLuint id = 0; + GL_CHECK_CODE(debug, glGenBuffers(1, &id)); + if ( !id ) { + debug.error("RENDER: Failed to generate buffer id"); + return gl_buffer_id(debug); + } + GL_CHECK_CODE(debug, glBindBuffer(target, id)); + return gl_buffer_id(debug, id); } - - ~gl_shader_id() noexcept { - reset(); + private: + gl_buffer_id(debug& debug, GLuint id) noexcept + : debug_(debug) + , id_(id) { + E2D_ASSERT(!id || glIsBuffer(id)); } + public: + gl_buffer_id(debug& debug) noexcept + : debug_(debug) {} - gl_shader_id(gl_shader_id&& other) noexcept { - reset(); - std::swap(id_, other.id_); - } - - void reset() noexcept { + ~gl_buffer_id() noexcept { if ( id_ ) { - glDeleteShader(id_); + GL_CHECK_CODE(debug_, glDeleteBuffers(1, &id_)); id_ = 0; } } + gl_buffer_id(gl_buffer_id&& other) noexcept + : debug_(other.debug_) + , id_(other.release()) {} + + GLuint release() noexcept { + GLuint result = 0; + std::swap(result, id_); + return result; + } + bool empty() const noexcept { return id_ == 0; } @@ -239,35 +365,101 @@ namespace return id_; } private: - GLuint id_; + debug& debug_; + GLuint id_ = 0; + }; + + class gl_shader_id final : private noncopyable { + public: + static gl_shader_id create(debug& debug, GLenum type) noexcept { + E2D_ASSERT( + type == GL_VERTEX_SHADER || + type == GL_FRAGMENT_SHADER); + GLuint id = 0; + GL_CHECK_CODE(debug, gl_create_shader_wrapper(type, &id)); + if ( !id ) { + debug.error("RENDER: Failed to generate shader id"); + return gl_shader_id(debug); + } + return gl_shader_id(debug, id); + } + private: + gl_shader_id(debug& debug, GLuint id) noexcept + : debug_(debug) + , id_(id) { + E2D_ASSERT(!id || glIsShader(id)); + } + public: + gl_shader_id(debug& debug) noexcept + : debug_(debug) {} + + ~gl_shader_id() noexcept { + if ( id_ ) { + GL_CHECK_CODE(debug_, glDeleteShader(id_)); + id_ = 0; + } + } + + gl_shader_id(gl_shader_id&& other) noexcept + : debug_(other.debug_) + , id_(other.release()) {} + + GLuint release() noexcept { + GLuint result = 0; + std::swap(result, id_); + return result; + } + + bool empty() const noexcept { + return id_ == 0; + } + + GLuint operator*() const noexcept { + return id_; + } + private: + debug& debug_; + GLuint id_ = 0; }; class gl_program_id final : private noncopyable { public: - gl_program_id() noexcept - : id_(0) {} - - gl_program_id(GLuint id) noexcept - : id_(id) { - E2D_ASSERT(glIsProgram(id)); + static gl_program_id create(debug& debug) noexcept { + GLuint id = 0; + GL_CHECK_CODE(debug, gl_create_program_wrapper(&id)); + if ( !id ) { + debug.error("RENDER: Failed to generate program id"); + return gl_program_id(debug); + } + return gl_program_id(debug, id); } + private: + gl_program_id(debug& debug, GLuint id) noexcept + : debug_(debug) + , id_(id) { + E2D_ASSERT(!id || glIsProgram(id)); + } + public: + gl_program_id(debug& debug) noexcept + : debug_(debug) {} ~gl_program_id() noexcept { - reset(); - } - - gl_program_id(gl_program_id&& other) noexcept { - reset(); - std::swap(id_, other.id_); - } - - void reset() noexcept { if ( id_ ) { - glDeleteProgram(id_); + GL_CHECK_CODE(debug_, glDeleteProgram(id_)); id_ = 0; } } + gl_program_id(gl_program_id&& other) noexcept + : debug_(other.debug_) + , id_(other.release()) {} + + GLuint release() noexcept { + GLuint result = 0; + std::swap(result, id_); + return result; + } + bool empty() const noexcept { return id_ == 0; } @@ -276,35 +468,52 @@ namespace return id_; } private: - GLuint id_; + debug& debug_; + GLuint id_ = 0; }; class gl_texture_id final : private noncopyable { public: - gl_texture_id() noexcept - : id_(0) {} - - gl_texture_id(GLuint id) noexcept - : id_(id) { - E2D_ASSERT(glIsTexture(id)); + static gl_texture_id create_and_bind(debug& debug, GLenum target) noexcept { + E2D_ASSERT( + target == GL_TEXTURE_2D || + target == GL_TEXTURE_CUBE_MAP); + GLuint id = 0; + GL_CHECK_CODE(debug, glGenTextures(1, &id)); + if ( !id ) { + debug.error("RENDER: Failed to generate texture id"); + return gl_texture_id(debug); + } + GL_CHECK_CODE(debug, glBindTexture(target, id)); + return gl_texture_id(debug, id); } + private: + gl_texture_id(debug& debug, GLuint id) noexcept + : debug_(debug) + , id_(id) { + E2D_ASSERT(!id || glIsTexture(id)); + } + public: + gl_texture_id(debug& debug) noexcept + : debug_(debug) {} ~gl_texture_id() noexcept { - reset(); - } - - gl_texture_id(gl_texture_id&& other) noexcept { - reset(); - std::swap(id_, other.id_); - } - - void reset() noexcept { if ( id_ ) { - glDeleteTextures(1, &id_); + GL_CHECK_CODE(debug_, glDeleteTextures(1, &id_)); id_ = 0; } } + gl_texture_id(gl_texture_id&& other) noexcept + : debug_(other.debug_) + , id_(other.release()) {} + + GLuint release() noexcept { + GLuint result = 0; + std::swap(result, id_); + return result; + } + bool empty() const noexcept { return id_ == 0; } @@ -313,45 +522,144 @@ namespace return id_; } private: - GLuint id_; + debug& debug_; + GLuint id_ = 0; }; - gl_shader_id compile_shader(debug& d, const char* const s, GLenum t) { - E2D_ASSERT(s); - E2D_ASSERT(t == GL_VERTEX_SHADER || t == GL_FRAGMENT_SHADER); - gl_shader_id id(glCreateShader(t)); - glShaderSource(*id, 1, &s, nullptr); - glCompileShader(*id); - GLint success = GL_FALSE; - glGetShaderiv(*id, GL_COMPILE_STATUS, &success); - if ( success ) { + 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; } + 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; - glGetShaderiv(*id, GL_INFO_LOG_LENGTH, &log_len); - vector error_buffer(math::numeric_cast(log_len) + 1, '\0'); - glGetShaderInfoLog(*id, log_len, nullptr, error_buffer.data()); - d.error("RENDER: vertex shader compilation failed:\n--> %0", error_buffer.data()); - return gl_shader_id(); + 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 + ? std::move(id) + : gl_shader_id(debug); } - gl_program_id link_program(debug& d, gl_shader_id vs, gl_shader_id fs) { + gl_program_id link_program(debug& debug, gl_shader_id vs, gl_shader_id fs) noexcept { E2D_ASSERT(!vs.empty() && !fs.empty()); - gl_program_id id(glCreateProgram()); - glAttachShader(*id, *vs); - glAttachShader(*id, *fs); - glLinkProgram(*id); - GLint success = GL_FALSE; - glGetProgramiv(*id, GL_LINK_STATUS, &success); - if ( success ) { + gl_program_id id = gl_program_id::create(debug); + if ( id.empty() ) { return id; } + 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")); + + GL_CHECK_CODE(debug, glLinkProgram(*id)); + GLint success = GL_FALSE; + GL_CHECK_CODE(debug, glGetProgramiv(*id, GL_LINK_STATUS, &success)); GLint log_len = 0; - glGetProgramiv(*id, GL_INFO_LOG_LENGTH, &log_len); - vector error_buffer(math::numeric_cast(log_len) + 1, '\0'); - glGetProgramInfoLog(*id, log_len, nullptr, error_buffer.data()); - d.error("RENDER: shader program linking failed:\n--> %0", error_buffer.data()); - return gl_program_id(); + 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 ( 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); + } + + bool validate_program(debug& debug, const gl_program_id& ps) noexcept { + E2D_ASSERT(!ps.empty()); + GL_CHECK_CODE(debug, glValidateProgram(*ps)); + GLint success = GL_FALSE; + GL_CHECK_CODE(debug, glGetProgramiv(*ps, GL_VALIDATE_STATUS, &success)); + 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()); + } + return success == GL_TRUE; } } @@ -363,10 +671,14 @@ namespace e2d class shader::internal_state final : private e2d::noncopyable { public: + debug& debug; gl_program_id id; public: - internal_state(gl_program_id id) - : id(std::move(id)) {} + internal_state(class debug& debug, gl_program_id nid) + : debug(debug) + , id(std::move(nid)) { + E2D_ASSERT(!id.empty()); + } ~internal_state() noexcept = default; }; @@ -376,12 +688,52 @@ namespace e2d class texture::internal_state final : private e2d::noncopyable { public: + debug& debug; gl_texture_id id; - v2u native_size; - v2u original_size; public: - internal_state(gl_texture_id id) - : id(std::move(id)) {} + internal_state(class debug& debug, gl_texture_id nid) + : debug(debug) + , id(std::move(nid)) { + E2D_ASSERT(!id.empty()); + } + ~internal_state() noexcept = default; + }; + + // + // index_buffer::internal_state + // + + class index_buffer::internal_state final : private e2d::noncopyable { + public: + debug& debug; + gl_buffer_id id; + std::size_t count = 0; + 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() noexcept = default; + }; + + // + // vertex_buffer::internal_state + // + + class vertex_buffer::internal_state final : private e2d::noncopyable { + public: + debug& debug; + gl_buffer_id id; + std::size_t count = 0; + 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() noexcept = default; }; @@ -391,11 +743,14 @@ namespace e2d class render::internal_state final : private e2d::noncopyable { public: - std::mutex mutex; + debug& debug; + window& window; m4f view; m4f projection; public: - internal_state() {} + internal_state(class debug& debug, class window& window) + : debug(debug) + , window(window) {} ~internal_state() noexcept = default; }; @@ -416,59 +771,109 @@ namespace e2d texture::~texture() noexcept = default; void texture::set_wrap(wrap u, wrap v) noexcept { - E2D_ASSERT(!state_->id.empty()); - glBindTexture(GL_TEXTURE_2D, *state_->id); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, convert_wrap(u)); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, convert_wrap(v)); - glBindTexture(GL_TEXTURE_2D, 0); + 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_TEXTURE_2D, GL_TEXTURE_WRAP_T, convert_wrap(v))); + GL_CHECK_CODE(state_->debug, glBindTexture( + GL_TEXTURE_2D, 0)); } void texture::set_filter(filter min, filter mag) noexcept { - E2D_ASSERT(!state_->id.empty()); - glBindTexture(GL_TEXTURE_2D, *state_->id); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, convert_filter(min)); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, convert_filter(mag)); - glBindTexture(GL_TEXTURE_2D, 0); + 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_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, convert_filter(mag))); + GL_CHECK_CODE(state_->debug, glBindTexture( + GL_TEXTURE_2D, 0)); } - const v2u& texture::native_size() const noexcept { - return state_->native_size; + // + // index_buffer + // + + index_buffer::index_buffer(internal_state_uptr state) + : 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( + GL_ELEMENT_ARRAY_BUFFER, + math::numeric_cast(sizeof(u16) * offset), + math::numeric_cast(sizeof(u16) * count), + indices)); + GL_CHECK_CODE(state_->debug, glBindBuffer( + GL_ELEMENT_ARRAY_BUFFER, 0)); } - const v2u& texture::original_size() const noexcept { - return state_->original_size; + std::size_t index_buffer::count() const noexcept { + return state_->count; + } + + // + // vertex_buffer + // + + vertex_buffer::vertex_buffer(internal_state_uptr state) + : 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( + GL_ARRAY_BUFFER, + *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( + GL_ARRAY_BUFFER, 0)); + } + + std::size_t vertex_buffer::count() const noexcept { + return state_->count; } // // render // - render::render(debug& debug) - : debug_(debug) - , state_(new internal_state()) {} + render::render(debug& d, window& w) + : state_(new internal_state(d, w)) {} render::~render() noexcept = default; shader_ptr render::create_shader(input_stream_uptr vertex, input_stream_uptr fragment) { str vertex_str; gl_shader_id vs = streams::try_read_tail(vertex_str, vertex) - ? compile_shader(debug_, vertex_str.c_str(), GL_VERTEX_SHADER) - : gl_shader_id(); + ? compile_shader(state_->debug, vertex_str.c_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(debug_, fragment_str.c_str(), GL_FRAGMENT_SHADER) - : gl_shader_id(); + ? compile_shader(state_->debug, fragment_str.c_str(), GL_FRAGMENT_SHADER) + : gl_shader_id(state_->debug); if ( fs.empty() ) { return nullptr; } - gl_program_id ps = link_program(debug_, std::move(vs), std::move(fs)); - if ( ps.empty() ) { + gl_program_id ps = link_program(state_->debug, std::move(vs), std::move(fs)); + if ( ps.empty() || !validate_program(state_->debug, ps) ) { return nullptr; } - return std::make_unique( - std::make_unique(std::move(ps))); + return std::make_shared( + std::make_unique(state_->debug, std::move(ps))); } texture_ptr render::create_texture(const image& image) { @@ -481,8 +886,47 @@ namespace e2d return nullptr; } + index_buffer_ptr render::create_index_buffer( + const u16* indices, std::size_t count, index_buffer::usage usage) + { + gl_buffer_id id = gl_buffer_id::create_and_bind( + state_->debug, GL_ELEMENT_ARRAY_BUFFER); + if ( id.empty() ) { + return nullptr; + } + GL_CHECK_CODE(state_->debug, glBufferData( + GL_ELEMENT_ARRAY_BUFFER, + math::numeric_cast(sizeof(u16) * count), + indices, + convert_usage(usage))); + GL_CHECK_CODE(state_->debug, glBindBuffer( + GL_ELEMENT_ARRAY_BUFFER, 0)); + return std::make_shared( + std::make_unique( + state_->debug, std::move(id), count)); + } + + vertex_buffer_ptr render::create_vertex_buffer( + const vertex* vertices, std::size_t count, vertex_buffer::usage usage) + { + gl_buffer_id id = gl_buffer_id::create_and_bind( + state_->debug, GL_ARRAY_BUFFER); + if ( id.empty() ) { + return nullptr; + } + GL_CHECK_CODE(state_->debug, glBufferData( + GL_ARRAY_BUFFER, + math::numeric_cast(sizeof(vertex) * count), + vertices, + convert_usage(usage))); + GL_CHECK_CODE(state_->debug, glBindBuffer( + GL_ARRAY_BUFFER, 0)); + return std::make_shared( + std::make_unique( + state_->debug, std::move(id), count)); + } + void render::clear(bool color, bool depth, bool stencil) noexcept { - std::lock_guard guard(state_->mutex); GLbitfield mask = 0; if ( color ) { mask |= GL_COLOR_BUFFER_BIT; @@ -493,121 +937,156 @@ namespace e2d if ( stencil ) { mask |= GL_STENCIL_BUFFER_BIT; } - glClear(mask); + GL_CHECK_CODE(state_->debug, glClear(mask)); + } + + void render::draw( + const shader_ptr& ps, + const index_buffer_ptr& ib, + const vertex_buffer_ptr& vb) noexcept + { + E2D_ASSERT(ps && ib && vb); + + 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)); + + 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, 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, + 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)); } void render::set_view(const m4f& view) noexcept { - std::lock_guard guard(state_->mutex); state_->view = view; } void render::set_projection(const m4f& projection) noexcept { - std::lock_guard guard(state_->mutex); state_->projection = projection; } void render::set_viewport(u32 x, u32 y, u32 w, u32 h) noexcept { - std::lock_guard guard(state_->mutex); - glViewport( + GL_CHECK_CODE(state_->debug, glViewport( math::numeric_cast(x), math::numeric_cast(y), math::numeric_cast(w), - math::numeric_cast(h)); + math::numeric_cast(h))); } void render::enable_state(state state) noexcept { - std::lock_guard guard(state_->mutex); - glEnable(convert_state(state)); + GL_CHECK_CODE(state_->debug, glEnable(convert_state(state))); } void render::disable_state(state state) noexcept { - std::lock_guard guard(state_->mutex); - 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 { - std::lock_guard guard(state_->mutex); - glBlendFunc(convert_blend_func(src), convert_blend_func(dst)); + GL_CHECK_CODE(state_->debug, glBlendFunc( + convert_blend_func(src), convert_blend_func(dst))); } void render::set_blend_color(const color& color) noexcept { - std::lock_guard guard(state_->mutex); - glBlendColor( + GL_CHECK_CODE(state_->debug, glBlendColor( math::numeric_cast(color.r), math::numeric_cast(color.g), math::numeric_cast(color.b), - math::numeric_cast(color.a)); + math::numeric_cast(color.a))); } void render::set_blend_equation(blend_equation blend_equation) noexcept { - std::lock_guard guard(state_->mutex); - glBlendEquation(convert_blend_equation(blend_equation)); + GL_CHECK_CODE(state_->debug, glBlendEquation( + convert_blend_equation(blend_equation))); } void render::set_cull_face(cull_face cull_face) noexcept { - std::lock_guard guard(state_->mutex); - glCullFace(convert_cull_face(cull_face)); + GL_CHECK_CODE(state_->debug, glCullFace( + convert_cull_face(cull_face))); } void render::set_depth_func(depth_func depth_func) noexcept { - std::lock_guard guard(state_->mutex); - glDepthFunc(convert_depth_func(depth_func)); + GL_CHECK_CODE(state_->debug, glDepthFunc( + convert_depth_func(depth_func))); } void render::set_depth_mask(bool yesno) noexcept { - std::lock_guard guard(state_->mutex); - glDepthMask(yesno ? GL_TRUE : GL_FALSE); + GL_CHECK_CODE(state_->debug, glDepthMask( + yesno ? GL_TRUE : GL_FALSE)); } void render::set_clear_depth(f32 value) noexcept { - std::lock_guard guard(state_->mutex); - glClearDepth(math::numeric_cast(value)); + GL_CHECK_CODE(state_->debug, glClearDepth( + math::numeric_cast(value))); } void render::set_stencil_func(stencil_func stencil_func, u32 ref, u32 mask) noexcept { - std::lock_guard guard(state_->mutex); - glStencilFunc( + GL_CHECK_CODE(state_->debug, glStencilFunc( convert_stencil_func(stencil_func), math::numeric_cast(ref), - math::numeric_cast(mask)); + math::numeric_cast(mask))); } void render::set_stencil_mask(u32 mask) noexcept { - std::lock_guard guard(state_->mutex); - glStencilMask(math::numeric_cast(mask)); + 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 { - std::lock_guard guard(state_->mutex); - glStencilOp( + GL_CHECK_CODE(state_->debug, glStencilOp( convert_stencil_op(fail), convert_stencil_op(zfail), - convert_stencil_op(zpass)); + convert_stencil_op(zpass))); } void render::set_clear_stencil(u32 value) noexcept { - std::lock_guard guard(state_->mutex); - glClearStencil(math::numeric_cast(value)); + GL_CHECK_CODE(state_->debug, glClearStencil( + math::numeric_cast(value))); } void render::set_clear_color(const color& color) noexcept { - std::lock_guard guard(state_->mutex); - glClearColor( + GL_CHECK_CODE(state_->debug, glClearColor( math::numeric_cast(color.r), math::numeric_cast(color.g), math::numeric_cast(color.b), - math::numeric_cast(color.a)); + math::numeric_cast(color.a))); } void render::set_color_mask(bool r, bool g, bool b, bool a) { - std::lock_guard guard(state_->mutex); - glColorMask( + GL_CHECK_CODE(state_->debug, glColorMask( r ? GL_TRUE : GL_FALSE, g ? GL_TRUE : GL_FALSE, b ? GL_TRUE : GL_FALSE, - a ? GL_TRUE : GL_FALSE); + a ? GL_TRUE : GL_FALSE)); } }