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  wxString vendor( glGetString(GL_VENDOR) );
46 
47  // workaround for intel GPU drivers: diable glCopyBuffer, causes crashes/freezes on certain driver versions
48  if( vendor.Contains ( "Intel ") )
49  {
50  wxLogDebug("Disabling glCopyBuffer() on intel GPU\n");
51  m_useCopyBuffer = false;
52  }
53 
54 
55  glGenBuffers( 1, &m_glBufferHandle );
56  glBindBuffer( GL_ARRAY_BUFFER, m_glBufferHandle );
57  glBufferData( GL_ARRAY_BUFFER, m_currentSize * VERTEX_SIZE, NULL, GL_DYNAMIC_DRAW );
58  glBindBuffer( GL_ARRAY_BUFFER, 0 );
59  checkGlError( "allocating video memory for cached container" );
60 }
61 
62 
64 {
65  if( m_isMapped )
66  Unmap();
67 
68  glDeleteBuffers( 1, &m_glBufferHandle );
69 }
70 
71 
73 {
74  wxCHECK( !IsMapped(), /*void*/ );
75 
76  glBindBuffer( GL_ARRAY_BUFFER, m_glBufferHandle );
77  m_vertices = static_cast<VERTEX*>( glMapBuffer( GL_ARRAY_BUFFER, GL_READ_WRITE ) );
78 
79  if( checkGlError( "mapping vertices buffer" ) == GL_NO_ERROR )
80  m_isMapped = true;
81 }
82 
83 
85 {
86  wxCHECK( IsMapped(), /*void*/ );
87 
88  glUnmapBuffer( GL_ARRAY_BUFFER );
89  checkGlError( "unmapping vertices buffer" );
90  glBindBuffer( GL_ARRAY_BUFFER, 0 );
91  m_vertices = NULL;
92  checkGlError( "unbinding vertices buffer" );
93 
94  m_isMapped = false;
95 }
96 
97 
98 bool CACHED_CONTAINER_GPU::defragmentResize( unsigned int aNewSize )
99 {
100  if( !m_useCopyBuffer )
101  return defragmentResizeMemcpy( aNewSize );
102 
103  wxCHECK( IsMapped(), false );
104 
105  wxLogTrace( "GAL_CACHED_CONTAINER_GPU",
106  wxT( "Resizing & defragmenting container from %d to %d" ), m_currentSize, aNewSize );
107 
108  // No shrinking if we cannot fit all the data
109  if( usedSpace() > aNewSize )
110  return false;
111 
112 #ifdef __WXDEBUG__
113  PROF_COUNTER totalTime;
114 #endif /* __WXDEBUG__ */
115 
116  GLuint newBuffer;
117 
118  // glCopyBufferSubData requires a buffer to be unmapped
119  glUnmapBuffer( GL_ARRAY_BUFFER );
120 
121  // Create a new destination buffer
122  glGenBuffers( 1, &newBuffer );
123 
124  // It would be best to use GL_COPY_WRITE_BUFFER here,
125  // but it is not available everywhere
126 #ifdef __WXDEBUG__
127  GLint eaBuffer = -1;
128  glGetIntegerv( GL_ELEMENT_ARRAY_BUFFER_BINDING, &eaBuffer );
129  wxASSERT( eaBuffer == 0 );
130 #endif /* __WXDEBUG__ */
131  glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, newBuffer );
132  glBufferData( GL_ELEMENT_ARRAY_BUFFER, aNewSize * VERTEX_SIZE, NULL, GL_DYNAMIC_DRAW );
133  checkGlError( "creating buffer during defragmentation" );
134 
135  ITEMS::iterator it, it_end;
136  int newOffset = 0;
137 
138  // Defragmentation
139  for( it = m_items.begin(), it_end = m_items.end(); it != it_end; ++it )
140  {
141  VERTEX_ITEM* item = *it;
142  int itemOffset = item->GetOffset();
143  int itemSize = item->GetSize();
144 
145  // Move an item to the new container
146  glCopyBufferSubData( GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER,
147  itemOffset * VERTEX_SIZE, newOffset * VERTEX_SIZE, itemSize * VERTEX_SIZE );
148 
149  // Update new offset
150  item->setOffset( newOffset );
151 
152  // Move to the next free space
153  newOffset += itemSize;
154  }
155 
156  // Move the current item and place it at the end
157  if( m_item->GetSize() > 0 )
158  {
159  glCopyBufferSubData( GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER,
160  m_item->GetOffset() * VERTEX_SIZE, newOffset * VERTEX_SIZE,
161  m_item->GetSize() * VERTEX_SIZE );
162 
163  m_item->setOffset( newOffset );
164  m_chunkOffset = newOffset;
165  }
166 
167  // Cleanup
168  glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
169  glBindBuffer( GL_ARRAY_BUFFER, 0 );
170 
171  // Previously we have unmapped the array buffer, now when it is also
172  // unbound, it may be officially marked as unmapped
173  m_isMapped = false;
174  glDeleteBuffers( 1, &m_glBufferHandle );
175 
176  // Switch to the new vertex buffer
177  m_glBufferHandle = newBuffer;
178  Map();
179  checkGlError( "switching buffers during defragmentation" );
180 
181 #ifdef __WXDEBUG__
182  totalTime.Stop();
183 
184  wxLogTrace( "GAL_CACHED_CONTAINER_GPU",
185  "Defragmented container storing %d vertices / %.1f ms",
186  m_currentSize - m_freeSpace, totalTime.msecs() );
187 #endif /* __WXDEBUG__ */
188 
189  m_freeSpace += ( aNewSize - m_currentSize );
190  m_currentSize = aNewSize;
191 
192  // Now there is only one big chunk of free memory
193  m_freeChunks.clear();
194  m_freeChunks.insert( std::make_pair( m_freeSpace, m_currentSize - m_freeSpace ) );
195 
196  return true;
197 }
198 
199 
200 bool CACHED_CONTAINER_GPU::defragmentResizeMemcpy( unsigned int aNewSize )
201 {
202  wxCHECK( IsMapped(), false );
203 
204  wxLogTrace( "GAL_CACHED_CONTAINER_GPU",
205  wxT( "Resizing & defragmenting container (memcpy) from %d to %d" ),
206  m_currentSize, aNewSize );
207 
208  // No shrinking if we cannot fit all the data
209  if( usedSpace() > aNewSize )
210  return false;
211 
212 #ifdef __WXDEBUG__
213  PROF_COUNTER totalTime;
214 #endif /* __WXDEBUG__ */
215 
216  GLuint newBuffer;
217  VERTEX* newBufferMem;
218 
219  // Create the destination buffer
220  glGenBuffers( 1, &newBuffer );
221 
222  // It would be best to use GL_COPY_WRITE_BUFFER here,
223  // but it is not available everywhere
224 #ifdef __WXDEBUG__
225  GLint eaBuffer = -1;
226  glGetIntegerv( GL_ELEMENT_ARRAY_BUFFER_BINDING, &eaBuffer );
227  wxASSERT( eaBuffer == 0 );
228 #endif /* __WXDEBUG__ */
229  glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, newBuffer );
230  glBufferData( GL_ELEMENT_ARRAY_BUFFER, aNewSize * VERTEX_SIZE, NULL, GL_DYNAMIC_DRAW );
231  newBufferMem = static_cast<VERTEX*>( glMapBuffer( GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY ) );
232  checkGlError( "creating buffer during defragmentation" );
233 
234  defragment( newBufferMem );
235 
236  // Cleanup
237  glUnmapBuffer( GL_ELEMENT_ARRAY_BUFFER );
238  glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
239  Unmap();
240  glDeleteBuffers( 1, &m_glBufferHandle );
241 
242  // Switch to the new vertex buffer
243  m_glBufferHandle = newBuffer;
244  Map();
245  checkGlError( "switching buffers during defragmentation" );
246 
247 #ifdef __WXDEBUG__
248  totalTime.Stop();
249 
250  wxLogTrace( "GAL_CACHED_CONTAINER_GPU",
251  "Defragmented container storing %d vertices / %.1f ms",
252  m_currentSize - m_freeSpace, totalTime.msecs() );
253 #endif /* __WXDEBUG__ */
254 
255  m_freeSpace += ( aNewSize - m_currentSize );
256  m_currentSize = aNewSize;
257 
258  // Now there is only one big chunk of free memory
259  m_freeChunks.clear();
260  m_freeChunks.insert( std::make_pair( m_freeSpace, m_currentSize - m_freeSpace ) );
261 
262  return true;
263 }
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:56
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:63
unsigned int GetSize() const
Function GetSize() Returns information about number of vertices stored.
Definition: vertex_item.h:56