1 module gfm.opengl.buffer;
2 
3 import bindbc.opengl;
4 
5 import gfm.opengl.opengl;
6 
7 /// OpenGL Buffer wrapper.
8 final class GLBuffer
9 {
10     public
11     {
12         /// Creates an empty buffer.
13         /// Throws: $(D OpenGLException) on error.
14         this(GLuint target, GLuint usage)
15         {
16             _usage = usage;
17             _target = target;
18             _firstLoad = true;
19 
20             glGenBuffers(1, &_buffer);
21             runtimeCheck();
22             _initialized = true;
23             _size = 0;
24         }
25 
26         /// Creates a buffer already filled with data.
27         /// Throws: $(D OpenGLException) on error.
28         this(T)(GLuint target, GLuint usage, T[] buffer)
29         {
30             this(target, usage);
31             setData(buffer);
32         }
33 
34         /// Releases the OpenGL buffer resource.
35         ~this()
36         {
37             if (_initialized)
38             {
39                 debug ensureNotInGC("GLBuffer");
40                 glDeleteBuffers(1, &_buffer);
41                 _initialized = false;
42             }
43         }
44 
45         /// Returns: Size of buffer in bytes.
46         @property size_t size() pure const nothrow
47         {
48             return _size;
49         }
50 
51         /// Returns: Copy bytes to the buffer.
52         /// Throws: $(D OpenGLException) on error.
53         void setData(T)(T[] buffer)
54         {
55             setData(buffer.length * T.sizeof, buffer.ptr);
56         }
57 
58         /// Returns: Copy bytes to the buffer.
59         /// Throws: $(D OpenGLException) on error.
60         void setData(size_t size, void * data)
61         {
62             bind();
63             _size = size;
64 
65             // discard previous data
66             if (!_firstLoad)
67             {
68                 glBufferData(_target, size, null, _usage);
69                 glBufferSubData(_target, 0, size, data);
70             }
71             else
72                 glBufferData(_target, size, data, _usage);
73 
74             runtimeCheck();
75 
76             _firstLoad = false;
77         }
78 
79         /// Copies bytes to a sub-part of the buffer. You can't adress data beyond the buffer's size.
80         /// Throws: $(D OpenGLException) on error.
81         void setSubData(size_t offset, size_t size, void* data)
82         {
83             bind();
84             glBufferSubData(_target, offset, size, data);
85             runtimeCheck();
86         }
87 
88         /// Gets a sub-part of a buffer.
89         /// Throws: $(D OpenGLException) on error.
90         void getSubData(size_t offset, size_t size, void* data)
91         {
92             bind();
93             glGetBufferSubData(_target, offset, size, data);
94             runtimeCheck();
95         }
96 
97         /// Gets the whole buffer content in a newly allocated array.
98         /// <b>This is intended for debugging purposes.</b>
99         /// Throws: $(D OpenGLException) on error.
100         ubyte[] getBytes()
101         {
102             auto buffer = new ubyte[_size];
103             getSubData(0, _size, buffer.ptr);
104             return buffer;
105         }
106 
107         /// Binds this buffer.
108         /// Throws: $(D OpenGLException) on error.
109         void bind()
110         {
111             glBindBuffer(_target, _buffer);
112             runtimeCheck();
113         }
114 
115         /// Unbinds this buffer.
116         /// Throws: $(D OpenGLException) on error.
117         void unbind()
118         {
119             glBindBuffer(_target, 0);
120         }
121 
122         /// Returns: Wrapped OpenGL resource handle.
123         GLuint handle() pure const nothrow
124         {
125             return _buffer;
126         }
127     }
128 
129     private
130     {
131         GLuint _buffer;
132         size_t _size;
133         GLuint _target;
134         GLuint _usage;
135         bool _firstLoad;
136         bool _initialized;
137     }
138 }