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 TUNGSTEN GRAPHICS 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(TGSI_PROCESSOR_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    int 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, TGSI_PROCESSOR_FRAGMENT, PIPE_SHADER_CAP_MAX_TEMPS)) {
     97 
     98       FREE(t_array);
     99       return NULL;
    100    }
    101 
    102    shader = ureg_create(TGSI_PROCESSOR_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    int i = 0, half_size;
    162    struct vertex2f v;
    163 
    164    assert(offsets && num_offsets);
    165 
    166    /* size needs to be odd */
    167    size = align(size + 1, 2) - 1;
    168    half_size = size >> 1;
    169 
    170    switch(shape) {
    171    case VL_MEDIAN_FILTER_BOX:
    172       *num_offsets = size*size;
    173       break;
    174 
    175    case VL_MEDIAN_FILTER_CROSS:
    176    case VL_MEDIAN_FILTER_X:
    177       *num_offsets = size + size - 1;
    178       break;
    179 
    180    case VL_MEDIAN_FILTER_HORIZONTAL:
    181    case VL_MEDIAN_FILTER_VERTICAL:
    182       *num_offsets = size;
    183       break;
    184 
    185    default:
    186       *num_offsets = 0;
    187       return;
    188    }
    189 
    190    *offsets = MALLOC(sizeof(struct vertex2f) * *num_offsets);
    191    if (!*offsets)
    192       return;
    193 
    194    switch(shape) {
    195    case VL_MEDIAN_FILTER_BOX:
    196       for (v.x = -half_size; v.x <= half_size; ++v.x)
    197          for (v.y = -half_size; v.y <= half_size; ++v.y)
    198             (*offsets)[i++] = v;
    199       break;
    200 
    201    case VL_MEDIAN_FILTER_CROSS:
    202       v.y = 0.0f;
    203       for (v.x = -half_size; v.x <= half_size; ++v.x)
    204          (*offsets)[i++] = v;
    205 
    206       v.x = 0.0f;
    207       for (v.y = -half_size; v.y <= half_size; ++v.y)
    208          if (v.y != 0.0f)
    209             (*offsets)[i++] = v;
    210       break;
    211 
    212    case VL_MEDIAN_FILTER_X:
    213       for (v.x = v.y = -half_size; v.x <= half_size; ++v.x, ++v.y)
    214          (*offsets)[i++] = v;
    215 
    216       for (v.x = -half_size, v.y = half_size; v.x <= half_size; ++v.x, --v.y)
    217          if (v.y != 0.0f)
    218             (*offsets)[i++] = v;
    219       break;
    220 
    221    case VL_MEDIAN_FILTER_HORIZONTAL:
    222       v.y = 0.0f;
    223       for (v.x = -half_size; v.x <= half_size; ++v.x)
    224          (*offsets)[i++] = v;
    225       break;
    226 
    227    case VL_MEDIAN_FILTER_VERTICAL:
    228       v.x = 0.0f;
    229       for (v.y = -half_size; v.y <= half_size; ++v.y)
    230          (*offsets)[i++] = v;
    231       break;
    232    }
    233 
    234    assert(i == *num_offsets);
    235 }
    236 
    237 bool
    238 vl_median_filter_init(struct vl_median_filter *filter, struct pipe_context *pipe,
    239                       unsigned width, unsigned height, unsigned size,
    240                       enum vl_median_filter_shape shape)
    241 {
    242    struct pipe_rasterizer_state rs_state;
    243    struct pipe_blend_state blend;
    244    struct pipe_sampler_state sampler;
    245    struct vertex2f *offsets = NULL;
    246    struct pipe_vertex_element ve;
    247    unsigned i, num_offsets = 0;
    248 
    249    assert(filter && pipe);
    250    assert(width && height);
    251    assert(size > 1 && size < 20);
    252 
    253    memset(filter, 0, sizeof(*filter));
    254    filter->pipe = pipe;
    255 
    256    memset(&rs_state, 0, sizeof(rs_state));
    257    rs_state.gl_rasterization_rules = true;
    258    rs_state.depth_clip = 1;
    259    filter->rs_state = pipe->create_rasterizer_state(pipe, &rs_state);
    260    if (!filter->rs_state)
    261       goto error_rs_state;
    262 
    263    memset(&blend, 0, sizeof blend);
    264    blend.rt[0].rgb_func = PIPE_BLEND_ADD;
    265    blend.rt[0].rgb_src_factor = PIPE_BLENDFACTOR_ONE;
    266    blend.rt[0].rgb_dst_factor = PIPE_BLENDFACTOR_ONE;
    267    blend.rt[0].alpha_func = PIPE_BLEND_ADD;
    268    blend.rt[0].alpha_src_factor = PIPE_BLENDFACTOR_ONE;
    269    blend.rt[0].alpha_dst_factor = PIPE_BLENDFACTOR_ONE;
    270    blend.logicop_func = PIPE_LOGICOP_CLEAR;
    271    blend.rt[0].colormask = PIPE_MASK_RGBA;
    272    filter->blend = pipe->create_blend_state(pipe, &blend);
    273    if (!filter->blend)
    274       goto error_blend;
    275 
    276    memset(&sampler, 0, sizeof(sampler));
    277    sampler.wrap_s = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
    278    sampler.wrap_t = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
    279    sampler.wrap_r = PIPE_TEX_WRAP_CLAMP_TO_EDGE;
    280    sampler.min_img_filter = PIPE_TEX_FILTER_NEAREST;
    281    sampler.min_mip_filter = PIPE_TEX_MIPFILTER_NONE;
    282    sampler.mag_img_filter = PIPE_TEX_FILTER_NEAREST;
    283    sampler.compare_mode = PIPE_TEX_COMPARE_NONE;
    284    sampler.compare_func = PIPE_FUNC_ALWAYS;
    285    sampler.normalized_coords = 1;
    286    filter->sampler = pipe->create_sampler_state(pipe, &sampler);
    287    if (!filter->sampler)
    288       goto error_sampler;
    289 
    290    filter->quad = vl_vb_upload_quads(pipe);
    291    if(!filter->quad.buffer)
    292       goto error_quad;
    293 
    294    memset(&ve, 0, sizeof(ve));
    295    ve.src_offset = 0;
    296    ve.instance_divisor = 0;
    297    ve.vertex_buffer_index = 0;
    298    ve.src_format = PIPE_FORMAT_R32G32_FLOAT;
    299    filter->ves = pipe->create_vertex_elements_state(pipe, 1, &ve);
    300    if (!filter->ves)
    301       goto error_ves;
    302 
    303    generate_offsets(shape, size, &offsets, &num_offsets);
    304    if (!offsets)
    305       goto error_offsets;
    306 
    307    for (i = 0; i < num_offsets; ++i) {
    308       offsets[i].x /= width;
    309       offsets[i].y /= height;
    310    }
    311 
    312    filter->vs = create_vert_shader(filter);
    313    if (!filter->vs)
    314       goto error_vs;
    315 
    316    filter->fs = create_frag_shader(filter, offsets, num_offsets);
    317    if (!filter->fs)
    318       goto error_fs;
    319 
    320    FREE(offsets);
    321    return true;
    322 
    323 error_fs:
    324    pipe->delete_vs_state(pipe, filter->vs);
    325 
    326 error_vs:
    327    FREE(offsets);
    328 
    329 error_offsets:
    330    pipe->delete_vertex_elements_state(pipe, filter->ves);
    331 
    332 error_ves:
    333    pipe_resource_reference(&filter->quad.buffer, NULL);
    334 
    335 error_quad:
    336    pipe->delete_sampler_state(pipe, filter->sampler);
    337 
    338 error_sampler:
    339    pipe->delete_blend_state(pipe, filter->blend);
    340 
    341 error_blend:
    342    pipe->delete_rasterizer_state(pipe, filter->rs_state);
    343 
    344 error_rs_state:
    345    return false;
    346 }
    347 
    348 void
    349 vl_median_filter_cleanup(struct vl_median_filter *filter)
    350 {
    351    assert(filter);
    352 
    353    filter->pipe->delete_sampler_state(filter->pipe, filter->sampler);
    354    filter->pipe->delete_blend_state(filter->pipe, filter->blend);
    355    filter->pipe->delete_rasterizer_state(filter->pipe, filter->rs_state);
    356    filter->pipe->delete_vertex_elements_state(filter->pipe, filter->ves);
    357    pipe_resource_reference(&filter->quad.buffer, NULL);
    358 
    359    filter->pipe->delete_vs_state(filter->pipe, filter->vs);
    360    filter->pipe->delete_fs_state(filter->pipe, filter->fs);
    361 }
    362 
    363 void
    364 vl_median_filter_render(struct vl_median_filter *filter,
    365                         struct pipe_sampler_view *src,
    366                         struct pipe_surface *dst)
    367 {
    368    struct pipe_viewport_state viewport;
    369    struct pipe_framebuffer_state fb_state;
    370 
    371    assert(filter && src && dst);
    372 
    373    memset(&viewport, 0, sizeof(viewport));
    374    viewport.scale[0] = dst->width;
    375    viewport.scale[1] = dst->height;
    376    viewport.scale[2] = 1;
    377    viewport.scale[3] = 1;
    378 
    379    memset(&fb_state, 0, sizeof(fb_state));
    380    fb_state.width = dst->width;
    381    fb_state.height = dst->height;
    382    fb_state.nr_cbufs = 1;
    383    fb_state.cbufs[0] = dst;
    384 
    385    filter->pipe->bind_rasterizer_state(filter->pipe, filter->rs_state);
    386    filter->pipe->bind_blend_state(filter->pipe, filter->blend);
    387    filter->pipe->bind_fragment_sampler_states(filter->pipe, 1, &filter->sampler);
    388    filter->pipe->set_fragment_sampler_views(filter->pipe, 1, &src);
    389    filter->pipe->bind_vs_state(filter->pipe, filter->vs);
    390    filter->pipe->bind_fs_state(filter->pipe, filter->fs);
    391    filter->pipe->set_framebuffer_state(filter->pipe, &fb_state);
    392    filter->pipe->set_viewport_state(filter->pipe, &viewport);
    393    filter->pipe->set_vertex_buffers(filter->pipe, 1, &filter->quad);
    394    filter->pipe->bind_vertex_elements_state(filter->pipe, filter->ves);
    395 
    396    util_draw_arrays(filter->pipe, PIPE_PRIM_QUADS, 0, 4);
    397 }
    398