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