diff --git a/src/gallium/auxiliary/util/u_transfer_helper.c b/src/gallium/auxiliary/util/u_transfer_helper.c index 0ba8abdf782..0dc9ecf5afc 100644 --- a/src/gallium/auxiliary/util/u_transfer_helper.c +++ b/src/gallium/auxiliary/util/u_transfer_helper.c @@ -351,7 +351,8 @@ flush_region(struct pipe_context *pctx, struct pipe_transfer *ptrans, const struct pipe_box *box) { struct u_transfer_helper *helper = pctx->screen->transfer_helper; - struct u_transfer *trans = u_transfer(ptrans); + /* using the function here hits an assert for the deinterleave cases */ + struct u_transfer *trans = (struct u_transfer *)ptrans; enum pipe_format iformat, format = ptrans->resource->format; unsigned width = box->width; unsigned height = box->height; @@ -544,3 +545,125 @@ u_transfer_helper_destroy(struct u_transfer_helper *helper) { free(helper); } + + +/* these two functions 'deinterleave' are meant to be used without the corresponding + * resource_create/destroy hooks, as they perform the interleaving on-the-fly + * + * drivers should expect to be passed the same buffer repeatedly with the format changed + * to indicate which component is being mapped + */ +void * +u_transfer_helper_deinterleave_transfer_map(struct pipe_context *pctx, + struct pipe_resource *prsc, + unsigned level, unsigned usage, + const struct pipe_box *box, + struct pipe_transfer **pptrans) +{ + struct u_transfer_helper *helper = pctx->screen->transfer_helper; + struct u_transfer *trans; + struct pipe_transfer *ptrans; + enum pipe_format format = prsc->format; + unsigned width = box->width; + unsigned height = box->height; + + if (!((helper->separate_stencil && util_format_is_depth_and_stencil(format)) || + (format == PIPE_FORMAT_Z32_FLOAT_S8X24_UINT && helper->separate_z32s8))) + return helper->vtbl->transfer_map(pctx, prsc, level, usage, box, pptrans); + + debug_assert(box->depth == 1); + + trans = calloc(1, sizeof(*trans)); + if (!trans) + return NULL; + + ptrans = &trans->base; + pipe_resource_reference(&ptrans->resource, prsc); + ptrans->level = level; + ptrans->usage = usage; + ptrans->box = *box; + ptrans->stride = util_format_get_stride(format, box->width); + ptrans->layer_stride = ptrans->stride * box->height; + + trans->staging = malloc(ptrans->layer_stride); + if (!trans->staging) + goto fail; + + trans->ptr = helper->vtbl->transfer_map(pctx, prsc, level, usage | PIPE_TRANSFER_DEPTH_ONLY, box, + &trans->trans); + if (!trans->ptr) + goto fail; + + trans->ptr2 = helper->vtbl->transfer_map(pctx, prsc, level, + usage | PIPE_TRANSFER_STENCIL_ONLY, box, &trans->trans2); + if (needs_pack(usage)) { + switch (prsc->format) { + case PIPE_FORMAT_Z32_FLOAT_S8X24_UINT: + util_format_z32_float_s8x24_uint_pack_z_float(trans->staging, + ptrans->stride, + trans->ptr, + trans->trans->stride, + width, height); + util_format_z32_float_s8x24_uint_pack_s_8uint(trans->staging, + ptrans->stride, + trans->ptr2, + trans->trans2->stride, + width, height); + break; + case PIPE_FORMAT_Z24_UNORM_S8_UINT: + util_format_z24_unorm_s8_uint_pack_separate(trans->staging, + ptrans->stride, + trans->ptr, + trans->trans->stride, + trans->ptr2, + trans->trans2->stride, + width, height); + break; + default: + unreachable("Unexpected format"); + } + } + + *pptrans = ptrans; + return trans->staging; + +fail: + if (trans->trans) + helper->vtbl->transfer_unmap(pctx, trans->trans); + if (trans->trans2) + helper->vtbl->transfer_unmap(pctx, trans->trans2); + pipe_resource_reference(&ptrans->resource, NULL); + free(trans->staging); + free(trans); + return NULL; +} + +void +u_transfer_helper_deinterleave_transfer_unmap(struct pipe_context *pctx, + struct pipe_transfer *ptrans) +{ + struct u_transfer_helper *helper = pctx->screen->transfer_helper; + enum pipe_format format = ptrans->resource->format; + + if ((helper->separate_stencil && util_format_is_depth_and_stencil(format)) || + (format == PIPE_FORMAT_Z32_FLOAT_S8X24_UINT && helper->separate_z32s8)) { + struct u_transfer *trans = (struct u_transfer *)ptrans; + + if (!(ptrans->usage & PIPE_TRANSFER_FLUSH_EXPLICIT)) { + struct pipe_box box; + u_box_2d(0, 0, ptrans->box.width, ptrans->box.height, &box); + flush_region(pctx, ptrans, &box); + } + + helper->vtbl->transfer_unmap(pctx, trans->trans); + if (trans->trans2) + helper->vtbl->transfer_unmap(pctx, trans->trans2); + + pipe_resource_reference(&ptrans->resource, NULL); + + free(trans->staging); + free(trans); + } else { + helper->vtbl->transfer_unmap(pctx, ptrans); + } +} diff --git a/src/gallium/auxiliary/util/u_transfer_helper.h b/src/gallium/auxiliary/util/u_transfer_helper.h index e27c3570499..1c866c626c8 100644 --- a/src/gallium/auxiliary/util/u_transfer_helper.h +++ b/src/gallium/auxiliary/util/u_transfer_helper.h @@ -129,6 +129,16 @@ struct u_transfer_helper * u_transfer_helper_create(const struct u_transfer_vtbl void u_transfer_helper_destroy(struct u_transfer_helper *helper); +void * +u_transfer_helper_deinterleave_transfer_map(struct pipe_context *pctx, + struct pipe_resource *prsc, + unsigned level, unsigned usage, + const struct pipe_box *box, + struct pipe_transfer **pptrans); + +void +u_transfer_helper_deinterleave_transfer_unmap(struct pipe_context *pctx, + struct pipe_transfer *ptrans); #ifdef __cplusplus } // extern "C" { #endif