My eyes, they burn! Also runs faster, slightly less buggy.
[ipdf/code.git] / src / graphicsbuffer.cpp
1 #include "graphicsbuffer.h"
2 #include "log.h"
3
4 using namespace IPDF;
5
6 static GLenum BufferUsageToGLUsage(GraphicsBuffer::BufferUsage buffer_usage)
7 {
8         GLenum usage;
9         switch (buffer_usage)
10         {
11         case GraphicsBuffer::BufferUsageStaticDraw:
12                 usage = GL_STATIC_DRAW;
13                 break;
14         case GraphicsBuffer::BufferUsageStaticRead:
15                 usage = GL_STATIC_READ;
16                 break;
17         case GraphicsBuffer::BufferUsageStaticCopy:
18                 usage = GL_STATIC_COPY;
19                 break;
20         case GraphicsBuffer::BufferUsageDynamicDraw:
21                 usage = GL_DYNAMIC_DRAW;
22                 break;
23         case GraphicsBuffer::BufferUsageDynamicRead:
24                 usage = GL_DYNAMIC_READ;
25                 break;
26         case GraphicsBuffer::BufferUsageDynamicCopy:
27                 usage = GL_DYNAMIC_COPY;
28                 break;
29         case GraphicsBuffer::BufferUsageStreamDraw:
30                 usage = GL_STREAM_DRAW;
31                 break;
32         case GraphicsBuffer::BufferUsageStreamRead:
33                 usage = GL_STREAM_READ;
34                 break;
35         case GraphicsBuffer::BufferUsageStreamCopy:
36                 usage = GL_STREAM_COPY;
37                 break;
38         default:
39                 SDL_assert(false && "Unknown buffer usage type."); //WTF?
40                 usage = GL_DYNAMIC_DRAW;
41         }
42         return usage;
43 }
44
45 static GLenum BufferTypeToGLType(GraphicsBuffer::BufferType buffer_type)
46 {
47         switch (buffer_type)
48         {
49         case GraphicsBuffer::BufferTypeVertex:
50                 return GL_ARRAY_BUFFER;
51         case GraphicsBuffer::BufferTypeIndex:
52                 return GL_ELEMENT_ARRAY_BUFFER;
53         case GraphicsBuffer::BufferTypePixelPack:
54                 return GL_PIXEL_PACK_BUFFER;
55         case GraphicsBuffer::BufferTypePixelUnpack:
56                 return GL_PIXEL_UNPACK_BUFFER;
57         case GraphicsBuffer::BufferTypeUniform:
58                 return GL_UNIFORM_BUFFER;
59         case GraphicsBuffer::BufferTypeTexture:
60                 return GL_TEXTURE_BUFFER;
61         case GraphicsBuffer::BufferTypeDrawIndirect:
62                 return GL_DRAW_INDIRECT_BUFFER;
63         default:
64                 return GL_COPY_READ_BUFFER;
65         }
66 }
67
68 GraphicsBuffer::GraphicsBuffer()
69 {
70         m_invalidated = true;
71         m_map_pointer = NULL;
72         m_buffer_size = 0;
73         m_buffer_shape_dirty = true;
74         m_buffer_handle = 0;
75         m_buffer_usage = BufferUsageDynamicDraw;
76         m_faking_map = false;
77         m_name = "Unnamed Buffer";
78         SetUsage(BufferUsageStaticDraw);
79 }
80
81 GraphicsBuffer::~GraphicsBuffer()
82 {
83         if (m_map_pointer)
84         {
85                 UnMap();
86         }
87         glDeleteBuffers(1, &m_buffer_handle);
88 }
89
90 void GraphicsBuffer::SetType(GraphicsBuffer::BufferType bufType)
91 {
92         m_buffer_type = bufType;
93 }
94
95 void GraphicsBuffer::SetName(const char *name)
96 {
97         m_name = name;
98 }
99
100 void GraphicsBuffer::SetUsage(GraphicsBuffer::BufferUsage bufUsage)
101 {
102         if (bufUsage != m_buffer_usage)
103         {
104                 m_buffer_usage = bufUsage;
105                 m_buffer_shape_dirty = true;
106         }
107 }
108
109 void GraphicsBuffer::Invalidate()
110 {
111         m_invalidated = true;
112         if (!m_buffer_shape_dirty)
113         {
114                 // Orphan the block of memory we're pointing to.
115                 Upload(m_buffer_size, NULL);
116         }
117         // Apparently not supported.
118         //glInvalidateBufferData(m_buffer_handle);
119 }
120
121 bool GraphicsBuffer::RecreateBuffer(const void *data)
122 {
123         // If the buffer is not dirty, don't recreate it.
124         if (!m_buffer_shape_dirty) return false;
125         // If the buffer is mapped, don't recreate it.
126         if (!m_faking_map && m_map_pointer) return false;
127         // If the buffer has data in it we need, don't recreate it.
128         if (!m_invalidated) return false;
129         if (m_buffer_handle)
130         {
131                 glDeleteBuffers(1, &m_buffer_handle);
132         }
133         if (m_buffer_size)
134         {
135                 glGenBuffers(1, &m_buffer_handle);
136                 glObjectLabel(GL_BUFFER, m_buffer_handle, -1, m_name);
137                 m_buffer_shape_dirty = false;
138                 Upload(m_buffer_size, data);
139         }
140         return true;
141 }
142
143 void* GraphicsBuffer::Map(bool read, bool write, bool invalidate)
144 {
145         GLbitfield access = ((read)?GL_MAP_READ_BIT:0) | ((write)?GL_MAP_WRITE_BIT:0) | ((invalidate)?GL_MAP_INVALIDATE_BUFFER_BIT:0);
146         GLenum target = BufferTypeToGLType(m_buffer_type);
147
148         if (invalidate)
149         {
150                 m_invalidated = true;
151
152                 // Intel's Mesa driver does not rename the buffer when we map with GL_MAP_INVALIDATE_BUFFER_BIT,
153                 // resulting in the CPU stalling waiting for rendering from the buffer to complete on the GPU.
154                 // We manually force the buffer to be renamed here to avoid this.
155                 m_buffer_shape_dirty = true;
156         }
157
158         if (m_map_pointer)
159                 Warn("Tried to map already mapped buffer!");    
160
161         // Intel really doesn't seem to like this.
162         if (!m_buffer_size)
163                 return (m_map_pointer = 0);
164
165
166         if (!read && m_buffer_usage == BufferUsage::BufferUsageStaticDraw)
167         {
168                 m_map_pointer = malloc(m_buffer_size);
169                 m_faking_map = true;
170                 return m_map_pointer;
171         }
172
173         RecreateBuffer();
174
175         Bind();
176         
177         m_map_pointer = glMapBufferRange(target, 0, m_buffer_size, access);
178         
179         return m_map_pointer;
180 }
181
182 void* GraphicsBuffer::MapRange(int offset, int length, bool read, bool write, bool invalidate)
183 {
184         GLbitfield access = ((read)?GL_MAP_READ_BIT:0) | ((write)?GL_MAP_WRITE_BIT:0) | ((invalidate)?GL_MAP_INVALIDATE_RANGE_BIT:0);
185         GLenum target = BufferTypeToGLType(m_buffer_type);
186
187         if (m_map_pointer)
188                 Warn("Tried to map already mapped buffer!");    
189
190         // This sometimes makes Intel corrupt memory?
191         if (!length) return (m_map_pointer = 0);
192
193         if ((size_t)(length + offset) > m_buffer_size)
194                 Fatal("Tried to map outside of range!");
195
196
197         RecreateBuffer();
198
199         Bind();
200         
201         m_map_pointer = glMapBufferRange(target, offset, length, access);
202         return m_map_pointer;
203 }
204
205 void GraphicsBuffer::UnMap()
206 {
207         GLenum target = BufferTypeToGLType(m_buffer_type);
208         
209         // If we're not mapped, unmapping is a no-op.
210         if (!m_map_pointer)
211                 return;
212
213         if (m_faking_map)
214         {
215                 Upload(m_buffer_size, m_map_pointer);
216                 free(m_map_pointer);
217                 m_map_pointer = NULL;
218                 m_invalidated = false;
219                 m_faking_map = false;
220                 return;
221         }
222         
223         Bind();
224         glUnmapBuffer(target);
225         m_map_pointer = NULL;
226         m_invalidated = false;
227 }
228
229 void GraphicsBuffer::Upload(size_t length, const void* data)
230 {
231         GLenum target = BufferTypeToGLType(m_buffer_type);
232         
233         GLenum usage = BufferUsageToGLUsage(m_buffer_usage);
234
235         m_invalidated = true;
236         m_buffer_size = length;
237         if (!RecreateBuffer(data))
238         {
239                 Bind();
240                 glBufferData(target, length+1, data, usage);
241         }
242         if (data != NULL)
243                 m_invalidated = false;
244 }
245
246 void GraphicsBuffer::UploadRange(size_t length, intptr_t offset, const void* data)
247 {
248         GLenum target = BufferTypeToGLType(m_buffer_type);
249
250         RecreateBuffer();
251         
252         Bind();
253         glBufferSubData(target, offset, length, data);
254         m_invalidated = false;
255 }
256
257 void GraphicsBuffer::Resize(size_t length)
258 {
259         if (!m_buffer_size)
260         {
261                 m_invalidated = true;
262                 m_buffer_size = length;
263                 return;
264         }
265         if (m_invalidated)
266         {
267                 m_buffer_size = length;
268                 m_buffer_shape_dirty = true;
269         }
270         else
271         {
272                 size_t oldsize = m_buffer_size;
273                 glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, -1, "Resizing buffer.");
274                 glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_HIGH, 0, NULL, GL_TRUE);
275                 // Create a new buffer and copy the old data into it.
276                 UnMap();
277                 GLuint old_buffer = m_buffer_handle;    
278                 glGenBuffers(1, &m_buffer_handle);
279                 Upload(length, NULL);
280                 glBindBuffer(GL_COPY_READ_BUFFER, old_buffer);
281                 glBindBuffer(GL_COPY_WRITE_BUFFER, m_buffer_handle);
282                 glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, oldsize);
283                 glDeleteBuffers(1, &old_buffer);
284                 m_buffer_size = length;
285                 glPopDebugGroup();
286         }
287 }
288
289 void GraphicsBuffer::Bind() const
290 {
291         if (m_buffer_type == BufferTypeUniform)
292                 glBindBufferBase(GL_UNIFORM_BUFFER, 0, m_buffer_handle);
293         else
294                 glBindBuffer(BufferTypeToGLType(m_buffer_type), m_buffer_handle);
295 }
296
297 void GraphicsBuffer::BindRange(size_t start, size_t size) const
298 {
299         glBindBufferRange(BufferTypeToGLType(m_buffer_type), 0, m_buffer_handle, start, size);
300 }

UCC git Repository :: git.ucc.asn.au