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?
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
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.
En este pdf hablan un poco de eso:
http://developer.nvidia.com/attach/6731Dicen 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
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?
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.
No tiene sentido. TODO se hace en GPU así que no hay paralelismo.
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.
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?
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
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:
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.
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:
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?
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
Voy a subir la demo y lo probamos con otras tarjetas, a ver si lo que pasa con la Radeon 9500 ocurre con otras.
La demo está en
Esa demonecesitas una tarjeta con shaders 1.1 y que pueda utilizar index buffer de 32 bits.
Mi test es:
Tarjeta:Radeon 9500 softmod 9700 128 MB
CPU:Athlon a 1.9 GHz
Ram:512
Test 1:88 fps
Test 2:128 fps
Test 3:202 fps
El test 3, en realidad no nos sirve en la comparación porque utiliza la visibilidad del octree.
Aceleradora: Radeon 9600 XT 128 MB
CPU: P4 3Ghz
Ram: 1GB
Test 1: 158 fps
Test 2: 196 fps
Test 3: 305 fps
Saludos.
Ah bueno comentar que si la CPU no pinta nada en todo esto, ¿por qué hay esa diferencia de fps?. Al fin y al cabo tu aceleradora es una 9500 reconvertida a 9700 y la mia es una 9600...
Saludos.
1 solo terremo +-50 solo cielo +-50 ambas +-50
2 +- 45 / +-50 / +- 50
3 +- 200 / +- 375 / +- 80
fx5200
256
1.8
Aclarar que mis resultados son sin mover la cámara del sitio.
Saludos.
9800XT 256MB
AMD64 3000+
1GB RAM
...(Si, ordena nuevo que pasa! :P )
Test1: 228
Test2: 226 ( :blink: soy el unico!?)
Test3: 391
En ningun caso los fps eran estables al 100% habia ligeras bajadas,por ejemplo el primero a 206, pero esporadicas en todo caso.
Wenas. La verdad, creo que mis datos no son muy fiables. Lo primero os dire, una ATI 9200 PRO 128 megas. Procesador PIV 2600, 1 gigaram ddr a 400
Lo raro es q se me ve toda la pantalla gris. No se si será pq mi tarjeta no lo soporte (q raro), q no tengo drivers actualizados (lo mas probable) o q directx furule mal (tengo la 9.0).
Cuando actualice os comento, pero por ahora mis datos (ahora vereis lo ilógico del asunto xDD)
1 test (TODO DE GOLPE): 625/630 fps
2 test (A CACHOS): 71/73 fps
3 test (OCTREE): 630/640 fps
Este al ejecutarlo por segunda vea baja a 128 y no se mueve, ni arriba ni abajo, 128 fps justos.
P.D: Lo dicho, actualizo y os comento
P.D2: No me parece normal q me baje a 70 fps (nooo) (nooo)
EDITADO Acabo de ver el haddd_debug.htm. Errores q me da:
Device:1 REF Behavior: NO SOPORTA T&L NºFormatos:2 {
****ESTE DEVICE NO CUMPLE LAS EXIGENCIAS MINIMAS DEL MOTOR****
Modo:0 Modo Ventana NO posible 00000016 X8R8G8B8No soporta FullScreen
Modo:1 Modo Ventana NO posible 00000017 R5G6B5No soporta FullScreen
}
Características del Device {
Versión de SDX:31. De D3D:0900
Versión del Adaptador:6396
Memoria de vídeo disponible:118 MB (120832 KB)
HAL (hw vp): RADEON 9200 SERIES (800x620x8) (D24S0)
Escritorio: Ancho=1024 Alto=768
FullScreenGamma OK
CanCalibrateGamma NO DISPONIBLE
Alpha en modo Flip/Discard OK
Nº Máximo de Streams para Vertex Buffers=12. Tamaño máximo en bytes(Stride)=1024
Nº Máximo de indexbuffer=x0000ffff.NO SE PUEDE UTILIZAR EL INDEXBUFFER DE 32 BITS
Nº Máximo de primitivas=x0000ffff.
Nº de RenderTarget simultáneos:1
---------SHADERS---------
Nº de constantes en el vertex shader:192
NO SOPORTA PIXEL SHADER 2.0
SI SOPORTA PIXEL SHADER 1.4. 8 constantes, 6 coordenadas de textura, 6 temporales
SI SOPORTA PIXEL SHADER 1.1. 8 constantes, 4 coordenadas de textura, 2 temporales
Soporta Querys de Oclusión
Si te sirve de algo, bienvenido sea xDD