Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright  2017 Red Hat
      3  *
      4  * Permission is hereby granted, free of charge, to any person obtaining a
      5  * copy of this software and associated documentation files (the "Software"),
      6  * to deal in the Software without restriction, including without limitation
      7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      8  * and/or sell copies of the Software, and to permit persons to whom the
      9  * Software is furnished to do so, subject to the following conditions:
     10  *
     11  * The above copyright notice and this permission notice (including the next
     12  * paragraph) shall be included in all copies or substantial portions of the
     13  * Software.
     14  *
     15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     21  * SOFTWARE.
     22  */
     23 
     24 #include "pipe/p_screen.h"
     25 
     26 #include "util/u_box.h"
     27 #include "util/u_format.h"
     28 #include "util/u_format_rgtc.h"
     29 #include "util/u_format_zs.h"
     30 #include "util/u_inlines.h"
     31 #include "util/u_transfer_helper.h"
     32 
     33 
     34 struct u_transfer_helper {
     35    const struct u_transfer_vtbl *vtbl;
     36    bool separate_z32s8;
     37    bool fake_rgtc;
     38    bool msaa_map;
     39 };
     40 
     41 static inline bool handle_transfer(struct pipe_resource *prsc)
     42 {
     43    struct u_transfer_helper *helper = prsc->screen->transfer_helper;
     44 
     45    if (helper->vtbl->get_internal_format) {
     46       enum pipe_format internal_format =
     47             helper->vtbl->get_internal_format(prsc);
     48       if (internal_format != prsc->format)
     49          return true;
     50    }
     51 
     52    if (helper->msaa_map && (prsc->nr_samples > 1))
     53       return true;
     54 
     55    return false;
     56 }
     57 
     58 /* The pipe_transfer ptr could either be the driver's, or u_transfer,
     59  * depending on whether we are intervening or not.  Check handle_transfer()
     60  * before dereferencing.
     61  */
     62 struct u_transfer {
     63    struct pipe_transfer base;
     64    /* Note that in case of MSAA resolve for transfer plus z32s8 or fake rgtc
     65     * we end up with stacked u_transfer's.  The MSAA resolve case doesn't call
     66     * helper->vtbl fxns directly, but calls back to pctx->transfer_map()/etc
     67     * so the format related handling can work in conjunction with MSAA resolve.
     68     */
     69    struct pipe_transfer *trans;   /* driver's transfer */
     70    struct pipe_transfer *trans2;  /* 2nd transfer for s8 stencil buffer in z32s8 */
     71    void *ptr, *ptr2;              /* ptr to trans, and trans2 */
     72    void *staging;                 /* staging buffer */
     73    struct pipe_resource *ss;      /* staging resource for MSAA resolves */
     74 };
     75 
     76 static inline struct u_transfer *
     77 u_transfer(struct pipe_transfer *ptrans)
     78 {
     79    debug_assert(handle_transfer(ptrans->resource));
     80    return (struct u_transfer *)ptrans;
     81 }
     82 
     83 struct pipe_resource *
     84 u_transfer_helper_resource_create(struct pipe_screen *pscreen,
     85                                   const struct pipe_resource *templ)
     86 {
     87    struct u_transfer_helper *helper = pscreen->transfer_helper;
     88    enum pipe_format format = templ->format;
     89    struct pipe_resource *prsc;
     90 
     91    if ((format == PIPE_FORMAT_Z32_FLOAT_S8X24_UINT) && helper->separate_z32s8) {
     92       struct pipe_resource t = *templ;
     93       struct pipe_resource *stencil;
     94 
     95       t.format = PIPE_FORMAT_Z32_FLOAT;
     96 
     97       prsc = helper->vtbl->resource_create(pscreen, &t);
     98       if (!prsc)
     99          return NULL;
    100 
    101       prsc->format = format;  /* frob the format back to the "external" format */
    102 
    103       t.format = PIPE_FORMAT_S8_UINT;
    104       stencil = helper->vtbl->resource_create(pscreen, &t);
    105 
    106       if (!stencil) {
    107          helper->vtbl->resource_destroy(pscreen, prsc);
    108          return NULL;
    109       }
    110 
    111       helper->vtbl->set_stencil(prsc, stencil);
    112    } else if ((util_format_description(format)->layout == UTIL_FORMAT_LAYOUT_RGTC) &&
    113          helper->fake_rgtc) {
    114       struct pipe_resource t = *templ;
    115       t.format = PIPE_FORMAT_R8G8B8A8_UNORM;
    116 
    117       prsc = helper->vtbl->resource_create(pscreen, &t);
    118       if (!prsc)
    119          return NULL;
    120 
    121       prsc->format = format;  /* frob the format back to the "external" format */
    122    } else {
    123       /* normal case, no special handling: */
    124       prsc = helper->vtbl->resource_create(pscreen, templ);
    125       if (!prsc)
    126          return NULL;
    127    }
    128 
    129    return prsc;
    130 }
    131 
    132 void
    133 u_transfer_helper_resource_destroy(struct pipe_screen *pscreen,
    134                                    struct pipe_resource *prsc)
    135 {
    136    struct u_transfer_helper *helper = pscreen->transfer_helper;
    137 
    138    if (helper->vtbl->get_stencil) {
    139       struct pipe_resource *stencil = helper->vtbl->get_stencil(prsc);
    140 
    141       pipe_resource_reference(&stencil, NULL);
    142    }
    143 
    144    helper->vtbl->resource_destroy(pscreen, prsc);
    145 }
    146 
    147 static bool needs_pack(unsigned usage)
    148 {
    149    return (usage & PIPE_TRANSFER_READ) &&
    150       !(usage & (PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE | PIPE_TRANSFER_DISCARD_RANGE));
    151 }
    152 
    153 /* In the case of transfer_map of a multi-sample resource, call back into
    154  * pctx->transfer_map() to map the staging resource, to handle cases of
    155  * MSAA + separate_z32s8 or fake_rgtc
    156  */
    157 static void *
    158 transfer_map_msaa(struct pipe_context *pctx,
    159                   struct pipe_resource *prsc,
    160                   unsigned level, unsigned usage,
    161                   const struct pipe_box *box,
    162                   struct pipe_transfer **pptrans)
    163 {
    164    struct pipe_screen *pscreen = pctx->screen;
    165    struct u_transfer *trans = calloc(1, sizeof(*trans));
    166    if (!trans)
    167       return NULL;
    168    struct pipe_transfer *ptrans = &trans->base;
    169 
    170    pipe_resource_reference(&ptrans->resource, prsc);
    171    ptrans->level = level;
    172    ptrans->usage = usage;
    173    ptrans->box = *box;
    174 
    175    struct pipe_resource tmpl = {
    176          .target = prsc->target,
    177          .format = prsc->format,
    178          .width0 = box->width,
    179          .height0 = box->height,
    180          .depth0 = 1,
    181          .array_size = 1,
    182    };
    183    trans->ss = pscreen->resource_create(pscreen, &tmpl);
    184    if (!trans->ss) {
    185       free(trans);
    186       return NULL;
    187    }
    188 
    189    if (needs_pack(usage)) {
    190       struct pipe_blit_info blit;
    191       memset(&blit, 0, sizeof(blit));
    192 
    193       blit.src.resource = ptrans->resource;
    194       blit.src.format = ptrans->resource->format;
    195       blit.src.level = ptrans->level;
    196       blit.src.box = *box;
    197 
    198       blit.dst.resource = trans->ss;
    199       blit.dst.format = trans->ss->format;
    200       blit.dst.box.width = box->width;
    201       blit.dst.box.height = box->height;
    202       blit.dst.box.depth = 1;
    203 
    204       blit.mask = util_format_get_mask(prsc->format);
    205       blit.filter = PIPE_TEX_FILTER_NEAREST;
    206 
    207       pctx->blit(pctx, &blit);
    208    }
    209 
    210    void *ss_map = pctx->transfer_map(pctx, trans->ss, 0, usage, box,
    211          &trans->trans);
    212    if (!ss_map) {
    213       free(trans);
    214       return NULL;
    215    }
    216 
    217    *pptrans = ptrans;
    218    return ss_map;
    219 }
    220 
    221 void *
    222 u_transfer_helper_transfer_map(struct pipe_context *pctx,
    223                                struct pipe_resource *prsc,
    224                                unsigned level, unsigned usage,
    225                                const struct pipe_box *box,
    226                                struct pipe_transfer **pptrans)
    227 {
    228    struct u_transfer_helper *helper = pctx->screen->transfer_helper;
    229    struct u_transfer *trans;
    230    struct pipe_transfer *ptrans;
    231    enum pipe_format format = prsc->format;
    232    unsigned width = box->width;
    233    unsigned height = box->height;
    234 
    235    if (!handle_transfer(prsc))
    236       return helper->vtbl->transfer_map(pctx, prsc, level, usage, box, pptrans);
    237 
    238    if (helper->msaa_map && (prsc->nr_samples > 1))
    239       return transfer_map_msaa(pctx, prsc, level, usage, box, pptrans);
    240 
    241    debug_assert(box->depth == 1);
    242 
    243    trans = calloc(1, sizeof(*trans));
    244    if (!trans)
    245       return NULL;
    246 
    247    ptrans = &trans->base;
    248    pipe_resource_reference(&ptrans->resource, prsc);
    249    ptrans->level = level;
    250    ptrans->usage = usage;
    251    ptrans->box   = *box;
    252    ptrans->stride = util_format_get_stride(format, box->width);
    253    ptrans->layer_stride = ptrans->stride * box->height;
    254 
    255    trans->staging = malloc(ptrans->layer_stride);
    256    if (!trans->staging)
    257       goto fail;
    258 
    259    trans->ptr = helper->vtbl->transfer_map(pctx, prsc, level, usage, box,
    260                                            &trans->trans);
    261    if (!trans->ptr)
    262       goto fail;
    263 
    264    if (prsc->format == PIPE_FORMAT_Z32_FLOAT_S8X24_UINT) {
    265       struct pipe_resource *stencil = helper->vtbl->get_stencil(prsc);
    266       trans->ptr2 = helper->vtbl->transfer_map(pctx, stencil, level,
    267                                                usage, box, &trans->trans2);
    268 
    269       if (needs_pack(usage)) {
    270          util_format_z32_float_s8x24_uint_pack_z_float(trans->staging,
    271                                                        ptrans->stride,
    272                                                        trans->ptr,
    273                                                        trans->trans->stride,
    274                                                        width, height);
    275          util_format_z32_float_s8x24_uint_pack_s_8uint(trans->staging,
    276                                                        ptrans->stride,
    277                                                        trans->ptr2,
    278                                                        trans->trans2->stride,
    279                                                        width, height);
    280       }
    281    } else if (needs_pack(usage) &&
    282               util_format_description(prsc->format)->layout == UTIL_FORMAT_LAYOUT_RGTC) {
    283       switch (prsc->format) {
    284       case PIPE_FORMAT_RGTC1_UNORM:
    285       case PIPE_FORMAT_RGTC1_SNORM:
    286       case PIPE_FORMAT_LATC1_UNORM:
    287       case PIPE_FORMAT_LATC1_SNORM:
    288          util_format_rgtc1_unorm_pack_rgba_8unorm(trans->staging,
    289                                                   ptrans->stride,
    290                                                   trans->ptr,
    291                                                   trans->trans->stride,
    292                                                   width, height);
    293          break;
    294       case PIPE_FORMAT_RGTC2_UNORM:
    295       case PIPE_FORMAT_RGTC2_SNORM:
    296       case PIPE_FORMAT_LATC2_UNORM:
    297       case PIPE_FORMAT_LATC2_SNORM:
    298          util_format_rgtc2_unorm_pack_rgba_8unorm(trans->staging,
    299                                                   ptrans->stride,
    300                                                   trans->ptr,
    301                                                   trans->trans->stride,
    302                                                   width, height);
    303          break;
    304       default:
    305          assert(!"Unexpected format");
    306          break;
    307       }
    308    } else {
    309       unreachable("bleh");
    310    }
    311 
    312    *pptrans = ptrans;
    313    return trans->staging;
    314 
    315 fail:
    316    if (trans->trans)
    317       helper->vtbl->transfer_unmap(pctx, trans->trans);
    318    if (trans->trans2)
    319       helper->vtbl->transfer_unmap(pctx, trans->trans2);
    320    pipe_resource_reference(&ptrans->resource, NULL);
    321    free(trans->staging);
    322    free(trans);
    323    return NULL;
    324 }
    325 
    326 static void
    327 flush_region(struct pipe_context *pctx, struct pipe_transfer *ptrans,
    328              const struct pipe_box *box)
    329 {
    330    struct u_transfer_helper *helper = pctx->screen->transfer_helper;
    331    struct u_transfer *trans = u_transfer(ptrans);
    332    enum pipe_format iformat, format = ptrans->resource->format;
    333    unsigned width = box->width;
    334    unsigned height = box->height;
    335    void *src, *dst;
    336 
    337    if (!(ptrans->usage & PIPE_TRANSFER_WRITE))
    338       return;
    339 
    340    if (trans->ss) {
    341       struct pipe_blit_info blit;
    342       memset(&blit, 0, sizeof(blit));
    343 
    344       blit.src.resource = trans->ss;
    345       blit.src.format = trans->ss->format;
    346       blit.src.box = *box;
    347 
    348       blit.dst.resource = ptrans->resource;
    349       blit.dst.format = ptrans->resource->format;
    350       blit.dst.level = ptrans->level;
    351 
    352       u_box_2d(ptrans->box.x + box->x,
    353                ptrans->box.y + box->y,
    354                box->width, box->height,
    355                &blit.dst.box);
    356 
    357       blit.mask = util_format_get_mask(ptrans->resource->format);
    358       blit.filter = PIPE_TEX_FILTER_NEAREST;
    359 
    360       pctx->blit(pctx, &blit);
    361 
    362       return;
    363    }
    364 
    365    iformat = helper->vtbl->get_internal_format(ptrans->resource);
    366 
    367    src = (uint8_t *)trans->staging +
    368          (box->y * ptrans->stride) +
    369          (box->x * util_format_get_blocksize(format));
    370    dst = (uint8_t *)trans->ptr +
    371          (box->y * trans->trans->stride) +
    372          (box->x * util_format_get_blocksize(iformat));
    373 
    374    switch (format) {
    375    case PIPE_FORMAT_Z32_FLOAT_S8X24_UINT:
    376       util_format_z32_float_s8x24_uint_unpack_z_float(dst,
    377                                                       trans->trans->stride,
    378                                                       src,
    379                                                       ptrans->stride,
    380                                                       width, height);
    381       /* fallthru */
    382    case PIPE_FORMAT_X32_S8X24_UINT:
    383       dst = (uint8_t *)trans->ptr2 +
    384             (box->y * trans->trans2->stride) +
    385             (box->x * util_format_get_blocksize(PIPE_FORMAT_S8_UINT));
    386 
    387       util_format_z32_float_s8x24_uint_unpack_s_8uint(dst,
    388                                                       trans->trans2->stride,
    389                                                       src,
    390                                                       ptrans->stride,
    391                                                       width, height);
    392       break;
    393    case PIPE_FORMAT_RGTC1_UNORM:
    394    case PIPE_FORMAT_RGTC1_SNORM:
    395    case PIPE_FORMAT_LATC1_UNORM:
    396    case PIPE_FORMAT_LATC1_SNORM:
    397       util_format_rgtc1_unorm_unpack_rgba_8unorm(dst,
    398                                                  trans->trans->stride,
    399                                                  src,
    400                                                  ptrans->stride,
    401                                                  width, height);
    402       break;
    403    case PIPE_FORMAT_RGTC2_UNORM:
    404    case PIPE_FORMAT_RGTC2_SNORM:
    405    case PIPE_FORMAT_LATC2_UNORM:
    406    case PIPE_FORMAT_LATC2_SNORM:
    407       util_format_rgtc2_unorm_unpack_rgba_8unorm(dst,
    408                                                  trans->trans->stride,
    409                                                  src,
    410                                                  ptrans->stride,
    411                                                  width, height);
    412       break;
    413    default:
    414       assert(!"Unexpected staging transfer type");
    415       break;
    416    }
    417 }
    418 
    419 void
    420 u_transfer_helper_transfer_flush_region(struct pipe_context *pctx,
    421                                         struct pipe_transfer *ptrans,
    422                                         const struct pipe_box *box)
    423 {
    424    struct u_transfer_helper *helper = pctx->screen->transfer_helper;
    425 
    426    if (handle_transfer(ptrans->resource)) {
    427       struct u_transfer *trans = u_transfer(ptrans);
    428 
    429       flush_region(pctx, ptrans, box);
    430 
    431       /* handle MSAA case, since there could be multiple levels of
    432        * wrapped transfer, call pctx->transfer_flush_region()
    433        * instead of helper->vtbl->transfer_flush_region()
    434        */
    435       if (trans->ss) {
    436          pctx->transfer_flush_region(pctx, trans->trans, box);
    437          return;
    438       }
    439 
    440       helper->vtbl->transfer_flush_region(pctx, trans->trans, box);
    441       if (trans->trans2)
    442          helper->vtbl->transfer_flush_region(pctx, trans->trans2, box);
    443 
    444    } else {
    445       helper->vtbl->transfer_flush_region(pctx, ptrans, box);
    446    }
    447 }
    448 
    449 void
    450 u_transfer_helper_transfer_unmap(struct pipe_context *pctx,
    451                                  struct pipe_transfer *ptrans)
    452 {
    453    struct u_transfer_helper *helper = pctx->screen->transfer_helper;
    454 
    455    if (handle_transfer(ptrans->resource)) {
    456       struct u_transfer *trans = u_transfer(ptrans);
    457 
    458       if (!(ptrans->usage & PIPE_TRANSFER_FLUSH_EXPLICIT)) {
    459          struct pipe_box box;
    460          u_box_2d(0, 0, ptrans->box.width, ptrans->box.height, &box);
    461          flush_region(pctx, ptrans, &box);
    462       }
    463 
    464       /* in MSAA case, there could be multiple levels of wrapping
    465        * so don't call helper->vtbl->transfer_unmap() directly
    466        */
    467       if (trans->ss) {
    468          pctx->transfer_unmap(pctx, trans->trans);
    469          pipe_resource_reference(&trans->ss, NULL);
    470       } else {
    471          helper->vtbl->transfer_unmap(pctx, trans->trans);
    472          if (trans->trans2)
    473             helper->vtbl->transfer_unmap(pctx, trans->trans2);
    474       }
    475 
    476       free(trans);
    477    } else {
    478       helper->vtbl->transfer_unmap(pctx, ptrans);
    479    }
    480 }
    481 
    482 struct u_transfer_helper *
    483 u_transfer_helper_create(const struct u_transfer_vtbl *vtbl,
    484                          bool separate_z32s8,
    485                          bool fake_rgtc,
    486                          bool msaa_map)
    487 {
    488    struct u_transfer_helper *helper = calloc(1, sizeof(*helper));
    489 
    490    helper->vtbl = vtbl;
    491    helper->separate_z32s8 = separate_z32s8;
    492    helper->fake_rgtc = fake_rgtc;
    493    helper->msaa_map = msaa_map;
    494 
    495    return helper;
    496 }
    497 
    498 void
    499 u_transfer_helper_destroy(struct u_transfer_helper *helper)
    500 {
    501    free(helper);
    502 }
    503