From: Dave Airlie Date: Mon, 21 Mar 2016 21:59:35 +0000 (+1000) Subject: softpipe: add image support to softpipe (v3) X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=eb9ad9faa3975fc4f044b81d3b4b793866ef5563;p=mesa.git softpipe: add image support to softpipe (v3) This adds support for ARB_shader_image_load_store to softpipe. v2: add RESQ support (Ilia) v3: constify, cleanup internals, add some comments (Brian). Reviewed-by: Brian Paul Signed-off-by: Dave Airlie --- diff --git a/src/gallium/auxiliary/tgsi/tgsi_exec.h b/src/gallium/auxiliary/tgsi/tgsi_exec.h index 2c81d5e827d..45fb8d43c88 100644 --- a/src/gallium/auxiliary/tgsi/tgsi_exec.h +++ b/src/gallium/auxiliary/tgsi/tgsi_exec.h @@ -497,8 +497,10 @@ tgsi_exec_get_shader_param(enum pipe_shader_cap param) case PIPE_SHADER_CAP_TGSI_DROUND_SUPPORTED: case PIPE_SHADER_CAP_TGSI_FMA_SUPPORTED: case PIPE_SHADER_CAP_MAX_SHADER_BUFFERS: - case PIPE_SHADER_CAP_MAX_SHADER_IMAGES: return 0; + case PIPE_SHADER_CAP_MAX_SHADER_IMAGES: + return PIPE_MAX_SHADER_IMAGES; + case PIPE_SHADER_CAP_MAX_UNROLL_ITERATIONS_HINT: return 32; } diff --git a/src/gallium/drivers/softpipe/Makefile.sources b/src/gallium/drivers/softpipe/Makefile.sources index 2af3d6af21a..efe88468e3f 100644 --- a/src/gallium/drivers/softpipe/Makefile.sources +++ b/src/gallium/drivers/softpipe/Makefile.sources @@ -10,6 +10,7 @@ C_SOURCES := \ sp_flush.h \ sp_fs_exec.c \ sp_fs.h \ + sp_image.c \ sp_limits.h \ sp_prim_vbuf.c \ sp_prim_vbuf.h \ @@ -31,6 +32,7 @@ C_SOURCES := \ sp_state_blend.c \ sp_state_clip.c \ sp_state_derived.c \ + sp_state_image.c \ sp_state.h \ sp_state_rasterizer.c \ sp_state_sampler.c \ diff --git a/src/gallium/drivers/softpipe/sp_context.c b/src/gallium/drivers/softpipe/sp_context.c index d2a32200e47..30b0276cfe0 100644 --- a/src/gallium/drivers/softpipe/sp_context.c +++ b/src/gallium/drivers/softpipe/sp_context.c @@ -50,7 +50,7 @@ #include "sp_query.h" #include "sp_screen.h" #include "sp_tex_sample.h" - +#include "sp_image.h" static void softpipe_destroy( struct pipe_context *pipe ) @@ -199,6 +199,10 @@ softpipe_create_context(struct pipe_screen *screen, softpipe->tgsi.sampler[i] = sp_create_tgsi_sampler(); } + for (i = 0; i < PIPE_SHADER_TYPES; i++) { + softpipe->tgsi.image[i] = sp_create_tgsi_image(); + } + softpipe->dump_fs = debug_get_bool_option( "SOFTPIPE_DUMP_FS", FALSE ); softpipe->dump_gs = debug_get_bool_option( "SOFTPIPE_DUMP_GS", FALSE ); @@ -216,6 +220,7 @@ softpipe_create_context(struct pipe_screen *screen, softpipe_init_streamout_funcs(&softpipe->pipe); softpipe_init_texture_funcs( &softpipe->pipe ); softpipe_init_vertex_funcs(&softpipe->pipe); + softpipe_init_image_funcs(&softpipe->pipe); softpipe->pipe.set_framebuffer_state = softpipe_set_framebuffer_state; @@ -223,7 +228,8 @@ softpipe_create_context(struct pipe_screen *screen, softpipe->pipe.clear = softpipe_clear; softpipe->pipe.flush = softpipe_flush_wrapped; - + softpipe->pipe.texture_barrier = softpipe_texture_barrier; + softpipe->pipe.memory_barrier = softpipe_memory_barrier; softpipe->pipe.render_condition = softpipe_render_condition; /* @@ -272,6 +278,16 @@ softpipe_create_context(struct pipe_screen *screen, (struct tgsi_sampler *) softpipe->tgsi.sampler[PIPE_SHADER_GEOMETRY]); + draw_image(softpipe->draw, + PIPE_SHADER_VERTEX, + (struct tgsi_image *) + softpipe->tgsi.image[PIPE_SHADER_VERTEX]); + + draw_image(softpipe->draw, + PIPE_SHADER_GEOMETRY, + (struct tgsi_image *) + softpipe->tgsi.image[PIPE_SHADER_GEOMETRY]); + if (debug_get_bool_option( "SOFTPIPE_NO_RAST", FALSE )) softpipe->no_rast = TRUE; diff --git a/src/gallium/drivers/softpipe/sp_context.h b/src/gallium/drivers/softpipe/sp_context.h index d18bbe693f3..20a12353b38 100644 --- a/src/gallium/drivers/softpipe/sp_context.h +++ b/src/gallium/drivers/softpipe/sp_context.h @@ -83,6 +83,7 @@ struct softpipe_context { struct pipe_scissor_state scissors[PIPE_MAX_VIEWPORTS]; struct pipe_sampler_view *sampler_views[PIPE_SHADER_TYPES][PIPE_MAX_SHADER_SAMPLER_VIEWS]; + struct pipe_image_view images[PIPE_SHADER_TYPES][PIPE_MAX_SHADER_IMAGES]; struct pipe_viewport_state viewports[PIPE_MAX_VIEWPORTS]; struct pipe_vertex_buffer vertex_buffer[PIPE_MAX_ATTRIBS]; struct pipe_index_buffer index_buffer; @@ -172,6 +173,7 @@ struct softpipe_context { /** TGSI exec things */ struct { struct sp_tgsi_sampler *sampler[PIPE_SHADER_TYPES]; + struct sp_tgsi_image *image[PIPE_SHADER_TYPES]; } tgsi; struct tgsi_exec_machine *fs_machine; diff --git a/src/gallium/drivers/softpipe/sp_flush.c b/src/gallium/drivers/softpipe/sp_flush.c index 5a29e26517d..59b8ad696ec 100644 --- a/src/gallium/drivers/softpipe/sp_flush.c +++ b/src/gallium/drivers/softpipe/sp_flush.c @@ -168,3 +168,29 @@ softpipe_flush_resource(struct pipe_context *pipe, return TRUE; } + +void softpipe_texture_barrier(struct pipe_context *pipe) +{ + struct softpipe_context *softpipe = softpipe_context(pipe); + uint i, sh; + + for (sh = 0; sh < Elements(softpipe->tex_cache); sh++) { + for (i = 0; i < softpipe->num_sampler_views[sh]; i++) { + sp_flush_tex_tile_cache(softpipe->tex_cache[sh][i]); + } + } + + for (i = 0; i < softpipe->framebuffer.nr_cbufs; i++) + if (softpipe->cbuf_cache[i]) + sp_flush_tile_cache(softpipe->cbuf_cache[i]); + + if (softpipe->zsbuf_cache) + sp_flush_tile_cache(softpipe->zsbuf_cache); + + softpipe->dirty_render_cache = FALSE; +} + +void softpipe_memory_barrier(struct pipe_context *pipe, unsigned flags) +{ + softpipe_texture_barrier(pipe); +} diff --git a/src/gallium/drivers/softpipe/sp_flush.h b/src/gallium/drivers/softpipe/sp_flush.h index ab5f77be264..0674b4a7e48 100644 --- a/src/gallium/drivers/softpipe/sp_flush.h +++ b/src/gallium/drivers/softpipe/sp_flush.h @@ -55,4 +55,6 @@ softpipe_flush_resource(struct pipe_context *pipe, boolean cpu_access, boolean do_not_block); +void softpipe_texture_barrier(struct pipe_context *pipe); +void softpipe_memory_barrier(struct pipe_context *pipe, unsigned flags); #endif diff --git a/src/gallium/drivers/softpipe/sp_fs_exec.c b/src/gallium/drivers/softpipe/sp_fs_exec.c index 2c5bf7ef5c9..bfd9a4b7496 100644 --- a/src/gallium/drivers/softpipe/sp_fs_exec.c +++ b/src/gallium/drivers/softpipe/sp_fs_exec.c @@ -62,14 +62,15 @@ sp_exec_fragment_shader(const struct sp_fragment_shader_variant *var) static void exec_prepare( const struct sp_fragment_shader_variant *var, struct tgsi_exec_machine *machine, - struct tgsi_sampler *sampler ) + struct tgsi_sampler *sampler, + struct tgsi_image *image ) { /* * Bind tokens/shader to the interpreter's machine state. */ tgsi_exec_machine_bind_shader(machine, var->tokens, - sampler, NULL); + sampler, image); } @@ -127,6 +128,7 @@ exec_run( const struct sp_fragment_shader_variant *var, /* convert 0 to 1.0 and 1 to -1.0 */ machine->Face = (float) (quad->input.facing * -2 + 1); + machine->NonHelperMask = quad->inout.mask; quad->inout.mask &= tgsi_exec_machine_run( machine ); if (quad->inout.mask == 0) return FALSE; diff --git a/src/gallium/drivers/softpipe/sp_image.c b/src/gallium/drivers/softpipe/sp_image.c new file mode 100644 index 00000000000..3488fa83185 --- /dev/null +++ b/src/gallium/drivers/softpipe/sp_image.c @@ -0,0 +1,762 @@ +/* + * Copyright 2016 Red Hat. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * on the rights to use, copy, modify, merge, publish, distribute, sub + * license, and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "sp_context.h" +#include "sp_image.h" +#include "sp_texture.h" + +#include "util/u_format.h" + +/* + * Get the offset into the base image + * first element for a buffer or layer/level for texture. + */ +static uint32_t +get_image_offset(const struct softpipe_resource *spr, + const struct pipe_image_view *iview, + enum pipe_format format, unsigned r_coord) +{ + int base_layer = 0; + + if (spr->base.target == PIPE_BUFFER) + return iview->u.buf.first_element * util_format_get_blocksize(format); + + if (spr->base.target == PIPE_TEXTURE_1D_ARRAY || + spr->base.target == PIPE_TEXTURE_2D_ARRAY || + spr->base.target == PIPE_TEXTURE_CUBE_ARRAY || + spr->base.target == PIPE_TEXTURE_CUBE || + spr->base.target == PIPE_TEXTURE_3D) + base_layer = r_coord + iview->u.tex.first_layer; + return softpipe_get_tex_image_offset(spr, iview->u.tex.level, base_layer); +} + +/* + * Does this texture instruction have a layer or depth parameter. + */ +static inline bool +has_layer_or_depth(unsigned tgsi_tex_instr) +{ + return (tgsi_tex_instr == TGSI_TEXTURE_3D || + tgsi_tex_instr == TGSI_TEXTURE_CUBE || + tgsi_tex_instr == TGSI_TEXTURE_1D_ARRAY || + tgsi_tex_instr == TGSI_TEXTURE_2D_ARRAY || + tgsi_tex_instr == TGSI_TEXTURE_CUBE_ARRAY || + tgsi_tex_instr == TGSI_TEXTURE_2D_ARRAY_MSAA); +} + +/* + * Is this texture instruction a single non-array coordinate. + */ +static inline bool +has_1coord(unsigned tgsi_tex_instr) +{ + return (tgsi_tex_instr == TGSI_TEXTURE_BUFFER || + tgsi_tex_instr == TGSI_TEXTURE_1D || + tgsi_tex_instr == TGSI_TEXTURE_1D_ARRAY); +} + +/* + * check the bounds vs w/h/d + */ +static inline bool +bounds_check(int width, int height, int depth, + int s, int t, int r) +{ + if (s < 0 || s >= width) + return false; + if (t < 0 || t >= height) + return false; + if (r < 0 || r >= depth) + return false; + return true; +} + +/* + * Checks if the texture target compatible with the image resource + * pipe target. + */ +static inline bool +has_compat_target(unsigned pipe_target, unsigned tgsi_target) +{ + switch (pipe_target) { + case PIPE_TEXTURE_1D: + if (tgsi_target == TGSI_TEXTURE_1D) + return true; + break; + case PIPE_TEXTURE_2D: + if (tgsi_target == TGSI_TEXTURE_2D) + return true; + break; + case PIPE_TEXTURE_RECT: + if (tgsi_target == TGSI_TEXTURE_RECT) + return true; + break; + case PIPE_TEXTURE_3D: + if (tgsi_target == TGSI_TEXTURE_3D || + tgsi_target == TGSI_TEXTURE_2D) + return true; + break; + case PIPE_TEXTURE_CUBE: + if (tgsi_target == TGSI_TEXTURE_CUBE || + tgsi_target == TGSI_TEXTURE_2D) + return true; + break; + case PIPE_TEXTURE_1D_ARRAY: + if (tgsi_target == TGSI_TEXTURE_1D || + tgsi_target == TGSI_TEXTURE_1D_ARRAY) + return true; + break; + case PIPE_TEXTURE_2D_ARRAY: + if (tgsi_target == TGSI_TEXTURE_2D || + tgsi_target == TGSI_TEXTURE_2D_ARRAY) + return true; + break; + case PIPE_TEXTURE_CUBE_ARRAY: + if (tgsi_target == TGSI_TEXTURE_CUBE || + tgsi_target == TGSI_TEXTURE_CUBE_ARRAY || + tgsi_target == TGSI_TEXTURE_2D) + return true; + break; + case PIPE_BUFFER: + return (tgsi_target == TGSI_TEXTURE_BUFFER); + } + return false; +} + +static bool +get_dimensions(const struct pipe_image_view *iview, + const struct softpipe_resource *spr, + unsigned tgsi_tex_instr, + enum pipe_format pformat, + unsigned *width, + unsigned *height, + unsigned *depth) +{ + if (tgsi_tex_instr == TGSI_TEXTURE_BUFFER) { + *width = iview->u.buf.last_element - iview->u.buf.first_element + 1; + *height = 1; + *depth = 1; + /* + * Bounds check the buffer size from the view + * and the buffer size from the underlying buffer. + */ + if (util_format_get_stride(pformat, *width) > + util_format_get_stride(spr->base.format, spr->base.width0)) + return false; + } else { + unsigned level; + + level = spr->base.target == PIPE_BUFFER ? 0 : iview->u.tex.level; + *width = u_minify(spr->base.width0, level); + *height = u_minify(spr->base.height0, level); + + if (spr->base.target == TGSI_TEXTURE_3D) + *depth = u_minify(spr->base.depth0, level); + else + *depth = spr->base.array_size; + + /* Make sure the resource and view have compatiable formats */ + if (util_format_get_blocksize(pformat) > + util_format_get_blocksize(spr->base.format)) + return false; + } + return true; +} + +static void +fill_coords(const struct tgsi_image_params *params, + unsigned index, + const int s[TGSI_QUAD_SIZE], + const int t[TGSI_QUAD_SIZE], + const int r[TGSI_QUAD_SIZE], + int *s_coord, int *t_coord, int *r_coord) +{ + *s_coord = s[index]; + *t_coord = has_1coord(params->tgsi_tex_instr) ? 0 : t[index]; + *r_coord = has_layer_or_depth(params->tgsi_tex_instr) ? + (params->tgsi_tex_instr == TGSI_TEXTURE_1D_ARRAY ? t[index] : r[index]) : 0; +} +/* + * Implement the image LOAD operation. + */ +static void +sp_tgsi_load(const struct tgsi_image *image, + const struct tgsi_image_params *params, + const int s[TGSI_QUAD_SIZE], + const int t[TGSI_QUAD_SIZE], + const int r[TGSI_QUAD_SIZE], + const int sample[TGSI_QUAD_SIZE], + float rgba[TGSI_NUM_CHANNELS][TGSI_QUAD_SIZE]) +{ + struct sp_tgsi_image *sp_img = (struct sp_tgsi_image *)image; + struct pipe_image_view *iview; + struct softpipe_resource *spr; + unsigned width, height, depth; + unsigned stride; + int c, j; + char *data_ptr; + unsigned offset = 0; + + if (params->unit > PIPE_MAX_SHADER_IMAGES) + goto fail_write_all_zero; + iview = &sp_img->sp_iview[params->unit]; + spr = (struct softpipe_resource *)iview->resource; + if (!spr) + goto fail_write_all_zero; + + if (!has_compat_target(spr->base.target, params->tgsi_tex_instr)) + goto fail_write_all_zero; + + if (!get_dimensions(iview, spr, params->tgsi_tex_instr, + params->format, &width, &height, &depth)) + return; + + stride = util_format_get_stride(params->format, width); + + for (j = 0; j < TGSI_QUAD_SIZE; j++) { + int s_coord, t_coord, r_coord; + bool fill_zero = false; + + if (!(params->execmask & (1 << j))) + fill_zero = true; + + fill_coords(params, j, s, t, r, &s_coord, &t_coord, &r_coord); + if (!bounds_check(width, height, depth, + s_coord, t_coord, r_coord)) + fill_zero = true; + + if (fill_zero) { + int nc = util_format_get_nr_components(params->format); + int ival = util_format_is_pure_integer(params->format); + for (c = 0; c < 4; c++) { + rgba[c][j] = 0; + if (c == 3 && nc < 4) { + if (ival) + ((int32_t *)rgba[c])[j] = 1; + else + rgba[c][j] = 1.0; + } + } + continue; + } + offset = get_image_offset(spr, iview, params->format, r_coord); + data_ptr = (char *)spr->data + offset; + + if (util_format_is_pure_sint(params->format)) { + int32_t sdata[4]; + + util_format_read_4i(params->format, + sdata, 0, + data_ptr, stride, + s_coord, t_coord, 1, 1); + for (c = 0; c < 4; c++) + ((int32_t *)rgba[c])[j] = sdata[c]; + } else if (util_format_is_pure_uint(params->format)) { + uint32_t sdata[4]; + util_format_read_4ui(params->format, + sdata, 0, + data_ptr, stride, + s_coord, t_coord, 1, 1); + for (c = 0; c < 4; c++) + ((uint32_t *)rgba[c])[j] = sdata[c]; + } else { + float sdata[4]; + util_format_read_4f(params->format, + sdata, 0, + data_ptr, stride, + s_coord, t_coord, 1, 1); + for (c = 0; c < 4; c++) + rgba[c][j] = sdata[c]; + } + } + return; +fail_write_all_zero: + for (j = 0; j < TGSI_QUAD_SIZE; j++) { + for (c = 0; c < 4; c++) + rgba[c][j] = 0; + } + return; +} + +/* + * Implement the image STORE operation. + */ +static void +sp_tgsi_store(const struct tgsi_image *image, + const struct tgsi_image_params *params, + const int s[TGSI_QUAD_SIZE], + const int t[TGSI_QUAD_SIZE], + const int r[TGSI_QUAD_SIZE], + const int sample[TGSI_QUAD_SIZE], + float rgba[TGSI_NUM_CHANNELS][TGSI_QUAD_SIZE]) +{ + struct sp_tgsi_image *sp_img = (struct sp_tgsi_image *)image; + struct pipe_image_view *iview; + struct softpipe_resource *spr; + unsigned width, height, depth; + unsigned stride; + char *data_ptr; + int j, c; + unsigned offset = 0; + unsigned pformat = params->format; + + if (params->unit > PIPE_MAX_SHADER_IMAGES) + return; + iview = &sp_img->sp_iview[params->unit]; + spr = (struct softpipe_resource *)iview->resource; + if (!spr) + return; + if (!has_compat_target(spr->base.target, params->tgsi_tex_instr)) + return; + + if (params->format == PIPE_FORMAT_NONE) + pformat = spr->base.format; + + if (!get_dimensions(iview, spr, params->tgsi_tex_instr, + pformat, &width, &height, &depth)) + return; + + stride = util_format_get_stride(pformat, width); + + for (j = 0; j < TGSI_QUAD_SIZE; j++) { + int s_coord, t_coord, r_coord; + + if (!(params->execmask & (1 << j))) + continue; + + fill_coords(params, j, s, t, r, &s_coord, &t_coord, &r_coord); + if (!bounds_check(width, height, depth, + s_coord, t_coord, r_coord)) + continue; + + offset = get_image_offset(spr, iview, pformat, r_coord); + data_ptr = (char *)spr->data + offset; + + if (util_format_is_pure_sint(pformat)) { + int32_t sdata[4]; + for (c = 0; c < 4; c++) + sdata[c] = ((int32_t *)rgba[c])[j]; + util_format_write_4i(pformat, sdata, 0, data_ptr, stride, + s_coord, t_coord, 1, 1); + } else if (util_format_is_pure_uint(pformat)) { + uint32_t sdata[4]; + for (c = 0; c < 4; c++) + sdata[c] = ((uint32_t *)rgba[c])[j]; + util_format_write_4ui(pformat, sdata, 0, data_ptr, stride, + s_coord, t_coord, 1, 1); + } else { + float sdata[4]; + for (c = 0; c < 4; c++) + sdata[c] = rgba[c][j]; + util_format_write_4f(pformat, sdata, 0, data_ptr, stride, + s_coord, t_coord, 1, 1); + } + } +} + +/* + * Implement atomic operations on unsigned integers. + */ +static void +handle_op_uint(const struct pipe_image_view *iview, + const struct tgsi_image_params *params, + bool just_read, + char *data_ptr, + uint qi, + unsigned stride, + unsigned opcode, + int s, + int t, + float rgba[TGSI_NUM_CHANNELS][TGSI_QUAD_SIZE], + float rgba2[TGSI_NUM_CHANNELS][TGSI_QUAD_SIZE]) +{ + uint c; + int nc = util_format_get_nr_components(params->format); + unsigned sdata[4]; + + util_format_read_4ui(params->format, + sdata, 0, + data_ptr, stride, + s, t, 1, 1); + + if (just_read) { + for (c = 0; c < nc; c++) { + ((uint32_t *)rgba[c])[qi] = sdata[c]; + } + return; + } + switch (opcode) { + case TGSI_OPCODE_ATOMUADD: + for (c = 0; c < nc; c++) { + unsigned temp = sdata[c]; + sdata[c] += ((uint32_t *)rgba[c])[qi]; + ((uint32_t *)rgba[c])[qi] = temp; + } + break; + case TGSI_OPCODE_ATOMXCHG: + for (c = 0; c < nc; c++) { + unsigned temp = sdata[c]; + sdata[c] = ((uint32_t *)rgba[c])[qi]; + ((uint32_t *)rgba[c])[qi] = temp; + } + break; + case TGSI_OPCODE_ATOMCAS: + for (c = 0; c < nc; c++) { + unsigned dst_x = sdata[c]; + unsigned cmp_x = ((uint32_t *)rgba[c])[qi]; + unsigned src_x = ((uint32_t *)rgba2[c])[qi]; + unsigned temp = sdata[c]; + sdata[c] = (dst_x == cmp_x) ? src_x : dst_x; + ((uint32_t *)rgba[c])[qi] = temp; + } + break; + case TGSI_OPCODE_ATOMAND: + for (c = 0; c < nc; c++) { + unsigned temp = sdata[c]; + sdata[c] &= ((uint32_t *)rgba[c])[qi]; + ((uint32_t *)rgba[c])[qi] = temp; + } + break; + case TGSI_OPCODE_ATOMOR: + for (c = 0; c < nc; c++) { + unsigned temp = sdata[c]; + sdata[c] |= ((uint32_t *)rgba[c])[qi]; + ((uint32_t *)rgba[c])[qi] = temp; + } + break; + case TGSI_OPCODE_ATOMXOR: + for (c = 0; c < nc; c++) { + unsigned temp = sdata[c]; + sdata[c] ^= ((uint32_t *)rgba[c])[qi]; + ((uint32_t *)rgba[c])[qi] = temp; + } + break; + case TGSI_OPCODE_ATOMUMIN: + for (c = 0; c < nc; c++) { + unsigned dst_x = sdata[c]; + unsigned src_x = ((uint32_t *)rgba[c])[qi]; + sdata[c] = MIN2(dst_x, src_x); + ((uint32_t *)rgba[c])[qi] = dst_x; + } + break; + case TGSI_OPCODE_ATOMUMAX: + for (c = 0; c < nc; c++) { + unsigned dst_x = sdata[c]; + unsigned src_x = ((uint32_t *)rgba[c])[qi]; + sdata[c] = MAX2(dst_x, src_x); + ((uint32_t *)rgba[c])[qi] = dst_x; + } + break; + case TGSI_OPCODE_ATOMIMIN: + for (c = 0; c < nc; c++) { + int dst_x = sdata[c]; + int src_x = ((uint32_t *)rgba[c])[qi]; + sdata[c] = MIN2(dst_x, src_x); + ((uint32_t *)rgba[c])[qi] = dst_x; + } + break; + case TGSI_OPCODE_ATOMIMAX: + for (c = 0; c < nc; c++) { + int dst_x = sdata[c]; + int src_x = ((uint32_t *)rgba[c])[qi]; + sdata[c] = MAX2(dst_x, src_x); + ((uint32_t *)rgba[c])[qi] = dst_x; + } + break; + default: + assert(!"Unexpected TGSI opcode in sp_tgsi_op"); + break; + } + util_format_write_4ui(params->format, sdata, 0, data_ptr, stride, + s, t, 1, 1); +} + +/* + * Implement atomic operations on signed integers. + */ +static void +handle_op_int(const struct pipe_image_view *iview, + const struct tgsi_image_params *params, + bool just_read, + char *data_ptr, + uint qi, + unsigned stride, + unsigned opcode, + int s, + int t, + float rgba[TGSI_NUM_CHANNELS][TGSI_QUAD_SIZE], + float rgba2[TGSI_NUM_CHANNELS][TGSI_QUAD_SIZE]) +{ + uint c; + int nc = util_format_get_nr_components(params->format); + int sdata[4]; + util_format_read_4i(params->format, + sdata, 0, + data_ptr, stride, + s, t, 1, 1); + + if (just_read) { + for (c = 0; c < nc; c++) { + ((int32_t *)rgba[c])[qi] = sdata[c]; + } + return; + } + switch (opcode) { + case TGSI_OPCODE_ATOMUADD: + for (c = 0; c < nc; c++) { + int temp = sdata[c]; + sdata[c] += ((int32_t *)rgba[c])[qi]; + ((int32_t *)rgba[c])[qi] = temp; + } + break; + case TGSI_OPCODE_ATOMXCHG: + for (c = 0; c < nc; c++) { + int temp = sdata[c]; + sdata[c] = ((int32_t *)rgba[c])[qi]; + ((int32_t *)rgba[c])[qi] = temp; + } + break; + case TGSI_OPCODE_ATOMCAS: + for (c = 0; c < nc; c++) { + int dst_x = sdata[c]; + int cmp_x = ((int32_t *)rgba[c])[qi]; + int src_x = ((int32_t *)rgba2[c])[qi]; + int temp = sdata[c]; + sdata[c] = (dst_x == cmp_x) ? src_x : dst_x; + ((int32_t *)rgba[c])[qi] = temp; + } + break; + case TGSI_OPCODE_ATOMAND: + for (c = 0; c < nc; c++) { + int temp = sdata[c]; + sdata[c] &= ((int32_t *)rgba[c])[qi]; + ((int32_t *)rgba[c])[qi] = temp; + } + break; + case TGSI_OPCODE_ATOMOR: + for (c = 0; c < nc; c++) { + int temp = sdata[c]; + sdata[c] |= ((int32_t *)rgba[c])[qi]; + ((int32_t *)rgba[c])[qi] = temp; + } + break; + case TGSI_OPCODE_ATOMXOR: + for (c = 0; c < nc; c++) { + int temp = sdata[c]; + sdata[c] ^= ((int32_t *)rgba[c])[qi]; + ((int32_t *)rgba[c])[qi] = temp; + } + break; + case TGSI_OPCODE_ATOMUMIN: + for (c = 0; c < nc; c++) { + int dst_x = sdata[c]; + int src_x = ((int32_t *)rgba[c])[qi]; + sdata[c] = MIN2(dst_x, src_x); + ((int32_t *)rgba[c])[qi] = dst_x; + } + break; + case TGSI_OPCODE_ATOMUMAX: + for (c = 0; c < nc; c++) { + int dst_x = sdata[c]; + int src_x = ((int32_t *)rgba[c])[qi]; + sdata[c] = MAX2(dst_x, src_x); + ((int32_t *)rgba[c])[qi] = dst_x; + } + break; + case TGSI_OPCODE_ATOMIMIN: + for (c = 0; c < nc; c++) { + int dst_x = sdata[c]; + int src_x = ((int32_t *)rgba[c])[qi]; + sdata[c] = MIN2(dst_x, src_x); + ((int32_t *)rgba[c])[qi] = dst_x; + } + break; + case TGSI_OPCODE_ATOMIMAX: + for (c = 0; c < nc; c++) { + int dst_x = sdata[c]; + int src_x = ((int32_t *)rgba[c])[qi]; + sdata[c] = MAX2(dst_x, src_x); + ((int32_t *)rgba[c])[qi] = dst_x; + } + break; + default: + assert(!"Unexpected TGSI opcode in sp_tgsi_op"); + break; + } + util_format_write_4i(params->format, sdata, 0, data_ptr, stride, + s, t, 1, 1); +} + +/* + * Implement atomic image operations. + */ +static void +sp_tgsi_op(const struct tgsi_image *image, + const struct tgsi_image_params *params, + unsigned opcode, + const int s[TGSI_QUAD_SIZE], + const int t[TGSI_QUAD_SIZE], + const int r[TGSI_QUAD_SIZE], + const int sample[TGSI_QUAD_SIZE], + float rgba[TGSI_NUM_CHANNELS][TGSI_QUAD_SIZE], + float rgba2[TGSI_NUM_CHANNELS][TGSI_QUAD_SIZE]) +{ + struct sp_tgsi_image *sp_img = (struct sp_tgsi_image *)image; + struct pipe_image_view *iview; + struct softpipe_resource *spr; + unsigned width, height, depth; + unsigned stride; + int j, c; + unsigned offset; + char *data_ptr; + + if (params->unit > PIPE_MAX_SHADER_IMAGES) + return; + iview = &sp_img->sp_iview[params->unit]; + spr = (struct softpipe_resource *)iview->resource; + if (!spr) + goto fail_write_all_zero; + if (!has_compat_target(spr->base.target, params->tgsi_tex_instr)) + goto fail_write_all_zero; + + if (!get_dimensions(iview, spr, params->tgsi_tex_instr, + params->format, &width, &height, &depth)) + goto fail_write_all_zero; + + stride = util_format_get_stride(spr->base.format, width); + + for (j = 0; j < TGSI_QUAD_SIZE; j++) { + int s_coord, t_coord, r_coord; + bool just_read = false; + + fill_coords(params, j, s, t, r, &s_coord, &t_coord, &r_coord); + if (!bounds_check(width, height, depth, + s_coord, t_coord, r_coord)) { + int nc = util_format_get_nr_components(params->format); + int ival = util_format_is_pure_integer(params->format); + int c; + for (c = 0; c < 4; c++) { + rgba[c][j] = 0; + if (c == 3 && nc < 4) { + if (ival) + ((int32_t *)rgba[c])[j] = 1; + else + rgba[c][j] = 1.0; + } + } + continue; + } + + /* just readback the value for atomic if execmask isn't set */ + if (!(params->execmask & (1 << j))) { + just_read = true; + } + + offset = get_image_offset(spr, iview, params->format, r_coord); + data_ptr = (char *)spr->data + offset; + + /* we should see atomic operations on r32 formats */ + if (util_format_is_pure_uint(params->format)) + handle_op_uint(iview, params, just_read, data_ptr, j, stride, + opcode, s_coord, t_coord, rgba, rgba2); + else if (util_format_is_pure_sint(params->format)) + handle_op_int(iview, params, just_read, data_ptr, j, stride, + opcode, s_coord, t_coord, rgba, rgba2); + else + assert(0); + } + return; +fail_write_all_zero: + for (j = 0; j < TGSI_QUAD_SIZE; j++) { + for (c = 0; c < 4; c++) + rgba[c][j] = 0; + } + return; +} + +static void +sp_tgsi_get_dims(const struct tgsi_image *image, + const struct tgsi_image_params *params, + int dims[4]) +{ + struct sp_tgsi_image *sp_img = (struct sp_tgsi_image *)image; + struct pipe_image_view *iview; + struct softpipe_resource *spr; + int level; + + if (params->unit > PIPE_MAX_SHADER_IMAGES) + return; + iview = &sp_img->sp_iview[params->unit]; + spr = (struct softpipe_resource *)iview->resource; + if (!spr) + return; + + if (params->tgsi_tex_instr == TGSI_TEXTURE_BUFFER) { + dims[0] = iview->u.buf.last_element - iview->u.buf.first_element + 1; + dims[1] = dims[2] = dims[3] = 0; + return; + } + + level = iview->u.tex.level; + dims[0] = u_minify(spr->base.width0, level); + switch (params->tgsi_tex_instr) { + case TGSI_TEXTURE_1D_ARRAY: + dims[1] = iview->u.tex.last_layer - iview->u.tex.first_layer + 1; + /* fallthrough */ + case TGSI_TEXTURE_1D: + return; + case TGSI_TEXTURE_2D_ARRAY: + dims[2] = iview->u.tex.last_layer - iview->u.tex.first_layer + 1; + /* fallthrough */ + case TGSI_TEXTURE_2D: + case TGSI_TEXTURE_CUBE: + case TGSI_TEXTURE_RECT: + dims[1] = u_minify(spr->base.height0, level); + return; + case TGSI_TEXTURE_3D: + dims[1] = u_minify(spr->base.height0, level); + dims[2] = u_minify(spr->base.depth0, level); + return; + case TGSI_TEXTURE_CUBE_ARRAY: + dims[1] = u_minify(spr->base.height0, level); + dims[2] = (iview->u.tex.last_layer - iview->u.tex.first_layer + 1) / 6; + break; + default: + assert(!"unexpected texture target in sp_get_dims()"); + return; + } +} + +struct sp_tgsi_image * +sp_create_tgsi_image(void) +{ + struct sp_tgsi_image *img = CALLOC_STRUCT(sp_tgsi_image); + if (!img) + return NULL; + + img->base.load = sp_tgsi_load; + img->base.store = sp_tgsi_store; + img->base.op = sp_tgsi_op; + img->base.get_dims = sp_tgsi_get_dims; + return img; +}; diff --git a/src/gallium/drivers/softpipe/sp_image.h b/src/gallium/drivers/softpipe/sp_image.h new file mode 100644 index 00000000000..3c73f838efe --- /dev/null +++ b/src/gallium/drivers/softpipe/sp_image.h @@ -0,0 +1,37 @@ +/* + * Copyright 2016 Red Hat. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * on the rights to use, copy, modify, merge, publish, distribute, sub + * license, and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef SP_IMAGE_H +#define SP_IMAGE_H +#include "tgsi/tgsi_exec.h" + +struct sp_tgsi_image +{ + struct tgsi_image base; + struct pipe_image_view sp_iview[PIPE_MAX_SHADER_IMAGES]; +}; + +struct sp_tgsi_image * +sp_create_tgsi_image(void); + +#endif diff --git a/src/gallium/drivers/softpipe/sp_state.h b/src/gallium/drivers/softpipe/sp_state.h index 7a2d3715f8b..570bc549cc4 100644 --- a/src/gallium/drivers/softpipe/sp_state.h +++ b/src/gallium/drivers/softpipe/sp_state.h @@ -56,6 +56,7 @@ struct tgsi_sampler; +struct tgsi_image; struct tgsi_exec_machine; struct vertex_info; @@ -81,7 +82,8 @@ struct sp_fragment_shader_variant void (*prepare)(const struct sp_fragment_shader_variant *shader, struct tgsi_exec_machine *machine, - struct tgsi_sampler *sampler); + struct tgsi_sampler *sampler, + struct tgsi_image *image); unsigned (*run)(const struct sp_fragment_shader_variant *shader, struct tgsi_exec_machine *machine, @@ -149,6 +151,9 @@ softpipe_init_streamout_funcs(struct pipe_context *pipe); void softpipe_init_vertex_funcs(struct pipe_context *pipe); +void +softpipe_init_image_funcs(struct pipe_context *pipe); + void softpipe_set_framebuffer_state(struct pipe_context *, const struct pipe_framebuffer_state *); diff --git a/src/gallium/drivers/softpipe/sp_state_derived.c b/src/gallium/drivers/softpipe/sp_state_derived.c index d4d03f1be50..65679e73515 100644 --- a/src/gallium/drivers/softpipe/sp_state_derived.c +++ b/src/gallium/drivers/softpipe/sp_state_derived.c @@ -343,7 +343,8 @@ update_fragment_shader(struct softpipe_context *softpipe, unsigned prim) softpipe->fs_variant->prepare(softpipe->fs_variant, softpipe->fs_machine, (struct tgsi_sampler *) softpipe-> - tgsi.sampler[PIPE_SHADER_FRAGMENT]); + tgsi.sampler[PIPE_SHADER_FRAGMENT], + (struct tgsi_image *)softpipe->tgsi.image[PIPE_SHADER_FRAGMENT]); } else { softpipe->fs_variant = NULL; diff --git a/src/gallium/drivers/softpipe/sp_state_image.c b/src/gallium/drivers/softpipe/sp_state_image.c new file mode 100644 index 00000000000..8909fa26864 --- /dev/null +++ b/src/gallium/drivers/softpipe/sp_state_image.c @@ -0,0 +1,57 @@ +/* + * Copyright 2016 Red Hat. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * on the rights to use, copy, modify, merge, publish, distribute, sub + * license, and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "sp_context.h" +#include "sp_state.h" +#include "sp_image.h" + +static void softpipe_set_shader_images(struct pipe_context *pipe, + unsigned shader, + unsigned start, + unsigned num, + struct pipe_image_view *images) +{ + struct softpipe_context *softpipe = softpipe_context(pipe); + unsigned i; + assert(shader < PIPE_SHADER_TYPES); + assert(start + num <= Elements(softpipe->sampler_views[shader])); + + /* set the new images */ + for (i = 0; i < num; i++) { + int idx = start + i; + + if (images) { + pipe_resource_reference(&softpipe->tgsi.image[shader]->sp_iview[idx].resource, images[i].resource); + softpipe->tgsi.image[shader]->sp_iview[idx] = images[i]; + } + else { + pipe_resource_reference(&softpipe->tgsi.image[shader]->sp_iview[idx].resource, NULL); + memset(&softpipe->tgsi.image[shader]->sp_iview[idx], 0, sizeof(struct pipe_image_view)); + } + } +} + +void softpipe_init_image_funcs(struct pipe_context *pipe) +{ + pipe->set_shader_images = softpipe_set_shader_images; +} diff --git a/src/gallium/drivers/softpipe/sp_texture.c b/src/gallium/drivers/softpipe/sp_texture.c index 52ec373f8f2..64666fee03f 100644 --- a/src/gallium/drivers/softpipe/sp_texture.c +++ b/src/gallium/drivers/softpipe/sp_texture.c @@ -270,9 +270,9 @@ softpipe_resource_get_handle(struct pipe_screen *screen, * Helper function to compute offset (in bytes) for a particular * texture level/face/slice from the start of the buffer. */ -static unsigned -sp_get_tex_image_offset(const struct softpipe_resource *spr, - unsigned level, unsigned layer) +unsigned +softpipe_get_tex_image_offset(const struct softpipe_resource *spr, + unsigned level, unsigned layer) { unsigned offset = spr->level_offset[level]; @@ -422,7 +422,7 @@ softpipe_transfer_map(struct pipe_context *pipe, pt->stride = spr->stride[level]; pt->layer_stride = spr->img_stride[level]; - spt->offset = sp_get_tex_image_offset(spr, level, box->z); + spt->offset = softpipe_get_tex_image_offset(spr, level, box->z); spt->offset += box->y / util_format_get_blockheight(format) * spt->base.stride + diff --git a/src/gallium/drivers/softpipe/sp_texture.h b/src/gallium/drivers/softpipe/sp_texture.h index fbf741a9c72..450c4b1cefc 100644 --- a/src/gallium/drivers/softpipe/sp_texture.h +++ b/src/gallium/drivers/softpipe/sp_texture.h @@ -116,5 +116,7 @@ softpipe_init_screen_texture_funcs(struct pipe_screen *screen); extern void softpipe_init_texture_funcs(struct pipe_context *pipe); - +unsigned +softpipe_get_tex_image_offset(const struct softpipe_resource *spr, + unsigned level, unsigned layer); #endif /* SP_TEXTURE */