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 ),
55 m_glBufferHandle( -1 )
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_isMapped = true;
103}
104
105
107{
108 wxCHECK( IsMapped(), /*void*/ );
109
110 // This gets called from ~CACHED_CONTAINER_GPU. To avoid throwing an exception from
111 // the dtor, catch it here instead.
112 try
113 {
114 glUnmapBuffer( GL_ARRAY_BUFFER );
115 checkGlError( "unmapping vertices buffer", __FILE__, __LINE__ );
116 glBindBuffer( GL_ARRAY_BUFFER, 0 );
117 m_vertices = nullptr;
118 checkGlError( "unbinding vertices buffer", __FILE__, __LINE__ );
119 }
120 catch( const std::runtime_error& err )
121 {
122 wxLogError( wxT( "OpenGL did not shut down properly.\n\n%s" ), err.what() );
123 }
124
125 m_isMapped = false;
126}
127
128
129bool CACHED_CONTAINER_GPU::defragmentResize( unsigned int aNewSize )
130{
131 if( !m_useCopyBuffer )
132 return defragmentResizeMemcpy( aNewSize );
133
134 wxCHECK( IsMapped(), false );
135
136 wxLogTrace( traceGalCachedContainerGpu,
137 wxT( "Resizing & defragmenting container from %d to %d" ), m_currentSize,
138 aNewSize );
139
140 // No shrinking if we cannot fit all the data
141 if( usedSpace() > aNewSize )
142 return false;
143
144#ifdef KICAD_GAL_PROFILE
145 PROF_TIMER totalTime;
146#endif /* KICAD_GAL_PROFILE */
147
148 GLuint newBuffer;
149
150 // glCopyBufferSubData requires a buffer to be unmapped
151 glUnmapBuffer( GL_ARRAY_BUFFER );
152
153 // Create a new destination buffer
154 glGenBuffers( 1, &newBuffer );
155
156 // It would be best to use GL_COPY_WRITE_BUFFER here,
157 // but it is not available everywhere
158#ifdef KICAD_GAL_PROFILE
159 GLint eaBuffer = -1;
160 glGetIntegerv( GL_ELEMENT_ARRAY_BUFFER_BINDING, &eaBuffer );
161 wxASSERT( eaBuffer == 0 );
162#endif /* KICAD_GAL_PROFILE */
163 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, newBuffer );
164 glBufferData( GL_ELEMENT_ARRAY_BUFFER, aNewSize * VERTEX_SIZE, nullptr, GL_DYNAMIC_DRAW );
165 checkGlError( "creating buffer during defragmentation", __FILE__, __LINE__ );
166
167 ITEMS::iterator it, it_end;
168 int newOffset = 0;
169
170 // Defragmentation
171 for( it = m_items.begin(), it_end = m_items.end(); it != it_end; ++it )
172 {
173 VERTEX_ITEM* item = *it;
174 int itemOffset = item->GetOffset();
175 int itemSize = item->GetSize();
176
177 // Move an item to the new container
178 glCopyBufferSubData( GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, itemOffset * VERTEX_SIZE,
179 newOffset * VERTEX_SIZE, itemSize * VERTEX_SIZE );
180
181 // Update new offset
182 item->setOffset( newOffset );
183
184 // Move to the next free space
185 newOffset += itemSize;
186 }
187
188 // Move the current item and place it at the end
189 if( m_item->GetSize() > 0 )
190 {
191 glCopyBufferSubData( GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER,
192 m_item->GetOffset() * VERTEX_SIZE, newOffset * VERTEX_SIZE,
194
195 m_item->setOffset( newOffset );
196 m_chunkOffset = newOffset;
197 }
198
199 // Cleanup
200 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
201 glBindBuffer( GL_ARRAY_BUFFER, 0 );
202
203 // Previously we have unmapped the array buffer, now when it is also
204 // unbound, it may be officially marked as unmapped
205 m_isMapped = false;
206 glDeleteBuffers( 1, &m_glBufferHandle );
207
208 // Switch to the new vertex buffer
209 m_glBufferHandle = newBuffer;
210 Map();
211 checkGlError( "switching buffers during defragmentation", __FILE__, __LINE__ );
212
213#ifdef KICAD_GAL_PROFILE
214 totalTime.Stop();
215
216 wxLogTrace( traceGalCachedContainerGpu, "Defragmented container storing %d vertices / %.1f ms",
217 m_currentSize - m_freeSpace, totalTime.msecs() );
218#endif /* KICAD_GAL_PROFILE */
219
220 m_freeSpace += ( aNewSize - m_currentSize );
221 m_currentSize = aNewSize;
222
223 KI_TRACE( traceGalProfile, "VBO size %d used %d\n", m_currentSize, AllItemsSize() );
224
225 // Now there is only one big chunk of free memory
226 m_freeChunks.clear();
227 m_freeChunks.insert( std::make_pair( m_freeSpace, m_currentSize - m_freeSpace ) );
228
229 return true;
230}
231
232
234{
235 wxCHECK( IsMapped(), false );
236
237 wxLogTrace( traceGalCachedContainerGpu,
238 wxT( "Resizing & defragmenting container (memcpy) from %d to %d" ), m_currentSize,
239 aNewSize );
240
241 // No shrinking if we cannot fit all the data
242 if( usedSpace() > aNewSize )
243 return false;
244
245#ifdef KICAD_GAL_PROFILE
246 PROF_TIMER totalTime;
247#endif /* KICAD_GAL_PROFILE */
248
249 GLuint newBuffer;
250 VERTEX* newBufferMem;
251
252 // Create the destination buffer
253 glGenBuffers( 1, &newBuffer );
254
255 // It would be best to use GL_COPY_WRITE_BUFFER here,
256 // but it is not available everywhere
257#ifdef KICAD_GAL_PROFILE
258 GLint eaBuffer = -1;
259 glGetIntegerv( GL_ELEMENT_ARRAY_BUFFER_BINDING, &eaBuffer );
260 wxASSERT( eaBuffer == 0 );
261#endif /* KICAD_GAL_PROFILE */
262
263 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, newBuffer );
264 glBufferData( GL_ELEMENT_ARRAY_BUFFER, aNewSize * VERTEX_SIZE, nullptr, GL_DYNAMIC_DRAW );
265 newBufferMem = static_cast<VERTEX*>( glMapBuffer( GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY ) );
266 checkGlError( "creating buffer during defragmentation", __FILE__, __LINE__ );
267
268 defragment( newBufferMem );
269
270 // Cleanup
271 glUnmapBuffer( GL_ELEMENT_ARRAY_BUFFER );
272 glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
273 Unmap();
274 glDeleteBuffers( 1, &m_glBufferHandle );
275
276 // Switch to the new vertex buffer
277 m_glBufferHandle = newBuffer;
278 Map();
279 checkGlError( "switching buffers during defragmentation", __FILE__, __LINE__ );
280
281#ifdef KICAD_GAL_PROFILE
282 totalTime.Stop();
283
284 wxLogTrace( traceGalCachedContainerGpu, "Defragmented container storing %d vertices / %.1f ms",
285 m_currentSize - m_freeSpace, totalTime.msecs() );
286#endif /* KICAD_GAL_PROFILE */
287
288 m_freeSpace += ( aNewSize - m_currentSize );
289 m_currentSize = aNewSize;
290
291 KI_TRACE( traceGalProfile, "VBO size %d used: %d \n", m_currentSize, AllItemsSize() );
292
293 // Now there is only one big chunk of free memory
294 m_freeChunks.clear();
295 m_freeChunks.insert( std::make_pair( m_freeSpace, m_currentSize - m_freeSpace ) );
296
297 return true;
298}
299
300
302{
303 unsigned int size = 0;
304
305 for( const auto& item : m_items )
306 {
307 size += item->GetSize();
308 }
309
310 return size;
311}
312
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.
Class to store VERTEX instances with caching.
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...
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: color4d.cpp:247
static constexpr size_t VERTEX_SIZE
Definition: vertex_common.h:67
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.