-#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;
}
-