#pragma once #include #include #include #include #include #include #include "OpenGL.h" #include "../GCM.h" #include "Utilities/geometry.h" namespace gl { #if 1//def _DEBUG struct __glcheck_impl_t { const char* file; const char* function; long line; constexpr __glcheck_impl_t(const char* file, const char* function, long line) : file(file) , function(function) , line(line) { } ~__glcheck_impl_t() noexcept(false) { if (GLenum err = glGetError()) { std::string error; switch (err) { case GL_INVALID_OPERATION: error = "invalid operation"; break; case GL_INVALID_ENUM: error = "invalid enum"; break; case GL_INVALID_VALUE: error = "invalid value"; break; case GL_OUT_OF_MEMORY: error = "out of memory"; break; case GL_INVALID_FRAMEBUFFER_OPERATION: error = "invalid framebuffer operation"; break; default: error = "unknown error"; break; } throw std::runtime_error(fmt::format("OpenGL error: %s. file '%s' function '%s' line %ld", error.c_str(), file, function, line)); } } }; #define __glcheck ::gl::__glcheck_impl_t{ __FILE__, __FUNCTION__, __LINE__ }, #else #define __glcheck #endif void enable_debugging(); class exception : public std::exception { protected: std::string m_what; public: const char* what() const noexcept override { return m_what.c_str(); } }; template class save_binding_state_base { GLint m_last_binding; public: save_binding_state_base(const Type& new_state) : save_binding_state_base() { new_state.bind(); } save_binding_state_base() { glGetIntegerv(GetStateId, &m_last_binding); } ~save_binding_state_base() { glBindBuffer(BindId, m_last_binding); } }; enum class filter { nearest = GL_NEAREST, linear = GL_LINEAR }; enum class min_filter { nearest = GL_NEAREST, linear = GL_LINEAR, nearest_mipmap_nearest = GL_NEAREST_MIPMAP_NEAREST, nearest_mipmap_linear = GL_NEAREST_MIPMAP_LINEAR, linear_mipmap_nearest = GL_LINEAR_MIPMAP_NEAREST, linear_mipmap_linear = GL_LINEAR_MIPMAP_LINEAR }; enum class buffers { none = 0, color = GL_COLOR_BUFFER_BIT, depth = GL_DEPTH_BUFFER_BIT, stencil = GL_STENCIL_BUFFER_BIT, color_depth = color | depth, color_depth_stencil = color | depth | stencil, color_stencil = color | stencil, depth_stencil = depth | stencil }; class pixel_pack_settings { bool m_swap_bytes = false; bool m_lsb_first = false; int m_row_length = 0; int m_image_height = 0; int m_skip_rows = 0; int m_skip_pixels = 0; int m_skip_images = 0; int m_aligment = 4; public: void apply() const { glPixelStorei(GL_PACK_SWAP_BYTES, m_swap_bytes ? GL_TRUE : GL_FALSE); glPixelStorei(GL_PACK_LSB_FIRST, m_lsb_first ? GL_TRUE : GL_FALSE); glPixelStorei(GL_PACK_ROW_LENGTH, m_row_length); glPixelStorei(GL_PACK_IMAGE_HEIGHT, m_image_height); glPixelStorei(GL_PACK_SKIP_ROWS, m_skip_rows); glPixelStorei(GL_PACK_SKIP_PIXELS, m_skip_pixels); glPixelStorei(GL_PACK_SKIP_IMAGES, m_skip_images); glPixelStorei(GL_PACK_ALIGNMENT, m_aligment); } pixel_pack_settings& swap_bytes(bool value = true) { m_swap_bytes = value; return *this; } pixel_pack_settings& lsb_first(bool value = true) { m_lsb_first = value; return *this; } pixel_pack_settings& row_length(int value) { m_row_length = value; return *this; } pixel_pack_settings& image_height(int value) { m_image_height = value; return *this; } pixel_pack_settings& skip_rows(int value) { m_skip_rows = value; return *this; } pixel_pack_settings& skip_pixels(int value) { m_skip_pixels = value; return *this; } pixel_pack_settings& skip_images(int value) { m_skip_images = value; return *this; } pixel_pack_settings& aligment(int value) { m_aligment = value; return *this; } }; class pixel_unpack_settings { bool m_swap_bytes = false; bool m_lsb_first = false; int m_row_length = 0; int m_image_height = 0; int m_skip_rows = 0; int m_skip_pixels = 0; int m_skip_images = 0; int m_aligment = 4; public: void apply() const { glPixelStorei(GL_UNPACK_SWAP_BYTES, m_swap_bytes ? GL_TRUE : GL_FALSE); glPixelStorei(GL_UNPACK_LSB_FIRST, m_lsb_first ? GL_TRUE : GL_FALSE); glPixelStorei(GL_UNPACK_ROW_LENGTH, m_row_length); glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, m_image_height); glPixelStorei(GL_UNPACK_SKIP_ROWS, m_skip_rows); glPixelStorei(GL_UNPACK_SKIP_PIXELS, m_skip_pixels); glPixelStorei(GL_UNPACK_SKIP_IMAGES, m_skip_images); glPixelStorei(GL_UNPACK_ALIGNMENT, m_aligment); } pixel_unpack_settings& swap_bytes(bool value = true) { m_swap_bytes = value; return *this; } pixel_unpack_settings& lsb_first(bool value = true) { m_lsb_first = value; return *this; } pixel_unpack_settings& row_length(int value) { m_row_length = value; return *this; } pixel_unpack_settings& image_height(int value) { m_image_height = value; return *this; } pixel_unpack_settings& skip_rows(int value) { m_skip_rows = value; return *this; } pixel_unpack_settings& skip_pixels(int value) { m_skip_pixels = value; return *this; } pixel_unpack_settings& skip_images(int value) { m_skip_images = value; return *this; } pixel_unpack_settings& aligment(int value) { m_aligment = value; return *this; } }; class vao; class buffer_pointer { public: enum class type { s8 = GL_BYTE, u8 = GL_UNSIGNED_BYTE, s16 = GL_SHORT, u16 = GL_UNSIGNED_SHORT, s32 = GL_INT, u32 = GL_UNSIGNED_INT, f16 = GL_HALF_FLOAT, f32 = GL_FLOAT, f64 = GL_DOUBLE, fixed = GL_FIXED, s32_2_10_10_10_rev = GL_INT_2_10_10_10_REV, u32_2_10_10_10_rev = GL_UNSIGNED_INT_2_10_10_10_REV, u32_10f_11f_11f_rev = GL_UNSIGNED_INT_10F_11F_11F_REV }; private: vao* m_vao; u32 m_offset; u32 m_stride; u32 m_size = 4; type m_type = type::f32; bool m_normalize = false; public: buffer_pointer(vao* vao, u32 offset = 0, u32 stride = 0) : m_vao(vao) , m_offset(offset) , m_stride(stride) { } const class ::gl::vao& get_vao() const { return *m_vao; } class ::gl::vao& get_vao() { return *m_vao; } buffer_pointer& offset(u32 value) { m_offset = value; return *this; } u32 offset() const { return m_offset; } buffer_pointer& stride(u32 value) { m_stride = value; return *this; } u32 stride() const { return m_stride; } buffer_pointer& size(u32 value) { m_size = value; return *this; } u32 size() const { return m_size; } buffer_pointer& set_type(type value) { m_type = value; return *this; } type get_type() const { return m_type; } buffer_pointer& normalize(bool value) { m_normalize = value; return *this; } bool normalize() const { return m_normalize; } buffer_pointer& operator >> (u32 value) { return stride(value); } buffer_pointer& config(type type_ = type::f32, u32 size_ = 4, bool normalize_ = false) { return set_type(type_).size(size_).normalize(normalize_); } }; class buffer { public: enum class target { pixel_pack = GL_PIXEL_PACK_BUFFER, pixel_unpack = GL_PIXEL_UNPACK_BUFFER, array = GL_ARRAY_BUFFER, element_array = GL_ELEMENT_ARRAY_BUFFER, uniform = GL_UNIFORM_BUFFER, texture = GL_TEXTURE_BUFFER }; enum class access { read = GL_READ_ONLY, write = GL_WRITE_ONLY, read_write = GL_READ_WRITE }; private: GLuint m_id = GL_NONE; GLsizeiptr m_size = 0; target m_target = target::array; public: buffer() = default; buffer(const buffer&) = delete; buffer(GLuint id) { set_id(id); } ~buffer() { if (created()) remove(); } class save_binding_state { GLint m_last_binding; GLenum m_target; public: save_binding_state(target target_, const buffer& new_state) : save_binding_state(target_) { new_state.bind(target_); } save_binding_state(target target_) { GLenum pname; switch (target_) { case target::pixel_pack: pname = GL_PIXEL_PACK_BUFFER_BINDING; break; case target::pixel_unpack: pname = GL_PIXEL_UNPACK_BUFFER_BINDING; break; case target::array: pname = GL_ARRAY_BUFFER_BINDING; break; case target::element_array: pname = GL_ELEMENT_ARRAY_BUFFER_BINDING; break; case target::uniform: pname = GL_UNIFORM_BUFFER_BINDING; break; case target::texture: pname = GL_TEXTURE_BUFFER_BINDING; break; } glGetIntegerv(pname, &m_last_binding); m_target = (GLenum)target_; } ~save_binding_state() { glBindBuffer(m_target, m_last_binding); } }; void recreate() { if (created()) { remove(); } create(); } void recreate(GLsizeiptr size, const void* data = nullptr) { if (created()) { remove(); } create(size, data); } void create() { glGenBuffers(1, &m_id); } void create(GLsizeiptr size, const void* data_ = nullptr) { create(); data(size, data_); } void create(target target_, GLsizeiptr size, const void* data_ = nullptr) { create(); m_target = target_; data(size, data_); } void data(GLsizeiptr size, const void* data_ = nullptr) { target target_ = current_target(); save_binding_state save(target_, *this); glBufferData((GLenum)target_, size, data_, GL_STREAM_DRAW); m_size = size; } void sub_data(GLintptr offset, GLsizeiptr size, const void* data_ = nullptr) { target target_ = current_target(); save_binding_state save(target_, *this); glBufferSubData((GLenum)target_, offset, size, data_); } void bind(target target_) const { glBindBuffer((GLenum)target_, m_id); } void bind() const { bind(current_target()); } target current_target() const { return m_target; } void remove() { glDeleteBuffers(1, &m_id); m_id = 0; } GLsizeiptr size() const { return m_size; } uint id() const { return m_id; } void set_id(uint id) { m_id = id; } bool created() const { return m_id != 0; } explicit operator bool() const { return created(); } void map(std::function impl, access access_) { target target_ = current_target(); save_binding_state save(target_, *this); if (GLubyte* ptr = (GLubyte*)glMapBuffer((GLenum)target_, (GLenum)access_)) { impl(ptr); glUnmapBuffer((GLenum)target_); } } class mapper { buffer *m_parent; GLubyte *m_data; public: mapper(buffer& parent, access access_) { m_parent = &parent; m_data = parent.map(access_); } ~mapper() { m_parent->unmap(); } GLubyte* get() const { return m_data; } }; GLubyte* map(access access_) { bind(current_target()); return (GLubyte*)glMapBuffer((GLenum)current_target(), (GLenum)access_); } void unmap() { glUnmapBuffer((GLenum)current_target()); } }; class ring_buffer : public buffer { u32 m_data_loc = 0; u32 m_mapped_block_size = 0; u32 m_mapped_block_offset; u32 m_mapped_reserve_offset; u32 m_mapped_bytes_available; void *m_mapped_base = nullptr; public: std::pair alloc_and_map(u32 alloc_size) { alloc_size = align(alloc_size, 0x100); buffer::bind(); u32 limit = m_data_loc + alloc_size; if (limit > buffer::size()) { if (alloc_size > buffer::size()) { buffer::data(alloc_size); } m_data_loc = 0; } void *ptr = glMapBufferRange((GLenum)buffer::current_target(), m_data_loc, alloc_size, GL_MAP_WRITE_BIT|GL_MAP_INVALIDATE_RANGE_BIT|GL_MAP_UNSYNCHRONIZED_BIT); u32 offset = m_data_loc; m_data_loc += alloc_size; return std::make_pair(ptr, offset); } void unmap() { buffer::unmap(); m_mapped_block_size = 0; m_mapped_base = 0; } void reserve_and_map(u32 max_size) { max_size = align(max_size, 0x1000); auto mapping = alloc_and_map(max_size); m_mapped_base = mapping.first; m_mapped_block_offset = mapping.second; m_mapped_reserve_offset = 0; m_mapped_bytes_available = max_size; } std::pair alloc_from_reserve(u32 size, u32 alignment = 16) { size = align(size, alignment); if (m_mapped_bytes_available < size || !m_mapped_base) { if (m_mapped_base) { //This doesn't really work for some reason, probably since the caller should bind the target //before making this call as the block may be reallocated LOG_ERROR(RSX, "reserved allocation exceeded. check for corruption!"); unmap(); } reserve_and_map((size > 4096) ? size : 4096); } EXPECTS(m_mapped_bytes_available >= size); void *ptr = (char*)m_mapped_base + m_mapped_reserve_offset; u32 offset = m_mapped_reserve_offset + m_mapped_block_offset; m_mapped_reserve_offset += size; m_mapped_bytes_available -= size; EXPECTS((offset & (alignment - 1)) == 0); return std::make_pair(ptr, offset); } void bind_range(u32 index, u32 offset, u32 size) const { glBindBufferRange((GLenum)current_target(), index, id(), offset, size); } }; class vao { template class entry { vao& m_parent; public: using save_binding_state = save_binding_state_base; entry(vao* parent) noexcept : m_parent(*parent) { } entry& operator = (const buffer& buf) noexcept { m_parent.bind(); buf.bind(BindId); return *this; } }; GLuint m_id = GL_NONE; public: entry pixel_pack_buffer{ this }; entry pixel_unpack_buffer{ this }; entry array_buffer{ this }; entry element_array_buffer{ this }; vao() = default; vao(vao&) = delete; vao(vao&& vao_) noexcept { swap(vao_); } vao(GLuint id) noexcept { set_id(id); } ~vao() noexcept { if (created()) remove(); } void swap(vao& vao_) noexcept { auto my_old_id = id(); set_id(vao_.id()); vao_.set_id(my_old_id); } vao& operator = (const vao& rhs) = delete; vao& operator = (vao&& rhs) noexcept { swap(rhs); return *this; } void bind() const noexcept { glBindVertexArray(m_id); } void create() noexcept { glGenVertexArrays(1, &m_id); } void remove() noexcept { glDeleteVertexArrays(1, &m_id); m_id = GL_NONE; } uint id() const noexcept { return m_id; } void set_id(uint id) noexcept { m_id = id; } bool created() const noexcept { return m_id != GL_NONE; } explicit operator bool() const noexcept { return created(); } void enable_for_attributes(std::initializer_list indexes) noexcept { for (auto &index : indexes) { glEnableVertexAttribArray(index); } } void disable_for_attributes(std::initializer_list indexes) noexcept { for (auto &index : indexes) { glDisableVertexAttribArray(index); } } void enable_for_attribute(GLuint index) noexcept { enable_for_attributes({ index }); } void disable_for_attribute(GLuint index) noexcept { disable_for_attributes({ index }); } buffer_pointer operator + (u32 offset) const noexcept { return{ (vao*)this, offset }; } buffer_pointer operator >> (u32 stride) const noexcept { return{ (vao*)this, {}, stride }; } operator buffer_pointer() const noexcept { return{ (vao*)this }; } }; class texture_view; class texture { GLuint m_id = 0; GLuint m_level = 0; class pixel_pack_settings m_pixel_pack_settings; class pixel_unpack_settings m_pixel_unpack_settings; public: enum class type { ubyte = GL_UNSIGNED_BYTE, ushort = GL_UNSIGNED_SHORT, uint = GL_UNSIGNED_INT, ubyte_3_3_2 = GL_UNSIGNED_BYTE_3_3_2, ubyte_2_3_3_rev = GL_UNSIGNED_BYTE_2_3_3_REV, ushort_5_6_5 = GL_UNSIGNED_SHORT_5_6_5, ushort_5_6_5_rev = GL_UNSIGNED_SHORT_5_6_5_REV, ushort_4_4_4_4 = GL_UNSIGNED_SHORT_4_4_4_4, ushort_4_4_4_4_rev = GL_UNSIGNED_SHORT_4_4_4_4_REV, ushort_5_5_5_1 = GL_UNSIGNED_SHORT_5_5_5_1, ushort_1_5_5_5_rev = GL_UNSIGNED_SHORT_1_5_5_5_REV, uint_8_8_8_8 = GL_UNSIGNED_INT_8_8_8_8, uint_8_8_8_8_rev = GL_UNSIGNED_INT_8_8_8_8_REV, uint_10_10_10_2 = GL_UNSIGNED_INT_10_10_10_2, uint_2_10_10_10_rev = GL_UNSIGNED_INT_2_10_10_10_REV, uint_24_8 = GL_UNSIGNED_INT_24_8, sbyte = GL_BYTE, sshort = GL_SHORT, sint = GL_INT, f16 = GL_HALF_FLOAT, f32 = GL_FLOAT, f64 = GL_DOUBLE, }; enum class channel { zero = GL_ZERO, one = GL_ONE, r = GL_RED, g = GL_GREEN, b = GL_BLUE, a = GL_ALPHA, }; enum class format { red = GL_RED, r = GL_R, rg = GL_RG, rgb = GL_RGB, rgba = GL_RGBA, bgr = GL_BGR, bgra = GL_BGRA, stencil = GL_STENCIL_INDEX, depth = GL_DEPTH_COMPONENT, depth_stencil = GL_DEPTH_STENCIL }; enum class internal_format { red = GL_RED, r = GL_R, rg = GL_RG, rgb = GL_RGB, rgba = GL_RGBA, bgr = GL_BGR, bgra = GL_BGRA, stencil = GL_STENCIL_INDEX, stencil8 = GL_STENCIL_INDEX8, depth = GL_DEPTH_COMPONENT, depth16 = GL_DEPTH_COMPONENT16, depth_stencil = GL_DEPTH_STENCIL, depth24_stencil8 = GL_DEPTH24_STENCIL8, compressed_rgb_s3tc_dxt1 = GL_COMPRESSED_RGB_S3TC_DXT1_EXT, compressed_rgba_s3tc_dxt1 = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, compressed_rgba_s3tc_dxt3 = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, compressed_rgba_s3tc_dxt5 = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT }; enum class wrap { repeat = GL_REPEAT, mirrored_repeat = GL_MIRRORED_REPEAT, clamp_to_edge = GL_CLAMP_TO_EDGE, clamp_to_border = GL_CLAMP_TO_BORDER, mirror_clamp = GL_MIRROR_CLAMP_EXT, //mirror_clamp_to_edge = GL_MIRROR_CLAMP_TO_EDGE, mirror_clamp_to_border = GL_MIRROR_CLAMP_TO_BORDER_EXT }; enum class compare_mode { none = GL_NONE, ref_to_texture = GL_COMPARE_REF_TO_TEXTURE }; enum class compare_func { never = GL_NEVER, less = GL_LESS, equal = GL_EQUAL, lequal = GL_LEQUAL, greater = GL_GREATER, notequal = GL_NOTEQUAL, gequal = GL_GEQUAL, always = GL_ALWAYS }; enum class target { texture1D = GL_TEXTURE_1D, texture2D = GL_TEXTURE_2D, texture3D = GL_TEXTURE_3D, textureBuffer = GL_TEXTURE_BUFFER }; enum class channel_type { none = GL_NONE, signed_normalized = GL_SIGNED_NORMALIZED, unsigned_normalized = GL_UNSIGNED_NORMALIZED, float_ = GL_FLOAT, int_ = GL_INT, uint_ = GL_UNSIGNED_INT }; enum class channel_name { red = GL_TEXTURE_RED_TYPE, green = GL_TEXTURE_GREEN_TYPE, blue = GL_TEXTURE_BLUE_TYPE, alpha = GL_TEXTURE_ALPHA_TYPE, depth = GL_TEXTURE_DEPTH_TYPE }; class save_binding_state { GLint m_last_binding; GLenum m_target; public: save_binding_state(const texture& new_binding) noexcept { GLenum pname; switch (new_binding.get_target()) { case target::texture1D: pname = GL_TEXTURE_BINDING_1D; break; case target::texture2D: pname = GL_TEXTURE_BINDING_2D; break; case target::texture3D: pname = GL_TEXTURE_BINDING_3D; break; case target::textureBuffer: pname = GL_TEXTURE_BINDING_BUFFER; break; } glGetIntegerv(pname, &m_last_binding); new_binding.bind(); m_target = (GLenum)new_binding.get_target(); } ~save_binding_state() noexcept { glBindTexture(m_target, m_last_binding); } }; class settings; private: target m_target = target::texture2D; public: target get_target() const noexcept { return m_target; } void set_target(target target) noexcept { m_target = target; } static bool compressed_format(internal_format format_) noexcept { switch (format_) { case internal_format::compressed_rgb_s3tc_dxt1: case internal_format::compressed_rgba_s3tc_dxt1: case internal_format::compressed_rgba_s3tc_dxt3: case internal_format::compressed_rgba_s3tc_dxt5: return true; } return false; } uint id() const noexcept { return m_id; } uint level() const noexcept { return m_level; } void recreate() noexcept { if (created()) remove(); create(); } void recreate(target target_) noexcept { if (created()) remove(); create(target_); } void create() noexcept { glGenTextures(1, &m_id); } void create(target target_) noexcept { set_target(target_); create(); } bool created() const noexcept { return m_id != 0; } void remove() noexcept { glDeleteTextures(1, &m_id); m_id = 0; } void set_id(GLuint id) noexcept { m_id = id; } void set_level(int level) noexcept { m_level = level; } texture_view with_level(int level); explicit operator bool() const noexcept { return created(); } void bind() const noexcept { glBindTexture((GLenum)get_target(), id()); } settings config(); void config(const settings& settings_); class pixel_pack_settings& pixel_pack_settings() { return m_pixel_pack_settings; } const class pixel_pack_settings& pixel_pack_settings() const { return m_pixel_pack_settings; } class pixel_unpack_settings& pixel_unpack_settings() { return m_pixel_unpack_settings; } const class pixel_unpack_settings& pixel_unpack_settings() const { return m_pixel_unpack_settings; } int width() const { save_binding_state save(*this); GLint result; glGetTexLevelParameteriv((GLenum)get_target(), level(), GL_TEXTURE_WIDTH, &result); return (int)result; } int height() const { save_binding_state save(*this); GLint result; glGetTexLevelParameteriv((GLenum)get_target(), level(), GL_TEXTURE_HEIGHT, &result); return (int)result; } int depth() const { save_binding_state save(*this); GLint result; glGetTexLevelParameteriv((GLenum)get_target(), level(), GL_TEXTURE_DEPTH, &result); return (int)result; } sizei size() const { return{ width(), height() }; } size3i size3d() const { return{ width(), height(), depth() }; } texture::format get_internal_format() const { save_binding_state save(*this); GLint result; glGetTexLevelParameteriv((GLenum)get_target(), level(), GL_TEXTURE_INTERNAL_FORMAT, &result); return (texture::format)result; } texture::channel_type get_channel_type(texture::channel_name channel) const { save_binding_state save(*this); GLint result; glGetTexLevelParameteriv((GLenum)get_target(), level(), (GLenum)channel, &result); return (texture::channel_type)result; } int get_channel_count() const { int result = 0; if (get_channel_type(channel_name::red) != channel_type::none) result++; if (get_channel_type(channel_name::green) != channel_type::none) result++; if (get_channel_type(channel_name::blue) != channel_type::none) result++; if (get_channel_type(channel_name::alpha) != channel_type::none) result++; if (get_channel_type(channel_name::depth) != channel_type::none) result++; return result; } bool compressed() const { save_binding_state save(*this); GLint result; glGetTexLevelParameteriv((GLenum)get_target(), level(), GL_TEXTURE_COMPRESSED, &result); return result != 0; } int compressed_size() const { save_binding_state save(*this); GLint result; glGetTexLevelParameteriv((GLenum)get_target(), level(), GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &result); return (int)result; } texture() = default; texture(texture&) = delete; texture(texture&& texture_) { swap(texture_); } texture(target target_, GLuint id = 0) { m_target = target_; set_id(id); } ~texture() { if (created()) remove(); } void swap(texture& texture_) { auto my_old_id = id(); auto my_old_target = get_target(); set_id(texture_.id()); set_target(texture_.get_target()); texture_.set_id(my_old_id); texture_.set_target(my_old_target); } texture& operator = (const texture& rhs) = delete; texture& operator = (texture&& rhs) { swap(rhs); return *this; } void copy_from(const void* src, texture::format format, texture::type type, class pixel_unpack_settings pixel_settings) { save_binding_state save(*this); pixel_settings.apply(); __glcheck glTexSubImage2D((GLenum)get_target(), level(), 0, 0, width(), height(), (GLenum)format, (GLenum)type, src); } void copy_from(buffer &buf, u32 gl_format_type, u32 offset, u32 length) { if (get_target() != target::textureBuffer) throw EXCEPTION("OpenGL error: texture cannot copy from buffer"); /* if (!offset) { copy_from(buf, gl_format_type); return; }*/ if (glTextureBufferRangeEXT == nullptr) throw EXCEPTION("OpenGL error: partial buffer access for textures is unsupported on your system"); __glcheck glTextureBufferRangeEXT(id(), (GLenum)target::textureBuffer, gl_format_type, buf.id(), offset, length); } void copy_from(buffer &buf, u32 gl_format_type) { save_binding_state save(*this); __glcheck glTexBuffer((GLenum)target::textureBuffer, gl_format_type, buf.id()); } void copy_from(const buffer& buf, texture::format format, texture::type type, class pixel_unpack_settings pixel_settings) { buffer::save_binding_state save_buffer(buffer::target::pixel_unpack, buf); copy_from(nullptr, format, type, pixel_settings); } void copy_from(void* dst, texture::format format, texture::type type) { copy_from(dst, format, type, pixel_unpack_settings()); } void copy_from(const buffer& buf, texture::format format, texture::type type) { copy_from(buf, format, type, pixel_unpack_settings()); } void copy_to(void* dst, texture::format format, texture::type type, class pixel_pack_settings pixel_settings) const { save_binding_state save(*this); pixel_settings.apply(); __glcheck glGetTexImage((GLenum)get_target(), level(), (GLenum)format, (GLenum)type, dst); } void copy_to(void* dst, texture::type type, class pixel_pack_settings pixel_settings) const { copy_to(dst, get_internal_format(), type, pixel_settings); } void copy_to(const buffer& buf, texture::format format, texture::type type, class pixel_pack_settings pixel_settings) const { buffer::save_binding_state save_buffer(buffer::target::pixel_pack, buf); copy_to(nullptr, format, type, pixel_settings); } void copy_to(const buffer& buf, texture::type type, class pixel_pack_settings pixel_settings) const { buffer::save_binding_state save_buffer(buffer::target::pixel_pack, buf); copy_to(nullptr, get_internal_format(), type, pixel_settings); } void copy_to(void* dst, texture::format format, texture::type type) const { copy_to(dst, format, type, pixel_pack_settings()); } void copy_to(void* dst, texture::type type) const { copy_to(dst, get_internal_format(), type, pixel_pack_settings()); } void copy_to(const buffer& buf, texture::format format, texture::type type) const { copy_to(buf, format, type, pixel_pack_settings()); } void copy_to(const buffer& buf, texture::type type) const { copy_to(buf, get_internal_format(), type, pixel_pack_settings()); } }; class rbo { GLuint m_id = 0; public: rbo() = default; rbo(GLuint id) { set_id(id); } ~rbo() { if (created()) remove(); } class save_binding_state { GLint m_old_value; public: save_binding_state(const rbo& new_state) { glGetIntegerv(GL_RENDERBUFFER_BINDING, &m_old_value); new_state.bind(); } ~save_binding_state() { glBindRenderbuffer(GL_RENDERBUFFER, m_old_value); } }; void recreate() { if (created()) remove(); create(); } void recreate(texture::format format, u32 width, u32 height) { if (created()) remove(); create(format, width, height); } void create() { glGenRenderbuffers(1, &m_id); } void create(texture::format format, u32 width, u32 height) { create(); storage(format, width, height); } void bind() const { glBindRenderbuffer(GL_RENDERBUFFER, m_id); } void storage(texture::format format, u32 width, u32 height) { save_binding_state save(*this); glRenderbufferStorage(GL_RENDERBUFFER, (GLenum)format, width, height); } void remove() { glDeleteRenderbuffers(1, &m_id); m_id = 0; } uint id() const { return m_id; } void set_id(uint id) { m_id = id; } bool created() const { return m_id != 0; } explicit operator bool() const { return created(); } }; class texture::settings { texture *m_parent; texture::channel m_swizzle_r = texture::channel::r; texture::channel m_swizzle_g = texture::channel::g; texture::channel m_swizzle_b = texture::channel::b; texture::channel m_swizzle_a = texture::channel::a; texture::format m_format = texture::format::rgba; texture::internal_format m_internal_format = texture::internal_format::rgba; texture::type m_type = texture::type::ubyte; gl::min_filter m_min_filter = gl::min_filter::nearest; gl::filter m_mag_filter = gl::filter::nearest; uint m_width = 0; uint m_height = 0; int m_level = 0; int m_compressed_image_size = 0; const void* m_pixels = nullptr; float m_aniso = 1.f; texture::compare_mode m_compare_mode = texture::compare_mode::none; texture::compare_func m_compare_func = texture::compare_func::greater; texture::wrap m_wrap_s = texture::wrap::repeat; texture::wrap m_wrap_t = texture::wrap::repeat; texture::wrap m_wrap_r = texture::wrap::repeat; float m_max_lod = 1000.f; float m_min_lod = -1000.f; float m_lod = 0.f; int m_max_level = 1000; bool m_generate_mipmap = false; color4f m_border_color; public: settings(texture *parent = nullptr) : m_parent(parent) { } ~settings() { apply(); } void apply(const texture &texture) const; void apply(); settings& swizzle( texture::channel r = texture::channel::r, texture::channel g = texture::channel::g, texture::channel b = texture::channel::b, texture::channel a = texture::channel::a); settings& format(texture::format format); settings& type(texture::type type); settings& internal_format(texture::internal_format format); settings& filter(min_filter min_filter, filter mag_filter); settings& width(uint width); settings& height(uint height); settings& size(sizei size); settings& level(int value); settings& compressed_image_size(int size); settings& pixels(const void* pixels); settings& aniso(float value); settings& compare_mode(texture::compare_mode value); settings& compare_func(texture::compare_func value); settings& compare(texture::compare_func func, texture::compare_mode mode); settings& wrap_s(texture::wrap value); settings& wrap_t(texture::wrap value); settings& wrap_r(texture::wrap value); settings& wrap(texture::wrap s, texture::wrap t, texture::wrap r); settings& max_lod(float value); settings& min_lod(float value); settings& lod(float value); settings& max_level(int value); settings& generate_mipmap(bool value); settings& mipmap(int level, int max_level, float lod, float min_lod, float max_lod, bool generate); settings& border_color(color4f value); }; GLenum draw_mode(rsx::primitive_type in); enum class indices_type { ubyte = GL_UNSIGNED_BYTE, ushort = GL_UNSIGNED_SHORT, uint = GL_UNSIGNED_INT }; class fbo { GLuint m_id = GL_NONE; public: fbo() = default; fbo(GLuint id) { set_id(id); } ~fbo() { if (created()) remove(); } class save_binding_state { GLint m_last_binding; public: save_binding_state(const fbo& new_binding) { glGetIntegerv(GL_FRAMEBUFFER_BINDING, &m_last_binding); new_binding.bind(); } ~save_binding_state() { glBindFramebuffer(GL_FRAMEBUFFER, m_last_binding); } }; class attachment { public: enum class type { color = GL_COLOR_ATTACHMENT0, depth = GL_DEPTH_ATTACHMENT, stencil = GL_STENCIL_ATTACHMENT, depth_stencil = GL_DEPTH_STENCIL_ATTACHMENT }; protected: GLuint m_id; fbo &m_parent; public: attachment(fbo& parent, type type) : m_id((int)type) , m_parent(parent) { } void set_id(uint id) { m_id = id; } uint id() const { return m_id; } void operator = (const rbo& rhs) { save_binding_state save(m_parent); glFramebufferRenderbuffer(GL_FRAMEBUFFER, m_id, GL_RENDERBUFFER, rhs.id()); } void operator = (const texture& rhs) { save_binding_state save(m_parent); switch (rhs.get_target()) { case texture::target::texture1D: glFramebufferTexture1D(GL_FRAMEBUFFER, m_id, GL_TEXTURE_1D, rhs.id(), rhs.level()); break; case texture::target::texture2D: glFramebufferTexture2D(GL_FRAMEBUFFER, m_id, GL_TEXTURE_2D, rhs.id(), rhs.level()); break; case texture::target::texture3D: glFramebufferTexture3D(GL_FRAMEBUFFER, m_id, GL_TEXTURE_3D, rhs.id(), rhs.level(), 0); break; } } }; class indexed_attachment : public attachment { public: indexed_attachment(fbo& parent, type type) : attachment(parent, type) { } attachment operator[](int index) const { return{ m_parent, type(id() + index) }; } std::vector range(int from, int count) const { std::vector result; for (int i = from; i < from + count; ++i) result.push_back((*this)[i]); return result; } using attachment::operator =; }; indexed_attachment color{ *this, attachment::type::color }; attachment depth{ *this, attachment::type::depth }; attachment stencil{ *this, attachment::type::stencil }; attachment depth_stencil{ *this, attachment::type::depth_stencil }; enum class target { read_frame_buffer = GL_READ_FRAMEBUFFER, draw_frame_buffer = GL_DRAW_FRAMEBUFFER }; void create(); void bind() const; void blit(const fbo& dst, areai src_area, areai dst_area, buffers buffers_ = buffers::color, filter filter_ = filter::nearest) const; void bind_as(target target_) const; void remove(); bool created() const; void check() const; void recreate(); void draw_buffer(const attachment& buffer) const; void draw_buffers(const std::initializer_list& indexes) const; void read_buffer(const attachment& buffer) const; void draw_arrays(rsx::primitive_type mode, GLsizei count, GLint first = 0) const; void draw_arrays(const buffer& buffer, rsx::primitive_type mode, GLsizei count, GLint first = 0) const; void draw_arrays(const vao& buffer, rsx::primitive_type mode, GLsizei count, GLint first = 0) const; void draw_elements(rsx::primitive_type mode, GLsizei count, indices_type type, const GLvoid *indices) const; void draw_elements(const buffer& buffer, rsx::primitive_type mode, GLsizei count, indices_type type, const GLvoid *indices) const; void draw_elements(rsx::primitive_type mode, GLsizei count, indices_type type, const buffer& indices, size_t indices_buffer_offset = 0) const; void draw_elements(const buffer& buffer_, rsx::primitive_type mode, GLsizei count, indices_type type, const buffer& indices, size_t indices_buffer_offset = 0) const; void draw_elements(rsx::primitive_type mode, GLsizei count, const GLubyte *indices) const; void draw_elements(const buffer& buffer, rsx::primitive_type mode, GLsizei count, const GLubyte *indices) const; void draw_elements(rsx::primitive_type mode, GLsizei count, const GLushort *indices) const; void draw_elements(const buffer& buffer, rsx::primitive_type mode, GLsizei count, const GLushort *indices) const; void draw_elements(rsx::primitive_type mode, GLsizei count, const GLuint *indices) const; void draw_elements(const buffer& buffer, rsx::primitive_type mode, GLsizei count, const GLuint *indices) const; void clear(buffers buffers_) const; void clear(buffers buffers_, color4f color_value, double depth_value, u8 stencil_value) const; void copy_from(const void* pixels, sizei size, gl::texture::format format_, gl::texture::type type_, class pixel_unpack_settings pixel_settings = pixel_unpack_settings()) const; void copy_from(const buffer& buf, sizei size, gl::texture::format format_, gl::texture::type type_, class pixel_unpack_settings pixel_settings = pixel_unpack_settings()) const; void copy_to(void* pixels, coordi coord, gl::texture::format format_, gl::texture::type type_, class pixel_pack_settings pixel_settings = pixel_pack_settings()) const; void copy_to(const buffer& buf, coordi coord, gl::texture::format format_, gl::texture::type type_, class pixel_pack_settings pixel_settings = pixel_pack_settings()) const; static fbo get_binded_draw_buffer(); static fbo get_binded_read_buffer(); static fbo get_binded_buffer(); GLuint id() const; void set_id(GLuint id); explicit operator bool() const { return created(); } }; extern const fbo screen; namespace glsl { class compilation_exception : public exception { public: explicit compilation_exception(const std::string& what_arg) { m_what = "compilation failed: '" + what_arg + "'"; } }; class link_exception : public exception { public: explicit link_exception(const std::string& what_arg) { m_what = "linkage failed: '" + what_arg + "'"; } }; class validation_exception : public exception { public: explicit validation_exception(const std::string& what_arg) { m_what = "compilation failed: '" + what_arg + "'"; } }; class not_found_exception : public exception { public: explicit not_found_exception(const std::string& what_arg) { m_what = what_arg + " not found."; } }; class shader { GLuint m_id = GL_NONE; public: enum class type { fragment = GL_FRAGMENT_SHADER, vertex = GL_VERTEX_SHADER, geometry = GL_GEOMETRY_SHADER }; shader() = default; shader(GLuint id) { set_id(id); } shader(type type_) { create(type_); } shader(type type_, const std::string& src) { create(type_); source(src); } ~shader() { if (created()) remove(); } void recreate(type type_) { if (created()) remove(); create(type_); } void create(type type_) { m_id = glCreateShader((GLenum)type_); } void source(const std::string& src) const { const char* str = src.c_str(); const GLint length = (GLint)src.length(); glShaderSource(m_id, 1, &str, &length); } shader& compile() { glCompileShader(m_id); GLint status = GL_FALSE; glGetShaderiv(m_id, GL_COMPILE_STATUS, &status); if (status == GL_FALSE) { GLint length = 0; glGetShaderiv(m_id, GL_INFO_LOG_LENGTH, &length); std::string error_msg; if (length) { std::unique_ptr buf(new char[length + 1]); glGetShaderInfoLog(m_id, length, nullptr, buf.get()); error_msg = buf.get(); } throw compilation_exception(error_msg); } return *this; } void remove() { glDeleteShader(m_id); m_id = 0; } uint id() const { return m_id; } void set_id(uint id) { m_id = id; } bool created() const { return m_id != 0; } explicit operator bool() const { return created(); } }; class program { GLuint m_id = 0; public: class uniform_t { program& m_program; GLint m_location; public: uniform_t(program& program, GLint location) : m_program(program) , m_location(location) { } GLint location() const { return m_location; } void operator = (int rhs) const { m_program.use(); glUniform1i(location(), rhs); } void operator = (float rhs) const { m_program.use(); glUniform1f(location(), rhs); } //void operator = (double rhs) const { m_program.use(); glUniform1d(location(), rhs); } void operator = (const color1i& rhs) const { m_program.use(); glUniform1i(location(), rhs.r); } void operator = (const color1f& rhs) const { m_program.use(); glUniform1f(location(), rhs.r); } //void operator = (const color1d& rhs) const { m_program.use(); glUniform1d(location(), rhs.r); } void operator = (const color2i& rhs) const { m_program.use(); glUniform2i(location(), rhs.r, rhs.g); } void operator = (const color2f& rhs) const { m_program.use(); glUniform2f(location(), rhs.r, rhs.g); } //void operator = (const color2d& rhs) const { m_program.use(); glUniform2d(location(), rhs.r, rhs.g); } void operator = (const color3i& rhs) const { m_program.use(); glUniform3i(location(), rhs.r, rhs.g, rhs.b); } void operator = (const color3f& rhs) const { m_program.use(); glUniform3f(location(), rhs.r, rhs.g, rhs.b); } //void operator = (const color3d& rhs) const { m_program.use(); glUniform3d(location(), rhs.r, rhs.g, rhs.b); } void operator = (const color4i& rhs) const { m_program.use(); glUniform4i(location(), rhs.r, rhs.g, rhs.b, rhs.a); } void operator = (const color4f& rhs) const { m_program.use(); glUniform4f(location(), rhs.r, rhs.g, rhs.b, rhs.a); } //void operator = (const color4d& rhs) const { m_program.use(); glUniform4d(location(), rhs.r, rhs.g, rhs.b, rhs.a); } }; class attrib_t { GLuint m_program; GLint m_location; public: attrib_t(GLuint program, GLint location) : m_program(program) , m_location(location) { } GLint location() const { return m_location; } void operator = (float rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib1f(location(), rhs); } void operator = (double rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib1d(location(), rhs); } void operator = (const color1f& rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib1f(location(), rhs.r); } void operator = (const color1d& rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib1d(location(), rhs.r); } void operator = (const color2f& rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib2f(location(), rhs.r, rhs.g); } void operator = (const color2d& rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib2d(location(), rhs.r, rhs.g); } void operator = (const color3f& rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib3f(location(), rhs.r, rhs.g, rhs.b); } void operator = (const color3d& rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib3d(location(), rhs.r, rhs.g, rhs.b); } void operator = (const color4f& rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib4f(location(), rhs.r, rhs.g, rhs.b, rhs.a); } void operator = (const color4d& rhs) const { glDisableVertexAttribArray(location()); glVertexAttrib4d(location(), rhs.r, rhs.g, rhs.b, rhs.a); } void operator =(buffer_pointer& pointer) const { pointer.get_vao().enable_for_attribute(location()); glVertexAttribPointer(location(), pointer.size(), (GLenum)pointer.get_type(), pointer.normalize(), pointer.stride(), (const void*)(size_t)pointer.offset()); } }; class uniforms_t { program& m_program; std::unordered_map locations; int active_texture = 0; public: uniforms_t(program* program) : m_program(*program) { } void clear() { locations.clear(); active_texture = 0; } bool has_location(const std::string &name, int *location = nullptr) { int result = glGetUniformLocation(m_program.id(), name.c_str()); if (result < 0) return false; locations[name] = result; if (location) *location = result; return true; } GLint location(const std::string &name) { auto finded = locations.find(name); if (finded != locations.end()) { return finded->second; } int result = glGetUniformLocation(m_program.id(), name.c_str()); if (result < 0) throw not_found_exception(name); locations[name] = result; return result; } int texture(GLint location, int active_texture, const gl::texture& texture) { glActiveTexture(GL_TEXTURE0 + active_texture); texture.bind(); (*this)[location] = active_texture; return active_texture; } int texture(const std::string &name, int active_texture, const gl::texture& texture_) { return texture(location(name), active_texture, texture_); } int texture(const std::string &name, const gl::texture& texture_) { int atex; auto finded = locations.find(name); if (finded != locations.end()) { atex = finded->second; } else { atex = active_texture++; } return texture(name, atex, texture_); } uniform_t operator[](GLint location) { return{ m_program, location }; } uniform_t operator[](const std::string &name) { return{ m_program, location(name) }; } void swap(uniforms_t& uniforms) { locations.swap(uniforms.locations); std::swap(active_texture, uniforms.active_texture); } } uniforms{ this }; class attribs_t { program& m_program; std::unordered_map m_locations; public: attribs_t(program* program) : m_program(*program) { } void clear() { m_locations.clear(); } GLint location(const std::string &name) { auto finded = m_locations.find(name); if (finded != m_locations.end()) { if (finded->second < 0) throw not_found_exception(name); return finded->second; } int result = glGetAttribLocation(m_program.id(), name.c_str()); if (result < 0) throw not_found_exception(name); m_locations[name] = result; return result; } bool has_location(const std::string &name, int *location_ = nullptr) { auto finded = m_locations.find(name); if (finded != m_locations.end()) { if (finded->second < 0) return false; *location_ = finded->second; return true; } int loc = glGetAttribLocation(m_program.id(), name.c_str()); m_locations[name] = loc; if (loc < 0) return false; *location_ = loc; return true; } attrib_t operator[](GLint location) { return{ m_program.id(), location }; } attrib_t operator[](const std::string &name) { return{ m_program.id(), location(name) }; } void swap(attribs_t& attribs) { m_locations.swap(attribs.m_locations); } } attribs{ this }; program& recreate() { if (created()) remove(); return create(); } program& create() { m_id = glCreateProgram(); return *this; } void remove() { glDeleteProgram(m_id); m_id = 0; uniforms.clear(); } static program get_current_program() { GLint id; glGetIntegerv(GL_CURRENT_PROGRAM, &id); return{ (GLuint)id }; } void use() { glUseProgram(m_id); } void link() { glLinkProgram(m_id); GLint status = GL_FALSE; glGetProgramiv(m_id, GL_LINK_STATUS, &status); if (status == GL_FALSE) { GLint length = 0; glGetProgramiv(m_id, GL_INFO_LOG_LENGTH, &length); std::string error_msg; if (length) { std::unique_ptr buf(new char[length + 1]); glGetProgramInfoLog(m_id, length, nullptr, buf.get()); error_msg = buf.get(); } throw link_exception(error_msg); } } void validate() { glValidateProgram(m_id); GLint status = GL_FALSE; glGetProgramiv(m_id, GL_VALIDATE_STATUS, &status); if (status == GL_FALSE) { GLint length = 0; glGetProgramiv(m_id, GL_INFO_LOG_LENGTH, &length); std::string error_msg; if (length) { std::unique_ptr buf(new char[length + 1]); glGetProgramInfoLog(m_id, length, nullptr, buf.get()); error_msg = buf.get(); } throw validation_exception(error_msg); } } void make() { link(); } uint id() const { return m_id; } void set_id(uint id) { uniforms.clear(); attribs.clear(); m_id = id; } bool created() const { return m_id != 0; } explicit operator bool() const { return created(); } program& attach(const shader& shader_) { glAttachShader(m_id, shader_.id()); return *this; } program& bind_attribute_location(const std::string& name, int index) { glBindAttribLocation(m_id, index, name.c_str()); return *this; } program& bind_fragment_data_location(const std::string& name, int color_number) { glBindFragDataLocation(m_id, color_number, name.c_str()); return *this; } int attribute_location(const std::string& name) { return glGetAttribLocation(m_id, name.c_str()); } int uniform_location(const std::string& name) { return glGetUniformLocation(m_id, name.c_str()); } program& operator += (const shader& rhs) { return attach(rhs); } program& operator += (std::initializer_list shaders) { for (auto &shader : shaders) *this += shader; return *this; } program() = default; program(const program&) = delete; program(program&& program_) { swap(program_); } program(GLuint id) { set_id(id); } ~program() { if (created()) remove(); } void swap(program& program_) { auto my_old_id = id(); set_id(program_.id()); program_.set_id(my_old_id); uniforms.swap(program_.uniforms); attribs.swap(program_.attribs); } program& operator = (const program& rhs) = delete; program& operator = (program&& rhs) { swap(rhs); return *this; } }; class shader_view : public shader { public: shader_view(GLuint id) : shader(id) { } ~shader_view() { set_id(0); } }; class program_view : public program { public: program_view(GLuint id) : program(id) { } ~program_view() { set_id(0); } }; } class texture_view : public texture { public: texture_view(texture::target target_, GLuint id) : texture(target_, id) { } ~texture_view() { set_id(0); } }; class fbo_view : public fbo { public: fbo_view(GLuint id) : fbo(id) { } ~fbo_view() { set_id(0); } }; class rbo_view : public rbo { public: rbo_view(GLuint id) : rbo(id) { } ~rbo_view() { set_id(0); } }; class buffer_view : public buffer { public: buffer_view(GLuint id) : buffer(id) { } ~buffer_view() { set_id(0); } }; }