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