nouveau: Initial buffer object support
authorBen Skeggs <darktama@iinet.net.au>
Wed, 27 Dec 2006 12:30:34 +0000 (23:30 +1100)
committerBen Skeggs <darktama@iinet.net.au>
Wed, 27 Dec 2006 12:53:25 +0000 (23:53 +1100)
src/mesa/drivers/dri/nouveau/Makefile
src/mesa/drivers/dri/nouveau/nouveau_bufferobj.c [new file with mode: 0644]
src/mesa/drivers/dri/nouveau/nouveau_bufferobj.h [new file with mode: 0644]
src/mesa/drivers/dri/nouveau/nouveau_context.c
src/mesa/drivers/dri/nouveau/nouveau_context.h

index 962978dc7fa8b79b83ce96afb52476f1182cb475..d31b42a568b8700a5d48557a73dc8ed85b09361e 100644 (file)
@@ -8,6 +8,7 @@ LIBNAME = nouveau_dri.so
 MINIGLX_SOURCES = 
 
 DRIVER_SOURCES = \
+       nouveau_bufferobj.c      \
        nouveau_buffers.c        \
        nouveau_card.c           \
        nouveau_context.c        \
diff --git a/src/mesa/drivers/dri/nouveau/nouveau_bufferobj.c b/src/mesa/drivers/dri/nouveau/nouveau_bufferobj.c
new file mode 100644 (file)
index 0000000..d36196a
--- /dev/null
@@ -0,0 +1,272 @@
+#include "bufferobj.h"
+#include "enums.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;
+
+       return nouveau_mem_gpu_offset_get(ctx, nbo->gpu_mem);
+}
+
+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);
+       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;
+}
+
+static void
+nouveauDeleteBuffer(GLcontext *ctx, struct gl_buffer_object *obj)
+{
+       nouveau_buffer_object *nbo = (nouveau_buffer_object *)obj;
+
+       DEBUG("obj=%p\n", obj);
+
+       if (nbo->gpu_mem) {
+               nouveau_mem_free(ctx, nbo->gpu_mem);
+       }
+       _mesa_delete_buffer_object(ctx, obj);
+}
+
+static void
+nouveauBufferData(GLcontext *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;
+       }
+
+       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);
+}
+
+/*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)
+{
+       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);
+}
+
+/*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)
+{
+       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);
+}
+
+static void *
+nouveauMapBuffer(GLcontext *ctx, GLenum target, GLenum access,
+                struct gl_buffer_object *obj)
+{
+       nouveauContextPtr nmesa = NOUVEAU_CONTEXT(ctx);
+       nouveau_buffer_object *nbo = (nouveau_buffer_object *)obj;
+
+       DEBUG("obj=%p, target=%s, access=%s\n",
+                       obj,
+                       _mesa_lookup_enum_by_nr(target),
+                       _mesa_lookup_enum_by_nr(access));
+
+       if (obj->Pointer) {
+               DEBUG("already mapped, return NULL\n");
+               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 = nbo->cpu_mem->map;
+       return obj->Pointer;
+}
+
+static GLboolean
+nouveauUnmapBuffer(GLcontext *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
+
+       obj->Pointer = NULL;
+       return GL_TRUE;
+}
+         
+void
+nouveauInitBufferObjects(GLcontext *ctx)
+{
+       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;
+}
+
diff --git a/src/mesa/drivers/dri/nouveau/nouveau_bufferobj.h b/src/mesa/drivers/dri/nouveau/nouveau_bufferobj.h
new file mode 100644 (file)
index 0000000..fccc349
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef __NOUVEAU_BUFFEROBJ_H__
+#define __NOUVEAU_BUFFEROBJ_H__
+
+#include "mtypes.h"
+#include "nouveau_buffers.h"
+
+typedef struct nouveau_buffer_object_t {
+       /* Base class, must be first */
+       struct gl_buffer_object mesa;
+
+       /* Memory used for GPU access to the buffer*/
+       nouveau_mem *           gpu_mem;
+       /* Buffer has been dirtied by the GPU */
+       GLboolean               gpu_dirty;
+
+       /* Memory used for CPU access to the buffer */
+       nouveau_mem *           cpu_mem;
+       /* Buffer has possibly been dirtied by the CPU */
+       GLboolean               cpu_dirty;
+} nouveau_buffer_object;
+
+extern uint32_t nouveau_bufferobj_gpu_ref(GLcontext *ctx, GLenum access,
+                                         struct gl_buffer_object *obj);
+
+extern void nouveauInitBufferObjects(GLcontext *ctx);
+
+#endif
index bb67f72f4a8f79062ff3ff860eb4f66953fe09d8..79da46fc0b6aa7af770c3b3c4f0ecceca437f238 100644 (file)
@@ -65,6 +65,7 @@ static const struct dri_debug_control debug_control[] =
 {
        { "shaders"   , DEBUG_SHADERS    },
        { "mem"       , DEBUG_MEM        },
+       { "bufferobj" , DEBUG_BUFFEROBJ  },
        { NULL        , 0                }
 };
 
@@ -224,6 +225,7 @@ GLboolean nouveauCreateContext( const __GLcontextModes *glVisual,
                        break;
        }
 
+       nouveauInitBufferObjects(ctx);
        if (!nouveauSyncInitFuncs(ctx))
           return GL_FALSE;
        nmesa->hw_func.InitCard(nmesa);
index b0952070c7792fbcdf8c855b263eb2a7e98d6644..134e2a417e34946586c6333777b71ad1d40af1f0 100644 (file)
@@ -218,6 +218,7 @@ extern int NOUVEAU_DEBUG;
 
 #define DEBUG_SHADERS  0x00000001
 #define DEBUG_MEM      0x00000002
+#define DEBUG_BUFFEROBJ 0x00000004
 
 #endif /* __NOUVEAU_CONTEXT_H__ */