diff options
Diffstat (limited to 'include/drm++/gem.hpp')
| -rw-r--r-- | include/drm++/gem.hpp | 81 |
1 files changed, 58 insertions, 23 deletions
diff --git a/include/drm++/gem.hpp b/include/drm++/gem.hpp index 4a02e55..35829ee 100644 --- a/include/drm++/gem.hpp +++ b/include/drm++/gem.hpp @@ -12,24 +12,30 @@ /// - Import a DMA-BUF via PRIME /// - Create a linear dumb buffer /// -/// Upon importing a DMA-BUF, a new handle to the underlying GEM object is created and the -/// reference count is increased. The file descriptor itself also holds a reference and must -/// therefore be closed. +/// Upon importing a DMA-BUF, the kernel first checks if a GEM object already exists for the +/// given DMA-BUF and returns the existing handle (not incrementing any reference counts!). +/// If no GEM object exists, a new one is created and the prime import is performed, incrementing +/// the reference counts accordingly. When exporting a DMA-BUF, the reference count of the DMA-BUF +/// is incremented. /// -/// When creating a dumb buffer, there are no guarantees about the underlying memory. It may be -/// in system memory or GPU memory and there are no guarantees about the performance of read/write -/// operations. A dumb buffer can however always be mapped into CPU-accessible memory. +/// Due to this, several GEM buffers may share the same handle. In order to provide a safe +/// abstraction, this library independently reference counts these handles, ensuring no +/// use-after-free or double-free occurs. /// /// * Dumb Buffers /// -/// A dumb buffer is a primitive DRM-native driver independent GEM object, wrapping around a linear +/// A dumb buffer is a primitive DRM-native driver-independent GEM object, wrapping around a linear /// memory allocation. /// +/// When creating a dumb buffer, there are no guarantees about the underlying memory. It may be +/// in system memory or GPU memory and there are no guarantees about the performance of read/write +/// operations. A dumb buffer can however always be mapped into CPU-accessible memory. +/// /// The only creation parameters are width, height and bpp (bits per pixel / color mode). The /// bpp parameter also specifies the DRM formats this buffer can be used with. The table below /// can serve as a reference, however there are no guarantees that a format is compatible. /// -/// Most drivers will support DRM_FORMAT_XRGB8888 with 32 bits per pixel. +/// Most drivers will support DRM_FORMAT_XRGB8888 with 32 bits per pixel on primary planes. /// /// +-----+------------------------+------------------------+ /// | BPP | Framebuffer format | Compatible formats | @@ -45,7 +51,7 @@ /// | 15 | * DRM_FORMAT_XRGB1555 | * DRM_FORMAT_BGRX1555 | /// | | | * DRM_FORMAT_RGBX1555 | /// | | | * DRM_FORMAT_XBGR1555 | -/// ------+------------------------+------------------------+ +/// +-----+------------------------+------------------------+ /// | 8 | * DRM_FORMAT_C8 | * DRM_FORMAT_D8 | /// | | | * DRM_FORMAT_R8 | /// +-----+------------------------+------------------------+ @@ -64,9 +70,40 @@ #include "drm++/helper.hpp" +#include <unordered_map> + namespace drm::gem { /// +/// GEM object manager. +/// +/// This class is responsible for reference counting GEM handles as described above and must +/// therefore outlive all GEM objects. +/// +class ObjectManager { + friend class Object; + friend class DumbBuffer; +public: + /// Create a new object manager. + ObjectManager(int fd) noexcept : m_fd(fd) {} + + // Convenience method to create a new objects + template<typename T, typename... Args> + [[nodiscard]] T create(Args&&... args) { + return {*this, std::forward<Args>(args)...}; + } + + // Default operators and destructor + NO_COPY(ObjectManager) + NO_MOVE(ObjectManager) + ~ObjectManager() noexcept = default; +private: + int m_fd; + + std::unordered_map<u32, u32> m_refcounts; // GEM handle -> reference count +}; + +/// /// GEM object wrapping a memory allocation /// /// @throws drm::ioctl::Exception on failure @@ -80,7 +117,7 @@ public: /// An import can fail for various driver-specific reasons, especially for foreign DMA-BUFs. /// /// @param close Close fd after import (regardless of success) - Object(int fd, int dmabuf_fd, bool close = true); + Object(ObjectManager& manager, int dmabuf_fd, bool close = true); /// Flags for exporting fds enum class ExportFlags : u32 { @@ -89,7 +126,7 @@ public: CloseOnExec = 1 << 1, //!< Close fd on execve() }; - /// Obtain a DMA-BUF file descriptor for the GEM object, incrementing the reference count. + /// Export a DMA-BUF file descriptor from the GEM object. /// Requires DRM_PRIME_CAP_EXPORT. /// /// An export can fail for various driver-specific reasons, including lack of support on this @@ -100,31 +137,30 @@ public: /// Change the handle of the GEM object. /// @param handle An unused GEM handle to change into. + /// @throws std::logic_error if the handle is still in use by another class instance. void changeHandle(u32 handle); // Private access - GETTER(fd) GETTER(handle) // Move constructor/operator - Object(Object&& other) noexcept : m_fd(other.m_fd), m_handle(other.m_handle) { - other.m_fd = -1; // invalidate other + Object(Object&& other) noexcept : m_manager(other.m_manager), m_handle(other.m_handle) { + other.m_manager = nullptr; // invalidate other } Object& operator=(Object&& other) noexcept { if (this != &other) { this->destruct(); this->m_handle = other.m_handle; - this->m_fd = other.m_fd; - other.m_fd = -1; // invalidate other + this->m_manager = other.m_manager; + other.m_manager = nullptr; // invalidate other } return *this; } // Copy constructor/operator - Object(const Object& other) = delete; - Object& operator=(const Object& other) = delete; + NO_COPY(Object) // Destructor ~Object() noexcept { @@ -132,10 +168,10 @@ public: } private: - int m_fd; // indicates object validity (>= 0) + ObjectManager* m_manager{nullptr}; // indicates object validity u32 m_handle; - Object(int fd) : m_fd(fd), m_handle(0) {} + Object(ObjectManager& manager) : m_manager(&manager), m_handle(0) {} void destruct() noexcept; }; @@ -148,7 +184,7 @@ private: class DumbBuffer : public Object { public: /// Create a new dumb buffer. - DumbBuffer(int fd, u32 width, u32 height, u32 bpp); + DumbBuffer(ObjectManager& manager, u32 width, u32 height, u32 bpp); /// Map the dumb buffer into userspace memory. May be called multiple times, /// will return the same pointer if already mapped. @@ -188,8 +224,7 @@ public: } // Copy constructor/operator - DumbBuffer(const DumbBuffer& other) = delete; - DumbBuffer& operator=(const DumbBuffer& other) = delete; + NO_COPY(DumbBuffer) // Destructor ~DumbBuffer() noexcept { |
