Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Porqué Podría Ser...

Iniciado por Haddd, 26 de Mayo de 2004, 10:15:52 PM

« anterior - próximo »

Haddd

 Tengo un terreno formado por 260.000 vértices y 500.000 caras.

Tengo 2 tipos de render:

1. Todo a la tarjeta con DrawIndexedPrimitive

2. Por medio de un Quadtree, recorriendo el Quadtree y haciendo DrawIndexedPrimitive para cada nodo final. No uso para nada el cálculo de visibilidad.

Es decir, que tengo 2 métodos que dibujan un montón de triángulos. Uno todos de golpe y el otro que los manda todos, pero de paquete en paquete .

Utilizan el mismo VertexBuffer, lo único que cambia es el IndexBuffer, que en:

1. Es todo el terreno
2. Cada nodo final tiene las caras que están dentro del recuadro de Quadtree.

Bien, los fps son:

1. 89 fps
2. 120 fps

¡Alucinante! Lo primero y más alucinante es que vaya tan rápido, pero lo segundo más alucinante es que sea más rápido si envio los triángulos por paquetes.

¿A Alguien se le ocurre la razón?


fiero

 Yo no soy un experto en render con aceleradoras, y después de los test que hiciste hace tiempo sobre mandar la geometria poco a poco o toda de golpe, los resultados no fueron los mismos en diferentes ordenadores, así que todavía entiendo menos.
Pero parece lógico pensar que lo ideal (lo más rápido) es llegar a una especie de término medio, entre la velocidad de la memoria ram, la velocidad de la tarjeta y la velocidad de la cpu, y calcular el tamaño óptimo del paquete de triangulos a enviar en cada llamada a DrawIndexedPrimitive, según los tres parámetros.
Enviando tantos triangulos de golpe en el primer caso igual hace que DrawIndexedPrimitive se quede un poco bloqueada.

saludos
www.videopanoramas.com Videopanoramas 3D player

Thenend

 La verdad es que no tengo muy claras las razones concretas pero tiene que ver con el hardware. Dicen que para las tarjetas actuales, el tamaño óptimo de un vertex buffer es de entre 2 y 4 MB. Cuantos menos cambios de streams hagas mejor y por lo tanto se debería intentar tener buffers grandes. Pero llega un momento en el que si es un paquete muy grande, hay problemas para alojarlo en la memoria por culpa de la fragmentación. En cambio, si son grupos de tamaño mediano se pueden ir colocando en los huecos libres.

Creo que por ahí van los tiros.

Thenend

 En este pdf hablan un poco de eso:

http://developer.nvidia.com/attach/6731

Dicen que 100 triangulos debe ser el mínimo y 10000 el máximo. Lo óptimo serían unos 500. Y tiene un grafico mu majo.

Vamos que los 260.000 que le metias le sentaban como un tiro  :P  

Thenend

 mmm, ahora que releo tu post, usas el mismo vertexbuffer y cambias los indexbuffers así que lo de la memoria que decía yo no creo que sea. Igual tiene algo que ver con algún proceso de paralelización que tenga la tarjeta. Si solo le mandas un index buffer, igual solo puede procesar un polígono detrás de otro, pero si le mandas varios streams puede intentar procesarlos a la vez. ¿Creéis que esto tiene sentido?

BeRSeRKeR

 Bueno, como podríes leer en el hilo que ha abierto Mars sobre qué es lo que estamos haciendo actualmente, llevo bastante tiempo sin programar cosas de estas así que no sé si lo que voy a decir tiene sentido.

Si no recuerdo mal (y si las cosas no han cambiado), el método Present del dispositivo 3D no retorna hasta que la imagen no se ha renderizado por lo que supongo que si mandas muchos vértices de golpe, es posible que estés perdiendo el paralelismo entre GPU y CPU y ésta última tenga que estar esperando a la GPU. De ahí que si mandas los vértices poco a poco (el tamaño óptimo ya no sé por donde anda :lol:), la cosa va más rápida.

Saludos.
¡Si te buscan en nombre de la ley, huye en nombre de la libertad!!

Haddd

 No tiene sentido. TODO se hace en GPU así que no hay paralelismo.

_Grey

 Diria que esta a medio camino de lo que dice fiero, y lo que comenta BeRSeRKeR, sobre "paralelismo".

Es decir, cuando haces el DrawXX(), probablemente la tageta cree "parones" (hasta que dibuje los poligonos, el paso de estos por el bus AGP que no es precisamente lo mas rapido de nuestros ordenatos.....) y al hacerlo con varios DrawXX(), ganas ese paralelismo al que hace referenia BeRSeRKeR.

Claro que todo esto es mas bien "metafisica".... por que a primera vista parece algo raro, voto por lo del paralelismo de BeRSeRKeR, por que NO TODO lo hace la GPU.

Saludos.

Thenend

 A lo que yo me refería es a que las tarjetas gráficas tienen varias unidades de muchas cosas. Por ejemplo, la GeForce 6800 tiene 16 pixel pipelines y 6 vertex units. Si solo le mandas un stream de polígonos dejarás de aprovechar 15 pixel pipelines y 5 vertex units que podrían estar procesándo streams también.

Supongo que habrá mas motivos para lo del tamaño óptimo de los batches y para lo que le ocurre a Haddd pero esto podría ser uno ¿no?

