KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * @author Maciej Suminski <[email protected]>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, you may find one here:
21 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22 * or you may search the http://www.gnu.org website for the version 2 license,
23 * or you may write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25 */
26
30#include <gal/opengl/shader.h>
31#include <gal/opengl/utils.h>
32
33#include <wx/log.h>
34
35#include <list>
36
37#include <core/profile.h>
38#include <trace_helpers.h>
39
40using namespace KIGFX;
41
49static const wxChar* const traceGalCachedContainerGpu = wxT( "KICAD_GAL_CACHED_CONTAINER_GPU" );
50
51
53 CACHED_CONTAINER( aSize ),
54 m_isMapped( false ),
56{
57 m_useCopyBuffer = GLEW_ARB_copy_buffer;
58
59 wxString vendor( glGetString( GL_VENDOR ) );
60
61 // workaround for intel GPU drivers:
62 // disable glCopyBuffer, causes crashes/freezes on certain driver versions
63 // Note, Intel's GL_VENDOR string varies depending on GPU/driver generation
64 // But generally always starts with Intel at least
65 if( vendor.StartsWith( "Intel" ) || vendor.Contains( "etnaviv" ) )
66 {
67 m_useCopyBuffer = false;
68 }
69
70 KI_TRACE( traceGalProfile, "VBO initial size: %d\n", m_currentSize );
71
72 glGenBuffers( 1, &m_glBufferHandle );
73 glBindBuffer( GL_ARRAY_BUFFER, m_glBufferHandle );
74 glBufferData( GL_ARRAY_BUFFER, m_currentSize * VERTEX_SIZE, nullptr, GL_DYNAMIC_DRAW );
75 glBindBuffer( GL_ARRAY_BUFFER, 0 );
76 checkGlError( "allocating video memory for cached container", __FILE__, __LINE__ );
77}
78
79
81{
82 if( m_isMapped )
83 Unmap();
84
85 if( glDeleteBuffers )
86 glDeleteBuffers( 1, &m_glBufferHandle );
87}
88
89
91{
92 wxCHECK( !IsMapped(), /*void*/ );
93
94 // OpenGL version might suddenly stop being available in Windows when an RDP session is started
95 if( !glBindBuffer )
96 throw std::runtime_error( "OpenGL no longer available!" );
97
98 glBindBuffer( GL_ARRAY_BUFFER, m_glBufferHandle );
99 m_vertices = static_cast<VERTEX*>( glMapBuffer( GL_ARRAY_BUFFER, GL_READ_WRITE ) );
100
101 if( checkGlError( "mapping vertices buffer", __FILE__, __LINE__ ) == GL_NO_ERROR
102 && m_vertices != nullptr )
103 {
104 m_isMapped = true;
105 }
106 else
107 {
108 m_vertices = nullptr;
109 glBindBuffer( GL_ARRAY_BUFFER, 0 );
110 throw std::runtime_error( "Could not map vertex buffer: glMapBuffer returned null" );
111 }
112}
113
114
116{
117 wxCHECK( IsMapped(), /*void*/ );
118
119 // This gets called from ~CACHED_CONTAINER_GPU. To avoid throwing an exception from
120 // the dtor, catch it here instead.
121 try
122 {
123 glUnmapBuffer( GL_ARRAY_BUFFER );
124 checkGlError( "unmapping vertices buffer", __FILE__, __LINE__ );
125 glBindBuffer( GL_ARRAY_BUFFER, 0 );
126 m_vertices = nullptr;
127 checkGlError( "unbinding vertices buffer", __FILE__, __LINE__ );
128 }
129 catch( const std::runtime_error& err )
130 {
131 wxLogError( wxT( "OpenGL did not shut down properly.\n\n%s" ), err.what() );
132 }
133
134 m_isMapped = false;
135}
136
137
138bool CACHED_CONTAINER_GPU::defragmentResize( unsigned int aNewSize )
139{
140 if( !m_useCopyBuffer )
141 return defragmentResizeMemcpy( aNewSize );
142
143 wxCHECK( IsMapped(), false );
144
145 wxLogTrace( traceGalCachedContainerGpu,
146 wxT( "Resizing & defragmenting container from %d to %d" ), m_currentSize,
147 aNewSize );
148
149 // No shrinking if we cannot fit all the data
150 if( usedSpace() > aNewSize )
151 return false;
152
153#ifdef KICAD_GAL_PROFILE
154 PROF_TIMER totalTime;
155#endif /* KICAD_GAL_PROFILE */
156
157 GLuint newBuffer;
158
159 // glCopyBufferSubData requires a buffer to be unmapped
160 glUnmapBuffer( GL_ARRAY_BUFFER );
161
162 // Create a new destination buffer
163 glGenBuffers( 1, &newBuffer );
164
165 // It would be best to use GL_COPY_WRITE_BUFFER here,
166 // but it is not available everywhere
167#ifdef KICAD_GAL_PROFILE
168 GLint eaBuffer = -1;
169 glGetIntegerv( GL_ELEMENT_ARRAY_BUFFER_BINDING, &eaBuffer );
170 wxASSERT( eaBuffer == 0 );
171#endif /* KICAD_GAL_PROFILE */
172 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, newBuffer );
173 glBufferData( GL_ELEMENT_ARRAY_BUFFER, aNewSize * VERTEX_SIZE, nullptr, GL_DYNAMIC_DRAW );
174 checkGlError( "creating buffer during defragmentation", __FILE__, __LINE__ );
175
176 ITEMS::iterator it, it_end;
177 int newOffset = 0;
178
179 // Defragmentation
180 for( it = m_items.begin(), it_end = m_items.end(); it != it_end; ++it )
181 {
182 VERTEX_ITEM* item = *it;
183 int itemOffset = item->GetOffset();
184 int itemSize = item->GetSize();
185
186 // Move an item to the new container
187 glCopyBufferSubData( GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, itemOffset * VERTEX_SIZE,
188 newOffset * VERTEX_SIZE, itemSize * VERTEX_SIZE );
189
190 // Update new offset
191 item->setOffset( newOffset );
192
193 // Move to the next free space
194 newOffset += itemSize;
195 }
196
197 // Move the current item and place it at the end
198 if( m_item->GetSize() > 0 )
199 {
200 glCopyBufferSubData( GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER,
201 m_item->GetOffset() * VERTEX_SIZE, newOffset * VERTEX_SIZE,
202 m_item->GetSize() * VERTEX_SIZE );
203
204 m_item->setOffset( newOffset );
205 m_chunkOffset = newOffset;
206 }
207
208 // Cleanup
209 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
210 glBindBuffer( GL_ARRAY_BUFFER, 0 );
211
212 // Previously we have unmapped the array buffer, now when it is also
213 // unbound, it may be officially marked as unmapped
214 m_isMapped = false;
215 glDeleteBuffers( 1, &m_glBufferHandle );
216
217 // Switch to the new vertex buffer
218 m_glBufferHandle = newBuffer;
219
220 try
221 {
222 Map();
223 }
224 catch( const std::runtime_error& )
225 {
226 // Map() failed, likely due to glMapBuffer returning null.
227 // The buffer is valid but we can't map it.
228 return false;
229 }
230
231 checkGlError( "switching buffers during defragmentation", __FILE__, __LINE__ );
232
233#ifdef KICAD_GAL_PROFILE
234 totalTime.Stop();
235
236 wxLogTrace( traceGalCachedContainerGpu, "Defragmented container storing %d vertices / %.1f ms",
237 m_currentSize - m_freeSpace, totalTime.msecs() );
238#endif /* KICAD_GAL_PROFILE */
239
240 m_freeSpace += ( aNewSize - m_currentSize );
241 m_currentSize = aNewSize;
242
243 KI_TRACE( traceGalProfile, "VBO size %d used %d\n", m_currentSize, AllItemsSize() );
244
245 // Now there is only one big chunk of free memory
246 m_freeChunks.clear();
247 m_freeChunks.insert( std::make_pair( m_freeSpace, m_currentSize - m_freeSpace ) );
248
249 return true;
250}
251
252
254{
255 wxCHECK( IsMapped(), false );
256
257 wxLogTrace( traceGalCachedContainerGpu,
258 wxT( "Resizing & defragmenting container (memcpy) from %d to %d" ), m_currentSize,
259 aNewSize );
260
261 // No shrinking if we cannot fit all the data
262 if( usedSpace() > aNewSize )
263 return false;
264
265#ifdef KICAD_GAL_PROFILE
266 PROF_TIMER totalTime;
267#endif /* KICAD_GAL_PROFILE */
268
269 GLuint newBuffer;
270 VERTEX* newBufferMem;
271
272 // Create the destination buffer
273 glGenBuffers( 1, &newBuffer );
274
275 // It would be best to use GL_COPY_WRITE_BUFFER here,
276 // but it is not available everywhere
277#ifdef KICAD_GAL_PROFILE
278 GLint eaBuffer = -1;
279 glGetIntegerv( GL_ELEMENT_ARRAY_BUFFER_BINDING, &eaBuffer );
280 wxASSERT( eaBuffer == 0 );
281#endif /* KICAD_GAL_PROFILE */
282
283 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, newBuffer );
284 glBufferData( GL_ELEMENT_ARRAY_BUFFER, aNewSize * VERTEX_SIZE, nullptr, GL_DYNAMIC_DRAW );
285 newBufferMem = static_cast<VERTEX*>( glMapBuffer( GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY ) );
286 checkGlError( "creating buffer during defragmentation", __FILE__, __LINE__ );
287
288 if( newBufferMem == nullptr )
289 {
290 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
291 glDeleteBuffers( 1, &newBuffer );
292 return false;
293 }
294
295 defragment( newBufferMem );
296
297 // Cleanup
298 glUnmapBuffer( GL_ELEMENT_ARRAY_BUFFER );
299 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
300 Unmap();
301 glDeleteBuffers( 1, &m_glBufferHandle );
302
303 // Switch to the new vertex buffer
304 m_glBufferHandle = newBuffer;
305
306 try
307 {
308 Map();
309 }
310 catch( const std::runtime_error& )
311 {
312 // Map() failed, likely due to glMapBuffer returning null.
313 // The buffer is valid but we can't map it.
314 return false;
315 }
316
317 checkGlError( "switching buffers during defragmentation", __FILE__, __LINE__ );
318
319#ifdef KICAD_GAL_PROFILE
320 totalTime.Stop();
321
322 wxLogTrace( traceGalCachedContainerGpu, "Defragmented container storing %d vertices / %.1f ms",
323 m_currentSize - m_freeSpace, totalTime.msecs() );
324#endif /* KICAD_GAL_PROFILE */
325
326 m_freeSpace += ( aNewSize - m_currentSize );
327 m_currentSize = aNewSize;
328
329 KI_TRACE( traceGalProfile, "VBO size %d used: %d \n", m_currentSize, AllItemsSize() );
330
331 // Now there is only one big chunk of free memory
332 m_freeChunks.clear();
333 m_freeChunks.insert( std::make_pair( m_freeSpace, m_currentSize - m_freeSpace ) );
334
335 return true;
336}
337
338
340{
341 unsigned int size = 0;
342
343 for( const auto& item : m_items )
344 {
345 size += item->GetSize();
346 }
347
348 return size;
349}
350
void Map() override
Finish the vertices updates stage.
bool m_isMapped
Vertex buffer handle.
virtual unsigned int AllItemsSize() const override
bool IsMapped() const override
Prepare the container for vertices updates.
bool defragmentResizeMemcpy(unsigned int aNewSize)
Flag saying if vertex buffer is currently mapped.
void Unmap() override
Finish the vertices updates stage.
CACHED_CONTAINER_GPU(unsigned int aSize=DEFAULT_SIZE)
bool defragmentResize(unsigned int aNewSize) override
Remove empty spaces between chunks and optionally resizes the container.
unsigned int m_glBufferHandle
Flag saying whether it is safe to use glCopyBufferSubData.
unsigned int m_chunkOffset
Maximal vertex index number stored in the container.
VERTEX_ITEM * m_item
Properties of currently modified chunk & item.
FREE_CHUNK_MAP m_freeChunks
Stored VERTEX_ITEMs.
void defragment(VERTEX *aTarget)
Transfer all stored data to a new buffer, removing empty spaces between the data chunks in the contai...
CACHED_CONTAINER(unsigned int aSize=DEFAULT_SIZE)
ITEMS m_items
Currently modified item.
unsigned int m_currentSize
Store the initial size, so it can be resized to this on Clear()
unsigned int m_freeSpace
Current container size, expressed in vertices.
unsigned int usedSpace() const
Return size of the used memory space.
void setOffset(unsigned int aOffset)
Set data offset in the container.
Definition vertex_item.h:84
unsigned int GetOffset() const
Return data offset in the container.
Definition vertex_item.h:68
unsigned int GetSize() const
Return information about number of vertices stored.
Definition vertex_item.h:58
A small class to help profiling.
Definition profile.h:49
void Stop()
Save the time when this function was called, and set the counter stane to stop.
Definition profile.h:88
double msecs(bool aSinceLast=false)
Definition profile.h:149
static const wxChar *const traceGalCachedContainerGpu
Flag to enable debug output of the GAL OpenGL GPU cached container.
const wxChar *const traceGalProfile
Flag to enable debug output of GAL performance profiling.
The Cairo implementation of the graphics abstraction layer.
Definition eda_group.h:33
static constexpr size_t VERTEX_SIZE
wxLogTrace helper definitions.
#define KI_TRACE(aWhat,...)
int checkGlError(const std::string &aInfo, const char *aFile, int aLine, bool aThrow)
Check if a recent OpenGL operation has failed.
Definition utils.cpp:45
Class to handle an item held in a container.