¿Son demasiadas las líneas de código push / pop para cada estado previo al dibujo?

Estoy tratando de simplificar la gestión de charts vectoriales en XNA; actualmente incorporando la preservación del estado. Las líneas 2X de código push / pop para X estados se sienten como demasiadas, y se siente mal tener 2 líneas de código que se vean idénticas, excepto una siendo push () y la otra siendo pop ().

El objective es erradicar esta repetitividad, y esperaba hacerlo al crear una interfaz en la que un cliente puede dar class / estructura refs en la que quiere restaurar después de las llamadas de representación.

También tenga en count que muchos progtwigdores principiantes usarán esto, por lo que forzar expresiones lambda u otras características avanzadas de C # para ser usadas en el código del cliente no es una buena idea.


Intenté lograr mi objective utilizando la class Ptr de Daniel Earwicker :

public class Ptr<T> { Func<T> getter; Action<T> setter; public Ptr(Func<T> g, Action<T> s) { getter = g; setter = s; } public T Deref { get { return getter(); } set { setter(value); } } } 

un método de extensión:

  //doesn't work for structs since this is just syntatic sugar public static Ptr<T> GetPtr <T> (this T obj) { return new Ptr<T>( ()=> obj, v=> obj=v ); } 

y una function de inserción:

  //returns a Pop Action for later calling public static Action Push <T> (ref T structure) where T: struct { T pushedValue = structure; //copies the struct data Ptr<T> p = structure.GetPtr(); return new Action( ()=> {p.Deref = pushedValue;} ); } 

Sin embargo, esto no funciona como se indica en el código.

¿Cómo puedo lograr mi objective?


Ejemplo de código a refactorizar:

  protected override void RenderLocally (GraphicsDevice device) { if (!(bool)isCompiled) {Compile();} //TODO: make sure state settings don't implicitly delete any buffers/resources RasterizerState oldRasterState = device.RasterizerState; DepthFormat oldFormat = device.PresentationParameters.DepthStencilFormat; DepthStencilState oldBufferState = device.DepthStencilState; { //Rendering code } device.RasterizerState = oldRasterState; device.DepthStencilState = oldBufferState; device.PresentationParameters.DepthStencilFormat = oldFormat; } 

Su function Push<T> no hará lo que usted quiere que haga. El Ptr<T> que creas dentro solo es accesible dentro de esa function. De hecho, espero que el comstackdor de C # optimice esa llamada ya que es efectivamente un noop.

La forma de resolver este problema es hacer todas sus posibles classs de datos de estado en lugar de estructuras. Las estructuras son una cosa muy única en C #. Deben ser absolutamente inmutables, datos bajo todas las circunstancias imaginables. Útil para asociar rápidamente un grupo de valores o implementar types numéricos como Vectores y valores especiales de tipo ID.

La solución rápida es implementar Push de esta manera:

 //returns a Pop Action for later calling public static Action Push<T>(Ptr<T> pt) where T: struct { T pushedValue = pt.Deref; //copies the struct data return new Action( ()=> {pt.Deref = pushedValue;} ); } 

Vea el progtwig de ejemplo aquí: http://pastie.org/4925186

Sus clientes deben tener conocimiento de Ptr para que funcione. Para hacer la transición de la tierra de valor-valor a la tierra de tipo de reference en estas funciones Push / Pop, debe pasar alnetworkingedor de la Ptr.

Ahora que se dice, mencionaste que esto debería ser para principiantes. ¿Por qué los haces pensar en pointers en C #? O tal vez no, pero eso es una consecuencia de este enfoque.

Si realmente vas a estar presionando y haciendo estallar el estado, ¿por qué no mantener una stack real? (O la list que usa con la semántica de Stack). ¿O tal vez simplemente copie los datos del usuario a un estado temporal antes de procesar, dejando intactos sus propios datos? Sin ver cómo su código usaría esta característica, es difícil para mí recomendar un buen enfoque.

Puedo ver que a algunos principiantes les gusta ver tanto Push () y Pop () en su código, ya que pueden comprender mejor lo que está sucediendo en lugar de asumir que todo es magia.

Gran parte de OpenGL es básicamente una máquina de estado. Lo importante es que en cada punto donde se produce el dibujo, se establece el estado correcto para todos los valores; que no queda un viejo y sobrante estado inválido.

El código de muestra generalmente lo asegura al apagar inmediatamente todo después de usarlo, o mediante un sistema push / pop como el que menciona en la pregunta.

Los juegos reales generalmente implementan un administrador de estado de renderizado para ubicarse entre el motor de renderizado y OpenGL, para optimizar los cambios de estado que se envían a OpenGL. Por lo tanto, en lugar de implementar push / pop, el código de representación solicita las características específicas que desea del administrador de estado, le dice al gerente de estado que aplique los cambios y luego quita esas características, y no le dice al gerente de estado que se aplique a de -peticiones.

El flujo de código normalmente se ve así:

 m_state.SetBool( UsingVertexArray, true ); m_state.SetBool( UsingColorArray, true ); m_state.Flush(); ActuallyRender(); m_state.SetBool( UsingVertexArray, false ); m_state.SetBool( UsingColorArray, false ); 

El administrador de estado de renderizado mantiene su propio estado interno; conoce tanto la última configuration de OpenGL como los últimos valores solicitados. Cuando se invoca "Flush ()" (a veces llamado "Apply ()" o algún otro nombre), encuentra la configuration con los valores modificados, y solo informa a OpenGL sobre esos valores modificados.

Entonces, si el siguiente elemento después de nuestro código de ejemplo anterior vuelve a activar "UsingVertexArray" y "UsingColorArray", m_state.Flush () no hace nada, ya que no ha habido cambios en el estado solicitado en comparación con la última vez "m_state.Flush () "fue llamado.

Este tipo de enfoque tiene la ventaja de ser bastante simple de entender, al time que solo envía cambios de estado real a OpenGL; no enviar correo no deseado con activar y desactivar los mismos valores una y otra vez, cuando todo realmente quiere que se enciendan.