nkeynes@1160: /** nkeynes@1160: * $Id$ nkeynes@1160: * nkeynes@1160: * Generic GL vertex buffer/vertex array support nkeynes@1160: * nkeynes@1160: * Copyright (c) 2011 Nathan Keynes. nkeynes@1160: * nkeynes@1160: * This program is free software; you can redistribute it and/or modify nkeynes@1160: * it under the terms of the GNU General Public License as published by nkeynes@1160: * the Free Software Foundation; either version 2 of the License, or nkeynes@1160: * (at your option) any later version. nkeynes@1160: * nkeynes@1160: * This program is distributed in the hope that it will be useful, nkeynes@1160: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@1160: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@1160: * GNU General Public License for more details. nkeynes@1160: */ nkeynes@1160: nkeynes@1160: #define GL_GLEXT_PROTOTYPES 1 nkeynes@1160: nkeynes@1160: #include nkeynes@1160: #include nkeynes@1160: #include nkeynes@1160: #include "lxdream.h" nkeynes@1160: #include "display.h" nkeynes@1160: #include "drivers/video_gl.h" nkeynes@1160: #include "pvr2/glutil.h" nkeynes@1160: nkeynes@1160: #define MIN_VERTEX_ARRAY_SIZE (1024*1024) nkeynes@1160: nkeynes@1160: vertex_buffer_t vertex_buffer_new( vertex_buffer_t vtable ) nkeynes@1160: { nkeynes@1160: vertex_buffer_t buf = g_malloc(sizeof(struct vertex_buffer)); nkeynes@1160: memcpy( buf, vtable, sizeof(struct vertex_buffer)); nkeynes@1160: buf->data = 0; nkeynes@1160: buf->id = 0; nkeynes@1160: buf->mapped_size = buf->capacity = 0; nkeynes@1160: buf->fence = 0; nkeynes@1160: return buf; nkeynes@1160: } nkeynes@1160: nkeynes@1160: /******************************* Default ***********************************/ nkeynes@1160: nkeynes@1160: static void *def_map( vertex_buffer_t buf, uint32_t size ) nkeynes@1160: { nkeynes@1160: buf->mapped_size = size; nkeynes@1160: if( size < MIN_VERTEX_ARRAY_SIZE ) nkeynes@1160: size = MIN_VERTEX_ARRAY_SIZE; nkeynes@1160: if( size > buf->capacity ) { nkeynes@1160: g_free(buf->data); nkeynes@1160: buf->data = g_malloc(size); nkeynes@1160: buf->capacity = size; nkeynes@1160: } nkeynes@1160: return buf->data; nkeynes@1160: } nkeynes@1160: nkeynes@1160: static void *def_unmap( vertex_buffer_t buf ) nkeynes@1160: { nkeynes@1160: return buf->data; nkeynes@1160: } nkeynes@1160: nkeynes@1160: static void def_finished( vertex_buffer_t buf ) nkeynes@1160: { nkeynes@1160: } nkeynes@1160: nkeynes@1160: static void def_destroy( vertex_buffer_t buf ) nkeynes@1160: { nkeynes@1160: g_free(buf->data); nkeynes@1160: buf->data = NULL; nkeynes@1160: g_free(buf); nkeynes@1160: } nkeynes@1160: nkeynes@1160: static struct vertex_buffer def_vtable = { def_map, def_unmap, def_finished, def_destroy }; nkeynes@1160: nkeynes@1160: static vertex_buffer_t def_create_buffer( ) nkeynes@1160: { nkeynes@1160: return vertex_buffer_new( &def_vtable ); nkeynes@1160: } nkeynes@1160: nkeynes@1160: /************************** vertex_array_range *****************************/ nkeynes@1160: nkeynes@1160: /** nkeynes@1160: * VAR extensions like the buffer to be allocated on page boundaries. nkeynes@1160: */ nkeynes@1160: static void var_alloc_pages( vertex_buffer_t buf, uint32_t size ) nkeynes@1160: { nkeynes@1160: if( size < MIN_VERTEX_ARRAY_SIZE ) nkeynes@1160: size = MIN_VERTEX_ARRAY_SIZE; nkeynes@1160: if( size > buf->capacity ) { nkeynes@1160: size = (size + 4096-1) & (~(4096-1)); nkeynes@1160: if( buf->data != NULL ) { nkeynes@1160: munmap( buf->data, buf->capacity ); nkeynes@1160: } nkeynes@1160: buf->data = mmap( NULL, size, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0 ); nkeynes@1160: assert( buf->data != MAP_FAILED ); nkeynes@1160: buf->capacity = size; nkeynes@1160: } nkeynes@1160: } nkeynes@1160: nkeynes@1160: #ifdef APPLE_BUILD nkeynes@1160: nkeynes@1160: static void *apple_map( vertex_buffer_t buf, uint32_t size ) nkeynes@1160: { nkeynes@1160: glFinishFenceAPPLE(buf->fence); nkeynes@1160: var_alloc_pages( buf, size ); nkeynes@1160: glVertexArrayRangeAPPLE(size, buf->data); nkeynes@1160: buf->mapped_size = size; nkeynes@1160: return buf->data; nkeynes@1160: } nkeynes@1160: nkeynes@1160: static void *apple_unmap( vertex_buffer_t buf ) nkeynes@1160: { nkeynes@1160: glFlushVertexArrayRangeAPPLE(buf->mapped_size, buf->data); nkeynes@1160: return buf->data; nkeynes@1160: } nkeynes@1160: nkeynes@1160: static void apple_finished( vertex_buffer_t buf ) nkeynes@1160: { nkeynes@1160: glSetFenceAPPLE(buf->fence); nkeynes@1160: } nkeynes@1160: nkeynes@1160: static void apple_destroy( vertex_buffer_t buf ) nkeynes@1160: { nkeynes@1160: glVertexArrayRangeAPPLE(0,0); nkeynes@1160: glDeleteFencesAPPLE(1, &buf->fence); nkeynes@1160: munmap( buf->data, buf->capacity ); nkeynes@1160: g_free(buf); nkeynes@1160: } nkeynes@1160: static struct vertex_buffer apple_vtable = { apple_map, apple_unmap, apple_finished, apple_destroy }; nkeynes@1160: nkeynes@1160: static vertex_buffer_t apple_create_buffer( uint32_t size ) nkeynes@1160: { nkeynes@1160: vertex_buffer_t buf = vertex_buffer_new( &apple_vtable ); nkeynes@1160: glGenFencesAPPLE(1, &buf->fence); nkeynes@1164: glSetFenceAPPLE(buf->fence); nkeynes@1160: return buf; nkeynes@1160: } nkeynes@1160: nkeynes@1160: #endif nkeynes@1160: nkeynes@1160: #ifdef GL_VERTEX_ARRAY_RANGE_NV nkeynes@1160: nkeynes@1164: #pragma weak glVertexArrayRangeNV nkeynes@1164: #pragma weak glFlushVertexArrayRangeNV nkeynes@1164: #pragma weak glFinishFenceNV nkeynes@1164: #pragma weak glSetFenceNV nkeynes@1164: #pragma weak glGenFencesNV nkeynes@1164: #pragma weak glDeleteFencesNV nkeynes@1164: nkeynes@1160: static void *nv_map( vertex_buffer_t buf, uint32_t size ) nkeynes@1160: { nkeynes@1160: glFinishFenceNV(buf->fence); nkeynes@1160: var_alloc_pages( buf, size ); nkeynes@1160: glVertexArrayRangeNV(size, buf->data); nkeynes@1160: buf->mapped_size = size; nkeynes@1160: return buf->data; nkeynes@1160: } nkeynes@1160: static void *nv_unmap( vertex_buffer_t buf ) nkeynes@1160: { nkeynes@1160: glFlushVertexArrayRangeNV(); nkeynes@1160: return buf->data; nkeynes@1160: } nkeynes@1160: nkeynes@1160: static void nv_finished( vertex_buffer_t buf ) nkeynes@1160: { nkeynes@1160: glSetFenceNV(buf->fence, GL_ALL_COMPLETED_NV); nkeynes@1160: } nkeynes@1160: nkeynes@1160: static void nv_destroy( vertex_buffer_t buf ) nkeynes@1160: { nkeynes@1160: glVertexArrayRangeNV(0,0); nkeynes@1160: glDeleteFencesNV(1, &buf->fence); nkeynes@1160: munmap( buf->data, buf->capacity ); nkeynes@1160: g_free(buf); nkeynes@1160: } nkeynes@1160: nkeynes@1160: static struct vertex_buffer nv_vtable = { nv_map, nv_unmap, nv_finished, nv_destroy }; nkeynes@1160: nkeynes@1160: static vertex_buffer_t nv_create_buffer( uint32_t size ) nkeynes@1160: { nkeynes@1160: vertex_buffer_t buf = vertex_buffer_new( &nv_vtable ); nkeynes@1160: glGenFencesNV(1, &buf->fence); nkeynes@1164: glSetFenceNV(buf->fence, GL_ALL_COMPLETED_NV); nkeynes@1160: return buf; nkeynes@1160: } nkeynes@1160: nkeynes@1160: #endif /* !GL_VERTEX_ARRAY_RANGE_NV */ nkeynes@1160: nkeynes@1160: /************************** vertex_buffer_object *****************************/ nkeynes@1160: nkeynes@1160: #ifdef GL_ARRAY_BUFFER_ARB nkeynes@1160: nkeynes@1160: static void *vbo_map( vertex_buffer_t buf, uint32_t size ) nkeynes@1160: { nkeynes@1160: glBindBufferARB( GL_ARRAY_BUFFER_ARB, buf->id ); nkeynes@1160: if( size > buf->capacity ) { nkeynes@1160: glBufferDataARB( GL_ARRAY_BUFFER_ARB, size, NULL, GL_STREAM_DRAW_ARB ); nkeynes@1160: assert( gl_check_error("Allocating vbo data") ); nkeynes@1160: buf->capacity = size; nkeynes@1160: } nkeynes@1160: buf->data = glMapBufferARB( GL_ARRAY_BUFFER_ARB, GL_WRITE_ONLY_ARB ); nkeynes@1160: buf->mapped_size = buf->capacity; nkeynes@1160: return buf->data; nkeynes@1160: } nkeynes@1160: nkeynes@1160: static void *vbo_unmap( vertex_buffer_t buf ) nkeynes@1160: { nkeynes@1160: glUnmapBufferARB( GL_ARRAY_BUFFER_ARB ); nkeynes@1160: return NULL; nkeynes@1160: } nkeynes@1160: nkeynes@1160: static void vbo_finished( vertex_buffer_t buf ) nkeynes@1160: { nkeynes@1160: glBindBufferARB( GL_ARRAY_BUFFER_ARB, 0 ); nkeynes@1160: } nkeynes@1160: nkeynes@1160: static void vbo_destroy( vertex_buffer_t buf ) nkeynes@1160: { nkeynes@1160: glBindBufferARB( GL_ARRAY_BUFFER_ARB, 0 ); nkeynes@1160: glDeleteBuffersARB( 1, &buf->id ); nkeynes@1160: } nkeynes@1160: nkeynes@1160: static struct vertex_buffer vbo_vtable = { vbo_map, vbo_unmap, vbo_finished, vbo_destroy }; nkeynes@1160: nkeynes@1160: static vertex_buffer_t vbo_create_buffer( uint32_t size ) nkeynes@1160: { nkeynes@1160: vertex_buffer_t buf = vertex_buffer_new( &vbo_vtable ); nkeynes@1160: glGenBuffersARB( 1, &buf->id ); nkeynes@1160: return buf; nkeynes@1160: } nkeynes@1160: nkeynes@1160: #endif nkeynes@1160: nkeynes@1160: /** nkeynes@1160: * Auto-detect the supported vertex buffer types, and select between them. nkeynes@1160: * Use vertex_buffer_object if available, otherwise vertex_array_range, nkeynes@1160: * otherwise just pure host buffers. nkeynes@1160: */ nkeynes@1160: void gl_vbo_init( display_driver_t driver ) { nkeynes@1160: /* VBOs are disabled for now as they won't work with the triangle sorting, nkeynes@1160: * plus they seem to be slower than the other options anyway. nkeynes@1160: */ nkeynes@1160: #ifdef ENABLE_VBO nkeynes@1160: #ifdef GL_ARRAY_BUFFER_ARB nkeynes@1160: if( isGLVertexBufferSupported() ) { nkeynes@1160: driver->create_vertex_buffer = vbo_create_buffer; nkeynes@1160: return; nkeynes@1160: } nkeynes@1160: #endif nkeynes@1160: #endif nkeynes@1160: nkeynes@1160: #ifdef APPLE_BUILD nkeynes@1160: if( isGLExtensionSupported("GL_APPLE_vertex_array_range") && nkeynes@1160: isGLExtensionSupported("GL_APPLE_fence") ) { nkeynes@1160: glEnableClientState( GL_VERTEX_ARRAY_RANGE_APPLE ); nkeynes@1160: driver->create_vertex_buffer = apple_create_buffer; nkeynes@1160: return; nkeynes@1160: } nkeynes@1160: #endif nkeynes@1160: nkeynes@1160: #ifdef GL_VERTEX_ARRAY_RANGE_NV nkeynes@1160: if( isGLExtensionSupported("GL_NV_vertex_array_range") && nkeynes@1160: isGLExtensionSupported("GL_NV_fence") ) { nkeynes@1160: glEnableClientState( GL_VERTEX_ARRAY_RANGE_NV ); nkeynes@1160: driver->create_vertex_buffer = nv_create_buffer; nkeynes@1160: return; nkeynes@1160: } nkeynes@1160: #endif nkeynes@1160: driver->create_vertex_buffer = def_create_buffer; nkeynes@1160: } nkeynes@1160: nkeynes@1160: void gl_vbo_fallback_init( display_driver_t driver ) { nkeynes@1160: driver->create_vertex_buffer = def_create_buffer; nkeynes@1160: }