Merge remote branch 'origin/master' into pipe-video
[mesa.git] / src / mesa / drivers / dri / nouveau / nouveau_bufferobj.c
index be6455a01ed29d8f39d6a50ced86e800146115f9..e60b91f64be082eda41f2c6988f861469af1cd20 100644 (file)
-#include "bufferobj.h"
-#include "enums.h"
-
+/*
+ * Copyright (C) 2009 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "nouveau_driver.h"
 #include "nouveau_bufferobj.h"
 #include "nouveau_context.h"
-#include "nouveau_drm.h"
-#include "nouveau_mem.h"
-#include "nouveau_msg.h"
-#include "nouveau_object.h"
-
-#define NOUVEAU_MEM_FREE(mem) do {      \
-       nouveau_mem_free(ctx, (mem));   \
-       (mem) = NULL;                   \
-} while(0)
-
-#define DEBUG(fmt,args...) do {                \
-       if (NOUVEAU_DEBUG & DEBUG_BUFFEROBJ) { \
-               fprintf(stderr, "%s: "fmt, __func__, ##args);  \
-       }                                      \
-} while(0)
-
-static GLboolean
-nouveau_bo_download_from_screen(GLcontext *ctx,        GLuint offset, GLuint size,
-                                               struct gl_buffer_object *bo)
-{
-       nouveauContextPtr nmesa = NOUVEAU_CONTEXT(ctx);
-       nouveau_buffer_object *nbo = (nouveau_buffer_object *)bo;
-       nouveau_mem *in_mem;
-
-       DEBUG("bo=%p, offset=%d, size=%d\n", bo, offset, size);
-
-       /* If there's a permanent backing store, blit directly into it */
-       if (nbo->cpu_mem) {
-               if (nbo->cpu_mem != nbo->gpu_mem) {
-                       DEBUG("..cpu_mem\n");
-                       nouveau_memformat_flat_emit(ctx, nbo->cpu_mem,
-                                                   nbo->gpu_mem,
-                                                   offset, offset, size);
-               }
-       } else {
-               DEBUG("..sys_mem\n");
-               in_mem = nouveau_mem_alloc(ctx, NOUVEAU_MEM_AGP, size, 0);
-               if (in_mem) {
-                       DEBUG("....via GART\n");
-                       /* otherwise, try blitting to faster memory and
-                        * copying from there
-                        */
-                       nouveau_memformat_flat_emit(ctx, in_mem, nbo->gpu_mem,
-                                                        0, offset, size);
-                       nouveau_notifier_wait_nop(ctx, nmesa->syncNotifier,
-                                                      NvSubMemFormat);
-                       _mesa_memcpy(nbo->cpu_mem_sys + offset,
-                                       in_mem->map, size);
-                       NOUVEAU_MEM_FREE(in_mem);
-               } else {
-                       DEBUG("....direct VRAM copy\n");
-                       /* worst case, copy directly from vram */
-                       _mesa_memcpy(nbo->cpu_mem_sys + offset,
-                                    nbo->gpu_mem + offset,
-                                    size);
-               }
-       }
 
-       return GL_TRUE;
-}
+#include "main/bufferobj.h"
 
-static GLboolean
-nouveau_bo_upload_to_screen(GLcontext *ctx, GLuint offset, GLuint size,
-                                           struct gl_buffer_object *bo)
+static inline char *
+get_bufferobj_map(struct gl_buffer_object *obj, unsigned flags)
 {
-       nouveauContextPtr nmesa = NOUVEAU_CONTEXT(ctx);
-       nouveau_buffer_object *nbo = (nouveau_buffer_object *)bo;
-       nouveau_mem *out_mem;
-
-       DEBUG("bo=%p, offset=%d, size=%d\n", bo, offset, size);
-
-       if (nbo->cpu_mem) {
-               if (nbo->cpu_mem != nbo->gpu_mem) {
-                       DEBUG("..cpu_mem\n");
-                       nouveau_memformat_flat_emit(ctx, nbo->gpu_mem,
-                                                   nbo->cpu_mem,
-                                                   offset, offset, size);
-               }
-       } else {
-               out_mem = nouveau_mem_alloc(ctx, NOUVEAU_MEM_AGP |
-                                                NOUVEAU_MEM_MAPPED,
-                                                size, 0);
-               if (out_mem) {
-                       DEBUG("....via GART\n");
-                       _mesa_memcpy(out_mem->map,
-                                       nbo->cpu_mem_sys + offset, size);
-                       nouveau_memformat_flat_emit(ctx, nbo->gpu_mem, out_mem,
-                                                   offset, 0, size);
-                       nouveau_notifier_wait_nop(ctx, nmesa->syncNotifier,
-                                                      NvSubMemFormat);
-                       NOUVEAU_MEM_FREE(out_mem);
-               } else {
-                       DEBUG("....direct VRAM copy\n");
-                       _mesa_memcpy(nbo->gpu_mem->map + offset,
-                                    nbo->cpu_mem_sys + offset,
-                                    size);
-               }
-       }
-
-       return GL_TRUE;
-}
+       struct nouveau_bufferobj *nbo = to_nouveau_bufferobj(obj);
+       void *map = NULL;
 
