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 = !!GLAD_GL_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#ifdef KICAD_GAL_PROFILE
71 wxLogTrace( traceGalProfile, "VBO initial size: %u", m_currentSize );
72#endif
73
74 glGenBuffers( 1, &m_glBufferHandle );
75 glBindBuffer( GL_ARRAY_BUFFER, m_glBufferHandle );
76 glBufferData( GL_ARRAY_BUFFER, m_currentSize * VERTEX_SIZE, nullptr, GL_DYNAMIC_DRAW );
77 glBindBuffer( GL_ARRAY_BUFFER, 0 );
78 checkGlError( "allocating video memory for cached container", __FILE__, __LINE__ );
79}
80
81
83{
84 if( m_isMapped )
85 Unmap();
86
87 if( glDeleteBuffers )
88 glDeleteBuffers( 1, &m_glBufferHandle );
89}
90
91
93{
94 wxCHECK( !IsMapped(), /*void*/ );
95
96 // OpenGL version might suddenly stop being available in Windows when an RDP session is started
97 if( !glBindBuffer )
98 throw std::runtime_error( "OpenGL no longer available!" );
99
100 glBindBuffer( GL_ARRAY_BUFFER, m_glBufferHandle );
101 m_vertices = static_cast<VERTEX*>( glMapBuffer( GL_ARRAY_BUFFER, GL_READ_WRITE ) );
102
103 if( checkGlError( "mapping vertices buffer", __FILE__, __LINE__ ) == GL_NO_ERROR
104 && m_vertices != nullptr )
105 {
106 m_isMapped = true;
107 }
108 else
109 {
110 m_vertices = nullptr;
111 glBindBuffer( GL_ARRAY_BUFFER, 0 );
112 throw std::runtime_error( "Could not map vertex buffer: glMapBuffer returned null" );
113 }
114}
115
116
118{
119 wxCHECK( IsMapped(), /*void*/ );
120
121 // This gets called from ~CACHED_CONTAINER_GPU. To avoid throwing an exception from
122 // the dtor, catch it here instead.
123 try
124 {
125 glUnmapBuffer( GL_ARRAY_BUFFER );
126 checkGlError( "unmapping vertices buffer", __FILE__, __LINE__ );
127 glBindBuffer( GL_ARRAY_BUFFER, 0 );
128 m_vertices = nullptr;
129 checkGlError( "unbinding vertices buffer", __FILE__, __LINE__ );
130 }
131 catch( const std::runtime_error& err )
132 {
133 wxLogError( wxT( "OpenGL did not shut down properly.\n\n%s" ), err.what() );
134 }
135
136 m_isMapped = false;
137}
138
139
140bool CACHED_CONTAINER_GPU::defragmentResize( unsigned int aNewSize )
141{
142 if( !m_useCopyBuffer )
143 return defragmentResizeMemcpy( aNewSize );
144
145 wxCHECK( IsMapped(), false );
146
147 wxLogTrace( traceGalCachedContainerGpu,
148 wxT( "Resizing & defragmenting container from %d to %d" ), m_currentSize,
149 aNewSize );
150
151 // No shrinking if we cannot fit all the data
152 if( usedSpace() > aNewSize )
153 return false;
154
155#ifdef KICAD_GAL_PROFILE
156 PROF_TIMER totalTime;
157#endif /* KICAD_GAL_PROFILE */
158
159 GLuint newBuffer;
160
161 // glCopyBufferSubData requires a buffer to be unmapped
162 glUnmapBuffer( GL_ARRAY_BUFFER );
163
164 // Create a new destination buffer
165 glGenBuffers( 1, &newBuffer );
166
167 // It would be best to use GL_COPY_WRITE_BUFFER here,
168 // but it is not available everywhere
169#ifdef KICAD_GAL_PROFILE
170 GLint eaBuffer = -1;
171 glGetIntegerv( GL_ELEMENT_ARRAY_BUFFER_BINDING, &eaBuffer );
172 wxASSERT( eaBuffer == 0 );
173#endif /* KICAD_GAL_PROFILE */
174 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, newBuffer );
175 glBufferData( GL_ELEMENT_ARRAY_BUFFER, aNewSize * VERTEX_SIZE, nullptr, GL_DYNAMIC_DRAW );
176 checkGlError( "creating buffer during defragmentation", __FILE__, __LINE__ );
177
178 ITEMS::iterator it, it_end;
179 int newOffset = 0;
180
181 // Defragmentation
182 for( it = m_items.begin(), it_end = m_items.end(); it != it_end; ++it )
183 {
184 VERTEX_ITEM* item = *it;
185 int itemOffset = item->GetOffset();
186 int itemSize = item->GetSize();
187
188 // Move an item to the new container
189 glCopyBufferSubData( GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, itemOffset * VERTEX_SIZE,
190 newOffset * VERTEX_SIZE, itemSize * VERTEX_SIZE );
191
192 // Update new offset
193 item->setOffset( newOffset );
194
195 // Move to the next free space
196 newOffset += itemSize;
197 }
198
199 // Move the current item and place it at the end
200 if( m_item->GetSize() > 0 )
201 {
202 glCopyBufferSubData( GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER,
203 m_item->GetOffset() * VERTEX_SIZE, newOffset * VERTEX_SIZE,
204 m_item->GetSize() * VERTEX_SIZE );
205
206 m_item->setOffset( newOffset );
207 m_chunkOffset = newOffset;
208 }
209
210 // Cleanup
211 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
212 glBindBuffer( GL_ARRAY_BUFFER, 0 );
213
214 // Previously we have unmapped the array buffer, now when it is also
215 // unbound, it may be officially marked as unmapped
216 m_isMapped = false;
217 glDeleteBuffers( 1, &m_glBufferHandle );
218
219 // Switch to the new vertex buffer
220 m_glBufferHandle = newBuffer;
221
222 try
223 {
224 Map();
225 }
226 catch( const std::runtime_error& )
227 {
228 // Map() failed, likely due to glMapBuffer returning null.
229 // The buffer is valid but we can't map it.
230 return false;
231 }
232
233 checkGlError( "switching buffers during defragmentation", __FILE__, __LINE__ );
234
235#ifdef KICAD_GAL_PROFILE
236 totalTime.Stop();
237
238 wxLogTrace( traceGalCachedContainerGpu, "Defragmented container storing %d vertices / %.1f ms",
239 m_currentSize - m_freeSpace, totalTime.msecs() );
240#endif /* KICAD_GAL_PROFILE */
241
242 m_freeSpace += ( aNewSize - m_currentSize );
243 m_currentSize = aNewSize;
244
245 wxLogTrace( traceGalProfile, "VBO size %d used %d", m_currentSize, AllItemsSize() );
246
247 // Now there is only one big chunk of free memory
248 m_freeChunks.clear();
249 m_freeChunks.insert( std::make_pair( m_freeSpace, m_currentSize - m_freeSpace ) );
250
251 return true;
252}
253
254
256{
257 wxCHECK( IsMapped(), false );
258
259 wxLogTrace( traceGalCachedContainerGpu,
260 wxT( "Resizing & defragmenting container (memcpy) from %d to %d" ), m_currentSize,
261 aNewSize );
262
263 // No shrinking if we cannot fit all the data
264 if( usedSpace() > aNewSize )
265 return false;
266
267#ifdef KICAD_GAL_PROFILE
268 PROF_TIMER totalTime;
269#endif /* KICAD_GAL_PROFILE */
270
271 GLuint newBuffer;
272 VERTEX* newBufferMem;
273
274 // Create the destination buffer
275 glGenBuffers( 1, &newBuffer );
276
277 // It would be best to use GL_COPY_WRITE_BUFFER here,
278 // but it is not available everywhere
279#ifdef KICAD_GAL_PROFILE
280 GLint eaBuffer = -1;
281 glGetIntegerv( GL_ELEMENT_ARRAY_BUFFER_BINDING, &eaBuffer );
282 wxASSERT( eaBuffer == 0 );
283#endif /* KICAD_GAL_PROFILE */
284
285 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, newBuffer );
286 glBufferData( GL_ELEMENT_ARRAY_BUFFER, aNewSize * VERTEX_SIZE, nullptr, GL_DYNAMIC_DRAW );
287 newBufferMem = static_cast<VERTEX*>( glMapBuffer( GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY ) );
288 checkGlError( "creating buffer during defragmentation", __FILE__, __LINE__ );
289
290 if( newBufferMem == nullptr )
291 {
292 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
293 glDeleteBuffers( 1, &newBuffer );
294 return false;
295 }
296
297 defragment( newBufferMem );
298
299 // Cleanup
300 glUnmapBuffer( GL_ELEMENT_ARRAY_BUFFER );
301 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
302 Unmap();
303 glDeleteBuffers( 1, &m_glBufferHandle );
304
305 // Switch to the new vertex buffer
306 m_glBufferHandle = newBuffer;
307
308 try
309 {
310 Map();
311 }
312 catch( const std::runtime_error& )
313 {
314 // Map() failed, likely due to glMapBuffer returning null.
315 // The buffer is valid but we can't map it.
316 return false;
317 }
318
319 checkGlError( "switching buffers during defragmentation", __FILE__, __LINE__ );
320
321#ifdef KICAD_GAL_PROFILE
322 totalTime.Stop();
323
324 wxLogTrace( traceGalCachedContainerGpu, "Defragmented container storing %d vertices / %.1f ms",
325 m_currentSize - m_freeSpace, totalTime.msecs() );
326#endif /* KICAD_GAL_PROFILE */
327
328 m_freeSpace += ( aNewSize - m_currentSize );
329 m_currentSize = aNewSize;
330
331 wxLogTrace( traceGalProfile, "VBO size %d used: %d", m_currentSize, AllItemsSize() );
332
333 // Now there is only one big chunk of free memory
334 m_freeChunks.clear();
335 m_freeChunks.insert( std::make_pair( m_freeSpace, m_currentSize - m_freeSpace ) );
336
337 return true;
338}
339
340
342{
343 unsigned int size = 0;
344
345 for( const auto& item : m_items )
346 {
347 size += item->GetSize();
348 }
349
350 return size;
351}
352
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:50
void Stop()
Save the time when this function was called, and set the counter stane to stop.
Definition profile.h:90
double msecs(bool aSinceLast=false)
Definition profile.h:151
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.
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.