Home | History | Annotate | Download | only in svga
      1 /**********************************************************
      2  * Copyright 2008-2009 VMware, Inc.  All rights reserved.
      3  *
      4  * Permission is hereby granted, free of charge, to any person
      5  * obtaining a copy of this software and associated documentation
      6  * files (the "Software"), to deal in the Software without
      7  * restriction, including without limitation the rights to use, copy,
      8  * modify, merge, publish, distribute, sublicense, and/or sell copies
      9  * of the Software, and to permit persons to whom the Software is
     10  * furnished to do so, subject to the following conditions:
     11  *
     12  * The above copyright notice and this permission notice shall be
     13  * included in all copies or substantial portions of the Software.
     14  *
     15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     18  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
     19  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
     20  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     22  * SOFTWARE.
     23  *
     24  **********************************************************/
     25 
     26 #include "svga_cmd.h"
     27 
     28 #include "pipe/p_state.h"
     29 #include "pipe/p_defines.h"
     30 #include "util/u_inlines.h"
     31 #include "os/os_thread.h"
     32 #include "util/u_format.h"
     33 #include "util/u_math.h"
     34 #include "util/u_memory.h"
     35 
     36 #include "svga_format.h"
     37 #include "svga_screen.h"
     38 #include "svga_context.h"
     39 #include "svga_resource_texture.h"
     40 #include "svga_resource_buffer.h"
     41 #include "svga_sampler_view.h"
     42 #include "svga_winsys.h"
     43 #include "svga_debug.h"
     44 
     45 
     46 /* XXX: This isn't a real hardware flag, but just a hack for kernel to
     47  * know about primary surfaces. Find a better way to accomplish this.
     48  */
     49 #define SVGA3D_SURFACE_HINT_SCANOUT (1 << 9)
     50 
     51 
     52 static INLINE void
     53 svga_transfer_dma_band(struct svga_context *svga,
     54                        struct svga_transfer *st,
     55                        SVGA3dTransferType transfer,
     56                        unsigned y, unsigned h, unsigned srcy,
     57                        SVGA3dSurfaceDMAFlags flags)
     58 {
     59    struct svga_texture *texture = svga_texture(st->base.resource);
     60    SVGA3dCopyBox box;
     61    enum pipe_error ret;
     62 
     63    box.x = st->base.box.x;
     64    box.y = y;
     65    box.z = st->base.box.z;
     66    box.w = st->base.box.width;
     67    box.h = h;
     68    box.d = 1;
     69    box.srcx = 0;
     70    box.srcy = srcy;
     71    box.srcz = 0;
     72 
     73    if (st->base.resource->target == PIPE_TEXTURE_CUBE) {
     74       st->face = st->base.box.z;
     75       box.z = 0;
     76    }
     77    else
     78       st->face = 0;
     79 
     80    SVGA_DBG(DEBUG_DMA, "dma %s sid %p, face %u, (%u, %u, %u) - (%u, %u, %u), %ubpp\n",
     81                 transfer == SVGA3D_WRITE_HOST_VRAM ? "to" : "from",
     82                 texture->handle,
     83                 st->face,
     84                 st->base.box.x,
     85                 y,
     86                 box.z,
     87                 st->base.box.x + st->base.box.width,
     88                 y + h,
     89                 box.z + 1,
     90                 util_format_get_blocksize(texture->b.b.format) * 8 /
     91                 (util_format_get_blockwidth(texture->b.b.format)*util_format_get_blockheight(texture->b.b.format)));
     92 
     93    ret = SVGA3D_SurfaceDMA(svga->swc, st, transfer, &box, 1, flags);
     94    if(ret != PIPE_OK) {
     95       svga_context_flush(svga, NULL);
     96       ret = SVGA3D_SurfaceDMA(svga->swc, st, transfer, &box, 1, flags);
     97       assert(ret == PIPE_OK);
     98    }
     99 }
    100 
    101 
    102 static INLINE void
    103 svga_transfer_dma(struct svga_context *svga,
    104                   struct svga_transfer *st,
    105                   SVGA3dTransferType transfer,
    106                   SVGA3dSurfaceDMAFlags flags)
    107 {
    108    struct svga_texture *texture = svga_texture(st->base.resource);
    109    struct svga_screen *screen = svga_screen(texture->b.b.screen);
    110    struct svga_winsys_screen *sws = screen->sws;
    111    struct pipe_fence_handle *fence = NULL;
    112 
    113    if (transfer == SVGA3D_READ_HOST_VRAM) {
    114       SVGA_DBG(DEBUG_PERF, "%s: readback transfer\n", __FUNCTION__);
    115    }
    116 
    117    /* Ensure any pending operations on host surfaces are queued on the command
    118     * buffer first.
    119     */
    120    svga_surfaces_flush( svga );
    121 
    122    if(!st->swbuf) {
    123       /* Do the DMA transfer in a single go */
    124 
    125       svga_transfer_dma_band(svga, st, transfer,
    126                              st->base.box.y, st->base.box.height, 0,
    127                              flags);
    128 
    129       if(transfer == SVGA3D_READ_HOST_VRAM) {
    130          svga_context_flush(svga, &fence);
    131          sws->fence_finish(sws, fence, 0);
    132          sws->fence_reference(sws, &fence, NULL);
    133       }
    134    }
    135    else {
    136       unsigned y, h, srcy;
    137       unsigned blockheight = util_format_get_blockheight(st->base.resource->format);
    138       h = st->hw_nblocksy * blockheight;
    139       srcy = 0;
    140       for(y = 0; y < st->base.box.height; y += h) {
    141          unsigned offset, length;
    142          void *hw, *sw;
    143 
    144          if (y + h > st->base.box.height)
    145             h = st->base.box.height - y;
    146 
    147          /* Transfer band must be aligned to pixel block boundaries */
    148          assert(y % blockheight == 0);
    149          assert(h % blockheight == 0);
    150 
    151          offset = y * st->base.stride / blockheight;
    152          length = h * st->base.stride / blockheight;
    153 
    154          sw = (uint8_t *)st->swbuf + offset;
    155 
    156          if (transfer == SVGA3D_WRITE_HOST_VRAM) {
    157             unsigned usage = PIPE_TRANSFER_WRITE;
    158 
    159             /* Wait for the previous DMAs to complete */
    160             /* TODO: keep one DMA (at half the size) in the background */
    161             if (y) {
    162                svga_context_flush(svga, NULL);
    163                usage |= PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE;
    164             }
    165 
    166             hw = sws->buffer_map(sws, st->hwbuf, usage);
    167             assert(hw);
    168             if (hw) {
    169                memcpy(hw, sw, length);
    170                sws->buffer_unmap(sws, st->hwbuf);
    171             }
    172          }
    173 
    174          svga_transfer_dma_band(svga, st, transfer, y, h, srcy, flags);
    175 
    176          /*
    177           * Prevent the texture contents to be discarded on the next band
    178           * upload.
    179           */
    180 
    181          flags.discard = FALSE;
    182 
    183          if(transfer == SVGA3D_READ_HOST_VRAM) {
    184             svga_context_flush(svga, &fence);
    185             sws->fence_finish(sws, fence, 0);
    186 
    187             hw = sws->buffer_map(sws, st->hwbuf, PIPE_TRANSFER_READ);
    188             assert(hw);
    189             if(hw) {
    190                memcpy(sw, hw, length);
    191                sws->buffer_unmap(sws, st->hwbuf);
    192             }
    193          }
    194       }
    195    }
    196 }
    197 
    198 
    199 static boolean
    200 svga_texture_get_handle(struct pipe_screen *screen,
    201                                struct pipe_resource *texture,
    202                                struct winsys_handle *whandle)
    203 {
    204    struct svga_winsys_screen *sws = svga_winsys_screen(texture->screen);
    205    unsigned stride;
    206 
    207    assert(svga_texture(texture)->key.cachable == 0);
    208    svga_texture(texture)->key.cachable = 0;
    209    stride = util_format_get_nblocksx(texture->format, texture->width0) *
    210             util_format_get_blocksize(texture->format);
    211    return sws->surface_get_handle(sws, svga_texture(texture)->handle, stride, whandle);
    212 }
    213 
    214 
    215 static void
    216 svga_texture_destroy(struct pipe_screen *screen,
    217 		     struct pipe_resource *pt)
    218 {
    219    struct svga_screen *ss = svga_screen(screen);
    220    struct svga_texture *tex = (struct svga_texture *)pt;
    221 
    222    ss->texture_timestamp++;
    223 
    224    svga_sampler_view_reference(&tex->cached_view, NULL);
    225 
    226    /*
    227      DBG("%s deleting %p\n", __FUNCTION__, (void *) tex);
    228    */
    229    SVGA_DBG(DEBUG_DMA, "unref sid %p (texture)\n", tex->handle);
    230    svga_screen_surface_destroy(ss, &tex->key, &tex->handle);
    231 
    232    FREE(tex);
    233 }
    234 
    235 
    236 /* XXX: Still implementing this as if it was a screen function, but
    237  * can now modify it to queue transfers on the context.
    238  */
    239 static struct pipe_transfer *
    240 svga_texture_get_transfer(struct pipe_context *pipe,
    241                           struct pipe_resource *texture,
    242                           unsigned level,
    243                           unsigned usage,
    244                           const struct pipe_box *box)
    245 {
    246    struct svga_context *svga = svga_context(pipe);
    247    struct svga_screen *ss = svga_screen(pipe->screen);
    248    struct svga_winsys_screen *sws = ss->sws;
    249    struct svga_transfer *st;
    250    unsigned nblocksx = util_format_get_nblocksx(texture->format, box->width);
    251    unsigned nblocksy = util_format_get_nblocksy(texture->format, box->height);
    252 
    253    /* We can't map texture storage directly */
    254    if (usage & PIPE_TRANSFER_MAP_DIRECTLY)
    255       return NULL;
    256 
    257    assert(box->depth == 1);
    258    st = CALLOC_STRUCT(svga_transfer);
    259    if (!st)
    260       return NULL;
    261 
    262    pipe_resource_reference(&st->base.resource, texture);
    263    st->base.level = level;
    264    st->base.usage = usage;
    265    st->base.box = *box;
    266    st->base.stride = nblocksx*util_format_get_blocksize(texture->format);
    267    st->base.layer_stride = 0;
    268 
    269    st->hw_nblocksy = nblocksy;
    270 
    271    st->hwbuf = svga_winsys_buffer_create(svga,
    272                                          1,
    273                                          0,
    274                                          st->hw_nblocksy*st->base.stride);
    275    while(!st->hwbuf && (st->hw_nblocksy /= 2)) {
    276       st->hwbuf = svga_winsys_buffer_create(svga,
    277                                             1,
    278                                             0,
    279                                             st->hw_nblocksy*st->base.stride);
    280    }
    281 
    282    if(!st->hwbuf)
    283       goto no_hwbuf;
    284 
    285    if(st->hw_nblocksy < nblocksy) {
    286       /* We couldn't allocate a hardware buffer big enough for the transfer,
    287        * so allocate regular malloc memory instead */
    288       if (0) {
    289          debug_printf("%s: failed to allocate %u KB of DMA, "
    290                       "splitting into %u x %u KB DMA transfers\n",
    291                       __FUNCTION__,
    292                       (nblocksy*st->base.stride + 1023)/1024,
    293                       (nblocksy + st->hw_nblocksy - 1)/st->hw_nblocksy,
    294                       (st->hw_nblocksy*st->base.stride + 1023)/1024);
    295       }
    296 
    297       st->swbuf = MALLOC(nblocksy*st->base.stride);
    298       if(!st->swbuf)
    299          goto no_swbuf;
    300    }
    301 
    302    if (usage & PIPE_TRANSFER_READ) {
    303       SVGA3dSurfaceDMAFlags flags;
    304       memset(&flags, 0, sizeof flags);
    305       svga_transfer_dma(svga, st, SVGA3D_READ_HOST_VRAM, flags);
    306    }
    307 
    308    return &st->base;
    309 
    310 no_swbuf:
    311    sws->buffer_destroy(sws, st->hwbuf);
    312 no_hwbuf:
    313    FREE(st);
    314    return NULL;
    315 }
    316 
    317 
    318 /* XXX: Still implementing this as if it was a screen function, but
    319  * can now modify it to queue transfers on the context.
    320  */
    321 static void *
    322 svga_texture_transfer_map( struct pipe_context *pipe,
    323 			   struct pipe_transfer *transfer )
    324 {
    325    struct svga_screen *ss = svga_screen(pipe->screen);
    326    struct svga_winsys_screen *sws = ss->sws;
    327    struct svga_transfer *st = svga_transfer(transfer);
    328 
    329    if(st->swbuf)
    330       return st->swbuf;
    331    else
    332       /* The wait for read transfers already happened when svga_transfer_dma
    333        * was called. */
    334       return sws->buffer_map(sws, st->hwbuf, transfer->usage);
    335 }
    336 
    337 
    338 /* XXX: Still implementing this as if it was a screen function, but
    339  * can now modify it to queue transfers on the context.
    340  */
    341 static void
    342 svga_texture_transfer_unmap(struct pipe_context *pipe,
    343 			    struct pipe_transfer *transfer)
    344 {
    345    struct svga_screen *ss = svga_screen(pipe->screen);
    346    struct svga_winsys_screen *sws = ss->sws;
    347    struct svga_transfer *st = svga_transfer(transfer);
    348 
    349    if(!st->swbuf)
    350       sws->buffer_unmap(sws, st->hwbuf);
    351 }
    352 
    353 
    354 static void
    355 svga_texture_transfer_destroy(struct pipe_context *pipe,
    356 			      struct pipe_transfer *transfer)
    357 {
    358    struct svga_context *svga = svga_context(pipe);
    359    struct svga_texture *tex = svga_texture(transfer->resource);
    360    struct svga_screen *ss = svga_screen(pipe->screen);
    361    struct svga_winsys_screen *sws = ss->sws;
    362    struct svga_transfer *st = svga_transfer(transfer);
    363 
    364    if (st->base.usage & PIPE_TRANSFER_WRITE) {
    365       SVGA3dSurfaceDMAFlags flags;
    366 
    367       memset(&flags, 0, sizeof flags);
    368       if (transfer->usage & PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE) {
    369          flags.discard = TRUE;
    370       }
    371       if (transfer->usage & PIPE_TRANSFER_UNSYNCHRONIZED) {
    372          flags.unsynchronized = TRUE;
    373       }
    374 
    375       svga_transfer_dma(svga, st, SVGA3D_WRITE_HOST_VRAM, flags);
    376       ss->texture_timestamp++;
    377       tex->view_age[transfer->level] = ++(tex->age);
    378       if (transfer->resource->target == PIPE_TEXTURE_CUBE)
    379          tex->defined[transfer->box.z][transfer->level] = TRUE;
    380       else
    381          tex->defined[0][transfer->level] = TRUE;
    382    }
    383 
    384    pipe_resource_reference(&st->base.resource, NULL);
    385    FREE(st->swbuf);
    386    sws->buffer_destroy(sws, st->hwbuf);
    387    FREE(st);
    388 }
    389 
    390 
    391 struct u_resource_vtbl svga_texture_vtbl =
    392 {
    393    svga_texture_get_handle,	      /* get_handle */
    394    svga_texture_destroy,	      /* resource_destroy */
    395    svga_texture_get_transfer,	      /* get_transfer */
    396    svga_texture_transfer_destroy,     /* transfer_destroy */
    397    svga_texture_transfer_map,	      /* transfer_map */
    398    u_default_transfer_flush_region,   /* transfer_flush_region */
    399    svga_texture_transfer_unmap,	      /* transfer_unmap */
    400    u_default_transfer_inline_write    /* transfer_inline_write */
    401 };
    402 
    403 
    404 struct pipe_resource *
    405 svga_texture_create(struct pipe_screen *screen,
    406                     const struct pipe_resource *template)
    407 {
    408    struct svga_screen *svgascreen = svga_screen(screen);
    409    struct svga_texture *tex = CALLOC_STRUCT(svga_texture);
    410 
    411    if (!tex)
    412       goto error1;
    413 
    414    tex->b.b = *template;
    415    tex->b.vtbl = &svga_texture_vtbl;
    416    pipe_reference_init(&tex->b.b.reference, 1);
    417    tex->b.b.screen = screen;
    418 
    419    assert(template->last_level < SVGA_MAX_TEXTURE_LEVELS);
    420    if(template->last_level >= SVGA_MAX_TEXTURE_LEVELS)
    421       goto error2;
    422 
    423    tex->key.flags = 0;
    424    tex->key.size.width = template->width0;
    425    tex->key.size.height = template->height0;
    426    tex->key.size.depth = template->depth0;
    427 
    428    if(template->target == PIPE_TEXTURE_CUBE) {
    429       tex->key.flags |= SVGA3D_SURFACE_CUBEMAP;
    430       tex->key.numFaces = 6;
    431    }
    432    else {
    433       tex->key.numFaces = 1;
    434    }
    435 
    436    tex->key.cachable = 1;
    437 
    438    if (template->bind & PIPE_BIND_SAMPLER_VIEW)
    439       tex->key.flags |= SVGA3D_SURFACE_HINT_TEXTURE;
    440 
    441    if (template->bind & PIPE_BIND_DISPLAY_TARGET) {
    442       tex->key.cachable = 0;
    443    }
    444 
    445    if (template->bind & PIPE_BIND_SHARED) {
    446       tex->key.cachable = 0;
    447    }
    448 
    449    if (template->bind & (PIPE_BIND_SCANOUT |
    450                          PIPE_BIND_CURSOR)) {
    451       tex->key.flags |= SVGA3D_SURFACE_HINT_SCANOUT;
    452       tex->key.cachable = 0;
    453    }
    454 
    455    /*
    456     * Note: Previously we never passed the
    457     * SVGA3D_SURFACE_HINT_RENDERTARGET hint. Mesa cannot
    458     * know beforehand whether a texture will be used as a rendertarget or not
    459     * and it always requests PIPE_BIND_RENDER_TARGET, therefore
    460     * passing the SVGA3D_SURFACE_HINT_RENDERTARGET here defeats its purpose.
    461     *
    462     * However, this was changed since other state trackers
    463     * (XA for example) uses it accurately and certain device versions
    464     * relies on it in certain situations to render correctly.
    465     */
    466    if((template->bind & PIPE_BIND_RENDER_TARGET) &&
    467       !util_format_is_s3tc(template->format))
    468       tex->key.flags |= SVGA3D_SURFACE_HINT_RENDERTARGET;
    469 
    470    if(template->bind & PIPE_BIND_DEPTH_STENCIL)
    471       tex->key.flags |= SVGA3D_SURFACE_HINT_DEPTHSTENCIL;
    472 
    473    tex->key.numMipLevels = template->last_level + 1;
    474 
    475    tex->key.format = svga_translate_format(svgascreen, template->format, template->bind);
    476    if(tex->key.format == SVGA3D_FORMAT_INVALID)
    477       goto error2;
    478 
    479    SVGA_DBG(DEBUG_DMA, "surface_create for texture\n", tex->handle);
    480    tex->handle = svga_screen_surface_create(svgascreen, &tex->key);
    481    if (tex->handle)
    482       SVGA_DBG(DEBUG_DMA, "  --> got sid %p (texture)\n", tex->handle);
    483 
    484    debug_reference(&tex->b.b.reference,
    485                    (debug_reference_descriptor)debug_describe_resource, 0);
    486 
    487    return &tex->b.b;
    488 
    489 error2:
    490    FREE(tex);
    491 error1:
    492    return NULL;
    493 }
    494 
    495 
    496 struct pipe_resource *
    497 svga_texture_from_handle(struct pipe_screen *screen,
    498 			 const struct pipe_resource *template,
    499 			 struct winsys_handle *whandle)
    500 {
    501    struct svga_winsys_screen *sws = svga_winsys_screen(screen);
    502    struct svga_winsys_surface *srf;
    503    struct svga_texture *tex;
    504    enum SVGA3dSurfaceFormat format = 0;
    505    assert(screen);
    506 
    507    /* Only supports one type */
    508    if ((template->target != PIPE_TEXTURE_2D &&
    509        template->target != PIPE_TEXTURE_RECT) ||
    510        template->last_level != 0 ||
    511        template->depth0 != 1) {
    512       return NULL;
    513    }
    514 
    515    srf = sws->surface_from_handle(sws, whandle, &format);
    516 
    517    if (!srf)
    518       return NULL;
    519 
    520    if (svga_translate_format(svga_screen(screen), template->format, template->bind) != format) {
    521       unsigned f1 = svga_translate_format(svga_screen(screen), template->format, template->bind);
    522       unsigned f2 = format;
    523 
    524       /* It's okay for XRGB and ARGB or depth with/out stencil to get mixed up */
    525       if ( !( (f1 == SVGA3D_X8R8G8B8 && f2 == SVGA3D_A8R8G8B8) ||
    526               (f1 == SVGA3D_A8R8G8B8 && f2 == SVGA3D_X8R8G8B8) ||
    527               (f1 == SVGA3D_Z_D24X8 && f2 == SVGA3D_Z_D24S8) ||
    528               (f1 == SVGA3D_Z_DF24 && f2 == SVGA3D_Z_D24S8_INT) ) ) {
    529          debug_printf("%s wrong format %u != %u\n", __FUNCTION__, f1, f2);
    530          return NULL;
    531       }
    532    }
    533 
    534    tex = CALLOC_STRUCT(svga_texture);
    535    if (!tex)
    536       return NULL;
    537 
    538    tex->b.b = *template;
    539    tex->b.vtbl = &svga_texture_vtbl;
    540    pipe_reference_init(&tex->b.b.reference, 1);
    541    tex->b.b.screen = screen;
    542 
    543    if (format == SVGA3D_X8R8G8B8)
    544       tex->b.b.format = PIPE_FORMAT_B8G8R8X8_UNORM;
    545    else if (format == SVGA3D_A8R8G8B8)
    546       tex->b.b.format = PIPE_FORMAT_B8G8R8A8_UNORM;
    547    else {
    548       /* ?? */
    549    }
    550 
    551    SVGA_DBG(DEBUG_DMA, "wrap surface sid %p\n", srf);
    552 
    553    tex->key.cachable = 0;
    554    tex->handle = srf;
    555 
    556    return &tex->b.b;
    557 }
    558