-GLboolean
-nouveau_bo_move_in(GLcontext *ctx, struct gl_buffer_object *bo)
-{
-       nouveau_buffer_object *nbo = (nouveau_buffer_object *)bo;
-
-       DEBUG("bo=%p\n", bo);
-
-       if (bo->OnCard)
-               return GL_TRUE;
-       assert(nbo->gpu_mem_flags);
-
-       nbo->gpu_mem = nouveau_mem_alloc(ctx, nbo->gpu_mem_flags |
-                                             NOUVEAU_MEM_MAPPED,
-                                             bo->Size, 0);
-       assert(nbo->gpu_mem);
-
-       if (nbo->cpu_mem_flags) {
-               if ((nbo->cpu_mem_flags|NOUVEAU_MEM_MAPPED) != nbo->gpu_mem->type) {
-                       DEBUG("..need cpu_mem buffer\n");
-
-                       nbo->cpu_mem = nouveau_mem_alloc(ctx,
-                                                        nbo->cpu_mem_flags |
-                                                        NOUVEAU_MEM_MAPPED,
-                                                        bo->Size, 0);
-
-                       if (nbo->cpu_mem) {
-                               DEBUG("....alloc ok, kill sys_mem buffer\n");
-                               _mesa_memcpy(nbo->cpu_mem->map,
-                                            nbo->cpu_mem_sys, bo->Size);
-                               FREE(nbo->cpu_mem_sys);
-                       }
-               } else {
-                       DEBUG("..cpu direct access to GPU buffer\n");
-                       nbo->cpu_mem = nbo->gpu_mem;
-               }
+       if (nbo->sys) {
+               map = nbo->sys;
+       } else if (nbo->bo) {
+               nouveau_bo_map(nbo->bo, flags);
+               map = nbo->bo->map;
+               nouveau_bo_unmap(nbo->bo);
        }
-       nouveau_bo_upload_to_screen(ctx, 0, bo->Size, bo);
 
-       bo->OnCard = GL_TRUE;
-       return GL_TRUE;
+       return map;
 }
 
-GLboolean
-nouveau_bo_move_out(GLcontext *ctx, struct gl_buffer_object *bo)
+static struct gl_buffer_object *
+nouveau_bufferobj_new(struct gl_context *ctx, GLuint buffer, GLenum target)
 {
-       nouveauContextPtr nmesa = NOUVEAU_CONTEXT(ctx);
-       nouveau_buffer_object *nbo = (nouveau_buffer_object *)bo;
-       GLuint nr_dirty;
-
-       DEBUG("bo=%p\n", bo);
-       if (!bo->OnCard)
-               return GL_TRUE;
-
-       nr_dirty = nouveau_bo_download_dirty(ctx, bo);
-       if (nbo->cpu_mem) {
-               if (nr_dirty && nbo->cpu_mem != nbo->gpu_mem)
-                       nouveau_notifier_wait_nop(ctx, nmesa->syncNotifier,
-                                                      NvSubMemFormat);
-               DEBUG("..destroy cpu_mem buffer\n");
-               nbo->cpu_mem_sys = malloc(bo->Size);
-               assert(nbo->cpu_mem_sys);
-               _mesa_memcpy(nbo->cpu_mem_sys, nbo->cpu_mem->map, bo->Size);
-               if (nbo->cpu_mem == nbo->gpu_mem)
-                       nbo->cpu_mem = NULL;
-               else
-                       NOUVEAU_MEM_FREE(nbo->cpu_mem);
-       }
-       NOUVEAU_MEM_FREE(nbo->gpu_mem);
+       struct nouveau_bufferobj *nbo;
 
-       bo->OnCard = GL_FALSE;
-       return GL_TRUE;
-}
+       nbo = CALLOC_STRUCT(nouveau_bufferobj);
+       if (!nbo)
+               return NULL;
 
-static void
-nouveau_bo_choose_storage_method(GLcontext *ctx, GLenum usage,
-                                                struct gl_buffer_object *bo)
-{
-       nouveau_buffer_object *nbo = (nouveau_buffer_object *)bo;
-       GLuint gpu_type = 0;
-       GLuint cpu_type = 0;
-
-       switch (usage) {
-       /* Client source, changes often, used by GL many times */
-       case GL_DYNAMIC_DRAW_ARB:
-               gpu_type = NOUVEAU_MEM_AGP | NOUVEAU_MEM_FB_ACCEPTABLE;
-               cpu_type = NOUVEAU_MEM_AGP;
-               break;
-       /* GL source, changes often, client reads many times */
-       case GL_DYNAMIC_READ_ARB:
-       /* Client source, specified once, used by GL many times */
-       case GL_STATIC_DRAW_ARB:
-       /* GL source, specified once, client reads many times */
-       case GL_STATIC_READ_ARB:
-       /* Client source, specified once, used by GL a few times */
-       case GL_STREAM_DRAW_ARB:
-       /* GL source, specified once, client reads a few times */
-       case GL_STREAM_READ_ARB:
-       /* GL source, changes often, used by GL many times*/
-       case GL_DYNAMIC_COPY_ARB:
-       /* GL source, specified once, used by GL many times */
-       case GL_STATIC_COPY_ARB:
-       /* GL source, specified once, used by GL a few times */
-       case GL_STREAM_COPY_ARB:
-               gpu_type = NOUVEAU_MEM_FB;
-               break;
-       default: 
-               assert(0);
-       }
+       _mesa_initialize_buffer_object(&nbo->base, buffer, target);
 
-       nbo->gpu_mem_flags = gpu_type;
-       nbo->cpu_mem_flags = cpu_type;
-       nbo->usage         = usage;
+       return &nbo->base;
 }
 
-void
-nouveau_bo_init_storage(GLcontext *ctx,        GLuint valid_gpu_access,
-                                       GLsizeiptrARB size,
-                                       const GLvoid *data,
-                                       GLenum usage,
-                                       struct gl_buffer_object *bo)
+static void
+nouveau_bufferobj_del(struct gl_context *ctx, struct gl_buffer_object *obj)
 {
-       nouveau_buffer_object *nbo = (nouveau_buffer_object *)bo;
-
-       DEBUG("bo=%p\n", bo);
-
-       /* Free up previous buffers if we can't reuse them */
-       if (nbo->usage != usage ||
-                       (nbo->gpu_mem && (nbo->gpu_mem->size != size))) {
-               if (nbo->cpu_mem_sys)
-                       FREE(nbo->cpu_mem_sys);
-               if (nbo->cpu_mem) {
-                       if (nbo->cpu_mem != nbo->gpu_mem)
-                               NOUVEAU_MEM_FREE(nbo->cpu_mem);
-                       else
-                               nbo->cpu_mem = NULL;
-               }
-               if (nbo->gpu_mem)
-                       NOUVEAU_MEM_FREE(nbo->gpu_mem);
-
-               bo->OnCard = GL_FALSE;
-               nbo->cpu_mem_sys = calloc(1, size);
-       }
+       struct nouveau_bufferobj *nbo = to_nouveau_bufferobj(obj);
 
-       nouveau_bo_choose_storage_method(ctx, usage, bo);
-       /* Force off flags that may not be ok for a given buffer */
-       nbo->gpu_mem_flags &= valid_gpu_access;
-
-       bo->Usage  = usage;
-       bo->Size   = size;
-
-       if (data) {
-               GLvoid *map = nouveau_bo_map(ctx, GL_WRITE_ONLY_ARB, bo);
-               _mesa_memcpy(map, data, size);
-               nouveau_bo_dirty_all(ctx, GL_FALSE, bo);
-               nouveau_bo_unmap(ctx, bo);
-       }
+       nouveau_bo_ref(NULL, &nbo->bo);
+       FREE(nbo->sys);
+       FREE(nbo);
 }
 
-void *
-nouveau_bo_map(GLcontext *ctx, GLenum access, struct gl_buffer_object *bo)
+static GLboolean
+nouveau_bufferobj_data(struct gl_context *ctx, GLenum target, GLsizeiptrARB size,
+                      const GLvoid *data, GLenum usage,
+                      struct gl_buffer_object *obj)
 {
-       nouveauContextPtr nmesa = NOUVEAU_CONTEXT(ctx);
-       nouveau_buffer_object *nbo = (nouveau_buffer_object *)bo;
-
-       DEBUG("bo=%p, access=%s\n", bo, _mesa_lookup_enum_by_nr(access));
+       struct nouveau_bufferobj *nbo = to_nouveau_bufferobj(obj);
+       int ret;
 
-       if (bo->OnCard && 
-               (access == GL_READ_ONLY_ARB || access == GL_READ_WRITE_ARB)) {
-               GLuint nr_dirty;
+       obj->Size = size;
+       obj->Usage = usage;
 
-               DEBUG("..on card\n");
-               nr_dirty = nouveau_bo_download_dirty(ctx, bo);
+       /* Free previous storage */
+       nouveau_bo_ref(NULL, &nbo->bo);
+       FREE(nbo->sys);
 
-               /* nouveau_bo_download_dirty won't wait unless it needs to
-                * free a temp buffer, which isn't the case if cpu_mem is
-                * present.
-                */
-               if (nr_dirty && nbo->cpu_mem && nbo->cpu_mem != nbo->gpu_mem)
-                       nouveau_notifier_wait_nop(ctx, nmesa->syncNotifier,
-                                                      NvSubMemFormat);
-       }
+       if (target == GL_ELEMENT_ARRAY_BUFFER_ARB ||
+           (size < 512 && usage == GL_DYNAMIC_DRAW_ARB) ||
+           context_chipset(ctx) < 0x10) {
+               /* Heuristic: keep it in system ram */
+               nbo->sys = MALLOC(size);
 
-       if (nbo->cpu_mem) {
-               DEBUG("..access via cpu_mem\n");
-               return nbo->cpu_mem->map;
        } else {
-               DEBUG("..access via cpu_mem_sys\n");
-               return nbo->cpu_mem_sys;
-       }
-}
-
-void
-nouveau_bo_unmap(GLcontext *ctx, struct gl_buffer_object *bo)
-{
-       DEBUG("unmap bo=%p\n", bo);
-}
-
-uint32_t
-nouveau_bo_gpu_ref(GLcontext *ctx, struct gl_buffer_object *bo)
-{
-       nouveau_buffer_object *nbo = (nouveau_buffer_object *)bo;
-
-       assert(nbo->mapped == GL_FALSE);
-
-       DEBUG("gpu_ref\n");
-       
-       if (!bo->OnCard) {
-               nouveau_bo_move_in(ctx, bo);
-               bo->OnCard = GL_TRUE;
-       }
-       nouveau_bo_upload_dirty(ctx, bo);
-
-       return nouveau_mem_gpu_offset_get(ctx, nbo->gpu_mem);
-}
-
-void
-nouveau_bo_dirty_linear(GLcontext *ctx, GLboolean on_card,
-                       uint32_t offset, uint32_t size,
-                       struct gl_buffer_object *bo)
-{
-       nouveau_buffer_object *nbo = (nouveau_buffer_object *)bo;
-       nouveau_bufferobj_dirty *dirty;
-       uint32_t start = offset;
-       uint32_t end = offset + size;
-       int i;
-
-       if (nbo->cpu_mem == nbo->gpu_mem)
-               return;
-
-       dirty = on_card ? &nbo->gpu_dirty : &nbo->cpu_dirty;
-
-       DEBUG("on_card=%d, offset=%d, size=%d, bo=%p\n",
-                       on_card, offset, size, bo);
-
-       for (i=0; i<dirty->nr_dirty; i++) {
-               nouveau_bufferobj_region *r = &dirty->dirty[i];
-
-               /* already dirty */
-               if (start >= r->start && end <= r->end) {
-                       DEBUG("..already dirty\n");
-                       return;
-               }
-
-               /* add to the end of a region */
-               if (start >= r->start && start <= r->end) {
-                       if (end > r->end) {
-                               DEBUG("..extend end of region\n");
-                               r->end = end;
-                               return;
-                       }
-               }
-
-               /* add to the start of a region */
-               if (start < r->start && end >= r->end) {
-                       DEBUG("..extend start of region\n");
-                       r->start = start;
-                       /* .. and to the end */
-                       if (end > r->end) {
-                               DEBUG("....and end\n");
-                               r->end = end;
-                       }
-                       return;
-               }
-       }
-
-       /* new region */
-       DEBUG("..new dirty\n");
-       dirty->nr_dirty++;
-       dirty->dirty = realloc(dirty->dirty,
-                              sizeof(nouveau_bufferobj_region) *
-                              dirty->nr_dirty);
-       dirty->dirty[dirty->nr_dirty - 1].start = start;
-       dirty->dirty[dirty->nr_dirty - 1].end   = end;
-}
-
-void
-nouveau_bo_dirty_all(GLcontext *ctx, GLboolean on_card,
-                    struct gl_buffer_object *bo)
-{
-       nouveau_buffer_object *nbo = (nouveau_buffer_object *)bo;
-       nouveau_bufferobj_dirty *dirty;
-
-       dirty = on_card ? &nbo->gpu_dirty : &nbo->cpu_dirty;
-       
-       DEBUG("dirty all\n");
-       if (dirty->nr_dirty) {
-               FREE(dirty->dirty);
-               dirty->dirty    = NULL;
-               dirty->nr_dirty = 0;
-       }
-
-       nouveau_bo_dirty_linear(ctx, on_card, 0, bo->Size, bo);
-}
-
-GLuint
-nouveau_bo_upload_dirty(GLcontext *ctx, struct gl_buffer_object *bo)
-{
-       nouveau_buffer_object *nbo = (nouveau_buffer_object *)bo;
-       nouveau_bufferobj_dirty *dirty = &nbo->cpu_dirty;
-       GLuint nr_dirty;
-       int i;
-
-       nr_dirty = dirty->nr_dirty;
-       if (!nr_dirty) {
-               DEBUG("clean\n");
-               return nr_dirty;
+               /* Get a hardware BO */
+               ret = nouveau_bo_new(context_dev(ctx),
+                                    NOUVEAU_BO_GART | NOUVEAU_BO_MAP, 0,
+                                    size, &nbo->bo);
+               assert(!ret);
        }
 
-       for (i=0; i<nr_dirty; i++) {
-               nouveau_bufferobj_region *r = &dirty->dirty[i];
-
-               DEBUG("dirty %d: o=0x%08x, s=0x%08x\n",
-                               i, r->start, r->end - r->start);
-               nouveau_bo_upload_to_screen(ctx,
-                                           r->start, r->end - r->start, bo);
-       }
-
-       FREE(dirty->dirty);
-       dirty->dirty    = NULL;
-       dirty->nr_dirty = 0;
-
-       return nr_dirty;
-}
-
-GLuint
-nouveau_bo_download_dirty(GLcontext *ctx, struct gl_buffer_object *bo)
-{
-       nouveau_buffer_object *nbo = (nouveau_buffer_object *)bo;
-       nouveau_bufferobj_dirty *dirty = &nbo->gpu_dirty;
-       GLuint nr_dirty;
-       int i;
-
-       nr_dirty = dirty->nr_dirty;
-       if (nr_dirty) {
-               DEBUG("clean\n");
-               return nr_dirty;
-       }
-       
-       for (i=0; i<nr_dirty; i++) {
-               nouveau_bufferobj_region *r = &dirty->dirty[i];
-
-               DEBUG("dirty %d: o=0x%08x, s=0x%08x\n",
-                               i, r->start, r->end - r->start);
-               nouveau_bo_download_from_screen(ctx,
-                                               r->start,
-                                               r->end - r->start, bo);
-       }
-
-       FREE(dirty->dirty);
-       dirty->dirty    = NULL;
-       dirty->nr_dirty = 0;
-
-       return nr_dirty;
-}
-
-static void
-nouveauBindBuffer(GLcontext *ctx, GLenum target, struct gl_buffer_object *obj)
-{
-}
-
-static struct gl_buffer_object *
-nouveauNewBufferObject(GLcontext *ctx, GLuint buffer, GLenum target)
-{
-       nouveau_buffer_object *nbo;
-
-       nbo = CALLOC_STRUCT(nouveau_buffer_object_t);
-       if (nbo)
-               _mesa_initialize_buffer_object(&nbo->mesa, buffer, target);
-       DEBUG("bo=%p\n", nbo);
+       if (data)
+               memcpy(get_bufferobj_map(obj, NOUVEAU_BO_WR), data, size);
 
-       return nbo ? &nbo->mesa : NULL;
+       return GL_TRUE;
 }
 
 static void
-nouveauDeleteBuffer(GLcontext *ctx, struct gl_buffer_object *obj)
+nouveau_bufferobj_subdata(struct gl_context *ctx, GLenum target, GLintptrARB offset,
+                         GLsizeiptrARB size, const GLvoid *data,
+                         struct gl_buffer_object *obj)
 {
-       nouveau_buffer_object *nbo = (nouveau_buffer_object *)obj;
-
-       if (nbo->gpu_dirty.nr_dirty)
-               FREE(nbo->gpu_dirty.dirty);
-       if (nbo->cpu_dirty.nr_dirty)
-               FREE(nbo->cpu_dirty.dirty);
-       if (nbo->cpu_mem) nouveau_mem_free(ctx, nbo->cpu_mem);
-       if (nbo->gpu_mem) nouveau_mem_free(ctx, nbo->gpu_mem);
-
-       _mesa_delete_buffer_object(ctx, obj);
+       memcpy(get_bufferobj_map(obj, NOUVEAU_BO_WR) + offset, data, size);
 }
 
 static void
-nouveauBufferData(GLcontext *ctx, GLenum target, GLsizeiptrARB size,
-                 const GLvoid *data, GLenum usage,
-                 struct gl_buffer_object *obj)
+nouveau_bufferobj_get_subdata(struct gl_context *ctx, GLenum target, GLintptrARB offset,
+                          GLsizeiptrARB size, GLvoid *data,
+                          struct gl_buffer_object *obj)
 {
-       GLuint gpu_flags;
-
-       DEBUG("target=%s, size=%d, data=%p, usage=%s, obj=%p\n",
-                       _mesa_lookup_enum_by_nr(target),
-                       (GLuint)size, data,
-                       _mesa_lookup_enum_by_nr(usage),
-                       obj);
-
-       switch (target) {
-       case GL_ELEMENT_ARRAY_BUFFER_ARB:
-               gpu_flags = 0;
-               break;
-       default:
-               gpu_flags = NOUVEAU_BO_VRAM_OK | NOUVEAU_BO_GART_OK;
-               break;
-       }
-       nouveau_bo_init_storage(ctx, gpu_flags, size, data, usage, obj);
+       memcpy(data, get_bufferobj_map(obj, NOUVEAU_BO_RD) + offset, size);
 }
 
-static void
-nouveauBufferSubData(GLcontext *ctx, GLenum target, GLintptrARB offset,
-                    GLsizeiptrARB size, const GLvoid *data,
-                    struct gl_buffer_object *obj)
+static void *
+nouveau_bufferobj_map(struct gl_context *ctx, GLenum target, GLenum access,
+                  struct gl_buffer_object *obj)
 {
-       GLvoid *out;
+       unsigned flags = 0;
 
-       DEBUG("target=%s, offset=0x%x, size=%d, data=%p, obj=%p\n",
-                       _mesa_lookup_enum_by_nr(target),
-                       (GLuint)offset, (GLuint)size, data, obj);
+       if (access == GL_READ_ONLY_ARB ||
+           access == GL_READ_WRITE_ARB)
+               flags |= GL_MAP_READ_BIT;
+       if (access == GL_WRITE_ONLY_ARB ||
+           access == GL_READ_WRITE_ARB)
+               flags |= GL_MAP_WRITE_BIT;
 
-       out = nouveau_bo_map(ctx, GL_WRITE_ONLY_ARB, obj);
-       _mesa_memcpy(out + offset, data, size);
-       nouveau_bo_dirty_linear(ctx, GL_FALSE, offset, size, obj);
-       nouveau_bo_unmap(ctx, obj);
+       return ctx->Driver.MapBufferRange(ctx, target, 0, obj->Size, flags,
+                                         obj);
 }
 
-static void
-nouveauGetBufferSubData(GLcontext *ctx, GLenum target, GLintptrARB offset,
-                    GLsizeiptrARB size, GLvoid *data,
-                    struct gl_buffer_object *obj)
+static void *
+nouveau_bufferobj_map_range(struct gl_context *ctx, GLenum target, GLintptr offset,
+                           GLsizeiptr length, GLbitfield access,
+                           struct gl_buffer_object *obj)
 {
-       const GLvoid *in;
+       unsigned flags = 0;
+       char *map;
 
-       DEBUG("target=%s, offset=0x%x, size=%d, data=%p, obj=%p\n",
-                       _mesa_lookup_enum_by_nr(target),
-                       (GLuint)offset, (GLuint)size, data, obj);
+       assert(!obj->Pointer);
 
-       in = nouveau_bo_map(ctx, GL_READ_ONLY_ARB, obj);
-       _mesa_memcpy(data, in + offset, size);
-       nouveau_bo_unmap(ctx, obj);
-}
+       if (access & GL_MAP_READ_BIT)
+               flags |= NOUVEAU_BO_RD;
+       if (access & GL_MAP_WRITE_BIT)
+               flags |= NOUVEAU_BO_WR;
+       if (access & GL_MAP_UNSYNCHRONIZED_BIT)
+               flags |= NOUVEAU_BO_NOSYNC;
 
-static void *
-nouveauMapBuffer(GLcontext *ctx, GLenum target, GLenum access,
-                struct gl_buffer_object *obj)
-{
-       DEBUG("target=%s, access=%s, obj=%p\n",
-                       _mesa_lookup_enum_by_nr(target),
-                       _mesa_lookup_enum_by_nr(access),
-                       obj
-                       );
-
-       /* Already mapped.. */
-       if (obj->Pointer)
+       map = get_bufferobj_map(obj, flags);
+       if (!map)
                return NULL;
 
-       /* Have to pass READ_WRITE here, nouveau_bo_map will only ensure that
-        * the cpu_mem buffer is up-to-date if we ask for read access.
-        *
-        * However, even if the client only asks for write access, we're still
-        * forced to reupload the entire buffer.  So, we need the cpu_mem buffer
-        * to have the correct data all the time.
-        */
-       obj->Pointer = nouveau_bo_map(ctx, GL_READ_WRITE_ARB, obj);
-
-       /* The GL spec says that a client attempting to write to a bufferobj
-        * mapped READ_ONLY object may have unpredictable results, possibly
-        * even program termination.
-        *
-        * We're going to use this, and only mark the buffer as dirtied if
-        * the client asks for write access.
-        */
-       if (target != GL_READ_ONLY_ARB) {
-               /* We have no way of knowing what was modified by the client,
-                * so the entire buffer gets dirtied. */
-               nouveau_bo_dirty_all(ctx, GL_FALSE, obj);
-       }
+       obj->Pointer = map + offset;
+       obj->Offset = offset;
+       obj->Length = length;
+       obj->AccessFlags = access;
 
        return obj->Pointer;
 }
 
 static GLboolean
-nouveauUnmapBuffer(GLcontext *ctx, GLenum target, struct gl_buffer_object *obj)
+nouveau_bufferobj_unmap(struct gl_context *ctx, GLenum target, struct gl_buffer_object *obj)
 {
-       DEBUG("target=%s, obj=%p\n", _mesa_lookup_enum_by_nr(target), obj);
-
        assert(obj->Pointer);
 
-       nouveau_bo_unmap(ctx, obj);
        obj->Pointer = NULL;
+       obj->Offset = 0;
+       obj->Length = 0;
+       obj->AccessFlags = 0;
+
        return GL_TRUE;
 }
-         
+
 void
-nouveauInitBufferObjects(GLcontext *ctx)
+nouveau_bufferobj_functions_init(struct dd_function_table *functions)
 {
-       ctx->Driver.BindBuffer          = nouveauBindBuffer;
-       ctx->Driver.NewBufferObject     = nouveauNewBufferObject;
-       ctx->Driver.DeleteBuffer        = nouveauDeleteBuffer;
-       ctx->Driver.BufferData          = nouveauBufferData;
-       ctx->Driver.BufferSubData       = nouveauBufferSubData;
-       ctx->Driver.GetBufferSubData    = nouveauGetBufferSubData;
-       ctx->Driver.MapBuffer           = nouveauMapBuffer;
-       ctx->Driver.UnmapBuffer         = nouveauUnmapBuffer;
+       functions->NewBufferObject = nouveau_bufferobj_new;
+       functions->DeleteBuffer = nouveau_bufferobj_del;
+       functions->BufferData = nouveau_bufferobj_data;
+       functions->BufferSubData = nouveau_bufferobj_subdata;
+       functions->GetBufferSubData = nouveau_bufferobj_get_subdata;
+       functions->MapBuffer = nouveau_bufferobj_map;
+       functions->MapBufferRange = nouveau_bufferobj_map_range;
+       functions->UnmapBuffer = nouveau_bufferobj_unmap;
 }
-