[g3dvl] move zscan into shaders
[mesa.git] / src / gallium / drivers / nvfx / nvfx_transfer.c
index b2ef27cf57917461bc03b6c4ba5c063e7fe2e7be..2debcb6eb8f92578fcb9221009438f0f16321549 100644 (file)
 #include "util/u_format.h"
 #include "util/u_memory.h"
 #include "util/u_math.h"
-#include "nouveau/nouveau_winsys.h"
+#include "util/u_staging.h"
 #include "nvfx_context.h"
 #include "nvfx_screen.h"
 #include "nvfx_state.h"
 #include "nvfx_resource.h"
 #include "nvfx_transfer.h"
 
-struct nvfx_transfer {
-       struct pipe_transfer base;
-       struct pipe_surface *surface;
-       boolean direct;
-};
-
-static void
-nvfx_compatible_transfer_tex(struct pipe_resource *pt, unsigned width, unsigned height,
-                            unsigned bind,
-                             struct pipe_resource *template)
+struct nvfx_staging_transfer
 {
-       memset(template, 0, sizeof(struct pipe_resource));
-       template->target = pt->target;
-       template->format = pt->format;
-       template->width0 = width;
-       template->height0 = height;
-       template->depth0 = 1;
-       template->last_level = 0;
-       template->nr_samples = pt->nr_samples;
-       template->bind = bind;
-       template->usage = PIPE_USAGE_DYNAMIC;
-       template->flags = NVFX_RESOURCE_FLAG_LINEAR;
-}
+       struct util_staging_transfer base;
 
+       unsigned offset;
+       unsigned map_count;
+};
 
-static unsigned nvfx_transfer_bind_flags( unsigned transfer_usage )
+struct pipe_transfer *
+nvfx_transfer_new(struct pipe_context *pipe,
+                 struct pipe_resource *pt,
+                 unsigned level,
+                 unsigned usage,
+                 const struct pipe_box *box)
 {
-       unsigned bind = 0;
+        if((usage & (PIPE_TRANSFER_UNSYNCHRONIZED | PIPE_TRANSFER_DONTBLOCK)) == PIPE_TRANSFER_DONTBLOCK)
+        {
+                struct nouveau_bo* bo = ((struct nvfx_resource*)pt)->bo;
+                if(bo && nouveau_bo_busy(bo, NOUVEAU_BO_WR))
+                        return NULL;
+        }
+
+       if(pt->target == PIPE_BUFFER)
+       {
+               // it would be nice if we could avoid all this ridiculous overhead...
+               struct pipe_transfer* tx;
+               struct nvfx_buffer* buffer = nvfx_buffer(pt);
+
+               tx = CALLOC_STRUCT(pipe_transfer);
+               if (!tx)
+                       return NULL;
 
-       if (transfer_usage & PIPE_TRANSFER_WRITE)
-               bind |= PIPE_BIND_BLIT_SOURCE;
+               pipe_resource_reference(&tx->resource, pt);
+               tx->level = level;
+               tx->usage = usage;
+               tx->box = *box;
 
-       if (transfer_usage & PIPE_TRANSFER_READ)
-               bind |= PIPE_BIND_BLIT_DESTINATION;
+               tx->layer_stride = tx->stride = util_format_get_stride(pt->format, box->width);
+               tx->data = buffer->data + util_format_get_stride(pt->format, box->x);
 
-       return bind;
+               return tx;
+       }
+       else
+       {
+               struct nvfx_staging_transfer* tx;
+               bool direct = !nvfx_resource_on_gpu(pt) && pt->flags & NVFX_RESOURCE_FLAG_LINEAR;
+
+               tx = CALLOC_STRUCT(nvfx_staging_transfer);
+               if(!tx)
+                       return NULL;
+
+               util_staging_transfer_init(pipe, pt, level, usage, box, direct, &tx->base);
+
+               if(direct)
+               {
+                       tx->base.base.stride = nvfx_subresource_pitch(pt, level);
+                       tx->base.base.layer_stride = tx->base.base.stride * u_minify(pt->height0, level);
+                       tx->offset = nvfx_subresource_offset(pt, box->z, level, box->z)
+                               + util_format_get_2d_size(pt->format, tx->base.base.stride, box->y)
+                               + util_format_get_stride(pt->format, box->x);
+               }
+               else
+               {
+                       tx->base.base.stride = nvfx_subresource_pitch(tx->base.staging_resource, 0);
+                       tx->base.base.layer_stride = tx->base.base.stride * tx->base.staging_resource->height0;
+                       tx->offset = 0;
+               }
+
+               assert(tx->base.base.stride);
+
+               return &tx->base.base;
+       }
 }
 
