Home | History | Annotate | Download | only in drm
      1 /**********************************************************
      2  * Copyright 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 
     27 #include "svga_cmd.h"
     28 
     29 #include "util/u_debug.h"
     30 #include "util/u_memory.h"
     31 #include "util/u_debug_stack.h"
     32 #include "pipebuffer/pb_buffer.h"
     33 #include "pipebuffer/pb_validate.h"
     34 
     35 #include "svga_winsys.h"
     36 #include "vmw_context.h"
     37 #include "vmw_screen.h"
     38 #include "vmw_buffer.h"
     39 #include "vmw_surface.h"
     40 #include "vmw_fence.h"
     41 
     42 #define VMW_COMMAND_SIZE (64*1024)
     43 #define VMW_SURFACE_RELOCS (1024)
     44 #define VMW_REGION_RELOCS (512)
     45 
     46 #define VMW_MUST_FLUSH_STACK 8
     47 
     48 struct vmw_region_relocation
     49 {
     50    struct SVGAGuestPtr *where;
     51    struct pb_buffer *buffer;
     52    /* TODO: put offset info inside where */
     53    uint32 offset;
     54 };
     55 
     56 struct vmw_svga_winsys_context
     57 {
     58    struct svga_winsys_context base;
     59 
     60    struct vmw_winsys_screen *vws;
     61 
     62 #ifdef DEBUG
     63    boolean must_flush;
     64    struct debug_stack_frame must_flush_stack[VMW_MUST_FLUSH_STACK];
     65 #endif
     66 
     67    struct {
     68       uint8_t buffer[VMW_COMMAND_SIZE];
     69       uint32_t size;
     70       uint32_t used;
     71       uint32_t reserved;
     72    } command;
     73 
     74    struct {
     75       struct vmw_svga_winsys_surface *handles[VMW_SURFACE_RELOCS];
     76       uint32_t size;
     77       uint32_t used;
     78       uint32_t staged;
     79       uint32_t reserved;
     80    } surface;
     81 
     82    struct {
     83       struct vmw_region_relocation relocs[VMW_REGION_RELOCS];
     84       uint32_t size;
     85       uint32_t used;
     86       uint32_t staged;
     87       uint32_t reserved;
     88    } region;
     89 
     90    struct pb_validate *validate;
     91 
     92    /**
     93     * The amount of GMR that is referred by the commands currently batched
     94     * in the context.
     95     */
     96    uint32_t seen_regions;
     97 
     98    /**
     99     * Whether this context should fail to reserve more commands, not because it
    100     * ran out of command space, but because a substantial ammount of GMR was
    101     * referred.
    102     */
    103    boolean preemptive_flush;
    104 };
    105 
    106 
    107 static INLINE struct vmw_svga_winsys_context *
    108 vmw_svga_winsys_context(struct svga_winsys_context *swc)
    109 {
    110    assert(swc);
    111    return (struct vmw_svga_winsys_context *)swc;
    112 }
    113 
    114 
    115 static INLINE unsigned
    116 vmw_translate_to_pb_flags(unsigned flags)
    117 {
    118    unsigned f = 0;
    119    if (flags & SVGA_RELOC_READ)
    120       f |= PB_USAGE_GPU_READ;
    121 
    122    if (flags & SVGA_RELOC_WRITE)
    123       f |= PB_USAGE_GPU_WRITE;
    124 
    125    return f;
    126 }
    127 
    128 static enum pipe_error
    129 vmw_swc_flush(struct svga_winsys_context *swc,
    130               struct pipe_fence_handle **pfence)
    131 {
    132    struct vmw_svga_winsys_context *vswc = vmw_svga_winsys_context(swc);
    133    struct pipe_fence_handle *fence = NULL;
    134    unsigned i;
    135    enum pipe_error ret;
    136 
    137    ret = pb_validate_validate(vswc->validate);
    138    assert(ret == PIPE_OK);
    139    if(ret == PIPE_OK) {
    140 
    141       /* Apply relocations */
    142       for(i = 0; i < vswc->region.used; ++i) {
    143          struct vmw_region_relocation *reloc = &vswc->region.relocs[i];
    144          struct SVGAGuestPtr ptr;
    145 
    146          if(!vmw_gmr_bufmgr_region_ptr(reloc->buffer, &ptr))
    147             assert(0);
    148 
    149          ptr.offset += reloc->offset;
    150 
    151          *reloc->where = ptr;
    152       }
    153 
    154       if (vswc->command.used || pfence != NULL)
    155          vmw_ioctl_command(vswc->vws,
    156 			   vswc->base.cid,
    157 			   0,
    158                            vswc->command.buffer,
    159                            vswc->command.used,
    160                            &fence);
    161 
    162       pb_validate_fence(vswc->validate, fence);
    163    }
    164 
    165    vswc->command.used = 0;
    166    vswc->command.reserved = 0;
    167 
    168    for(i = 0; i < vswc->surface.used + vswc->surface.staged; ++i) {
    169       struct vmw_svga_winsys_surface *vsurf =
    170 	 vswc->surface.handles[i];
    171       p_atomic_dec(&vsurf->validated);
    172       vmw_svga_winsys_surface_reference(&vswc->surface.handles[i], NULL);
    173    }
    174 
    175    vswc->surface.used = 0;
    176    vswc->surface.reserved = 0;
    177 
    178    for(i = 0; i < vswc->region.used + vswc->region.staged; ++i) {
    179       pb_reference(&vswc->region.relocs[i].buffer, NULL);
    180    }
    181 
    182    vswc->region.used = 0;
    183    vswc->region.reserved = 0;
    184 
    185 #ifdef DEBUG
    186    vswc->must_flush = FALSE;
    187 #endif
    188    vswc->preemptive_flush = FALSE;
    189    vswc->seen_regions = 0;
    190 
    191    if(pfence)
    192       vmw_fence_reference(vswc->vws, pfence, fence);
    193 
    194    vmw_fence_reference(vswc->vws, &fence, NULL);
    195 
    196    return ret;
    197 }
    198 
    199 
    200 static void *
    201 vmw_swc_reserve(struct svga_winsys_context *swc,
    202                 uint32_t nr_bytes, uint32_t nr_relocs )
    203 {
    204    struct vmw_svga_winsys_context *vswc = vmw_svga_winsys_context(swc);
    205 
    206 #ifdef DEBUG
    207    /* Check if somebody forgot to check the previous failure */
    208    if(vswc->must_flush) {
    209       debug_printf("Forgot to flush:\n");
    210       debug_backtrace_dump(vswc->must_flush_stack, VMW_MUST_FLUSH_STACK);
    211       assert(!vswc->must_flush);
    212    }
    213 #endif
    214 
    215    assert(nr_bytes <= vswc->command.size);
    216    if(nr_bytes > vswc->command.size)
    217       return NULL;
    218 
    219    if(vswc->preemptive_flush ||
    220       vswc->command.used + nr_bytes > vswc->command.size ||
    221       vswc->surface.used + nr_relocs > vswc->surface.size ||
    222       vswc->region.used + nr_relocs > vswc->region.size) {
    223 #ifdef DEBUG
    224       vswc->must_flush = TRUE;
    225       debug_backtrace_capture(vswc->must_flush_stack, 1,
    226                               VMW_MUST_FLUSH_STACK);
    227 #endif
    228       return NULL;
    229    }
    230 
    231    assert(vswc->command.used + nr_bytes <= vswc->command.size);
    232    assert(vswc->surface.used + nr_relocs <= vswc->surface.size);
    233    assert(vswc->region.used + nr_relocs <= vswc->region.size);
    234 
    235    vswc->command.reserved = nr_bytes;
    236    vswc->surface.reserved = nr_relocs;
    237    vswc->surface.staged = 0;
    238    vswc->region.reserved = nr_relocs;
    239    vswc->region.staged = 0;
    240 
    241    return vswc->command.buffer + vswc->command.used;
    242 }
    243 
    244 
    245 static void
    246 vmw_swc_surface_relocation(struct svga_winsys_context *swc,
    247                            uint32 *where,
    248                            struct svga_winsys_surface *surface,
    249                            unsigned flags)
    250 {
    251    struct vmw_svga_winsys_context *vswc = vmw_svga_winsys_context(swc);
    252    struct vmw_svga_winsys_surface *vsurf;
    253 
    254    if(!surface) {
    255       *where = SVGA3D_INVALID_ID;
    256       return;
    257    }
    258 
    259    assert(vswc->surface.staged < vswc->surface.reserved);
    260 
    261    vsurf = vmw_svga_winsys_surface(surface);
    262 
    263    *where = vsurf->sid;
    264 
    265    vmw_svga_winsys_surface_reference(&vswc->surface.handles[vswc->surface.used + vswc->surface.staged], vsurf);
    266    p_atomic_inc(&vsurf->validated);
    267    ++vswc->surface.staged;
    268 }
    269 
    270 
    271 static void
    272 vmw_swc_region_relocation(struct svga_winsys_context *swc,
    273                           struct SVGAGuestPtr *where,
    274                           struct svga_winsys_buffer *buffer,
    275                           uint32 offset,
    276                           unsigned flags)
    277 {
    278    struct vmw_svga_winsys_context *vswc = vmw_svga_winsys_context(swc);
    279    struct vmw_region_relocation *reloc;
    280    unsigned translated_flags;
    281    enum pipe_error ret;
    282 
    283    assert(vswc->region.staged < vswc->region.reserved);
    284 
    285    reloc = &vswc->region.relocs[vswc->region.used + vswc->region.staged];
    286    reloc->where = where;
    287    pb_reference(&reloc->buffer, vmw_pb_buffer(buffer));
    288    reloc->offset = offset;
    289 
    290    ++vswc->region.staged;
    291 
    292    translated_flags = vmw_translate_to_pb_flags(flags);
    293    ret = pb_validate_add_buffer(vswc->validate, reloc->buffer, translated_flags);
    294    /* TODO: Update pipebuffer to reserve buffers and not fail here */
    295    assert(ret == PIPE_OK);
    296    (void)ret;
    297 
    298    /*
    299     * Flush preemptively the FIFO commands to keep the GMR working set within
    300     * the GMR pool size.
    301     *
    302     * This is necessary for applications like SPECviewperf that generate huge
    303     * amounts of immediate vertex data, so that we don't pile up too much of
    304     * that vertex data neither in the guest nor in the host.
    305     *
    306     * Note that in the current implementation if a region is referred twice in
    307     * a command stream, it will be accounted twice. We could detect repeated
    308     * regions and count only once, but there is no incentive to do that, since
    309     * regions are typically short-lived; always referred in a single command;
    310     * and at the worst we just flush the commands a bit sooner, which for the
    311     * SVGA virtual device it's not a performance issue since flushing commands
    312     * to the FIFO won't cause flushing in the host.
    313     */
    314    vswc->seen_regions += reloc->buffer->size;
    315    if(vswc->seen_regions >= VMW_GMR_POOL_SIZE/3)
    316       vswc->preemptive_flush = TRUE;
    317 }
    318 
    319 
    320 static void
    321 vmw_swc_commit(struct svga_winsys_context *swc)
    322 {
    323    struct vmw_svga_winsys_context *vswc = vmw_svga_winsys_context(swc);
    324 
    325    assert(vswc->command.reserved);
    326    assert(vswc->command.used + vswc->command.reserved <= vswc->command.size);
    327    vswc->command.used += vswc->command.reserved;
    328    vswc->command.reserved = 0;
    329 
    330    assert(vswc->surface.staged <= vswc->surface.reserved);
    331    assert(vswc->surface.used + vswc->surface.staged <= vswc->surface.size);
    332    vswc->surface.used += vswc->surface.staged;
    333    vswc->surface.staged = 0;
    334    vswc->surface.reserved = 0;
    335 
    336    assert(vswc->region.staged <= vswc->region.reserved);
    337    assert(vswc->region.used + vswc->region.staged <= vswc->region.size);
    338    vswc->region.used += vswc->region.staged;
    339    vswc->region.staged = 0;
    340    vswc->region.reserved = 0;
    341 }
    342 
    343 
    344 static void
    345 vmw_swc_destroy(struct svga_winsys_context *swc)
    346 {
    347    struct vmw_svga_winsys_context *vswc = vmw_svga_winsys_context(swc);
    348    unsigned i;
    349 
    350    for(i = 0; i < vswc->region.used; ++i) {
    351       pb_reference(&vswc->region.relocs[i].buffer, NULL);
    352    }
    353 
    354    for(i = 0; i < vswc->surface.used; ++i) {
    355       p_atomic_dec(&vswc->surface.handles[i]->validated);
    356       vmw_svga_winsys_surface_reference(&vswc->surface.handles[i], NULL);
    357    }
    358    pb_validate_destroy(vswc->validate);
    359    vmw_ioctl_context_destroy(vswc->vws, swc->cid);
    360    FREE(vswc);
    361 }
    362 
    363 
    364 struct svga_winsys_context *
    365 vmw_svga_winsys_context_create(struct svga_winsys_screen *sws)
    366 {
    367    struct vmw_winsys_screen *vws = vmw_winsys_screen(sws);
    368    struct vmw_svga_winsys_context *vswc;
    369 
    370    vswc = CALLOC_STRUCT(vmw_svga_winsys_context);
    371    if(!vswc)
    372       return NULL;
    373 
    374    vswc->base.destroy = vmw_swc_destroy;
    375    vswc->base.reserve = vmw_swc_reserve;
    376    vswc->base.surface_relocation = vmw_swc_surface_relocation;
    377    vswc->base.region_relocation = vmw_swc_region_relocation;
    378    vswc->base.commit = vmw_swc_commit;
    379    vswc->base.flush = vmw_swc_flush;
    380 
    381    vswc->base.cid = vmw_ioctl_context_create(vws);
    382 
    383    vswc->vws = vws;
    384 
    385    vswc->command.size = VMW_COMMAND_SIZE;
    386    vswc->surface.size = VMW_SURFACE_RELOCS;
    387    vswc->region.size = VMW_REGION_RELOCS;
    388 
    389    vswc->validate = pb_validate_create();
    390    if(!vswc->validate) {
    391       FREE(vswc);
    392       return NULL;
    393    }
    394 
    395    return &vswc->base;
    396 }
    397