Merge remote branch 'origin/master' into pipe-video
[mesa.git] / src / mesa / drivers / dri / nouveau / nouveau_bufferobj.c
index d36196aeef28277aeb96e00c55bb38bf18efc194..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_buffers.h"
 #include "nouveau_context.h"
-#include "nouveau_drm.h"
-#include "nouveau_object.h"
-#include "nouveau_msg.h"
-
-#define DEBUG(fmt,args...) do {                \
-       if (NOUVEAU_DEBUG & DEBUG_BUFFEROBJ) { \
-               fprintf(stderr, "%s: "fmt, __func__, ##args);  \
-       }                                      \
-} while(0)
-
-/* Wrapper for nouveau_mem_gpu_offset_get() that marks the bufferobj dirty
- * if the GPU modifies the data.
- */
-uint32_t
-nouveau_bufferobj_gpu_ref(GLcontext *ctx, GLenum access,
-                         struct gl_buffer_object *obj)
-{
-       nouveau_buffer_object *nbo = (nouveau_buffer_object *)obj;
-
-       DEBUG("obj=%p, access=%s\n", obj, _mesa_lookup_enum_by_nr(access));
 
-       if (access == GL_WRITE_ONLY_ARB || access == GL_READ_WRITE_ARB)
-               nbo->gpu_dirty = GL_TRUE;
+#include "main/bufferobj.h"
 
-       return nouveau_mem_gpu_offset_get(ctx, nbo->gpu_mem);
-}
-
-static void
-nouveauBindBuffer(GLcontext *ctx, GLenum target, struct gl_buffer_object *obj)
+static inline char *
+get_bufferobj_map(struct gl_buffer_object *obj, unsigned flags)
 {
+       struct nouveau_bufferobj *nbo = to_nouveau_bufferobj(obj);
+       void *map = NULL;
+
+       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);
+       }
+
+       return map;
 }
 
 static struct gl_buffer_object *
-nouveauNewBufferObject(GLcontext *ctx, GLuint buffer, GLenum target)
+nouveau_bufferobj_new(struct gl_context *ctx, GLuint buffer, GLenum target)
 {
-       nouveau_buffer_object *nbo;
+       struct nouveau_bufferobj *nbo;
 
-       nbo = CALLOC_STRUCT(nouveau_buffer_object_t);
-       DEBUG("name=0x%08x, target=%s, obj=%p\n",
-                       buffer, _mesa_lookup_enum_by_nr(target), nbo);
-       _mesa_initialize_buffer_object(&nbo->mesa, buffer, target);
-       return &nbo->mesa;
+       nbo = CALLOC_STRUCT(nouveau_bufferobj);
+       if (!nbo)
+               return NULL;
+
+       _mesa_initialize_buffer_object(&nbo->base, buffer, target);
+
+       return &nbo->base;
 }
 
 static void
-nouveauDeleteBuffer(GLcontext *ctx, struct gl_buffer_object *obj)
+nouveau_bufferobj_del(struct gl_context *ctx, struct gl_buffer_object *obj)
 {
-       nouveau_buffer_object *nbo = (nouveau_buffer_object *)obj;
+       struct nouveau_bufferobj *nbo = to_nouveau_bufferobj(obj);
 
-       DEBUG("obj=%p\n", obj);
-
-       if (nbo->gpu_mem) {
-               nouveau_mem_free(ctx, nbo->gpu_mem);
-       }
-       _mesa_delete_buffer_object(ctx, obj);
+       nouveau_bo_ref(NULL, &nbo->bo);
+       FREE(nbo->sys);
+       FREE(nbo);
 }
 
