KiCad PCB EDA Suite
cached_container_gpu.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright 2013-2017 CERN
5  * @author Maciej Suminski <maciej.suminski@cern.ch>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
27 #include <gal/opengl/vertex_item.h>
28 #include <gal/opengl/shader.h>
29 #include <gal/opengl/utils.h>
30 
31 #include <list>
32 
33 #ifdef __WXDEBUG__
34 #include <wx/log.h>
35 #include <profile.h>
36 #endif /* __WXDEBUG__ */
37 
38 using namespace KIGFX;
39 
41  CACHED_CONTAINER( aSize ), m_isMapped( false ), m_glBufferHandle( -1 )
42 {
43  m_useCopyBuffer = GLEW_ARB_copy_buffer;
44 
45  glGenBuffers( 1, &m_glBufferHandle );
46  glBindBuffer( GL_ARRAY_BUFFER, m_glBufferHandle );
47  glBufferData( GL_ARRAY_BUFFER, m_currentSize * VERTEX_SIZE, NULL, GL_DYNAMIC_DRAW );
48  glBindBuffer( GL_ARRAY_BUFFER, 0 );
49  checkGlError( "allocating video memory for cached container" );
50 }
51 
52 
54 {
55  if( m_isMapped )
56  Unmap();
57 
58  glDeleteBuffers( 1, &m_glBufferHandle );
59 }
60 
61 
63 {
64  wxCHECK( !IsMapped(), /*void*/ );
65 
66  glBindBuffer( GL_ARRAY_BUFFER, m_glBufferHandle );
67  m_vertices = static_cast<VERTEX*>( glMapBuffer( GL_ARRAY_BUFFER, GL_READ_WRITE ) );
68 
69  if( checkGlError( "mapping vertices buffer" ) == GL_NO_ERROR )
70  m_isMapped = true;
71 }
72 
73 
75 {
76  wxCHECK( IsMapped(), /*void*/ );
77 
78  glUnmapBuffer( GL_ARRAY_BUFFER );
79  checkGlError( "unmapping vertices buffer" );
80  glBindBuffer( GL_ARRAY_BUFFER, 0 );
81  m_vertices = NULL;
82  checkGlError( "unbinding vertices buffer" );
83 
84  m_isMapped = false;
85 }
86 
87 
88 bool CACHED_CONTAINER_GPU::defragmentResize( unsigned int aNewSize )
89 {
90  if( !m_useCopyBuffer )
91  return defragmentResizeMemcpy( aNewSize );
92 
93  wxCHECK( IsMapped(), false );
94 
95  wxLogTrace( "GAL_CACHED_CONTAINER_GPU",
96  wxT( "Resizing & defragmenting container from %d to %d" ), m_currentSize, aNewSize );
97 
98  // No shrinking if we cannot fit all the data
99  if( usedSpace() > aNewSize )
100  return false;
101 
102 #ifdef __WXDEBUG__
103  PROF_COUNTER totalTime;
104 #endif /* __WXDEBUG__ */
105 
106  GLuint newBuffer;
107 
108  // glCopyBufferSubData requires a buffer to be unmapped
109  glUnmapBuffer( GL_ARRAY_BUFFER );
110 
111  // Create a new destination buffer
112  glGenBuffers( 1, &newBuffer );
113 
114  // It would be best to use GL_COPY_WRITE_BUFFER here,
115  // but it is not available everywhere
116 #ifdef __WXDEBUG__
117  GLint eaBuffer = -1;
118  glGetIntegerv( GL_ELEMENT_ARRAY_BUFFER_BINDING, &eaBuffer );
119  wxASSERT( eaBuffer == 0 );
120 #endif /* __WXDEBUG__ */
121  glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, newBuffer );
122  glBufferData( GL_ELEMENT_ARRAY_BUFFER, aNewSize * VERTEX_SIZE, NULL, GL_DYNAMIC_DRAW );
123  checkGlError( "creating buffer during defragmentation" );
124 
125  ITEMS::iterator it, it_end;
126  int newOffset = 0;
127 
128  // Defragmentation
129  for( it = m_items.begin(), it_end = m_items.end(); it != it_end; ++it )
130  {
131  VERTEX_ITEM* item = *it;
132  int itemOffset = item->GetOffset();
133  int itemSize = item->GetSize();
134 
135  // Move an item to the new container
136  glCopyBufferSubData( GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER,
137  itemOffset * VERTEX_SIZE, newOffset * VERTEX_SIZE, itemSize * VERTEX_SIZE );
138 
139  // Update new offset
140  item->setOffset( newOffset );
141 
142  // Move to the next free space
143  newOffset += itemSize;
144  }
145 
146  // Move the current item and place it at the end
147  if( m_item->GetSize() > 0 )
148  {
149  glCopyBufferSubData( GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER,
150  m_item->GetOffset() * VERTEX_SIZE, newOffset * VERTEX_SIZE,
151  m_item->GetSize() * VERTEX_SIZE );
152 
153  m_item->setOffset( newOffset );
154  m_chunkOffset = newOffset;
155  }
156 
157  // Cleanup
158  glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
159  glBindBuffer( GL_ARRAY_BUFFER, 0 );
160 
161  // Previously we have unmapped the array buffer, now when it is also
162  // unbound, it may be officially marked as unmapped
163  m_isMapped = false;
164  glDeleteBuffers( 1, &m_glBufferHandle );
165 
166  // Switch to the new vertex buffer
167  m_glBufferHandle = newBuffer;
168  Map();
169  checkGlError( "switching buffers during defragmentation" );
170 
171 #ifdef __WXDEBUG__
172  totalTime.Stop();
173 
174  wxLogTrace( "GAL_CACHED_CONTAINER_GPU",
175  "Defragmented container storing %d vertices / %.1f ms",
176  m_currentSize - m_freeSpace, totalTime.msecs() );
177 #endif /* __WXDEBUG__ */
178 
179  m_freeSpace += ( aNewSize - m_currentSize );
180  m_currentSize = aNewSize;
181 
182  // Now there is only one big chunk of free memory
183  m_freeChunks.clear();
184  m_freeChunks.insert( std::make_pair( m_freeSpace, m_currentSize - m_freeSpace ) );
185 
186  return true;
187 }
188 
189 
190 bool CACHED_CONTAINER_GPU::defragmentResizeMemcpy( unsigned int aNewSize )
191 {
192  wxCHECK( IsMapped(), false );
193 
194  wxLogTrace( "GAL_CACHED_CONTAINER_GPU",
195  wxT( "Resizing & defragmenting container (memcpy) from %d to %d" ),
196  m_currentSize, aNewSize );
197 
198  // No shrinking if we cannot fit all the data
199  if( usedSpace() > aNewSize )
200  return false;
201 
202 #ifdef __WXDEBUG__
203  PROF_COUNTER totalTime;
204 #endif /* __WXDEBUG__ */
205 
206  GLuint newBuffer;
207  VERTEX* newBufferMem;
208 
209  // Create the destination buffer
210  glGenBuffers( 1, &newBuffer );
211 
212  // It would be best to use GL_COPY_WRITE_BUFFER here,
213  // but it is not available everywhere
214 #ifdef __WXDEBUG__
215  GLint eaBuffer = -1;
216  glGetIntegerv( GL_ELEMENT_ARRAY_BUFFER_BINDING, &eaBuffer );
217  wxASSERT( eaBuffer == 0 );
218 #endif /* __WXDEBUG__ */
219  glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, newBuffer );
220  glBufferData( GL_ELEMENT_ARRAY_BUFFER, aNewSize * VERTEX_SIZE, NULL, GL_DYNAMIC_DRAW );
221  newBufferMem = static_cast<VERTEX*>( glMapBuffer( GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY ) );
222  checkGlError( "creating buffer during defragmentation" );
223 
224  defragment( newBufferMem );
225 
226  // Cleanup
227  glUnmapBuffer( GL_ELEMENT_ARRAY_BUFFER );
228  glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
229  Unmap();
230  glDeleteBuffers( 1, &m_glBufferHandle );
231 
232  // Switch to the new vertex buffer
233  m_glBufferHandle = newBuffer;
234  Map();
235  checkGlError( "switching buffers during defragmentation" );
236 
237 #ifdef __WXDEBUG__
238  totalTime.Stop();
239 
240  wxLogTrace( "GAL_CACHED_CONTAINER_GPU",
241  "Defragmented container storing %d vertices / %.1f ms",
242  m_currentSize - m_freeSpace, totalTime.msecs() );
243 #endif /* __WXDEBUG__ */
244 
245  m_freeSpace += ( aNewSize - m_currentSize );
246  m_currentSize = aNewSize;
247 
248  // Now there is only one big chunk of free memory
249  m_freeChunks.clear();
250  m_freeChunks.insert( std::make_pair( m_freeSpace, m_currentSize - m_freeSpace ) );
251 
252  return true;
253 }
void Stop()
save the time when this function was called, and set the counter stane to stop
Definition: profile.h:82
unsigned int usedSpace() const
Function usedSpace() returns size of the used memory space.
bool m_useCopyBuffer
Flag saying whether it is safe to use glCopyBufferSubData
Class CAIRO_GAL is the cairo implementation of the graphics abstraction layer.
Definition: class_module.h:58
Data structure for vertices {X,Y,Z,R,G,B,A,shader&param}
Definition: vertex_common.h:50
Class to control vertex container and GPU with possibility of emulating old-style OpenGL 1...
void defragment(VERTEX *aTarget)
Transfers all stored data to a new buffer, removing empty spaces between the data chunks in the conta...
unsigned int m_glBufferHandle
Vertex buffer handle
unsigned int m_currentSize
Current container size, expressed in vertices
int checkGlError(const std::string &aInfo, bool aThrow)
Checks if one of recent OpenGL operations has failed.
Definition: utils.cpp:30
CACHED_CONTAINER_GPU(unsigned int aSize=DEFAULT_SIZE)
bool m_isMapped
Flag saying if vertex buffer is currently mapped
The class PROF_COUNTER is a small class to help profiling.
Definition: profile.h:45
VERTEX * m_vertices
Actual storage memory
bool IsMapped() const override
Returns true if vertex buffer is currently mapped.
ITEMS m_items
Stored VERTEX_ITEMs
VERTEX_ITEM * m_item
Currently modified item
unsigned int m_freeSpace
Free space left in the container, expressed in vertices
Class to store VERTEX instances with caching.
void setOffset(unsigned int aOffset)
Function SetOffset() Sets data offset in the container.
Definition: vertex_item.h:87
unsigned int GetOffset() const
Function GetOffset() Returns data offset in the container.
Definition: vertex_item.h:66
bool defragmentResizeMemcpy(unsigned int aNewSize)
FREE_CHUNK_MAP m_freeChunks
Stores size & offset of free chunks.
double msecs() const
Definition: profile.h:124
Class to handle an item held in a container.
bool defragmentResize(unsigned int aNewSize) override
Function defragmentResize() removes empty spaces between chunks and optionally resizes the container...
static constexpr size_t VERTEX_SIZE
Definition: vertex_common.h:57
unsigned int GetSize() const
Function GetSize() Returns information about number of vertices stored.
Definition: vertex_item.h:56