summaryrefslogtreecommitdiff
path: root/include/drm++/gem.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'include/drm++/gem.hpp')
-rw-r--r--include/drm++/gem.hpp81
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 {