-static void
-nouveauBufferData(GLcontext *ctx, GLenum target, GLsizeiptrARB size,
-                 const GLvoid *data, GLenum usage,
-                 struct gl_buffer_object *obj)
+static GLboolean
+nouveau_bufferobj_data(struct gl_context *ctx, GLenum target, GLsizeiptrARB size,
+                      const GLvoid *data, GLenum usage,
+                      struct gl_buffer_object *obj)
 {
-       nouveau_buffer_object *nbo = (nouveau_buffer_object *)obj;
-
-       DEBUG("obj=%p, target=%s, usage=%s, size=%d, data=%p\n",
-                       obj,
-                       _mesa_lookup_enum_by_nr(target),
-                       _mesa_lookup_enum_by_nr(usage),
-                       (unsigned int)size,
-                       data);
-
-       if (nbo->gpu_mem && nbo->gpu_mem->size != size)
-               nouveau_mem_free(ctx, nbo->gpu_mem);
-
-       /* Always have the GPU access the data from VRAM if possible.  For
-        * some "usage" values it may be better from AGP be default?
-        *
-        * TODO: At some point we should drop the NOUVEAU_MEM_MAPPED flag.
-        * TODO: Use the NOUVEAU_MEM_AGP_ACCEPTABLE flag.
-        * TODO: What about PCI-E and shared system memory?
-        */
-       if (!nbo->gpu_mem)
-               nbo->gpu_mem = nouveau_mem_alloc(ctx,
-                                                NOUVEAU_MEM_FB |
-                                                NOUVEAU_MEM_MAPPED,
-                                                size,
-                                                0);
-
-       if (!nbo->gpu_mem) {
-               MESSAGE("AIII bufferobj malloc failed\n");
-               return;
-       }
+       struct nouveau_bufferobj *nbo = to_nouveau_bufferobj(obj);
+       int ret;
 
+       obj->Size = size;
        obj->Usage = usage;
-       obj->Size  = size;
-       if (!data)
-               return;
 
-       ctx->Driver.MapBuffer(ctx, target, GL_WRITE_ONLY_ARB, obj);
-       _mesa_memcpy(nbo->cpu_mem->map, data, size);
-       ctx->Driver.UnmapBuffer(ctx, target, obj);
+       /* Free previous storage */
+       nouveau_bo_ref(NULL, &nbo->bo);
+       FREE(nbo->sys);
+
+       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);
+
+       } else {
+               /* Get a hardware BO */
+               ret = nouveau_bo_new(context_dev(ctx),
+                                    NOUVEAU_BO_GART | NOUVEAU_BO_MAP, 0,
+                                    size, &nbo->bo);
+               assert(!ret);
+       }
+
+       if (data)
+               memcpy(get_bufferobj_map(obj, NOUVEAU_BO_WR), data, size);
+
+       return GL_TRUE;
 }
 
-/*TODO: we don't need to DMA the entire buffer like MapBuffer does.. */
 static void
-nouveauBufferSubData(GLcontext *ctx, GLenum target, GLintptrARB offset,
-                    GLsizeiptrARB size, const GLvoid *data,
-                    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)
 {
-       DEBUG("obj=%p, target=%s, offset=0x%x, size=%d, data=%p\n",
-                       obj,
-                       _mesa_lookup_enum_by_nr(target),
-                       (unsigned int)offset,
-                       (unsigned int)size,
-                       data);
-
-       ctx->Driver.MapBuffer(ctx, target, GL_WRITE_ONLY_ARB, obj);
-       _mesa_memcpy((GLubyte *)obj->Pointer + offset, data, size);
-       ctx->Driver.UnmapBuffer(ctx, target, obj);
+       memcpy(get_bufferobj_map(obj, NOUVEAU_BO_WR) + offset, data, size);
 }
 
-/*TODO: we don't need to DMA the entire buffer like MapBuffer does.. */
 static void
-nouveauGetBufferSubData(GLcontext *ctx, GLenum target, GLintptrARB offset,
-                    GLsizeiptrARB size, GLvoid *data,
-                    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)
 {
-       DEBUG("obj=%p, target=%s, offset=0x%x, size=%d, data=%p\n",
-                       obj,
-                       _mesa_lookup_enum_by_nr(target),
-                       (unsigned int)offset,
-                       (unsigned int)size,
-                       data);
-
-       ctx->Driver.MapBuffer(ctx, target, GL_READ_ONLY_ARB, obj);
-       _mesa_memcpy(data, (GLubyte *)obj->Pointer + offset, size);
-       ctx->Driver.UnmapBuffer(ctx, target, obj);
+       memcpy(data, get_bufferobj_map(obj, NOUVEAU_BO_RD) + offset, size);
+}
+
+static void *
+nouveau_bufferobj_map(struct gl_context *ctx, GLenum target, GLenum access,
+                  struct gl_buffer_object *obj)
+{
+       unsigned flags = 0;
+
+       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;
+
+       return ctx->Driver.MapBufferRange(ctx, target, 0, obj->Size, flags,
+                                         obj);
 }
 
 static void *