-struct pipe_transfer *
-nvfx_miptree_transfer_new(struct pipe_context *pipe,
-                         struct pipe_resource *pt,
-                         struct pipe_subresource sr,
-                         unsigned usage,
-                         const struct pipe_box *box)
+static void nvfx_buffer_dirty_interval(struct nvfx_buffer* buffer, unsigned begin, unsigned size, boolean unsynchronized)
 {
-       struct pipe_screen *pscreen = pipe->screen;
-       struct nvfx_miptree *mt = (struct nvfx_miptree *)pt;
-       struct nvfx_transfer *tx;
-       struct pipe_resource tx_tex_template, *tx_tex;
-       static int no_transfer = -1;
-       unsigned bind = nvfx_transfer_bind_flags(usage);
-       if(no_transfer < 0)
-               no_transfer = debug_get_bool_option("NOUVEAU_NO_TRANSFER", FALSE);
-
-
-       tx = CALLOC_STRUCT(nvfx_transfer);
-       if (!tx)
-               return NULL;
-
-       /* Don't handle 3D transfers yet.
-        */
-       assert(box->depth == 1);
-
-       pipe_resource_reference(&tx->base.resource, pt);
-       tx->base.sr = sr;
-       tx->base.usage = usage;
-       tx->base.box = *box;
-       tx->base.stride = mt->level[sr.level].pitch;
-
-       /* Direct access to texture */
-       if ((pt->usage == PIPE_USAGE_DYNAMIC ||
-            no_transfer) &&
-           pt->flags & NVFX_RESOURCE_FLAG_LINEAR)
+       struct nvfx_screen* screen = nvfx_screen(buffer->base.base.screen);
+       buffer->last_update_static = buffer->bytes_to_draw_until_static < 0;
+       if(buffer->dirty_begin == buffer->dirty_end)
        {
-               tx->direct = true;
-
-               /* XXX: just call the internal nvfx function.  
-                */
-               tx->surface = pscreen->get_tex_surface(pscreen, pt,
-                                                      sr.face, sr.level,
-                                                      box->z,
-                                                      bind);
-               return &tx->base;
+               buffer->dirty_begin = begin;
+               buffer->dirty_end = begin + size;
+               buffer->dirty_unsynchronized = unsynchronized;
        }
-
-       tx->direct = false;
-
-       nvfx_compatible_transfer_tex(pt, box->width, box->height, bind, &tx_tex_template);
-
-       tx_tex = pscreen->resource_create(pscreen, &tx_tex_template);
-       if (!tx_tex)
+       else
        {
-               FREE(tx);
-               return NULL;
+               buffer->dirty_begin = MIN2(buffer->dirty_begin, begin);
+               buffer->dirty_end = MAX2(buffer->dirty_end, begin + size);
+               buffer->dirty_unsynchronized &= unsynchronized;
        }
 
-       tx->base.stride = ((struct nvfx_miptree*)tx_tex)->level[0].pitch;
-
-       tx->surface = pscreen->get_tex_surface(pscreen, tx_tex,
-                                              0, 0, 0,
-                                              bind);
-
-       pipe_resource_reference(&tx_tex, NULL);
-
-       if (!tx->surface)
+       if(unsynchronized)
        {
-               pipe_surface_reference(&tx->surface, NULL);
-               FREE(tx);
-               return NULL;
+               // TODO: revisit this, it doesn't seem quite right
+               //printf("UNSYNC UPDATE %p %u %u\n", buffer, begin, size);
+               buffer->bytes_to_draw_until_static += size * screen->static_reuse_threshold;
        }
+       else
+               buffer->bytes_to_draw_until_static = buffer->size * screen->static_reuse_threshold;
+}
 
