From ecd6fce2611e88ff8468a354cff8eda39f260a31 Mon Sep 17 00:00:00 2001 From: Rob Clark Date: Wed, 31 Aug 2016 17:44:01 -0400 Subject: [PATCH] mesa/st: support lowering multi-planar YUV Support multi-planar YUV for external EGLImage's (currently just in the dma-buf import path) by lowering to multiple texture fetch's for each plane and CSC in shader. There was some discussion of alternative approaches for tracking the additional UV or U/V planes: https://lists.freedesktop.org/archives/mesa-dev/2016-September/127832.html They all seemed worse than pipe_resource::next Signed-off-by: Rob Clark --- src/gallium/auxiliary/util/u_inlines.h | 4 +- src/gallium/include/pipe/p_state.h | 6 + src/gallium/include/state_tracker/st_api.h | 3 + src/gallium/state_trackers/dri/dri2.c | 119 ++++++++++++++++---- src/gallium/state_trackers/dri/dri_screen.c | 11 ++ src/mesa/main/mtypes.h | 16 +++ src/mesa/program/ir_to_mesa.cpp | 1 + src/mesa/state_tracker/st_atom_sampler.c | 41 ++++++- src/mesa/state_tracker/st_atom_shader.c | 3 + src/mesa/state_tracker/st_atom_texture.c | 58 ++++++++++ src/mesa/state_tracker/st_cb_eglimage.c | 18 +++ src/mesa/state_tracker/st_context.c | 7 +- src/mesa/state_tracker/st_glsl_to_nir.cpp | 1 + src/mesa/state_tracker/st_glsl_to_tgsi.cpp | 4 + src/mesa/state_tracker/st_manager.c | 1 + src/mesa/state_tracker/st_program.c | 35 ++++++ src/mesa/state_tracker/st_program.h | 37 ++++++ src/mesa/state_tracker/st_texture.h | 21 ++++ 18 files changed, 359 insertions(+), 27 deletions(-) diff --git a/src/gallium/auxiliary/util/u_inlines.h b/src/gallium/auxiliary/util/u_inlines.h index c2a0b08d151..b7b83135838 100644 --- a/src/gallium/auxiliary/util/u_inlines.h +++ b/src/gallium/auxiliary/util/u_inlines.h @@ -136,8 +136,10 @@ pipe_resource_reference(struct pipe_resource **ptr, struct pipe_resource *tex) struct pipe_resource *old_tex = *ptr; if (pipe_reference_described(&(*ptr)->reference, &tex->reference, - (debug_reference_descriptor)debug_describe_resource)) + (debug_reference_descriptor)debug_describe_resource)) { + pipe_resource_reference(&old_tex->next, NULL); old_tex->screen->resource_destroy(old_tex->screen, old_tex); + } *ptr = tex; } diff --git a/src/gallium/include/pipe/p_state.h b/src/gallium/include/pipe/p_state.h index ebd03379e94..415ea85a2ad 100644 --- a/src/gallium/include/pipe/p_state.h +++ b/src/gallium/include/pipe/p_state.h @@ -498,6 +498,12 @@ struct pipe_resource unsigned bind; /**< bitmask of PIPE_BIND_x */ unsigned flags; /**< bitmask of PIPE_RESOURCE_FLAG_x */ + + /** + * For planar images, ie. YUV EGLImage external, etc, pointer to the + * next plane. + */ + struct pipe_resource *next; }; diff --git a/src/gallium/include/state_tracker/st_api.h b/src/gallium/include/state_tracker/st_api.h index 21d5177aa2f..06abfc56507 100644 --- a/src/gallium/include/state_tracker/st_api.h +++ b/src/gallium/include/state_tracker/st_api.h @@ -200,6 +200,9 @@ struct st_egl_image /* this is owned by the caller */ struct pipe_resource *texture; + /* format only differs from texture->format for multi-planar (YUV): */ + enum pipe_format format; + unsigned level; unsigned layer; }; diff --git a/src/gallium/state_trackers/dri/dri2.c b/src/gallium/state_trackers/dri/dri2.c index 43a5df11d0a..a22e7ee6ace 100644 --- a/src/gallium/state_trackers/dri/dri2.c +++ b/src/gallium/state_trackers/dri/dri2.c @@ -83,6 +83,21 @@ static int convert_fourcc(int format, int *dri_components_p) format = __DRI_IMAGE_FORMAT_GR88; dri_components = __DRI_IMAGE_COMPONENTS_RG; break; + /* + * For multi-planar YUV formats, we return the format of the first + * plane only. Since there is only one caller which supports multi- + * planar YUV it gets to figure out the remaining planes on it's + * own. + */ + case __DRI_IMAGE_FOURCC_YUV420: + case __DRI_IMAGE_FOURCC_YVU420: + format = __DRI_IMAGE_FORMAT_R8; + dri_components = __DRI_IMAGE_COMPONENTS_Y_U_V; + break; + case __DRI_IMAGE_FOURCC_NV12: + format = __DRI_IMAGE_FORMAT_R8; + dri_components = __DRI_IMAGE_COMPONENTS_Y_UV; + break; default: return -1; } @@ -90,6 +105,11 @@ static int convert_fourcc(int format, int *dri_components_p) return format; } +/* NOTE this probably isn't going to do the right thing for YUV images + * (but I think the same can be said for intel_query_image()). I think + * only needed for exporting dmabuf's, so I think I won't loose much + * sleep over it. + */ static int convert_to_fourcc(int format) { switch(format) { @@ -762,14 +782,16 @@ dri2_lookup_egl_image(struct dri_screen *screen, void *handle) static __DRIimage * dri2_create_image_from_winsys(__DRIscreen *_screen, int width, int height, int format, - struct winsys_handle *whandle, + int num_handles, struct winsys_handle *whandle, void *loaderPrivate) { struct dri_screen *screen = dri_screen(_screen); + struct pipe_screen *pscreen = screen->base.screen; __DRIimage *img; struct pipe_resource templ; unsigned tex_usage; enum pipe_format pf; + int i; tex_usage = PIPE_BIND_RENDER_TARGET | PIPE_BIND_SAMPLER_VIEW; @@ -783,19 +805,47 @@ dri2_create_image_from_winsys(__DRIscreen *_screen, memset(&templ, 0, sizeof(templ)); templ.bind = tex_usage; - templ.format = pf; templ.target = screen->target; templ.last_level = 0; - templ.width0 = width; - templ.height0 = height; templ.depth0 = 1; templ.array_size = 1; - img->texture = screen->base.screen->resource_from_handle(screen->base.screen, - &templ, whandle, PIPE_HANDLE_USAGE_READ_WRITE); - if (!img->texture) { - FREE(img); - return NULL; + for (i = num_handles - 1; i >= 0; i--) { + struct pipe_resource *tex; + + /* TODO: something a lot less ugly */ + switch (i) { + case 0: + templ.width0 = width; + templ.height0 = height; + templ.format = pf; + break; + case 1: + templ.width0 = width / 2; + templ.height0 = height / 2; + templ.format = (num_handles == 2) ? + PIPE_FORMAT_RG88_UNORM : /* NV12, etc */ + PIPE_FORMAT_R8_UNORM; /* I420, etc */ + break; + case 2: + templ.width0 = width / 2; + templ.height0 = height / 2; + templ.format = PIPE_FORMAT_R8_UNORM; + break; + default: + unreachable("too many planes!"); + } + + tex = pscreen->resource_from_handle(pscreen, + &templ, &whandle[i], PIPE_HANDLE_USAGE_READ_WRITE); + if (!tex) { + pipe_resource_reference(&img->texture, NULL); + FREE(img); + return NULL; + } + + tex->next = img->texture; + img->texture = tex; } img->level = 0; @@ -826,7 +876,7 @@ dri2_create_image_from_name(__DRIscreen *_screen, whandle.stride = pitch * util_format_get_blocksize(pf); return dri2_create_image_from_winsys(_screen, width, height, format, - &whandle, loaderPrivate); + 1, &whandle, loaderPrivate); } static __DRIimage * @@ -836,12 +886,26 @@ dri2_create_image_from_fd(__DRIscreen *_screen, int *offsets, unsigned *error, int *dri_components, void *loaderPrivate) { - struct winsys_handle whandle; + struct winsys_handle whandles[3]; int format; __DRIimage *img = NULL; unsigned err = __DRI_IMAGE_ERROR_SUCCESS; + int expected_num_fds, i; - if (num_fds != 1) { + switch (fourcc) { + case __DRI_IMAGE_FOURCC_YUV420: + case __DRI_IMAGE_FOURCC_YVU420: + expected_num_fds = 3; + break; + case __DRI_IMAGE_FOURCC_NV12: + expected_num_fds = 2; + break; + default: + expected_num_fds = 1; + break; + } + + if (num_fds != expected_num_fds) { err = __DRI_IMAGE_ERROR_BAD_MATCH; goto exit; } @@ -852,19 +916,30 @@ dri2_create_image_from_fd(__DRIscreen *_screen, goto exit; } - if (fds[0] < 0) { - err = __DRI_IMAGE_ERROR_BAD_ALLOC; - goto exit; + memset(whandles, 0, sizeof(whandles)); + + for (i = 0; i < num_fds; i++) { + if (fds[i] < 0) { + err = __DRI_IMAGE_ERROR_BAD_ALLOC; + goto exit; + } + + whandles[i].type = DRM_API_HANDLE_TYPE_FD; + whandles[i].handle = (unsigned)fds[i]; + whandles[i].stride = (unsigned)strides[i]; + whandles[i].offset = (unsigned)offsets[i]; } - memset(&whandle, 0, sizeof(whandle)); - whandle.type = DRM_API_HANDLE_TYPE_FD; - whandle.handle = (unsigned)fds[0]; - whandle.stride = (unsigned)strides[0]; - whandle.offset = (unsigned)offsets[0]; + if (fourcc == __DRI_IMAGE_FOURCC_YVU420) { + /* convert to YUV420 by swapping 2nd and 3rd planes: */ + struct winsys_handle tmp = whandles[1]; + whandles[1] = whandles[2]; + whandles[2] = tmp; + fourcc = __DRI_IMAGE_FOURCC_YUV420; + } img = dri2_create_image_from_winsys(_screen, width, height, format, - &whandle, loaderPrivate); + num_fds, whandles, loaderPrivate); if(img == NULL) err = __DRI_IMAGE_ERROR_BAD_ALLOC; @@ -1067,7 +1142,7 @@ dri2_from_names(__DRIscreen *screen, int width, int height, int format, whandle.offset = offsets[0]; img = dri2_create_image_from_winsys(screen, width, height, format, - &whandle, loaderPrivate); + 1, &whandle, loaderPrivate); if (img == NULL) return NULL; diff --git a/src/gallium/state_trackers/dri/dri_screen.c b/src/gallium/state_trackers/dri/dri_screen.c index 79bcb5abce7..aa0ad09ce58 100644 --- a/src/gallium/state_trackers/dri/dri_screen.c +++ b/src/gallium/state_trackers/dri/dri_screen.c @@ -334,6 +334,17 @@ dri_get_egl_image(struct st_manager *smapi, stimg->texture = NULL; pipe_resource_reference(&stimg->texture, img->texture); + switch (img->dri_components) { + case __DRI_IMAGE_COMPONENTS_Y_U_V: + stimg->format = PIPE_FORMAT_IYUV; + break; + case __DRI_IMAGE_COMPONENTS_Y_UV: + stimg->format = PIPE_FORMAT_NV12; + break; + default: + stimg->format = img->texture->format; + break; + } stimg->level = img->level; stimg->layer = img->layer; diff --git a/src/mesa/main/mtypes.h b/src/mesa/main/mtypes.h index 85aeb1e0df3..ee811ff5ddf 100644 --- a/src/mesa/main/mtypes.h +++ b/src/mesa/main/mtypes.h @@ -45,6 +45,7 @@ #include "compiler/shader_enums.h" #include "main/formats.h" /* MESA_FORMAT_COUNT */ #include "compiler/glsl/list.h" +#include "util/bitscan.h" #ifdef __cplusplus @@ -1929,6 +1930,7 @@ struct gl_program GLbitfield TexturesUsed[MAX_COMBINED_TEXTURE_IMAGE_UNITS]; /**< TEXTURE_x_BIT bitmask */ GLbitfield SamplersUsed; /**< Bitfield of which samplers are used */ GLbitfield ShadowSamplers; /**< Texture units used for shadow sampling. */ + GLbitfield ExternalSamplersUsed; /**< Texture units used for samplerExternalOES */ GLboolean UsesGather; /**< Does this program use gather4 at all? */ @@ -2460,6 +2462,20 @@ struct gl_linked_shader struct gl_shader_info info; }; +static inline GLbitfield gl_external_samplers(struct gl_linked_shader *shader) +{ + GLbitfield external_samplers = 0; + GLbitfield mask = shader->active_samplers; + + while (mask) { + int idx = u_bit_scan(&mask); + if (shader->SamplerTargets[idx] == TEXTURE_EXTERNAL_INDEX) + external_samplers |= (1 << idx); + } + + return external_samplers; +} + /** * A GLSL shader object. */ diff --git a/src/mesa/program/ir_to_mesa.cpp b/src/mesa/program/ir_to_mesa.cpp index 530c00fa785..debc18d95c9 100644 --- a/src/mesa/program/ir_to_mesa.cpp +++ b/src/mesa/program/ir_to_mesa.cpp @@ -2921,6 +2921,7 @@ get_mesa_program(struct gl_context *ctx, prog->SamplersUsed = shader->active_samplers; prog->ShadowSamplers = shader->shadow_samplers; + prog->ExternalSamplersUsed = gl_external_samplers(shader); _mesa_update_shader_textures_used(shader_program, prog); /* Set the gl_FragDepth layout. */ diff --git a/src/mesa/state_tracker/st_atom_sampler.c b/src/mesa/state_tracker/st_atom_sampler.c index 6b36ac74aad..065df6dd006 100644 --- a/src/mesa/state_tracker/st_atom_sampler.c +++ b/src/mesa/state_tracker/st_atom_sampler.c @@ -243,13 +243,13 @@ update_shader_samplers(struct st_context *st, struct pipe_sampler_state *samplers, unsigned *num_samplers) { + GLbitfield samplers_used = prog->SamplersUsed; + GLbitfield free_slots = ~prog->SamplersUsed; + GLbitfield external_samplers_used = prog->ExternalSamplersUsed; GLuint unit; - GLbitfield samplers_used; const GLuint old_max = *num_samplers; const struct pipe_sampler_state *states[PIPE_MAX_SAMPLERS]; - samplers_used = prog->SamplersUsed; - if (*num_samplers == 0 && samplers_used == 0x0) return; @@ -275,6 +275,41 @@ update_shader_samplers(struct st_context *st, } } + /* For any external samplers with multiplaner YUV, stuff the additional + * sampler states we need at the end. + * + * Just re-use the existing sampler-state from the primary slot. + */ + while (unlikely(external_samplers_used)) { + GLuint unit = u_bit_scan(&external_samplers_used); + GLuint extra = 0; + struct st_texture_object *stObj = + st_get_texture_object(st->ctx, prog, unit); + struct pipe_sampler_state *sampler = samplers + unit; + + if (!stObj) + continue; + + switch (st_get_view_format(stObj)) { + case PIPE_FORMAT_NV12: + /* we need one additional sampler: */ + extra = u_bit_scan(&free_slots); + states[extra] = sampler; + break; + case PIPE_FORMAT_IYUV: + /* we need two additional samplers: */ + extra = u_bit_scan(&free_slots); + states[extra] = sampler; + extra = u_bit_scan(&free_slots); + states[extra] = sampler; + break; + default: + break; + } + + *num_samplers = MAX2(*num_samplers, extra + 1); + } + cso_set_samplers(st->cso_context, shader_stage, *num_samplers, states); } diff --git a/src/mesa/state_tracker/st_atom_shader.c b/src/mesa/state_tracker/st_atom_shader.c index 7a23469cede..2f700a2fc55 100644 --- a/src/mesa/state_tracker/st_atom_shader.c +++ b/src/mesa/state_tracker/st_atom_shader.c @@ -51,6 +51,7 @@ #include "st_context.h" #include "st_atom.h" #include "st_program.h" +#include "st_texture.h" /** Compress the fog function enums into a 2-bit value */ @@ -142,6 +143,8 @@ update_fp( struct st_context *st ) } } + key.external = st_get_external_sampler_key(st, &stfp->Base.Base); + st->fp_variant = st_get_fp_variant(st, stfp, &key); st_reference_fragprog(st, &st->fp, stfp); diff --git a/src/mesa/state_tracker/st_atom_texture.c b/src/mesa/state_tracker/st_atom_texture.c index 19df6622ef1..efc8c90ffae 100644 --- a/src/mesa/state_tracker/st_atom_texture.c +++ b/src/mesa/state_tracker/st_atom_texture.c @@ -408,6 +408,15 @@ update_single_texture(struct st_context *st, } } + switch (view_format) { + case PIPE_FORMAT_NV12: + case PIPE_FORMAT_IYUV: + view_format = PIPE_FORMAT_R8_UNORM; + break; + default: + break; + } + *sampler_view = st_get_texture_sampler_view_from_stobj(st, stObj, view_format, glsl_version); @@ -426,6 +435,8 @@ update_textures(struct st_context *st, { const GLuint old_max = *num_textures; GLbitfield samplers_used = prog->SamplersUsed; + GLbitfield free_slots = ~prog->SamplersUsed; + GLbitfield external_samplers_used = prog->ExternalSamplersUsed; GLuint unit; struct gl_shader_program *shader = st->ctx->_Shader->CurrentProgram[mesa_shader]; @@ -460,6 +471,53 @@ update_textures(struct st_context *st, pipe_sampler_view_reference(&(sampler_views[unit]), sampler_view); } + /* For any external samplers with multiplaner YUV, stuff the additional + * sampler views we need at the end. + * + * Trying to cache the sampler view in the stObj looks painful, so just + * re-create the sampler view for the extra planes each time. Main use + * case is video playback (ie. fps games wouldn't be using this) so I + * guess no point to try to optimize this feature. + */ + while (unlikely(external_samplers_used)) { + GLuint unit = u_bit_scan(&external_samplers_used); + GLuint extra = 0; + struct st_texture_object *stObj = + st_get_texture_object(st->ctx, prog, unit); + struct pipe_sampler_view tmpl; + + if (!stObj) + continue; + + /* use original view as template: */ + tmpl = *sampler_views[unit]; + + switch (st_get_view_format(stObj)) { + case PIPE_FORMAT_NV12: + /* we need one additional R8G8 view: */ + tmpl.format = PIPE_FORMAT_RG88_UNORM; + tmpl.swizzle_g = PIPE_SWIZZLE_Y; /* tmpl from Y plane is R8 */ + extra = u_bit_scan(&free_slots); + sampler_views[extra] = + st->pipe->create_sampler_view(st->pipe, stObj->pt->next, &tmpl); + break; + case PIPE_FORMAT_IYUV: + /* we need two additional R8 views: */ + tmpl.format = PIPE_FORMAT_R8_UNORM; + extra = u_bit_scan(&free_slots); + sampler_views[extra] = + st->pipe->create_sampler_view(st->pipe, stObj->pt->next, &tmpl); + extra = u_bit_scan(&free_slots); + sampler_views[extra] = + st->pipe->create_sampler_view(st->pipe, stObj->pt->next->next, &tmpl); + break; + default: + break; + } + + *num_textures = MAX2(*num_textures, extra + 1); + } + cso_set_sampler_views(st->cso_context, shader_stage, *num_textures, diff --git a/src/mesa/state_tracker/st_cb_eglimage.c b/src/mesa/state_tracker/st_cb_eglimage.c index 1782d154dd6..7bea5653e46 100644 --- a/src/mesa/state_tracker/st_cb_eglimage.c +++ b/src/mesa/state_tracker/st_cb_eglimage.c @@ -119,6 +119,24 @@ st_bind_surface(struct gl_context *ctx, GLenum target, texFormat = st_pipe_format_to_mesa_format(ps->format); + /* TODO RequiredTextureImageUnits should probably be reset back + * to 1 somewhere if different texture is bound?? + */ + if (texFormat == MESA_FORMAT_NONE) { + switch (ps->format) { + case PIPE_FORMAT_NV12: + texFormat = MESA_FORMAT_R_UNORM8; + texObj->RequiredTextureImageUnits = 2; + break; + case PIPE_FORMAT_IYUV: + texFormat = MESA_FORMAT_R_UNORM8; + texObj->RequiredTextureImageUnits = 3; + break; + default: + unreachable("bad YUV format!"); + } + } + _mesa_init_teximage_fields(ctx, texImage, ps->width, ps->height, 1, 0, internalFormat, texFormat); diff --git a/src/mesa/state_tracker/st_context.c b/src/mesa/state_tracker/st_context.c index 81b3387626c..b9dc0c67da3 100644 --- a/src/mesa/state_tracker/st_context.c +++ b/src/mesa/state_tracker/st_context.c @@ -255,11 +255,16 @@ void st_invalidate_state(struct gl_context * ctx, GLbitfield new_state) st->active_states = st_get_active_states(ctx); } - if (new_state & _NEW_TEXTURE) + if (new_state & _NEW_TEXTURE) { st->dirty |= st->active_states & (ST_NEW_SAMPLER_VIEWS | ST_NEW_SAMPLERS | ST_NEW_IMAGE_UNITS); + if (ctx->FragmentProgram._Current && + ctx->FragmentProgram._Current->Base.ExternalSamplersUsed) { + st->dirty |= ST_NEW_FS_STATE; + } + } if (new_state & _NEW_PROGRAM_CONSTANTS) st->dirty |= st->active_states & ST_NEW_CONSTANTS; diff --git a/src/mesa/state_tracker/st_glsl_to_nir.cpp b/src/mesa/state_tracker/st_glsl_to_nir.cpp index 5b5fad44f18..f43460ba67a 100644 --- a/src/mesa/state_tracker/st_glsl_to_nir.cpp +++ b/src/mesa/state_tracker/st_glsl_to_nir.cpp @@ -423,6 +423,7 @@ st_nir_get_mesa_program(struct gl_context *ctx, prog->SamplersUsed = shader->active_samplers; prog->ShadowSamplers = shader->shadow_samplers; + prog->ExternalSamplersUsed = gl_external_samplers(shader); _mesa_update_shader_textures_used(shader_program, prog); _mesa_reference_program(ctx, &shader->Program, prog); diff --git a/src/mesa/state_tracker/st_glsl_to_tgsi.cpp b/src/mesa/state_tracker/st_glsl_to_tgsi.cpp index 507a7826e38..81ea233a8ec 100644 --- a/src/mesa/state_tracker/st_glsl_to_tgsi.cpp +++ b/src/mesa/state_tracker/st_glsl_to_tgsi.cpp @@ -4347,6 +4347,10 @@ count_resources(glsl_to_tgsi_visitor *v, gl_program *prog) } } } + + if (inst->tex_target == TEXTURE_EXTERNAL_INDEX) + prog->ExternalSamplersUsed |= 1 << inst->sampler.index; + if (inst->buffer.file != PROGRAM_UNDEFINED && ( is_resource_instruction(inst->op) || inst->op == TGSI_OPCODE_STORE)) { diff --git a/src/mesa/state_tracker/st_manager.c b/src/mesa/state_tracker/st_manager.c index e2da054d2a5..fece5d5dffd 100644 --- a/src/mesa/state_tracker/st_manager.c +++ b/src/mesa/state_tracker/st_manager.c @@ -845,6 +845,7 @@ st_manager_get_egl_image_surface(struct st_context *st, void *eglimg) return NULL; u_surface_default_template(&surf_tmpl, stimg.texture); + surf_tmpl.format = stimg.format; surf_tmpl.u.tex.level = stimg.level; surf_tmpl.u.tex.first_layer = stimg.layer; surf_tmpl.u.tex.last_layer = stimg.layer; diff --git a/src/mesa/state_tracker/st_program.c b/src/mesa/state_tracker/st_program.c index 2a4edfa7b7a..91887dc27db 100644 --- a/src/mesa/state_tracker/st_program.c +++ b/src/mesa/state_tracker/st_program.c @@ -53,6 +53,7 @@ #include "st_cb_bitmap.h" #include "st_cb_drawpixels.h" #include "st_context.h" +#include "st_tgsi_lower_yuv.h" #include "st_program.h" #include "st_mesa_to_tgsi.h" #include "st_atifs_to_tgsi.h" @@ -1024,8 +1025,23 @@ st_create_fp_variant(struct st_context *st, NIR_PASS_V(tgsi.ir.nir, nir_lower_drawpixels, &options); } + if (unlikely(key->external.lower_nv12 || key->external.lower_iyuv)) { + nir_lower_tex_options options = {0}; + options.lower_y_uv_external = key->external.lower_nv12; + options.lower_y_u_v_external = key->external.lower_iyuv; + NIR_PASS_V(tgsi.ir.nir, nir_lower_tex, &options); + } + st_finalize_nir(st, &stfp->Base.Base, tgsi.ir.nir); + if (unlikely(key->external.lower_nv12 || key->external.lower_iyuv)) { + /* This pass needs to happen *after* nir_lower_sampler */ + NIR_PASS_V(tgsi.ir.nir, st_nir_lower_tex_src_plane, + ~stfp->Base.Base.SamplersUsed, + key->external.lower_nv12, + key->external.lower_iyuv); + } + variant->driver_shader = pipe->create_fs_state(pipe, &tgsi); variant->key = *key; @@ -1122,6 +1138,25 @@ st_create_fp_variant(struct st_context *st, fprintf(stderr, "mesa: cannot create a shader for glDrawPixels\n"); } + if (unlikely(key->external.lower_nv12 || key->external.lower_iyuv)) { + const struct tgsi_token *tokens; + + /* samplers inserted would conflict, but this should be unpossible: */ + assert(!(key->bitmap || key->drawpixels)); + + tokens = st_tgsi_lower_yuv(tgsi.tokens, + ~stfp->Base.Base.SamplersUsed, + key->external.lower_nv12, + key->external.lower_iyuv); + if (tokens) { + if (tgsi.tokens != stfp->tgsi.tokens) + tgsi_free_tokens(tgsi.tokens); + tgsi.tokens = tokens; + } else { + fprintf(stderr, "mesa: cannot create a shader for samplerExternalOES\n"); + } + } + if (ST_DEBUG & DEBUG_TGSI) { tgsi_dump(tgsi.tokens, 0); debug_printf("\n"); diff --git a/src/mesa/state_tracker/st_program.h b/src/mesa/state_tracker/st_program.h index 8e11bf02fa9..ea55d476ff0 100644 --- a/src/mesa/state_tracker/st_program.h +++ b/src/mesa/state_tracker/st_program.h @@ -39,6 +39,7 @@ #include "program/program.h" #include "pipe/p_state.h" #include "st_context.h" +#include "st_texture.h" #include "st_glsl_to_tgsi.h" @@ -48,6 +49,40 @@ extern "C" { #define ST_DOUBLE_ATTRIB_PLACEHOLDER 0xffffffff +struct st_external_sampler_key +{ + GLuint lower_nv12; /**< bitmask of 2 plane YUV samplers */ + GLuint lower_iyuv; /**< bitmask of 3 plane YUV samplers */ +}; + +static inline struct st_external_sampler_key +st_get_external_sampler_key(struct st_context *st, struct gl_program *prog) +{ + unsigned mask = prog->ExternalSamplersUsed; + struct st_external_sampler_key key; + + memset(&key, 0, sizeof(key)); + + while (unlikely(mask)) { + unsigned unit = u_bit_scan(&mask); + struct st_texture_object *stObj = + st_get_texture_object(st->ctx, prog, unit); + + switch (st_get_view_format(stObj)) { + case PIPE_FORMAT_NV12: + key.lower_nv12 |= (1 << unit); + break; + case PIPE_FORMAT_IYUV: + key.lower_iyuv |= (1 << unit); + break; + default: + break; + } + } + + return key; +} + /** Fragment program variant key */ struct st_fp_variant_key { @@ -72,6 +107,8 @@ struct st_fp_variant_key /** needed for ATI_fragment_shader */ char texture_targets[MAX_NUM_FRAGMENT_REGISTERS_ATI]; + + struct st_external_sampler_key external; }; diff --git a/src/mesa/state_tracker/st_texture.h b/src/mesa/state_tracker/st_texture.h index b2ddc14ddfc..d2c4f3f880c 100644 --- a/src/mesa/state_tracker/st_texture.h +++ b/src/mesa/state_tracker/st_texture.h @@ -170,6 +170,27 @@ st_create_texture_sampler_view(struct pipe_context *pipe, texture->format); } +static inline struct st_texture_object * +st_get_texture_object(struct gl_context *ctx, + const struct gl_program *prog, + unsigned unit) +{ + const GLuint texUnit = prog->SamplerUnits[unit]; + struct gl_texture_object *texObj = ctx->Texture.Unit[texUnit]._Current; + + if (!texObj) + return NULL; + + return st_texture_object(texObj); +} + +static inline enum pipe_format +st_get_view_format(struct st_texture_object *stObj) +{ + if (!stObj) + return PIPE_FORMAT_NONE; + return stObj->surface_based ? stObj->surface_format : stObj->pt->format; +} extern struct pipe_resource * -- 2.30.2