source: trunk/src/gl/gl_device.cc @ 311

Last change on this file since 311 was 303, checked in by epyon, 11 years ago
  • program is now handle-based
  • all device constructs are now handle-based and do not dynamically allocate
File size: 14.4 KB
Line 
1// Copyright (C) 2012-2013 Kornel Kisielewicz
2// This file is part of NV Libraries.
3// For conditions of distribution and use, see copyright notice in nv.hh
4
5#include "nv/gl/gl_device.hh"
6
7#include "nv/gl/gl_window.hh"
8#include "nv/logging.hh"
9#include "nv/lib/sdl.hh"
10#include "nv/lib/sdl_image.hh"
11#include "nv/gl/gl_enum.hh"
12#include "nv/lib/gl.hh"
13
14using namespace nv;
15
16window* gl_device::create_window( uint16 width, uint16 height, bool fullscreen )
17{
18        return new gl_window( this, width, height, fullscreen );
19}
20
21window* nv::gl_device::adopt_window( void* sys_w_handle, void* sys_dc )
22{
23        return new gl_window( this, sys_w_handle, sys_dc );
24}
25
26
27
28gl_device::gl_device()
29{
30        nv::load_sdl_library();
31        m_info = NULL;
32
33        if ( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_JOYSTICK ) < 0 )
34        {
35                NV_LOG( LOG_CRITICAL, "Video initialization failed: " << SDL_GetError( ) );
36                return; // TODO: Error report
37        }
38
39#if NV_SDL_VERSION == NV_SDL_12
40        m_info = SDL_GetVideoInfo( );
41
42        if ( !m_info )
43        {
44                NV_LOG( LOG_CRITICAL, "Video query failed: " << SDL_GetError( ) );
45                return; // TODO: Error report
46        }
47#endif
48
49}
50
51program gl_device::create_program( const string& vs_source, const string& fs_source )
52{
53        program result = m_programs.create();
54        gl_program_info* info = m_programs.get( result );
55
56        info->glid = glCreateProgram();
57        compile( info, vs_source, fs_source );
58        prepare_program( result );
59        return result;
60}
61
62// this is a temporary function that will be removed once we find a way to
63// pass binary file data around
64image_data* nv::gl_device::create_image_data( const std::string& filename )
65{
66        load_sdl_image_library();
67        SDL_Surface* image = IMG_Load( filename.c_str() );
68        if (!image)
69        {
70                NV_LOG( LOG_ERROR, "Image file " << filename.c_str() << " not found!" );
71                return nullptr;
72        }
73        // TODO: BGR vs RGB, single channel
74        assert( image->format->BytesPerPixel > 2 );
75        image_format format(image->format->BytesPerPixel == 3 ? RGB : RGBA, UBYTE );
76        image_data* data = new image_data( format, glm::ivec2( image->w, image->h ), (nv::uint8*)image->pixels );
77        return data;
78}
79
80uint32 gl_device::get_ticks()
81{
82        return SDL_GetTicks();
83}
84
85void gl_device::delay( uint32 ms )
86{
87        return SDL_Delay( ms );
88}
89
90gl_device::~gl_device()
91{
92        while ( m_vertex_arrays.size() > 0 )
93                release( m_vertex_arrays.get_handle(0) );
94        while ( m_textures.size() > 0 )
95                release( m_textures.get_handle(0) );
96        while ( m_buffers.size() > 0 )
97                release( m_buffers.get_handle(0) );
98        while ( m_programs.size() > 0 )
99                release( m_programs.get_handle(0) );
100
101        SDL_Quit();
102}
103
104nv::texture nv::gl_device::create_texture( ivec2 size, image_format aformat, sampler asampler, void* data /*= nullptr */ )
105{
106        unsigned glid = 0;
107        glGenTextures( 1, &glid );
108
109        glBindTexture( GL_TEXTURE_2D, glid );
110
111        // Detect if mipmapping was requested
112        if (( asampler.filter_min != sampler::LINEAR && asampler.filter_min != sampler::NEAREST ) ||
113                ( asampler.filter_max != sampler::LINEAR && asampler.filter_max != sampler::NEAREST ))
114        {
115                glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
116        }
117
118        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (int)nv::sampler_filter_to_enum( asampler.filter_min ) );
119        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (int)nv::sampler_filter_to_enum( asampler.filter_max ) );
120        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (int)nv::sampler_wrap_to_enum( asampler.wrap_s) );
121        glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (int)nv::sampler_wrap_to_enum( asampler.wrap_t) );
122
123        if (data)
124        {
125                glTexImage2D( GL_TEXTURE_2D, 0, (GLint)nv::image_format_to_enum(aformat.format), size.x, size.y, 0, nv::image_format_to_enum(aformat.format), nv::datatype_to_gl_enum(aformat.type), data );
126        }
127
128        glBindTexture( GL_TEXTURE_2D, 0 );
129
130        texture result = m_textures.create();
131        gl_texture_info* info = m_textures.get( result );
132        info->format  = aformat;
133        info->sampler = asampler;
134        info->size    = size;
135        info->glid    = glid;
136        return result;
137}
138
139void nv::gl_device::release( texture t )
140{
141        gl_texture_info* info = m_textures.get( t );
142        if ( info )
143        {
144                if ( info->glid != 0 )
145                {
146                        glDeleteTextures( 1, &(info->glid) );
147                }
148                m_textures.destroy( t );
149        }
150}
151
152void nv::gl_device::release( buffer b )
153{
154        gl_buffer_info* info = m_buffers.get( b );
155        if ( info )
156        {
157                if ( info->glid != 0 )
158                {
159                        glDeleteBuffers( 1, &(info->glid) );
160                }
161                m_buffers.destroy( b );
162        }
163}
164
165const texture_info* nv::gl_device::get_texture_info( texture t ) const
166{
167        return m_textures.get( t );
168}
169
170nv::buffer nv::gl_device::create_buffer( buffer_type type, buffer_hint hint, size_t size, const void* source /*= nullptr */ )
171{
172        unsigned glid   = 0;
173        unsigned glenum = buffer_type_to_enum( type );
174        glGenBuffers( 1, &glid );
175
176        glBindBuffer( glenum, glid );
177        glBufferData( glenum, (GLsizeiptr)size, source, buffer_hint_to_enum( hint ) );
178        glBindBuffer( glenum, 0 );
179
180        buffer result = m_buffers.create();
181        gl_buffer_info* info = m_buffers.get( result );
182        info->type = type;
183        info->hint = hint;
184        info->size = size;
185        info->glid = glid;
186        return result;
187}
188
189const buffer_info* nv::gl_device::get_buffer_info( buffer t ) const
190{
191        return m_buffers.get( t );
192}
193
194nv::vertex_array nv::gl_device::create_vertex_array()
195{
196        vertex_array result = m_vertex_arrays.create();
197        vertex_array_info* info = m_vertex_arrays.get( result );
198        info->count       = 0;
199        info->index       = buffer();
200        info->index_owner = false;
201        info->index_type  = USHORT;
202        return result;
203}
204
205void nv::gl_device::release( vertex_array va )
206{
207        vertex_array_info* info = m_vertex_arrays.get( va );
208        if ( info )
209        {
210                for ( uint32 i = 0; i < info->count; ++i )
211                {
212                        if ( info->attr[i].owner ) release( info->attr[i].vbuffer );
213                }
214                if ( info->index.is_valid() && info->index_owner) release( info->index );
215                m_vertex_arrays.destroy( va );
216        }
217}
218
219void nv::gl_device::release( program p )
220{
221        gl_program_info* info = m_programs.get( p );
222        if ( info )
223        {
224                for ( auto& i : info->m_uniform_map )
225                        delete i.second;
226
227                glDetachShader( info->glid, info->glidv );
228                glDetachShader( info->glid, info->glidf );
229                glDeleteShader( info->glidv );
230                glDeleteShader( info->glidf );
231                glDeleteProgram( info->glid );
232
233                m_programs.destroy( p );
234        }
235}
236
237const vertex_array_info* nv::gl_device::get_vertex_array_info( vertex_array va ) const
238{
239        return m_vertex_arrays.get( va );
240}
241
242vertex_array_info* nv::gl_device::get_vertex_array_info_mutable( vertex_array va )
243{
244        return m_vertex_arrays.get( va );
245}
246
247void nv::gl_device::prepare_program( program p )
248{
249        gl_program_info* info = m_programs.get( p );
250        if ( info )
251        {
252                auto& map  = get_uniform_factory();
253                auto& lmap = get_link_uniform_factory();
254
255                for ( auto& i : info->m_uniform_map )
256                {
257                        auto j = lmap.find( i.first );
258                        if ( j != lmap.end() )
259                        {
260                                j->second->set( i.second );
261                        }                       
262
263                        auto k = map.find( i.first );
264                        if ( k != map.end() )
265                        {
266                                info->m_engine_uniforms.push_back( k->second->create( i.second ) );
267                        }                               
268                }
269        }
270}
271
272uniform_base* nv::gl_device::get_uniform( program p, const string& name, bool fatal /*= true */ ) const
273{
274        const gl_program_info* info = m_programs.get( p );
275        {
276                uniform_map::const_iterator i = info->m_uniform_map.find( name );
277                if ( i != info->m_uniform_map.end() )
278                {
279                        return i->second;
280                }
281                if ( fatal )
282                {
283                        NV_LOG( LOG_ERROR, "Uniform '" << name << "' not found in program!" );
284                        NV_THROW( runtime_error, ( "Uniform '"+name+"' not found!" ) );
285                }
286        }
287        return nullptr;
288}
289
290int nv::gl_device::get_attribute_location( program p, const string& name, bool fatal /*= true */ ) const
291{
292        const gl_program_info* info = m_programs.get( p );
293        if ( info )
294        {
295                attribute_map::const_iterator i = info->m_attribute_map.find( name );
296                if ( i != info->m_attribute_map.end() )
297                {
298                        return i->second.location;
299                }
300                if ( fatal )
301                {
302                        NV_LOG( LOG_ERROR, "Attribute '" << name << "' not found in program!" );
303                        NV_THROW( runtime_error, ( "Attribute '"+name+"' not found!" ) );
304                }
305        }
306        return -1;
307}
308
309bool nv::gl_device::compile( gl_program_info* p, const string& vertex_program, const string& fragment_program )
310{
311        if (!compile( GL_VERTEX_SHADER,   vertex_program, p->glidv ))   { return false; }
312        if (!compile( GL_FRAGMENT_SHADER, fragment_program, p->glidf )) { return false; }
313
314        glBindAttribLocation( p->glid, static_cast<GLuint>( slot::POSITION   ), "nv_position"  );
315        glBindAttribLocation( p->glid, static_cast<GLuint>( slot::TEXCOORD   ), "nv_texcoord"  );
316        glBindAttribLocation( p->glid, static_cast<GLuint>( slot::NORMAL     ), "nv_normal"    );
317        glBindAttribLocation( p->glid, static_cast<GLuint>( slot::COLOR      ), "nv_color"     );
318        glBindAttribLocation( p->glid, static_cast<GLuint>( slot::TANGENT    ), "nv_tangent"   );
319        glBindAttribLocation( p->glid, static_cast<GLuint>( slot::BONEINDEX  ), "nv_boneindex" );
320        glBindAttribLocation( p->glid, static_cast<GLuint>( slot::BONEWEIGHT ), "nv_boneweight");
321
322        glAttachShader( p->glid, p->glidf );
323        glAttachShader( p->glid, p->glidv );
324        glLinkProgram( p->glid );
325
326        const uint32 buffer_size = 2048;
327        char buffer[ buffer_size ] = { 0 };
328        int length;
329        int status;
330
331        glGetProgramiv( p->glid, GL_LINK_STATUS, &status );
332        glGetProgramInfoLog( p->glid, buffer_size, &length, buffer );
333
334        NV_LOG( LOG_INFO, "Program #" << p->glid << (status == GL_FALSE ? " failed to compile!" : " compiled successfully.") );
335
336        if ( length > 0 )
337        {
338                NV_LOG( LOG_INFO, "Program #" << p->glid << " log: " << buffer );
339        }
340
341        if ( status == GL_FALSE )
342        {
343                return false;
344        }
345
346        glValidateProgram( p->glid );
347        glGetProgramiv( p->glid, GL_VALIDATE_STATUS, &status );
348
349        if ( status == GL_FALSE )
350        {
351                glGetProgramInfoLog( p->glid, buffer_size, &length, buffer );
352                NV_LOG( LOG_ERROR, "Program #" << p->glid << " validation error : " << buffer );
353                return false;
354        }
355        load_attributes( p );
356        load_uniforms( p );
357        return true;
358}
359
360void nv::gl_device::update_uniforms( gl_program_info* p )
361{
362        for ( uniform_map::iterator i = p->m_uniform_map.begin();       i != p->m_uniform_map.end(); ++i )
363        {
364                uniform_base* ubase = i->second;
365                if ( ubase->is_dirty() )
366                {
367                        int uloc = ubase->get_location();
368                        switch( ubase->get_type() )
369                        {
370                        case FLOAT          : glUniform1fv( uloc, ubase->get_length(), ((uniform< enum_to_type< FLOAT >::type >*)( ubase ))->get_value() ); break;
371                        case INT            : glUniform1iv( uloc, ubase->get_length(), ((uniform< enum_to_type< INT >::type >*)( ubase ))->get_value() ); break;
372                        case FLOAT_VECTOR_2 : glUniform2fv( uloc, ubase->get_length(), (GLfloat*)((uniform< enum_to_type< FLOAT_VECTOR_2 >::type >*)( ubase ))->get_value()); break;
373                        case FLOAT_VECTOR_3 : glUniform3fv( uloc, ubase->get_length(), (GLfloat*)((uniform< enum_to_type< FLOAT_VECTOR_3 >::type >*)( ubase ))->get_value()); break;
374                        case FLOAT_VECTOR_4 : glUniform4fv( uloc, ubase->get_length(), (GLfloat*)((uniform< enum_to_type< FLOAT_VECTOR_4 >::type >*)( ubase ))->get_value()); break;
375                        case INT_VECTOR_2   : glUniform2iv( uloc, ubase->get_length(), (GLint*)((uniform< enum_to_type< INT_VECTOR_2 >::type >*)( ubase ))->get_value()); break;
376                        case INT_VECTOR_3   : glUniform3iv( uloc, ubase->get_length(), (GLint*)((uniform< enum_to_type< INT_VECTOR_3 >::type >*)( ubase ))->get_value()); break;
377                        case INT_VECTOR_4   : glUniform4iv( uloc, ubase->get_length(), (GLint*)((uniform< enum_to_type< INT_VECTOR_4 >::type >*)( ubase ))->get_value()); break;
378                        case FLOAT_MATRIX_2 : glUniformMatrix2fv( uloc, ubase->get_length(), GL_FALSE, (GLfloat*)((uniform< enum_to_type< FLOAT_MATRIX_2 >::type >*)( ubase ))->get_value()); break;
379                        case FLOAT_MATRIX_3 : glUniformMatrix3fv( uloc, ubase->get_length(), GL_FALSE, (GLfloat*)((uniform< enum_to_type< FLOAT_MATRIX_3 >::type >*)( ubase ))->get_value()); break;
380                        case FLOAT_MATRIX_4 : glUniformMatrix4fv( uloc, ubase->get_length(), GL_FALSE, (GLfloat*)((uniform< enum_to_type< FLOAT_MATRIX_4 >::type >*)( ubase ))->get_value()); break;
381                        default : break; // error?
382                        }
383                        ubase->clean();
384                }
385        }
386}
387
388void nv::gl_device::load_attributes( gl_program_info* p )
389{
390        int params;
391        glGetProgramiv( p->glid, GL_ACTIVE_ATTRIBUTES, &params );
392
393        for ( unsigned i = 0; i < (unsigned)params; ++i )
394        {
395                int attr_nlen;
396                int attr_len;
397                unsigned attr_type;
398                char name_buffer[128];
399
400                glGetActiveAttrib( p->glid, i, 128, &attr_nlen, &attr_len, &attr_type, name_buffer );
401
402                string name( name_buffer, size_t(attr_nlen) );
403
404                // skip built-ins
405                if ( name.substr(0,3) == "gl_" ) continue;
406
407                int attr_loc = glGetAttribLocation( p->glid, name.c_str() );
408
409                attribute& attr = p->m_attribute_map[ name ];
410                attr.name     = name;
411                attr.location = attr_loc;
412                attr.type     = gl_enum_to_datatype( attr_type );
413                attr.length   = attr_len;
414        }
415}
416
417void nv::gl_device::load_uniforms( gl_program_info* p )
418{
419        int params;
420        glGetProgramiv( p->glid, GL_ACTIVE_UNIFORMS, &params );
421
422        for ( unsigned i = 0; i < size_t(params); ++i )
423        {
424                int uni_nlen;
425                int uni_len;
426                unsigned uni_type;
427                char name_buffer[128];
428
429                glGetActiveUniform( p->glid, i, 128, &uni_nlen, &uni_len, &uni_type, name_buffer );
430
431                string name( name_buffer, size_t(uni_nlen) );
432
433                // skip built-ins
434                if ( name.substr(0,3) == "gl_" ) continue;
435
436                int uni_loc = glGetUniformLocation( p->glid, name.c_str() );
437                datatype utype = gl_enum_to_datatype( uni_type );
438
439                // check for array
440                string::size_type arrchar = name.find('[');
441                if ( arrchar != string::npos )
442                {
443                        name = name.substr( 0, arrchar );
444                }
445
446                uniform_base* u = uniform_base::create( utype, name, uni_loc, uni_len );
447                NV_ASSERT( u, "Unknown uniform type!" );
448                p->m_uniform_map[ name ] = u;
449        }
450}
451
452bool nv::gl_device::compile( uint32 sh_type, const std::string& shader_code, unsigned& glid )
453{
454        glid = glCreateShader( sh_type );
455
456        const char* pc = shader_code.c_str();
457
458        glShaderSource( glid,   1, &pc, 0 );
459        glCompileShader( glid );
460
461        const uint32 buffer_size = 1024;
462        char buffer[ buffer_size ] = { 0 };
463        int length;
464        int compile_ok = GL_FALSE;
465        glGetShaderiv(glid, GL_COMPILE_STATUS, &compile_ok);
466        glGetShaderInfoLog( glid, buffer_size, &length, buffer );
467
468        if ( length > 0 )
469        {
470                if ( compile_ok == 0 )
471                {
472                        NV_LOG( LOG_ERROR, "Shader #" << glid << " error: " << buffer );
473                }
474                else
475                {
476                        NV_LOG( LOG_INFO, "Shader #" << glid << " compiled successfully: " << buffer );
477                }
478        }
479        else
480        {
481                NV_LOG( LOG_INFO, "Shader #" << glid << " compiled successfully." );
482        }
483        return compile_ok != 0;
484
485}
486
487
Note: See TracBrowser for help on using the repository browser.