// ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // gl_shader.cpp // Neal Binnendyk, nealabq -at- gmail, nealabq.com // Copyright (c) 2010. All rights reserved. // _______________________________________________________________________________________________ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // Concepts: // To use a type in a variant, it has to be a "bounded type", which has these requirements: // CopyConstructible [20.1.3]. // Destructor upholds the no-throw exception-safety guarantee. // Complete at the point of variant template instantiation. // // But we don't have a boost concept to capture all that. There is nothing like this: // BOOST_CONCEPT_ASSERT(( BoundedType< my_type > )); // // In fact, the boost concepts don't seem to work at all, at least not in boost 1-39. // These does not compile (syntax errors with parens) (experiment in global namespace): // // # include // BOOST_CONCEPT_ASSERT( (EqualityComparable< int >)); // // # include // BOOST_CONCEPT_ASSERT( (BidirectionalIterator< std::list< int >::iterator >)); // // Otherwise we could at least do this: // BOOST_CONCEPT_ASSERT(( boost::CopyConstructible< shader_type > )); // BOOST_CONCEPT_ASSERT(( boost::CopyConstructible< ARB::shader_type > )); // // BOOST_CONCEPT_ASSERT(( boost::CopyConstructible< shader_program_type > )); // BOOST_CONCEPT_ASSERT(( boost::CopyConstructible< ARB::shader_program_type > )); // // Owner objects and variants // The owner objects are single-owner, not shared-owner. They should NOT support copy -- // neither a copy ctor nor a copy assignment op. // // But a class has to support copy -- both ctor and assop -- in order to be used as part // of a boost variant. Without copy you can still create a variant instance, but only with // the default ctor, which sets the variant to the first class in the variant list. // // Without copy there is no way to change the class inside the variant. So in our case // it'll always be stuck as the first type (reset_type). // // I thought I could get around this by creating special ctor/assop members that looked // like copy but actually took other classes. Namely the tag classes: // shader_owner_type::reset_type // shader_program_owner_type::reset_type // But the variant code wasn't fooled. It requires an exact copy. // // I don't know if this is because of inherent restrictions in c++ or because the boost::variant // implementers did not implement this the way I'd like. // // There are several ways to disable copy. Normally we'd use both (1) and (3) from this list. // // 1 Private copy ctor/assop - compile-time error // In-class and friend methods can still access private methods. // // 2 Implementation with static assert failure - compile-time error // Only works for template classes. // // 3 No implementation - link-time error // Harder to track than a compile-time error. // // 4 Implementation with assert failure - run-time error // // 5 Implementation with assert that fails without special incantation. // Usually the incantation is a flag set by a wrapper. // Elaborate and complicated. Makes code harder to understand. // // 6 Unexpected implementation. // This is what we do. It's pretty weak. Perhaps it'd be better to not use variant // with these classes at all. // _______________________________________________________________________________________________ # include "all.h" # include "gl_shader.h" // _______________________________________________________________________________________________ // namespace gl_env { namespace shader { // _______________________________________________________________________________________________ /* ctor */ shader_program_owner_any_type:: shader_program_owner_any_type ( char const * p_src_vertex_shader , char const * p_src_fragment_shader ) : is_init_attempted_ ( false) , is_init_successful_ ( false) , p_vertex_shader_src_ ( p_src_vertex_shader) , vertex_shader_ ( ) , p_fragment_shader_src_ ( p_src_fragment_shader) , fragment_shader_ ( ) , shader_program_ ( ) { } // _______________________________________________________________________________________________ /* private visitor-functor type */ namespace /* anonymous */ { struct get_shader_program__visitor_functor_type // // Visitor functors are used with variant types. // This one extracts a shader_program_variant_type from a similar owner variant-type. // : public boost::static_visitor< shader_program_variant_type > { shader_program_variant_type operator ()( reset_type) const { return reset_type( ); } template< typename SHADER_PROGRAM_OWNER_TYPE > shader_program_variant_type operator ()( SHADER_PROGRAM_OWNER_TYPE const & shader_program_owner) const { return shader_program_owner; } };} /* end namespace anonymous */ shader_program_variant_type shader_program_owner_any_type:: get_shader_program__no_init( ) const { get_shader_program__visitor_functor_type functor; return boost::apply_visitor( functor, shader_program_); } // _______________________________________________________________________________________________ /* protected method */ void shader_program_owner_any_type:: init_private( ) { is_init_successful_ = ( global::are_shaders_supported( ) && create_vertex_shader__normal( ) && create_fragment_shader__normal( ) && create_shader_program__normal( ) ) || ( global::are_shaders_supported__ARB( ) && create_vertex_shader__ARB( ) && create_fragment_shader__ARB( ) && create_shader_program__ARB( ) ); if ( ! is_init_successful_ ) { shader_program_ = reset_type( ); fragment_shader_ = reset_type( ); vertex_shader_ = reset_type( ); } is_init_attempted_ = true; } void shader_program_owner_any_type:: uninit( ) { is_init_successful_ = false; is_init_attempted_ = false; shader_program_ = reset_type( ); fragment_shader_ = reset_type( ); vertex_shader_ = reset_type( ); } // _______________________________________________________________________________________________ /* protected method */ bool shader_program_owner_any_type:: create_vertex_shader__normal( ) { typedef shader_owner_type owner_type; vertex_shader_ = owner_type::reset_type( ); owner_type * const p_shader = boost::get< owner_type >( & vertex_shader_); if ( p_shader ) { if ( p_vertex_shader_src_ ) { p_shader->create_vertex_shader( p_vertex_shader_src_); } return true; } d_assert( false); return false; } /* protected method */ bool shader_program_owner_any_type:: create_fragment_shader__normal( ) { typedef shader_owner_type owner_type; fragment_shader_ = owner_type::reset_type( ); owner_type * const p_shader = boost::get< owner_type >( & fragment_shader_); if ( p_shader ) { if ( p_fragment_shader_src_ ) { p_shader->create_fragment_shader( p_fragment_shader_src_); } return true; } d_assert( false); return false; } /* protected method */ bool shader_program_owner_any_type:: create_shader_program__normal( ) { typedef shader_program_owner_type program_owner_type; typedef shader_owner_type sh_owner_type; shader_program_ = program_owner_type::reset_type( ); program_owner_type * const p_program = boost::get< program_owner_type >( & shader_program_ ); sh_owner_type * const p_vertex_sh = boost::get< sh_owner_type >( & vertex_shader_ ); sh_owner_type * const p_fragment_sh = boost::get< sh_owner_type >( & fragment_shader_); if ( p_program && p_vertex_sh && p_fragment_sh ) { p_program->create_shader_program( *p_vertex_sh, *p_fragment_sh); return true; } d_assert( false); return false; } // _______________________________________________________________________________________________ /* protected method */ bool shader_program_owner_any_type:: create_vertex_shader__ARB( ) { typedef ARB::shader_owner_type owner_type; vertex_shader_ = owner_type::reset_type( ); owner_type * const p_shader = boost::get< owner_type >( & vertex_shader_); if ( p_shader ) { if ( p_vertex_shader_src_ ) { p_shader->create_vertex_shader( p_vertex_shader_src_); } return true; } d_assert( false); return false; } /* protected method */ bool shader_program_owner_any_type:: create_fragment_shader__ARB( ) { typedef ARB::shader_owner_type owner_type; fragment_shader_ = owner_type::reset_type( ); owner_type * const p_shader = boost::get< owner_type >( & fragment_shader_); if ( p_shader ) { if ( p_fragment_shader_src_ ) { p_shader->create_fragment_shader( p_fragment_shader_src_); } return true; } d_assert( false); return false; } /* protected method */ bool shader_program_owner_any_type:: create_shader_program__ARB( ) { typedef ARB::shader_program_owner_type program_owner_type; typedef ARB::shader_owner_type sh_owner_type; shader_program_ = program_owner_type::reset_type( ); program_owner_type * const p_program = boost::get< program_owner_type >( & shader_program_ ); sh_owner_type * const p_vertex_sh = boost::get< sh_owner_type >( & vertex_shader_ ); sh_owner_type * const p_fragment_sh = boost::get< sh_owner_type >( & fragment_shader_); if ( p_program && p_vertex_sh && p_fragment_sh ) { p_program->create_shader_program( *p_vertex_sh, *p_fragment_sh); return true; } d_assert( false); return false; } // _______________________________________________________________________________________________ // _______________________________________________________________________________________________ /* static */ bool with_shader_program:: is_reset( shader_program_variant_type const & program) { return 0 != boost::get< reset_type >( & program); } bool with_shader_program:: has_restore( ) const { return ! is_reset( previous_program_shader_); } bool with_shader_program:: forget__no_restore( ) { if ( has_restore( ) ) { previous_program_shader_ = reset_type( ); d_assert( ! has_restore( )); return true; } return false; } bool with_shader_program:: restore( ) { if ( has_restore( ) ) { activate_private( previous_program_shader_); /* discard return */ previous_program_shader_ = reset_type( ); d_assert( ! has_restore( )); return true; } return false; } // _______________________________________________________________________________________________ /* ctor */ with_shader_program:: with_shader_program( ) : previous_program_shader_( ) { d_assert( ! has_restore( )); } /* ctor */ with_shader_program:: with_shader_program( shader_program_owner_any_type & program_owner) : previous_program_shader_( ) { d_assert( ! has_restore( )); d_verify( activate( program_owner)); } /* ctor */ with_shader_program:: with_shader_program( shader_program_owner_any_type const & program_owner) : previous_program_shader_( ) { d_assert( ! has_restore( )); d_verify( activate__no_init( program_owner)); } /* ctor */ with_shader_program:: with_shader_program( shader_program_variant_type const & program) : previous_program_shader_( ) { d_assert( ! has_restore( )); d_verify( activate( program)); } // _______________________________________________________________________________________________ bool with_shader_program:: activate__no_init( shader_program_owner_any_type const & program_owner) // { return activate( program_owner.get_shader_program__no_init( )); } bool with_shader_program:: activate( shader_program_owner_any_type & program_owner) // { return activate( program_owner.get_shader_program( )); } bool with_shader_program:: activate( shader_program_variant_type const & program) // // Returns true if this was called when (! has_restore( )). // This usually ends with has_restore( ) set true, unless is_reset( program). { if ( ! has_restore( ) ) { previous_program_shader_ = activate_private( program); // Usually has_restore( ) will be true here, but if is_reset( program) then // has_restore( ) will be false here. d_assert( has_restore( ) == ! is_reset( program)); return true; } // Don't save the returned value, the previously activated program. // When we restore we'll use previous_program_shader_, the previously active // program we encountered first. activate_private( program); return false; } // _______________________________________________________________________________________________ /* private visitor-functor type */ namespace /* anonymous */ { struct use_program__visitor_functor_type : public boost::static_visitor< shader_program_variant_type > { shader_program_variant_type operator ()( reset_type) const { return reset_type( ); } template< typename SHADER_PROGRAM_TYPE > shader_program_variant_type operator ()( SHADER_PROGRAM_TYPE const & shader_program) const { return shader_program.activate( ); } };} /* end namespace anonymous */ /* static, protected */ shader_program_variant_type with_shader_program:: activate_private( shader_program_variant_type const & shader_program_to_activate) { use_program__visitor_functor_type functor; return boost::apply_visitor( functor, shader_program_to_activate); } // _______________________________________________________________________________________________ // } /* end namespace shader */ } /* end namespace gl_env */ // _______________________________________________________________________________________________ // _______________________________________________________________________________________________ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // gl_shader.cpp - End of File // _______________________________________________________________________________________________ // |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||