-       if (usage & PIPE_TRANSFER_READ) {
-               struct nvfx_screen *nvscreen = nvfx_screen(pscreen);
-               struct pipe_surface *src;
-
-               src = pscreen->get_tex_surface(pscreen, pt,
-                                              sr.face, sr.level, box->z,
-                                              PIPE_BIND_BLIT_SOURCE);
+static void nvfx_transfer_flush_region( struct pipe_context *pipe,
+                                     struct pipe_transfer *ptx,
+                                     const struct pipe_box *box)
+{
+       if(ptx->resource->target == PIPE_BUFFER && (ptx->usage & PIPE_TRANSFER_FLUSH_EXPLICIT))
+       {
+               struct nvfx_buffer* buffer = nvfx_buffer(ptx->resource);
+               nvfx_buffer_dirty_interval(buffer,
+                               (uint8_t*)ptx->data - buffer->data + util_format_get_stride(buffer->base.base.format, box->x),
+                               util_format_get_stride(buffer->base.base.format, box->width),
+                               !!(ptx->usage & PIPE_TRANSFER_UNSYNCHRONIZED));
+       }
+}
 
-               /* TODO: Check if SIFM can deal with x,y,w,h when swizzling */
-               /* TODO: Check if SIFM can un-swizzle */
-               nvscreen->eng2d->copy(nvscreen->eng2d,
-                                     tx->surface, 0, 0,
-                                     src,
-                                     box->x, box->y,
-                                     box->width, box->height);
+static void
+nvfx_transfer_destroy(struct pipe_context *pipe, struct pipe_transfer *ptx)
+{
+       if(ptx->resource->target == PIPE_BUFFER)
+       {
+               struct nvfx_buffer* buffer = nvfx_buffer(ptx->resource);
+               if((ptx->usage & (PIPE_TRANSFER_WRITE | PIPE_TRANSFER_FLUSH_EXPLICIT)) == PIPE_TRANSFER_WRITE)
+                       nvfx_buffer_dirty_interval(buffer,
+                               (uint8_t*)ptx->data - buffer->data,
+                               ptx->stride,
+                               !!(ptx->usage & PIPE_TRANSFER_UNSYNCHRONIZED));
+               pipe_resource_reference(&ptx->resource, 0);
+               FREE(ptx);
+       }
+       else
+       {
+               struct nouveau_channel* chan = nvfx_context(pipe)->screen->base.channel;
+               util_staging_transfer_destroy(pipe, ptx);
 
-               pipe_surface_reference(&src, NULL);
+               FIRE_RING(chan);
        }
+}
 
-       return &tx->base;
+void *
+nvfx_transfer_map(struct pipe_context *pipe, struct pipe_transfer *ptx)
+{
+       if(ptx->resource->target == PIPE_BUFFER)
+               return ptx->data;
+       else
+       {
+               struct nvfx_staging_transfer *tx = (struct nvfx_staging_transfer *)ptx;
+               if(!ptx->data)
+               {
+                       struct nvfx_miptree *mt = (struct nvfx_miptree *)tx->base.staging_resource;
+                       uint8_t *map = nouveau_screen_bo_map(pipe->screen, mt->base.bo, nouveau_screen_transfer_flags(ptx->usage));
+                       ptx->data = map + tx->offset;
+               }
+
+               ++tx->map_count;
+               return ptx->data;
+       }
 }
 
 void
-nvfx_miptree_transfer_del(struct pipe_context *pipe,
-                         struct pipe_transfer *ptx)
+nvfx_transfer_unmap(struct pipe_context *pipe, struct pipe_transfer *ptx)
 {
-       struct nvfx_transfer *tx = (struct nvfx_transfer *)ptx;
-
-       if (!tx->direct && (ptx->usage & PIPE_TRANSFER_WRITE)) {
-               struct pipe_screen *pscreen = pipe->screen;
-               struct nvfx_screen *nvscreen = nvfx_screen(pscreen);
-               struct pipe_surface *dst;
-
-               dst = pscreen->get_tex_surface(pscreen,
-                                              ptx->resource,
-                                              ptx->sr.face,
-                                              ptx->sr.level,
-                                              ptx->box.z,
-                                              PIPE_BIND_BLIT_DESTINATION);
-
-               /* TODO: Check if SIFM can deal with x,y,w,h when swizzling */
-               nvscreen->eng2d->copy(nvscreen->eng2d,
-                                     dst, ptx->box.x, ptx->box.y,
-                                     tx->surface, 0, 0,
-                                     ptx->box.width, ptx->box.height);
-
-               pipe_surface_reference(&dst, NULL);
+       if(ptx->resource->target != PIPE_BUFFER)
+       {
+               struct nvfx_staging_transfer *tx = (struct nvfx_staging_transfer *)ptx;
+               struct nvfx_miptree *mt = (struct nvfx_miptree *)tx->base.staging_resource;
+
+               if(!--tx->map_count)
+               {
+                       nouveau_screen_bo_unmap(pipe->screen, mt->base.bo);
+                       ptx->data = 0;
+               }
        }
-
-       pipe_surface_reference(&tx->surface, NULL);
-       pipe_resource_reference(&ptx->resource, NULL);
-       FREE(ptx);
 }
 
-void *
-nvfx_miptree_transfer_map(struct pipe_context *pipe, struct pipe_transfer *ptx)
+static void nvfx_transfer_inline_write( struct pipe_context *pipe,
+                                     struct pipe_resource *pr,
+                                     unsigned level,
+                                     unsigned usage,
+                                     const struct pipe_box *box,
+                                     const void *data,
+                                     unsigned stride,
+                                     unsigned slice_stride)
 {
-       struct pipe_screen *pscreen = pipe->screen;
-       struct nvfx_transfer *tx = (struct nvfx_transfer *)ptx;
-       struct nv04_surface *ns = (struct nv04_surface *)tx->surface;
-       struct nvfx_miptree *mt = (struct nvfx_miptree *)tx->surface->texture;
-       uint8_t *map = nouveau_screen_bo_map(pscreen, mt->base.bo,
-                                            nouveau_screen_transfer_flags(ptx->usage));
-
-       if(!tx->direct)
-               return map + ns->base.offset;
+       if(pr->target != PIPE_BUFFER)
+       {
+               u_default_transfer_inline_write(pipe, pr, level, usage, box, data, stride, slice_stride);
+       }
        else
-               return (map + ns->base.offset + 
-                       ptx->box.y * ns->pitch + 
-                       ptx->box.x * util_format_get_blocksize(ptx->resource->format));
+       {
+               struct nvfx_buffer* buffer = nvfx_buffer(pr);
+               unsigned begin = util_format_get_stride(pr->format, box->x);
+               unsigned size = util_format_get_stride(pr->format, box->width);
+               memcpy(buffer->data + begin, data, size);
+               nvfx_buffer_dirty_interval(buffer, begin, size,
+                               !!(pr->flags & PIPE_TRANSFER_UNSYNCHRONIZED));
+       }
 }
 
 void
-nvfx_miptree_transfer_unmap(struct pipe_context *pipe, struct pipe_transfer *ptx)
+nvfx_init_transfer_functions(struct pipe_context *pipe)
 {
-       struct pipe_screen *pscreen = pipe->screen;
-       struct nvfx_transfer *tx = (struct nvfx_transfer *)ptx;
-       struct nvfx_miptree *mt = (struct nvfx_miptree *)tx->surface->texture;
-
-       nouveau_screen_bo_unmap(pscreen, mt->base.bo);
+       pipe->get_transfer = nvfx_transfer_new;
+       pipe->transfer_map = nvfx_transfer_map;
+       pipe->transfer_flush_region = nvfx_transfer_flush_region;
+       pipe->transfer_unmap = nvfx_transfer_unmap;
+       pipe->transfer_destroy = nvfx_transfer_destroy;
+       pipe->transfer_inline_write = nvfx_transfer_inline_write;
 }