#include "stdafx.h" #include "buffer_object.h" namespace gl { void buffer::allocate(GLsizeiptr size, const void* data_, memory_type type, GLenum usage) { if (const auto& caps = get_driver_caps(); caps.ARB_buffer_storage_supported) { GLenum flags = 0; if (type == memory_type::host_visible) { switch (usage) { case GL_STREAM_DRAW: case GL_STATIC_DRAW: case GL_DYNAMIC_DRAW: flags |= GL_MAP_WRITE_BIT; break; case GL_STREAM_READ: case GL_STATIC_READ: case GL_DYNAMIC_READ: flags |= GL_MAP_READ_BIT; break; default: fmt::throw_exception("Unsupported buffer usage 0x%x", usage); } } else { // Local memory hints if (usage == GL_DYNAMIC_COPY) { flags |= GL_DYNAMIC_STORAGE_BIT; } } if ((flags & GL_MAP_READ_BIT) && !caps.vendor_AMD) { // This flag stops NVIDIA from allocating read-only memory in VRAM. // NOTE: On AMD, allocating client-side memory via CLIENT_STORAGE_BIT or // making use of GL_AMD_pinned_memory brings everything down to a crawl. // Afaict there is no reason for this; disabling pixel pack/unpack operations does not alleviate the problem. // The driver seems to eventually figure out the optimal storage location by itself. flags |= GL_CLIENT_STORAGE_BIT; } DSA_CALL2(NamedBufferStorage, m_id, size, data_, flags); m_size = size; } else { data(size, data_, usage); } m_memory_type = type; } buffer::~buffer() { if (created()) remove(); } void buffer::recreate() { if (created()) { remove(); } create(); } void buffer::recreate(GLsizeiptr size, const void* data) { if (created()) { remove(); } create(size, data); } void buffer::create() { glGenBuffers(1, &m_id); save_binding_state save(current_target(), *this); } void buffer::create(GLsizeiptr size, const void* data_, memory_type type, GLenum usage) { create(); allocate(size, data_, type, usage); } void buffer::create(target target_, GLsizeiptr size, const void* data_, memory_type type, GLenum usage) { m_target = target_; create(); allocate(size, data_, type, usage); } void buffer::remove() { if (m_id != GL_NONE) { glDeleteBuffers(1, &m_id); m_id = GL_NONE; m_size = 0; } } void buffer::data(GLsizeiptr size, const void* data_, GLenum usage) { ensure(m_memory_type != memory_type::local); DSA_CALL2(NamedBufferData, m_id, size, data_, usage); m_size = size; } void buffer::sub_data(GLsizeiptr offset, GLsizeiptr length, GLvoid* data) { ensure(m_memory_type == memory_type::local); DSA_CALL2(NamedBufferSubData, m_id, offset, length, data); } GLubyte* buffer::map(GLsizeiptr offset, GLsizeiptr length, access access_) { ensure(m_memory_type == memory_type::host_visible); GLenum access_bits = static_cast(access_); if (access_bits == GL_MAP_WRITE_BIT) access_bits |= GL_MAP_UNSYNCHRONIZED_BIT; auto raw_data = DSA_CALL2_RET(MapNamedBufferRange, id(), offset, length, access_bits); return reinterpret_cast(raw_data); } void buffer::unmap() { ensure(m_memory_type == memory_type::host_visible); DSA_CALL2(UnmapNamedBuffer, id()); } void buffer::bind_range(u32 index, u32 offset, u32 size) const { glBindBufferRange(static_cast(current_target()), index, id(), offset, size); } void buffer::bind_range(target target_, u32 index, u32 offset, u32 size) const { glBindBufferRange(static_cast(target_), index, id(), offset, size); } void buffer::copy_to(buffer* other, u64 src_offset, u64 dst_offset, u64 size) { if (get_driver_caps().ARB_dsa_supported) { glCopyNamedBufferSubData(this->id(), other->id(), src_offset, dst_offset, size); } else { glNamedCopyBufferSubDataEXT(this->id(), other->id(), src_offset, dst_offset, size); } } // Buffer view void buffer_view::update(buffer* _buffer, u32 offset, u32 range, GLenum format) { ensure(_buffer->size() >= (offset + range)); m_buffer = _buffer; m_offset = offset; m_range = range; m_format = format; } bool buffer_view::in_range(u32 address, u32 size, u32& new_offset) const { if (address < m_offset) return false; const u32 _offset = address - m_offset; if (m_range < _offset) return false; const auto remaining = m_range - _offset; if (size <= remaining) { new_offset = _offset; return true; } return false; } }