-#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)
+#include "main/bufferobj.h"
-/* 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)
+static inline char *
+get_bufferobj_map(struct gl_context *ctx, struct gl_buffer_object *obj,
+ unsigned flags)
{
- 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);
-}
+ 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, context_client(ctx));
+ map = nbo->bo->map;
+ }
-static void
-nouveauBindBuffer(GLcontext *ctx, GLenum target, struct gl_buffer_object *obj)
-{
+ return map;
}
static struct gl_buffer_object *
-nouveauNewBufferObject(GLcontext *ctx, GLuint buffer, GLenum target)
+nouveau_bufferobj_new(struct gl_context *ctx, GLuint buffer)
{
- 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;
-}
+ struct nouveau_bufferobj *nbo;
-static void
-nouveauDeleteBuffer(GLcontext *ctx, struct gl_buffer_object *obj)
-{
- nouveau_buffer_object *nbo = (nouveau_buffer_object *)obj;
+ nbo = CALLOC_STRUCT(nouveau_bufferobj);
+ if (!nbo)
+ return NULL;
- DEBUG("obj=%p\n", obj);
+ _mesa_initialize_buffer_object(ctx, &nbo->base, buffer);
- if (nbo->gpu_mem) {
- nouveau_mem_free(ctx, nbo->gpu_mem);
- }
- _mesa_delete_buffer_object(ctx, obj);
+ return &nbo->base;
}
static void
-nouveauBufferData(GLcontext *ctx, GLenum target, GLsizeiptrARB size,
- const GLvoid *data, GLenum usage,
- 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;
-
- 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);
+ struct nouveau_bufferobj *nbo = to_nouveau_bufferobj(obj);
- if (nbo->gpu_mem && nbo->gpu_mem->size != size)
- nouveau_mem_free(ctx, nbo->gpu_mem);
+ nouveau_bo_ref(NULL, &nbo->bo);
+ free(nbo->sys);
+ free(nbo);
+}
- /* 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);
+static GLboolean
+nouveau_bufferobj_data(struct gl_context *ctx, GLenum target, GLsizeiptrARB size,
+ const GLvoid *data, GLenum usage, GLbitfield storageFlags,
+ struct gl_buffer_object *obj)
+{
+ struct nouveau_bufferobj *nbo = to_nouveau_bufferobj(obj);
+ int ret;
- if (!nbo->gpu_mem) {
- MESSAGE("AIII bufferobj malloc failed\n");
- return;
+ obj->Size = size;
+ obj->Usage = usage;
+ obj->StorageFlags = storageFlags;
+
+ /* Free previous storage */
+ nouveau_bo_ref(NULL, &nbo->bo);
+ free(nbo->sys);
+ nbo->sys = NULL;
+
+ 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,
+ ctx->Const.MinMapBufferAlignment,
+ size, NULL, &nbo->bo);
+ assert(!ret);
}
- obj->Usage = usage;
- obj->Size = size;
- if (!data)
- return;
+ if (data)
+ memcpy(get_bufferobj_map(ctx, obj, NOUVEAU_BO_WR), data, size);
- ctx->Driver.MapBuffer(ctx, target, GL_WRITE_ONLY_ARB, obj);
- _mesa_memcpy(nbo->cpu_mem->map, data, size);
- ctx->Driver.UnmapBuffer(ctx, target, obj);
+ 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, 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(ctx, 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, 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(ctx, obj, NOUVEAU_BO_RD) + offset, size);
}
static void *
-nouveauMapBuffer(GLcontext *ctx, GLenum target, GLenum access,
- struct gl_buffer_object *obj)
+nouveau_bufferobj_map_range(struct gl_context *ctx, GLintptr offset,
+ GLsizeiptr length, GLbitfield access,
+ struct gl_buffer_object *obj,
+ gl_map_buffer_index index)
{
- nouveauContextPtr nmesa = NOUVEAU_CONTEXT(ctx);
- nouveau_buffer_object *nbo = (nouveau_buffer_object *)obj;
+ unsigned flags = 0;
+ char *map;
- DEBUG("obj=%p, target=%s, access=%s\n",
- obj,
- _mesa_lookup_enum_by_nr(target),
- _mesa_lookup_enum_by_nr(access));
+ assert(!obj->Mappings[index].Pointer);
- if (obj->Pointer) {
- DEBUG("already mapped, return NULL\n");
- return NULL;
+ if (!(access & GL_MAP_UNSYNCHRONIZED_BIT)) {
+ if (access & GL_MAP_READ_BIT)
+ flags |= NOUVEAU_BO_RD;
+ if (access & GL_MAP_WRITE_BIT)
+ flags |= NOUVEAU_BO_WR;
}
-#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
+ map = get_bufferobj_map(ctx, obj, flags);
+ if (!map)
+ return NULL;
- /* 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->Mappings[index].Pointer = map + offset;
+ obj->Mappings[index].Offset = offset;
+ obj->Mappings[index].Length = length;
+ obj->Mappings[index].AccessFlags = access;
- obj->Pointer = nbo->cpu_mem->map;
- return obj->Pointer;
+ return obj->Mappings[index].Pointer;
}
static GLboolean
-nouveauUnmapBuffer(GLcontext *ctx, GLenum target, struct gl_buffer_object *obj)
+nouveau_bufferobj_unmap(struct gl_context *ctx, struct gl_buffer_object *obj,
+ gl_map_buffer_index index)
{
- 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;
+ assert(obj->Mappings[index].Pointer);
- /* we can avoid this wait in some cases.. */
- nouveau_notifier_wait_nop(ctx,
- nmesa->syncNotifier,
- NvSubMemFormat);
+ obj->Mappings[index].Pointer = NULL;
+ obj->Mappings[index].Offset = 0;
+ obj->Mappings[index].Length = 0;
+ obj->Mappings[index].AccessFlags = 0;
- /* 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)
+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->MapBufferRange = nouveau_bufferobj_map_range;
+ functions->UnmapBuffer = nouveau_bufferobj_unmap;
}
-