nouveau: new bufferobj code.

The old code suffered from a number of issues, the most severe being that
with the Mesa VBO merge even swtcl used the driver's bufferobj interface.
On most VBO types (or non-AGP cards) the buffer ended up in vram, and
killed swtcl performance greatly.  All bufferobj's start in system memory
now, until they get referenced as a "real" VBO.

The other big change is that only potentially "damaged" areas are
uploaded/downloaded to/from the hardware.
This commit is contained in:
Ben Skeggs
2007-02-06 00:39:50 +11:00
parent 3805ccf020
commit e7654b22aa
3 changed files with 564 additions and 171 deletions
+505 -161
View File
@@ -8,29 +8,458 @@
#include "nouveau_object.h"
#include "nouveau_msg.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)
/* 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 GLboolean
nouveau_bo_download_from_screen(GLcontext *ctx, GLuint offset, GLuint size,
struct gl_buffer_object *bo)
{
nouveau_buffer_object *nbo = (nouveau_buffer_object *)obj;
nouveauContextPtr nmesa = NOUVEAU_CONTEXT(ctx);
nouveau_buffer_object *nbo = (nouveau_buffer_object *)bo;
nouveau_mem *in_mem;
DEBUG("obj=%p, access=%s\n", obj, _mesa_lookup_enum_by_nr(access));
DEBUG("bo=%p, offset=%d, size=%d\n", bo, offset, size);
if (access == GL_WRITE_ONLY_ARB || access == GL_READ_WRITE_ARB)
nbo->gpu_dirty = GL_TRUE;
/* 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 AGP\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;
}
static GLboolean
nouveau_bo_upload_to_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 *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 AGP\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;
}
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;
}
}
nouveau_bo_upload_to_screen(ctx, 0, bo->Size, bo);
bo->OnCard = GL_TRUE;
return GL_TRUE;
}
GLboolean
nouveau_bo_move_out(GLcontext *ctx, struct gl_buffer_object *bo)
{
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);
bo->OnCard = GL_FALSE;
return GL_TRUE;
}
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);
}
nbo->gpu_mem_flags = gpu_type;
nbo->cpu_mem_flags = cpu_type;
nbo->usage = usage;
}
void
nouveau_bo_init_storage(GLcontext *ctx, GLuint valid_gpu_access,
GLsizeiptrARB size,
const GLvoid *data,
GLenum usage,
struct gl_buffer_object *bo)
{
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);
}
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);
}
}
void *
nouveau_bo_map(GLcontext *ctx, GLenum access, struct gl_buffer_object *bo)
{
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));
if (bo->OnCard &&
(access == GL_READ_ONLY_ARB || access == GL_READ_WRITE_ARB)) {
GLuint nr_dirty;
DEBUG("..on card\n");
nr_dirty = nouveau_bo_download_dirty(ctx, bo);
/* 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 (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;
}
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)
{
@@ -42,10 +471,11 @@ 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;
if (nbo)
_mesa_initialize_buffer_object(&nbo->mesa, buffer, target);
DEBUG("bo=%p\n", nbo);
return nbo ? &nbo->mesa : NULL;
}
static void
@@ -53,11 +483,13 @@ nouveauDeleteBuffer(GLcontext *ctx, struct gl_buffer_object *obj)
{
nouveau_buffer_object *nbo = (nouveau_buffer_object *)obj;
DEBUG("obj=%p\n", 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);
if (nbo->gpu_mem) {
nouveau_mem_free(ctx, nbo->gpu_mem);
}
_mesa_delete_buffer_object(ctx, obj);
}
@@ -66,193 +498,105 @@ 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;
GLuint gpu_flags;
DEBUG("obj=%p, target=%s, usage=%s, size=%d, data=%p\n",
obj,
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),
(unsigned int)size,
data);
obj);
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;
switch (target) {
case GL_ELEMENT_ARRAY_BUFFER_ARB:
gpu_flags = 0;
break;
default:
gpu_flags = NOUVEAU_BO_VRAM_OK | NOUVEAU_BO_AGP_OK;
break;
}
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);
nouveau_bo_init_storage(ctx, gpu_flags, size, data, usage, 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);
GLvoid *out;
ctx->Driver.MapBuffer(ctx, target, GL_WRITE_ONLY_ARB, obj);
_mesa_memcpy((GLubyte *)obj->Pointer + offset, data, size);
ctx->Driver.UnmapBuffer(ctx, target, obj);
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);
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);
}
/*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);
const GLvoid *in;
ctx->Driver.MapBuffer(ctx, target, GL_READ_ONLY_ARB, obj);
_mesa_memcpy(data, (GLubyte *)obj->Pointer + offset, size);
ctx->Driver.UnmapBuffer(ctx, target, obj);
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);
in = nouveau_bo_map(ctx, GL_READ_ONLY_ARB, obj);
_mesa_memcpy(data, in + offset, size);
nouveau_bo_unmap(ctx, 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,
DEBUG("target=%s, access=%s, obj=%p\n",
_mesa_lookup_enum_by_nr(target),
_mesa_lookup_enum_by_nr(access));
_mesa_lookup_enum_by_nr(access),
obj
);
if (obj->Pointer) {
DEBUG("already mapped, return NULL\n");
/* Already mapped.. */
if (obj->Pointer)
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.
/* 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.
*/
if (nbo->gpu_mem->type & NOUVEAU_MEM_FB) {
DEBUG("Data in VRAM, copying to AGP for CPU access\n");
obj->Pointer = nouveau_bo_map(ctx, GL_READ_WRITE_ARB, obj);
/* 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..
/* 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 (!nbo->cpu_mem) {
DEBUG("Returning direct pointer to VRAM\n");
nbo->cpu_mem = nbo->gpu_mem;
nbo->cpu_dirty = GL_FALSE;
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 = 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("target=%s, obj=%p\n", _mesa_lookup_enum_by_nr(target), 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);
nouveau_bo_unmap(ctx, obj);
obj->Pointer = NULL;
return GL_TRUE;
}
@@ -4,24 +4,74 @@
#include "mtypes.h"
#include "nouveau_buffers.h"
#define NOUVEAU_BO_VRAM_OK (NOUVEAU_MEM_FB | NOUVEAU_MEM_FB_ACCEPTABLE)
#define NOUVEAU_BO_AGP_OK (NOUVEAU_MEM_AGP | NOUVEAU_MEM_AGP_ACCEPTABLE)
typedef struct nouveau_bufferobj_region_t {
uint32_t start;
uint32_t end;
} nouveau_bufferobj_region;
typedef struct nouveau_bufferobj_dirty_t {
nouveau_bufferobj_region *dirty;
int nr_dirty;
} nouveau_bufferobj_dirty;
typedef struct nouveau_buffer_object_t {
/* Base class, must be first */
struct gl_buffer_object mesa;
GLboolean mapped;
GLenum usage;
/* Memory used for GPU access to the buffer*/
GLuint gpu_mem_flags;
nouveau_mem * gpu_mem;
/* Buffer has been dirtied by the GPU */
GLboolean gpu_dirty;
nouveau_bufferobj_dirty gpu_dirty;
/* Memory used for CPU access to the buffer */
GLuint cpu_mem_flags;
nouveau_mem * cpu_mem;
/* Buffer has possibly been dirtied by the CPU */
GLboolean cpu_dirty;
GLvoid * cpu_mem_sys;
nouveau_bufferobj_dirty cpu_dirty;
} nouveau_buffer_object;
extern uint32_t nouveau_bufferobj_gpu_ref(GLcontext *ctx, GLenum access,
struct gl_buffer_object *obj);
extern void
nouveau_bo_init_storage(GLcontext *ctx, GLuint valid_gpu_access,
GLsizeiptrARB size, const GLvoid *data, GLenum usage,
struct gl_buffer_object *bo);
extern void nouveauInitBufferObjects(GLcontext *ctx);
extern GLboolean
nouveau_bo_move_in(GLcontext *ctx, struct gl_buffer_object *bo);
extern GLboolean
nouveau_bo_move_out(GLcontext *ctx, struct gl_buffer_object *bo);
extern void *
nouveau_bo_map(GLcontext *ctx, GLenum usage, struct gl_buffer_object *bo);
extern void
nouveau_bo_unmap(GLcontext *ctx, struct gl_buffer_object *bo);
extern uint32_t
nouveau_bo_gpu_ref(GLcontext *ctx, struct gl_buffer_object *bo);
extern void
nouveau_bo_dirty_linear(GLcontext *ctx, GLboolean on_card,
uint32_t offset, uint32_t size,
struct gl_buffer_object *bo);
extern void
nouveau_bo_dirty_all(GLcontext *ctx, GLboolean on_card,
struct gl_buffer_object *bo);
extern GLuint
nouveau_bo_upload_dirty(GLcontext *ctx, struct gl_buffer_object *bo);
extern GLuint
nouveau_bo_download_dirty(GLcontext *ctx, struct gl_buffer_object *bo);
extern void
nouveauInitBufferObjects(GLcontext *ctx);
#endif
+2 -3
View File
@@ -32,14 +32,13 @@ NV30FPUploadToHW(GLcontext *ctx, nouveauShader *nvs)
GL_ARRAY_BUFFER_ARB);
/* Should use STATIC_DRAW_ARB if shader doesn't use changable params */
ctx->Driver.BufferData(ctx, GL_ARRAY_BUFFER_ARB,
nouveau_bo_init_storage(ctx, NOUVEAU_BO_VRAM_OK,
nvs->program_size * sizeof(uint32_t),
(const GLvoid *)nvs->program,
GL_DYNAMIC_DRAW_ARB,
nvs->program_buffer);
offset = nouveau_bufferobj_gpu_ref(ctx, GL_READ_ONLY_ARB,
nvs->program_buffer);
offset = nouveau_bo_gpu_ref(ctx, nvs->program_buffer);
/* Not using state cache here, updated programs at the same address don't
* seem to take effect unless the ACTIVE_PROGRAM method is called again.