// Copyright (C) 2012-2013 ChaosForge / Kornel Kisielewicz // http://chaosforge.org/ // // This file is part of NV Libraries. // For conditions of distribution and use, see copyright notice in nv.hh /** * @file device.hh * @author Kornel Kisielewicz epyon@chaosforge.org * @brief Device class */ #ifndef NV_DEVICE_HH #define NV_DEVICE_HH #include #include #include #include #include #include namespace nv { class window; class program; struct texture_tag {}; struct vertex_array_tag {}; struct buffer_tag {}; typedef handle< uint32, 16, 16, buffer_tag > buffer; typedef handle< uint32, 16, 16, texture_tag > texture; typedef handle< uint32, 16, 16, vertex_array_tag > vertex_array; struct sampler { enum filter { LINEAR, NEAREST, NEAREST_MIPMAP_NEAREST, LINEAR_MIPMAP_NEAREST, NEAREST_MIPMAP_LINEAR, LINEAR_MIPMAP_LINEAR }; enum wrap { CLAMP_TO_EDGE, CLAMP_TO_BORDER, MIRRORED_REPEAT, REPEAT }; filter filter_min; filter filter_max; wrap wrap_s; wrap wrap_t; sampler() : filter_min( LINEAR ), filter_max( LINEAR ), wrap_s( REPEAT ), wrap_t( REPEAT ) {} sampler( filter min, filter max, wrap s, wrap t ) : filter_min( min ), filter_max( max ), wrap_s( s ), wrap_t( t ) {} sampler( filter f, wrap w ) : filter_min( f ), filter_max( f ), wrap_s( w ), wrap_t( w ) {} }; struct buffer_info { buffer_type type; buffer_hint hint; size_t size; }; struct texture_info { ivec2 size; image_format format; sampler sampler; }; struct vertex_buffer_attribute { buffer vbuffer; datatype dtype; size_t components; size_t offset; size_t stride; slot location; bool owner; }; struct vertex_array_info { static const int MAX_ATTRIBUTES = 24; uint32 count; buffer index; bool index_owner; datatype index_type; vertex_buffer_attribute attr[MAX_ATTRIBUTES]; }; class device { friend class context; public: virtual window* create_window( uint16 width, uint16 height, bool fullscreen ) = 0; virtual window* adopt_window( void* sys_w_handle, void* sys_dc ) = 0; virtual program* create_program( const string& vs_source, const string& fs_source ) = 0; virtual buffer create_buffer( buffer_type type, buffer_hint hint, size_t size, const void* source = nullptr ) = 0; virtual image_data* create_image_data( const std::string& filename ) = 0; // temporary virtual texture create_texture( ivec2 size, image_format aformat, sampler asampler, void* data = nullptr ) = 0; virtual void release( texture ) = 0; virtual void release( buffer ) = 0; virtual const texture_info* get_texture_info( texture ) = 0; virtual const buffer_info* get_buffer_info( buffer ) = 0; virtual uint32 get_ticks() = 0; virtual void delay( uint32 ms ) = 0; virtual texture create_texture( image_data* data, sampler asampler ) { return create_texture( data->get_size(), data->get_format(), asampler, (void*)data->get_data() ); } virtual vertex_array create_vertex_array() { vertex_array result = m_vertex_arrays.create(); vertex_array_info* info = m_vertex_arrays.get( result ); info->count = 0; info->index = buffer(); info->index_owner = false; info->index_type = USHORT; return result; } virtual void release( vertex_array va ) { vertex_array_info* info = m_vertex_arrays.get( va ); if ( info ) { for ( uint32 i = 0; i < info->count; ++i ) { if ( info->attr[i].owner ) release( info->attr[i].vbuffer ); } if ( info->index.is_valid() && info->index_owner) release( info->index ); m_vertex_arrays.destroy( va ); } }; template < typename VTX, slot SLOT > void add_vertex_buffer_impl( vertex_array, buffer, const std::false_type& ) { } template < typename VTX, slot SLOT > void add_vertex_buffer_impl( vertex_array va, buffer vb, const std::true_type& ) { typedef vertex_slot_info< VTX, SLOT > vinfo; typedef datatype_traits< typename vinfo::value_type > dt_traits; add_vertex_buffer( va, SLOT, vb, type_to_enum< dt_traits::base_type >::type, dt_traits::size, vinfo::offset, sizeof( VTX ), false ); } template < typename VTX, slot SLOT > void add_vertex_buffer( vertex_array va, buffer vb ) { add_vertex_buffer_impl< VTX, SLOT >( va, vb, std::integral_constant< bool, vertex_has_slot< VTX, SLOT >::value >() ); } template < typename VTX > void add_vertex_buffers( vertex_array va, buffer vb ) { add_vertex_buffer< VTX, slot::POSITION > ( va, vb ); add_vertex_buffer< VTX, slot::TEXCOORD > ( va, vb ); add_vertex_buffer< VTX, slot::NORMAL > ( va, vb ); add_vertex_buffer< VTX, slot::TANGENT > ( va, vb ); add_vertex_buffer< VTX, slot::BONEINDEX > ( va, vb ); add_vertex_buffer< VTX, slot::BONEWEIGHT >( va, vb ); add_vertex_buffer< VTX, slot::COLOR > ( va, vb ); } void add_vertex_buffers( vertex_array va, buffer buf, const mesh_raw_channel* channel ) { for ( uint32 s = 0; s < channel->desc.count; ++s ) { const vertex_descriptor_slot& slot = channel->desc.slots[s]; const datatype_info& info = get_datatype_info(slot.etype); add_vertex_buffer( va, slot.vslot, buf, info.base , info.elements, slot.offset, channel->desc.size, false ); } } template < typename VTX > vertex_array create_vertex_array( const VTX* v, size_t count, buffer_hint hint ) { // TODO: vb will not be owned or freed! vertex_array va = create_vertex_array(); buffer vb = create_buffer( VERTEX_BUFFER, hint, count * sizeof( VTX ), v ); add_vertex_buffers< VTX >( va, vb ); return va; } template < typename VTX > vertex_array create_vertex_array( const std::vector< VTX >& data, buffer_hint hint ) { return create_vertex_array( data.data(), data.size(), hint ); } template < typename VTX, typename IDX > vertex_array create_vertex_array( const VTX* v, size_t vcount, const IDX* i, size_t icount, buffer_hint hint ) { vertex_array va = create_vertex_array( v, vcount, hint ); buffer ib = create_buffer( INDEX_BUFFER, hint, icount * sizeof( IDX ), i ); va->set_index_buffer( ib, type_to_enum< IDX >::type, true ); return va; } template < typename VTX, typename IDX > vertex_array create_vertex_array( const std::vector< VTX >& data, const std::vector< IDX >& idata, buffer_hint hint ) { return create_vertex_array( data.data(), data.size(), idata.data(), idata.size(), hint ); } void replace_vertex_buffer( vertex_array va, buffer vb, bool owner ) { vertex_array_info* info = m_vertex_arrays.get( va ); if ( info ) { for ( uint32 i = 0; i < info->count; ++i ) { vertex_buffer_attribute& vba = info->attr[i]; if ( vba.owner ) release( vba.vbuffer ); vba.vbuffer = vb; vba.owner = owner; } } } void replace_vertex_buffer( vertex_array va, buffer vb, slot location, bool owner ) { vertex_array_info* info = m_vertex_arrays.get( va ); if ( info ) { for ( uint32 i = 0; i < info->count; ++i ) { if ( info->attr[i].location == location ) { vertex_buffer_attribute& vba = info->attr[i]; if ( vba.owner ) release( vba.vbuffer ); vba.vbuffer = vb; vba.owner = owner; } } } } void update_attribute_offset( vertex_array va, slot location, size_t offset ) { vertex_array_info* info = m_vertex_arrays.get( va ); if ( info ) { for ( uint32 i = 0; i < info->count; ++i ) { if ( info->attr[i].location == location ) { info->attr[i].offset = offset; } } } } void set_index_buffer( vertex_array va, buffer b, datatype datatype, bool owner ) { vertex_array_info* info = m_vertex_arrays.get( va ); if ( info ) { if ( get_buffer_info( b )->type == INDEX_BUFFER ) { if (info->index.is_valid() && info->index_owner) release( info->index ); info->index = b; info->index_owner = owner; info->index_type = datatype; } } } void add_vertex_buffer( vertex_array va, slot location, buffer buf, datatype datatype, size_t components, size_t offset = 0, size_t stride = 0, bool owner = true ) { vertex_array_info* info = m_vertex_arrays.get( va ); if ( info ) { NV_ASSERT( info->count < vertex_array_info::MAX_ATTRIBUTES, "MAX_ATTRIBUTES reached!" ); vertex_buffer_attribute& p = info->attr[ info->count ]; p.vbuffer = buf; p.dtype = datatype; p.components = components; p.offset = offset; p.stride = stride; p.owner = owner; p.location = location; info->count++; } } buffer find_buffer( vertex_array va, slot location ) { vertex_array_info* info = m_vertex_arrays.get( va ); if ( info ) { for ( uint32 i = 0; i < info->count; ++i ) { if ( info->attr[i].location == location ) { return info->attr[i].vbuffer; } } } return buffer(); } // TODO: HINTS ARE DIFFERENT! vertex_array create_vertex_array( const mesh_data* data, buffer_hint hint ) { vertex_array va = create_vertex_array(); const std::vector< mesh_raw_channel* >& channels = data->get_raw_channels(); for ( uint32 ch = 0; ch < channels.size(); ++ch ) { const mesh_raw_channel* channel = channels[ch]; if ( channel->count > 0 ) { buffer_type type = channel->get_buffer_type(); buffer b = create_buffer( type, hint, channel->size(), channel->data ); // TODO: no if switch if ( type == INDEX_BUFFER ) { set_index_buffer( va, b, channel->desc.slots[0].etype, true ); } else { add_vertex_buffers( va, b, channel ); } } } return va; } virtual ~device() { // TODO: releases buffers via gl_context which is destoryed! while ( m_vertex_arrays.size() > 0 ) release( m_vertex_arrays.get_handle(0) ); } protected: entity_store< vertex_array_info, vertex_array > m_vertex_arrays; }; } // namespace nv #endif // NV_DEVICE_HH