Thenend

 Vale, supongo que lo de que no se aprovechan la paralelización de la tarjeta no tiene mucho sentido ¿Por qué no se van a poder mandar vertices del mismo stream a distintas unidades? Y con los pixels lo mismo.

Esto dicen en en FAQ de Microsoft:

CitarConcurrency. If you can arrange to perform rendering concurrently with other processing, then you will be taking full advantage of system performance. This goal can conflict with the goal of reducing renderstate changes. You need to strike a balance between batching to reduce state changes and pushing data out to the driver early to help achieve concurrency. Using multiple vertex buffers in round-robin fashion can help with concurrency.

Entonces supongo que el problema está al mandar paquetes muy grandes como un indexbuffer de 500.000x3=1.500.000 índices. Pero no entiendo por qué esto estropea la concurrencia o por qué hace que funcione mas lento  O_O

Thenend

 Sigo investigando  :P . Lo que he encontrado ahora es esto:

CitarVertex Buffer Locking problems - If you are not using vertex buffers correctly, the driver will
stall and wait for the hardware to finish using the buffer. . A "spin lock" is used to describe
when the CPU is wait for a shared resource. When the application is requesting a shared
resource (via a lock) and the GPU is using that resource, the CPU must stall until the
resource is free.

Es decir, que mientras la tarjeta está utilizando un recurso (aquí dice VB pero lo mismo será para cualquier memoria que lockees) no puedes modificarlo, lógico. Y para que el procesador pueda ir modificando, por ejemplo, un indexbuffer mientras se procesa otro, es mejor usar varios buffers pequeños en vez de uno grande para ir manejandolos a lo round-robin como dice arriba y así evitar el "spin lock". Pero el caso es que ¿Qué ocurre si alojas el buffer en la memoria de video y no la lockeas nunca? Cuando cambias de IB no se enviará nada, solo se mandará procesar el buffer que ya está en la tarjeta y el programa seguirá funcionando tan tranquilo ¿es así? Entonces ¿hay algún problema con tener un megaindexbuffer en memoria de video continuamente y mandarlo renderizar con una simple instrucción?  :huh:

Virgil

 
Cita de: "Haddd"No tiene sentido. TODO se hace en GPU así que no hay paralelismo.
¿Como que no hay paralelismo?

Mientras el GPU procesa un grupo de vértices y/o índices, tú le estás copiando otro grupo, allí es donde se produce el paralelismo.

Saludos.

Haddd

 Yo no copio nada TODO está en memoria de vídeo. Bueno, es un suponer, porque yo creo los buffers con D3DPOOL_DEFAULT. Por eso no tengo nada de paralelismo. Hombre, espero que una Radeon 9500 sea capaz de tener index buffers en RAM. :blink:


Thenend

 Pero paralelismo entre la CPU y la GPU siempre hay ¿no? Cuando llamas a EndScene() la tarjeta sigue trabajando con los datos que le has pasado al driver y, mientras tú ejecutas tu programa en la CPU, la GPU se va encargando de dibujar el frame.

Lo que creo que dice Haddd es que como todo está en la memoria de video, la orden "SetIndices" no debería copiar nada y por lo tanto debería ser muy rápida. Por lo que la CPU NO podría ir ganando tiempo al copiar mientras se procesaran los demás buffers. Nada más terminar uno, ejecutaría la orden rápidamente y estaría otra vez atascado hasta que terminara el siguiente, solo habría ganado el poquito tiempo que tarde en ejecutarse el SetIndices.

Pregunta ¿Cuánto tarda el SetIndices aunque el buffer ya esté en memoria?

fiero

 
Cita de: "Haddd"No tiene sentido. TODO se hace en GPU así que no hay paralelismo.
Pues si, cuanto más leo mi respuesta anterior menos me convence XD...
Según lo que cuentas, el cuello de botella lo provoca la función DrawIndexedPrimitive, o sea, que la CPU está muy liberada, por lo que llamar varias veces a la función o una sola ahora me perece que deberia ser lo mismo.

Por tanto, se me ocurren 2 opciones. O que la tarjeta realice algún proceso en paralelo, como dice Thenend, en ese caso , hacer varias llamadas activaría esos procesos. O que la tarjeta disponga de buffers y cachés internos para el proceso de triángulos, y al mandarle demasiada geometría de golpe hace que se superen los tamaños de esos buffers y la tarjeta se "atragante"...

Para comprobar si se trata del segundo caso, puedes hacer una prueba. Un buche con una llamada a DrawIndexedPrimitive midiendo el tiempo que le cuesta a la función, empezando por 1000 triangulos por ejemplo, e ir incrementando el numero de triangulos en 1000 en cada loop. Después dividiendo el tiempo, entre el numero de paquetes de 1000, deberia dar siempre un resultado similar. Excepto cuando llegues al límite de proceso en el que el tiempo se dispara de repente. Igual estoy diciendo tonterias, y el tiempo se va incrementando gradualmente al incrementar los triangulos... no se... (con mi aceleradora no puedo hacer esas pruebas)

saludos
www.videopanoramas.com Videopanoramas 3D player






Stratos es un servicio gratuito, cuyos costes se cubren en parte con la publicidad.
Por favor, desactiva el bloqueador de anuncios en esta web para ayudar a que siga adelante.
Muchísimas gracias.