-nouveauMapBuffer(GLcontext *ctx, GLenum target, GLenum access,
-                struct gl_buffer_object *obj)
+nouveau_bufferobj_map_range(struct gl_context *ctx, GLenum target, GLintptr offset,
+                           GLsizeiptr length, GLbitfield access,
+                           struct gl_buffer_object *obj)
 {
-       nouveauContextPtr nmesa = NOUVEAU_CONTEXT(ctx);
-       nouveau_buffer_object *nbo = (nouveau_buffer_object *)obj;
+       unsigned flags = 0;
+       char *map;
+
+       assert(!obj->Pointer);
 
-       DEBUG("obj=%p, target=%s, access=%s\n",
-                       obj,
-                       _mesa_lookup_enum_by_nr(target),
-                       _mesa_lookup_enum_by_nr(access));
+       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;
 
-       if (obj->Pointer) {
-               DEBUG("already mapped, return NULL\n");
+       map = get_bufferobj_map(obj, flags);
+       if (!map)
                return NULL;
-       }
 
-#ifdef ALLOW_MULTI_SUBCHANNEL
-       /* If GPU is accessing the data from VRAM, copy to faster AGP memory
-        * before CPU access to the buffer.
-        */
-       if (nbo->gpu_mem->type & NOUVEAU_MEM_FB) {
-               DEBUG("Data in VRAM, copying to AGP for CPU access\n");
-
-               /* This can happen if BufferData grows the GPU-access buffer */
-               if (nbo->cpu_mem && nbo->cpu_mem->size != nbo->gpu_mem->size) {
-                       nouveau_mem_free(ctx, nbo->cpu_mem);
-                       nbo->cpu_mem = NULL;
-               }
-
-               if (!nbo->cpu_mem) {
-                       nbo->cpu_mem = nouveau_mem_alloc(ctx,
-                                                        NOUVEAU_MEM_AGP |
-                                                        NOUVEAU_MEM_MAPPED,
-                                                        nbo->gpu_mem->size,
-                                                        0);
-
-                       /* Mark GPU data as modified, so it gets copied to
-                        * the new buffer */
-                       nbo->gpu_dirty = GL_TRUE;
-               }
-
-               if (nbo->cpu_mem && nbo->gpu_dirty) {
-                       nouveau_memformat_flat_emit(ctx, nbo->cpu_mem,
-                                                        nbo->gpu_mem,
-                                                        0, 0,
-                                                        nbo->gpu_mem->size);
-
-                       nouveau_notifier_wait_nop(ctx,
-                                                 nmesa->syncNotifier,
-                                                 NvSubMemFormat);
-                       nbo->gpu_dirty = GL_FALSE;
-               }
-
-               /* buffer isn't guaranteed to be up-to-date on the card now */
-               nbo->cpu_dirty = GL_TRUE;
-       }
-#endif
-
-       /* If the copy to AGP failed for some reason, just return a pointer
-        * directly to vram..
-        */
-       if (!nbo->cpu_mem) {
-               DEBUG("Returning direct pointer to VRAM\n");
-               nbo->cpu_mem   = nbo->gpu_mem;
-               nbo->cpu_dirty = GL_FALSE;
-       }
+       obj->Pointer = map + offset;
+       obj->Offset = offset;
+       obj->Length = length;
+       obj->AccessFlags = access;
 
-       obj->Pointer = nbo->cpu_mem->map;
        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)
 {
-       nouveauContextPtr nmesa = NOUVEAU_CONTEXT(ctx);
-       nouveau_buffer_object *nbo = (nouveau_buffer_object *)obj;
-
-       DEBUG("obj=%p, target=%s\n", obj, _mesa_lookup_enum_by_nr(target));
-
-#ifdef ALLOW_MULTI_SUBCHANNEL
-       if (nbo->cpu_dirty && nbo->cpu_mem != nbo->gpu_mem) {
-               DEBUG("Copying potentially modified data back to GPU\n");
-
-               /* blit from GPU buffer -> CPU  buffer */
-               nouveau_memformat_flat_emit(ctx, nbo->gpu_mem, nbo->cpu_mem,
-                                           0, 0, nbo->cpu_mem->size);
-
-               /* buffer is now up-to-date on the hardware (or rather, will
-                * be by the time any other commands in this channel reference
-                * the data.)
-                */
-               nbo->cpu_dirty = GL_FALSE;
-
-               /* we can avoid this wait in some cases.. */
-               nouveau_notifier_wait_nop(ctx,
-                                         nmesa->syncNotifier,
-                                         NvSubMemFormat);
-
-               /* If it's likely CPU access to the buffer will occur often,
-                * keep the cpu_mem around to avoid repeated allocs.
-                */
-               if (obj->Usage != GL_DYNAMIC_DRAW_ARB) {
-
-                       nouveau_mem_free(ctx, nbo->cpu_mem);
-                       nbo->cpu_mem = NULL;
-               }
-       }
-#endif
+       assert(obj->Pointer);
 
        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;
 }
-