Home | History | Annotate | Download | only in vl
      1 /**************************************************************************
      2  *
      3  * Copyright 2012 Christian Knig.
      4  * All Rights Reserved.
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a
      7  * copy of this software and associated documentation files (the
      8  * "Software"), to deal in the Software without restriction, including
      9  * without limitation the rights to use, copy, modify, merge, publish,
     10  * distribute, sub license, and/or sell copies of the Software, and to
     11  * permit persons to whom the Software is furnished to do so, subject to
     12  * the following conditions:
     13  *
     14  * The above copyright notice and this permission notice (including the
     15  * next paragraph) shall be included in all copies or substantial portions
     16  * of the Software.
     17  *
     18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
     21  * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
     22  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
     23  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
     24  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     25  *
     26  **************************************************************************/
     27 
     28 #include "pipe/p_context.h"
     29 
     30 #include "tgsi/tgsi_ureg.h"
     31 
     32 #include "util/u_draw.h"
     33 #include "util/u_memory.h"
     34 #include "util/u_math.h"
     35 
     36 #include "vl_types.h"
     37 #include "vl_vertex_buffers.h"
     38 #include "vl_median_filter.h"
     39 
     40 enum VS_OUTPUT
     41 {
     42    VS_O_VPOS = 0,
     43    VS_O_VTEX = 0
     44 };
     45 
     46 static void *
     47 create_vert_shader(struct vl_median_filter *filter)
     48 {
     49    struct ureg_program *shader;
     50    struct ureg_src i_vpos;
     51    struct ureg_dst o_vpos, o_vtex;
     52 
     53    shader = ureg_create(PIPE_SHADER_VERTEX);
     54    if (!shader)
     55       return NULL;
     56 
     57    i_vpos = ureg_DECL_vs_input(shader, 0);
     58    o_vpos = ureg_DECL_output(shader, TGSI_SEMANTIC_POSITION, VS_O_VPOS);
     59    o_vtex = ureg_DECL_output(shader, TGSI_SEMANTIC_GENERIC, VS_O_VTEX);
     60 
     61    ureg_MOV(shader, o_vpos, i_vpos);
     62    ureg_MOV(shader, o_vtex, i_vpos);
     63 
     64    ureg_END(shader);
     65 
     66    return ureg_create_shader_and_destroy(shader, filter->pipe);
     67 }
     68 
     69 static inline bool
     70 is_vec_zero(struct vertex2f v)
     71 {
     72    return v.x == 0.0f && v.y == 0.0f;
     73 }
     74 
     75 static void *
     76 create_frag_shader(struct vl_median_filter *filter,
     77                    struct vertex2f *offsets,
     78                    unsigned num_offsets)
     79 {
     80    struct pipe_screen *screen = filter->pipe->screen;
     81    struct ureg_program *shader;
     82    struct ureg_src i_vtex;
     83    struct ureg_src sampler;
     84    struct ureg_dst *t_array = MALLOC(sizeof(struct ureg_dst) * num_offsets);
     85    struct ureg_dst o_fragment;
     86    const unsigned median = num_offsets >> 1;
     87    unsigned i, j;
     88 
     89    assert(num_offsets & 1); /* we need an odd number of offsets */
     90    if (!(num_offsets & 1)) { /* yeah, we REALLY need an odd number of offsets!!! */
     91       FREE(t_array);
     92       return NULL;
     93    }
     94 
     95    if (num_offsets > screen->get_shader_param(
     96       screen, PIPE_SHADER_FRAGMENT, PIPE_SHADER_CAP_MAX_TEMPS)) {
     97 
     98       FREE(t_array);
     99       return NULL;
    100    }
    101 
    102    shader = ureg_create(PIPE_SHADER_FRAGMENT);
    103    if (!shader) {
    104       FREE(t_array);
    105       return NULL;
    106    }
    107 
    108    i_vtex = ureg_DECL_fs_input(shader, TGSI_SEMANTIC_GENERIC, VS_O_VTEX, TGSI_INTERPOLATE_LINEAR);
    109    sampler = ureg_DECL_sampler(shader, 0);
    110 
    111    for (i = 0; i < num_offsets; ++i)
    112       t_array[i] = ureg_DECL_temporary(shader);
    113    o_fragment = ureg_DECL_output(shader, TGSI_SEMANTIC_COLOR, 0);
    114 
    115    /*
    116     * t_array[0..*] = vtex + offset[0..*]
    117     * t_array[0..*] = tex(t_array[0..*], sampler)
    118     * result = partial_bubblesort(t_array)[mid]
    119     */
    120 
    121    for (i = 0; i < num_offsets; ++i) {
    122       if (!is_vec_zero(offsets[i])) {
    123          ureg_ADD(shader, ureg_writemask(t_array[i], TGSI_WRITEMASK_XY),
    124                   i_vtex, ureg_imm2f(shader, offsets[i].x, offsets[i].y));
    125          ureg_MOV(shader, ureg_writemask(t_array[i], TGSI_WRITEMASK_ZW),
    126                   ureg_imm1f(shader, 0.0f));
    127       }
    128    }
    129 
    130    for (i = 0; i < num_offsets; ++i) {
    131       struct ureg_src src = is_vec_zero(offsets[i]) ? i_vtex : ureg_src(t_array[i]);
    132       ureg_TEX(shader, t_array[i], TGSI_TEXTURE_2D, src, sampler);
    133    }
    134 
    135    // TODO: Couldn't this be improved even more?
    136    for (i = 0; i <= median; ++i) {
    137       for (j = 1; j < (num_offsets - i - 1); ++j) {
    138          struct ureg_dst tmp = ureg_DECL_temporary(shader);
    139          ureg_MOV(shader, tmp, ureg_src(t_array[j]));
    140          ureg_MAX(shader, t_array[j], ureg_src(t_array[j]), ureg_src(t_array[j - 1]));
    141          ureg_MIN(shader, t_array[j - 1], ureg_src(tmp), ureg_src(t_array[j - 1]));
    142          ureg_release_temporary(shader, tmp);
    143       }
    144       if (i == median)
    145          ureg_MAX(shader, t_array[j], ureg_src(t_array[j]), ureg_src(t_array[j - 1]));
    146       else
    147          ureg_MIN(shader, t_array[j - 1], ureg_src(t_array[j]), ureg_src(t_array[j - 1]));
    148    }
    149    ureg_MOV(shader, o_fragment, ureg_src(t_array[median]));
    150 
    151    ureg_END(shader);
    152 
    153    FREE(t_array);
    154    return ureg_create_shader_and_destroy(shader, filter->pipe);
    155 }
    156 
    157 static void
    158 generate_offsets(enum vl_median_filter_shape shape, unsigned size,
    159                  struct vertex2f **offsets, unsigned *num_offsets)
    160 {
    161    unsigned i = 0;
    162    int half_size;
    163    struct vertex2f v;
    164 
    165    assert(offsets && num_offsets);
    166 
    167    /* size needs to be odd */
    168    size = align(size + 1, 2) - 1;
    169    half_size = size >> 1;
    170 
    171    switch(shape) {
    172    case VL_MEDIAN_FILTER_BOX:
    173       *num_offsets = size*size;
    174       break;
    175 
    176    case VL_MEDIAN_FILTER_CROSS:
    177    case VL_MEDIAN_FILTER_X:
    178       *num_offsets = size + size - 1;
    179       break;
    180 
    181    case VL_MEDIAN_FILTER_HORIZONTAL:
    182    case VL_MEDIAN_FILTER_VERTICAL:
    183       *num_offsets = size;
    184       break;
    185 
    186    default:
    187       *num_offsets = 0;
    188       return;
    189    }
    190 
    191    *offsets = MALLOC(sizeof(struct vertex2f) * *num_offsets);
    192    if (!*offsets)
    193       return;
    194 
    195    switch(shape) {
    196    case VL_MEDIAN_FILTER_BOX:
    197       for (v.x = -half_size; v.x <= half_size; ++v.x)
    198          for (v.y = -half_size; v.y <= half_size; ++v.y)
    199             (*offsets)[i++] = v;
    200       break;
    201 
    202    case VL_MEDIAN_FILTER_CROSS:
    203       v.y = 0.0f;
    204       for (v.x = -half_size; v.x <= half_size; ++v.x)
    205          (*offsets)[i++] = v;
    206 
    207       v.x = 0.0f;
    208       for (v.y = -half_size; v.y <= half_size; ++v.y)
    209          if (v.y != 0.0f)
    210             (*offsets)[i++] = v;
    211       break;
    212 
    213    case VL_MEDIAN_FILTER_X:
    214       for (v.x = v.y = -half_size; v.x <= half_size; ++v.x, ++v.y)
    215          (*offsets)[i++] = v;
    216 
    217       for (v.x = -half_size, v.y = half_size; v.x <= half_size; ++v.x, --v.y)
    218          if (v.y != 0.0f)
    219             (*offsets)[i++] = v;
    220       break;
    221 
    222    case VL_MEDIAN_FILTER_HORIZONTAL:
    223       v.y = 0.0f;
    224       for (v.x = -half_size; v.x <= half_size; ++v.x)
    225          (*offsets)[i++] = v;
    226       break;
    227 
    228    case VL_MEDIAN_FILTER_VERTICAL:
    229       v.x = 0.0f;
    230       for (v.y = -half_size; v.y <= half_size; ++v.y)
    231          (*offsets)[i++] = v;
    232       break;
    233    }
    234 
    235    assert(i == *num_offsets);
    236 }
    237 
    238 bool
    239 vl_median_filter_init(struct vl_median_filter *filter, struct pipe_context *pipe,
    240                       unsigned width, unsigned height, unsigned size,
    241                       enum vl_median_filter_shape shape)
    242 {
    243    struct pipe_rasterizer_state rs_state;
    244    struct pipe_blend_state blend;
    245    struct pipe_sampler_state sampler;
    246    struct vertex2f *offsets = NULL;
    247    struct pipe_vertex_element ve;
    248    unsigned i, num_offsets = 0;
    249 
    250    assert(filter && pipe);
    251    assert(width && height);
    252    assert(size > 1 && size < 20);
    253 
    254    memset(filter, 0, sizeof(*filter));
    255    filter->pipe = pipe;
    256 
    257    memset(&rs_state, 0, sizeof(rs_state));
    258    rs_state.half_pixel_center = true;
    259    rs_state.bottom_edge_rule = true;
    260    rs_state.depth_clip = 1;
    261    filter->rs_state = pipe->create_rasterizer_state(pipe, &rs_state);
    262    if (!filter->rs_state)
    263       goto error_rs_state;
    264 
    265    memset(&blend, 0, sizeof blend);
    266    blend.rt[0].rgb_func = PIPE_BLEND_ADD;
    267    blend.rt[0].rgb_src_factor = PIPE_BLENDFACTOR_ONE;
    268    blend.rt[0].rgb_dst_factor = PIPE_BLENDFACTOR_ONE;
    269    blend.rt[0].alpha_func = PIPE_BLEND_ADD;
    270    blend.rt[0].alpha_src_factor = PIPE_BLENDFACTOR_ONE;
    271    blend.rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_ONE;
    272    blend.logicop_func = PIPE_LOGICOP_CLEAR;
    273    blend.rt[0].colormask = PIPE_MASK_RGBA;
    274    filter->blend = pipe->create_blend_state(pipe, &blend);
    275    if (!filter->blend)
    276       goto error_blend;
    277 
    278    memset(&sampler, 0, sizeof(sampler));
    279    sampler.wrap_s = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
    280    sampler.wrap_t = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
    281    sampler.wrap_r = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
    282    sampler.min_img_filter = PIPE_TEX_FILTER_NEAREST;
    283    sampler.min_mip_filter = PIPE_TEX_MIPFILTER_NONE;
    284    sampler.mag_img_filter = PIPE_TEX_FILTER_NEAREST;
    285    sampler.compare_mode = PIPE_TEX_COMPARE_NONE;
    286    sampler.compare_func = PIPE_FUNC_ALWAYS;
    287    sampler.normalized_coords = 1;
    288    filter->sampler = pipe->create_sampler_state(pipe, &sampler);
    289    if (!filter->sampler)
    290       goto error_sampler;
    291 
    292    filter->quad = vl_vb_upload_quads(pipe);
    293    if(!filter->quad.buffer)
    294       goto error_quad;
    295 
    296    memset(&ve, 0, sizeof(ve));
    297    ve.src_offset = 0;
    298    ve.instance_divisor = 0;
    299    ve.vertex_buffer_index = 0;
    300    ve.src_format = PIPE_FORMAT_R32G32_FLOAT;
    301    filter->ves = pipe->create_vertex_elements_state(pipe, 1, &ve);
    302    if (!filter->ves)
    303       goto error_ves;
    304 
    305    generate_offsets(shape, size, &offsets, &num_offsets);
    306    if (!offsets)
    307       goto error_offsets;
    308 
    309    for (i = 0; i < num_offsets; ++i) {
    310       offsets[i].x /= width;
    311       offsets[i].y /= height;
    312    }
    313 
    314    filter->vs = create_vert_shader(filter);
    315    if (!filter->vs)
    316       goto error_vs;
    317 
    318    filter->fs = create_frag_shader(filter, offsets, num_offsets);
    319    if (!filter->fs)
    320       goto error_fs;
    321 
    322    FREE(offsets);
    323    return true;
    324 
    325 error_fs:
    326    pipe->delete_vs_state(pipe, filter->vs);
    327 
    328 error_vs:
    329    FREE(offsets);
    330 
    331 error_offsets:
    332    pipe->delete_vertex_elements_state(pipe, filter->ves);
    333 
    334 error_ves:
    335    pipe_resource_reference(&filter->quad.buffer, NULL);
    336 
    337 error_quad:
    338    pipe->delete_sampler_state(pipe, filter->sampler);
    339 
    340 error_sampler:
    341    pipe->delete_blend_state(pipe, filter->blend);
    342 
    343 error_blend:
    344    pipe->delete_rasterizer_state(pipe, filter->rs_state);
    345 
    346 error_rs_state:
    347    return false;
    348 }
    349 
    350 void
    351 vl_median_filter_cleanup(struct vl_median_filter *filter)
    352 {
    353    assert(filter);
    354 
    355    filter->pipe->delete_sampler_state(filter->pipe, filter->sampler);
    356    filter->pipe->delete_blend_state(filter->pipe, filter->blend);
    357    filter->pipe->delete_rasterizer_state(filter->pipe, filter->rs_state);
    358    filter->pipe->delete_vertex_elements_state(filter->pipe, filter->ves);
    359    pipe_resource_reference(&filter->quad.buffer, NULL);
    360 
    361    filter->pipe->delete_vs_state(filter->pipe, filter->vs);
    362    filter->pipe->delete_fs_state(filter->pipe, filter->fs);
    363 }
    364 
    365 void
    366 vl_median_filter_render(struct vl_median_filter *filter,
    367                         struct pipe_sampler_view *src,
    368                         struct pipe_surface *dst)
    369 {
    370    struct pipe_viewport_state viewport;
    371    struct pipe_framebuffer_state fb_state;
    372 
    373    assert(filter && src && dst);
    374 
    375    memset(&viewport, 0, sizeof(viewport));
    376    viewport.scale[0] = dst->width;
    377    viewport.scale[1] = dst->height;
    378    viewport.scale[2] = 1;
    379 
    380    memset(&fb_state, 0, sizeof(fb_state));
    381    fb_state.width = dst->width;
    382    fb_state.height = dst->height;
    383    fb_state.nr_cbufs = 1;
    384    fb_state.cbufs[0] = dst;
    385 
    386    filter->pipe->bind_rasterizer_state(filter->pipe, filter->rs_state);
    387    filter->pipe->bind_blend_state(filter->pipe, filter->blend);
    388    filter->pipe->bind_sampler_states(filter->pipe, PIPE_SHADER_FRAGMENT,
    389                                      0, 1, &filter->sampler);
    390    filter->pipe->set_sampler_views(filter->pipe, PIPE_SHADER_FRAGMENT,
    391                                    0, 1, &src);
    392    filter->pipe->bind_vs_state(filter->pipe, filter->vs);
    393    filter->pipe->bind_fs_state(filter->pipe, filter->fs);
    394    filter->pipe->set_framebuffer_state(filter->pipe, &fb_state);
    395    filter->pipe->set_viewport_states(filter->pipe, 0, 1, &viewport);
    396    filter->pipe->set_vertex_buffers(filter->pipe, 0, 1, &filter->quad);
    397    filter->pipe->bind_vertex_elements_state(filter->pipe, filter->ves);
    398 
    399    util_draw_arrays(filter->pipe, PIPE_PRIM_QUADS, 0, 4);
    400 }
    401