-
Notifications
You must be signed in to change notification settings - Fork 9
Vertex Arrays
Vertex arrays are the preferred interface for submitting geometry information. Directly using OpenGL 1.1 vertex arrays will have a performance advantage over immediate mode simply because there are fewer function calls, but there are higher performance techniques.
glArrayElement offers little advantage over the normal immediate mode calls; use glDrawArrays, or glDrawElements.
Use the smallest vertex types possible: use GL_UNSIGNED_BYTE rather than FLOAT for colours; use SHORTs rather than FLOATs for vertex and texcoords. Don't enable unused arrays (for example, don't enable a normal array unless lighting is enabled; there are exceptions though). Use BYTE or SHORT indices rather than INT. Use the [native vertex format][NativeVertex] where possible.
Use strips and fans where possible, though indexed independent triangles are still pretty efficient.
Note: there's no need to do any explicit cache flushes or use uncached pointers when passing vertex pointers into PSPGL. PSPGL will do all the appropriate cache management for itself.
PSPGL supports the CVA extension. This allows you to set up the vertex arrays, and then call glLockArraysEXT. This will cause PSPGL to convert the vertex data into hardware form, and cache it in the PSP GE's local memory. You may then use the vertex data with multiple calls to glDrawArrays/glDraw(Range)Elements.
There's probably no advantage in using this unless you use the array multiple times, and don't lock too many unused vertices (ie, only lock the part of the arrays you're actually using). You must unlock the arrays with glUnlockArraysEXT before updating the array pointers with gl*Pointer; if you don't, the pointer update will be effectively ignored. Similarly, changing the contents of a locked array will have no effect.
It will only lock up to 128k of vertex data. If you try to lock more, the call will have no effect. Try to keep your vertex arrays under this limit, or use a vertex buffer object in native form.
(Note: you'll need to carefully read the specification of the ARB_vertex_buffer_object extension [see references] to understand the discussion below. You can safely ignore all this though.)
PSPGL implements the VBO extension (now part of OpenGL 1.5). Buffer objects are a powerful general way for an application to have more control over how OpenGL allocates and uses memory, and are used for more then just vertex arrays. However, vertex buffer objects are very useful.
Buffer objects are primiarily useful when you arrange your data into a form which is directly usable by the hardware without any conversion. If you do this, then a buffer object allows the hardware to directly DMA the data out of the buffer without conversion or copying. Unfortunately the PSP is a bit more rigid than other 3D hardware about what vertex and texture formats and arrangements in memory it will accept, so this takes some care.
PSPGL currently ignores the "usage" parameter of glBufferDataARB, but the intention is that it will be used to decide whether data will be placed in system memory or EDRAM (though ultimately buffers will tend to migrate between the two memory pools).
Because the PSP uses a MIPS CPU without coherent caches, the caches must be managed in software. PSPGL will do this for you, but only if you use the API correctly. This means that you have to be careful to use the glMapBufferARB/glUnmapBufferARB functions properly. PSPGL tries hard to raise GL errors if you use a mapped buffer as an argument to a GL call, so be sure to check for errors when debugging VBO code. NEVER use a buffer pointer after you've unmapped it.
The "access" parameter of glMapBufferARB is used to determine how the cache is treated for the new memory:
Mapping access | Mapping type | Map action | Unmap action | Notes |
GL_WRITE_ONLY_ARB |
Uncached | sync with hardware if busy | - | The mapping is uncached to help prevent cache pollution; reads will
work from a write-only mapping, but they'll probably be very slow. If you're replacing
the entire contents of the buffer while the hardware is potentially using it,
it is more efficient to use glBufferDataARB to replace the buffer with a new
one, because this doesn't require waiting on the hardware. |
GL_READ_ONLY_ARB |
Cached | - | cache is invalidated but not flushed | This will still be writable, but writing to it may cause very strange, non-deterministic results; the cache lines may not be flushed to memory for the hardware to see, or they may be discarded without ever being written (giving the appearance of data which "sticks" for a while, but then reverts to its old value). |
GL_READ_WRITE_ARB |
Cached | sync with hardware if busy | dirty lines are flushed, and the cache is invalidated | Safe for all usage, but not as efficient. Use only if you really need to have read-write access to the memory. |
In general, the assumption is that buffer objects are intended for buffers shared with hardware. They are kept in CPU cache only while mapped for access by the CPU, and are otherwise evicted from the cache. This leaves the CPU cache free for other data, and makes sure the hardware always sees a consistent view of the memory. If you don't put your arrays in buffer objects into a form which is directly useful to hardware, you end up using buffer objects like an inefficient form of malloc with bad cache characteristics.
Note that it is always an error to map a buffer while it is still in use; PSPGL enforces this by raising an error if you try to use a buffer while its mapped, and waiting for the hardware to finish if you create a writable mapping.
To arrange a vertex array in native vertex format, you must specify your arrays in the following order in memory (leaving out any array you're not enabling), with sizes and types as follows:
Array | Types | Size |
GL_WEIGHT_ARRAY_PSP | GL_BYTE, GL_SHORT, GL_FLOAT | 1-8 |
GL_TEX_COORD_ARRAY | GL_BYTE, GL_SHORT, GL_FLOAT | 2 |
GL_COLOR_ARRAY | GL_UNSIGNED_BYTE | 4 |
GL_NORMAL_ARRAY | GL_BYTE, GL_SHORT, GL_FLOAT | 3 |
GL_VERTEX_ARRAY | GL_BYTE, GL_SHORT, GL_FLOAT | 3 |
See the Wiki for more details.
Note: Your array of vertices must be packed, so there is no non-vertex data between each vertex. Also, you must enable all these arrays with glEnableClientState for them to be considered as part of the "native format" check. For example, if you sometimes need normals and sometimes not, then it is probably better to always keep the normal array enabled; if you disable them when you disable lighting, then PSPGL needs to copy and re-pack your arrays when you use them, which is likely more expensive than simply letting the hardware ignore an unused normal element in each vertex.
You may also put index data into a buffer object, using the GL_ELEMENT_ARRAY_BUFFER_ARB target. This is pretty straightforward. The only constraint is that you use GL_UNSIGNED_BYTE or GL_UNSIGNED_SHORT as your index type; the hardware doesn't seem to support 32-bit indices.