softpipe: add SSBO/shader atomics support.

This adds support for the features requires for ARB_shader_storage_buffer_object
and ARB_shader_atomic_counters, ARB_shader_atomic_counter_ops.

[airlied: some cleanups applied]
Reviewed-by: Brian Paul <brianp@vmware.com>
Reviewed-by: Roland Scheidegger <sroland@vmware.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
This commit is contained in:
Dave Airlie 2016-04-11 13:02:20 +10:00
parent c2aeeca455
commit afa8707ba9
13 changed files with 473 additions and 11 deletions

View file

@ -150,7 +150,7 @@ GL 4.2, GLSL 4.20:
GL_ARB_texture_compression_bptc DONE (i965, nvc0, r600, radeonsi)
GL_ARB_compressed_texture_pixel_storage DONE (all drivers)
GL_ARB_shader_atomic_counters DONE (i965, nvc0)
GL_ARB_shader_atomic_counters DONE (i965, nvc0, softpipe)
GL_ARB_texture_storage DONE (all drivers)
GL_ARB_transform_feedback_instanced DONE (i965, nv50, nvc0, r600, radeonsi, llvmpipe, softpipe)
GL_ARB_base_instance DONE (i965, nv50, nvc0, r600, radeonsi, llvmpipe, softpipe)
@ -179,7 +179,7 @@ GL 4.3, GLSL 4.30:
GL_ARB_program_interface_query DONE (all drivers)
GL_ARB_robust_buffer_access_behavior not started
GL_ARB_shader_image_size DONE (i965, radeonsi, softpipe)
GL_ARB_shader_storage_buffer_object DONE (i965, nvc0)
GL_ARB_shader_storage_buffer_object DONE (i965, nvc0, softpipe)
GL_ARB_stencil_texturing DONE (i965/gen8+, nv50, nvc0, r600, radeonsi, llvmpipe, softpipe)
GL_ARB_texture_buffer_range DONE (nv50, nvc0, i965, r600, radeonsi, llvmpipe)
GL_ARB_texture_query_levels DONE (all drivers that support GLSL 1.30)
@ -230,10 +230,10 @@ GLES3.1, GLSL ES 3.1
GL_ARB_explicit_uniform_location DONE (all drivers that support GLSL)
GL_ARB_framebuffer_no_attachments DONE (i965, nvc0, r600, radeonsi)
GL_ARB_program_interface_query DONE (all drivers)
GL_ARB_shader_atomic_counters DONE (i965, nvc0)
GL_ARB_shader_atomic_counters DONE (i965, nvc0, softpipe)
GL_ARB_shader_image_load_store DONE (i965, softpipe, radeonsi)
GL_ARB_shader_image_size DONE (i965, softpipe, radeonsi)
GL_ARB_shader_storage_buffer_object DONE (i965, nvc0)
GL_ARB_shader_storage_buffer_object DONE (i965, nvc0, softpipe)
GL_ARB_shading_language_packing DONE (all drivers)
GL_ARB_separate_shader_objects DONE (all drivers)
GL_ARB_stencil_texturing DONE (i965/gen8+, nv50, nvc0, r600, radeonsi, llvmpipe, softpipe)

View file

@ -46,9 +46,11 @@ Note: some of the new features are only available with certain drivers.
<ul>
<li>GL_ARB_framebuffer_no_attachments on nvc0, r600, radeonsi</li>
<li>GL_ARB_internalformat_query2 on all drivers</li>
<li>GL_ARB_shader_atomic_counter_ops on nvc0</li>
<li>GL_ARB_shader_atomic_counters on softpipe</li>
<li>GL_ARB_shader_atomic_counter_ops on nvc0, softpipe</li>
<li>GL_ARB_shader_image_load_store on radeonsi, softpipe</li>
<li>GL_ARB_shader_image_size on radeonsi, softpipe</li>
<li>GL_ARB_shader_storage_buffer_objects on softpipe</li>
<li>GL_ATI_fragment_shader on all Gallium drivers</li>
<li>GL_EXT_base_instance on all drivers that support GL_ARB_base_instance</li>
<li>GL_OES_draw_buffers_indexed and GL_EXT_draw_buffers_indexed on all drivers that support GL_ARB_draw_buffers_blend</li>

View file

@ -528,8 +528,9 @@ tgsi_exec_get_shader_param(enum pipe_shader_cap param)
return 1;
case PIPE_SHADER_CAP_TGSI_DROUND_SUPPORTED:
case PIPE_SHADER_CAP_TGSI_FMA_SUPPORTED:
case PIPE_SHADER_CAP_MAX_SHADER_BUFFERS:
return 0;
case PIPE_SHADER_CAP_MAX_SHADER_BUFFERS:
return PIPE_MAX_SHADER_BUFFERS;
case PIPE_SHADER_CAP_MAX_SHADER_IMAGES:
return PIPE_MAX_SHADER_IMAGES;

View file

@ -1,4 +1,5 @@
C_SOURCES := \
sp_buffer.c \
sp_clear.c \
sp_clear.h \
sp_context.c \

View file

@ -0,0 +1,371 @@
/*
* 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_buffer.h"
#include "sp_texture.h"
#include "util/u_format.h"
static bool
get_dimensions(const struct pipe_shader_buffer *bview,
const struct softpipe_resource *spr,
unsigned *width)
{
*width = bview->buffer_size;
/*
* Bounds check the buffer size from the view
* and the buffer size from the underlying buffer.
*/
if (*width > spr->base.width0)
return false;
return true;
}
/*
* Implement the image LOAD operation.
*/
static void
sp_tgsi_load(const struct tgsi_buffer *buffer,
const struct tgsi_buffer_params *params,
const int s[TGSI_QUAD_SIZE],
float rgba[TGSI_NUM_CHANNELS][TGSI_QUAD_SIZE])
{
struct sp_tgsi_buffer *sp_buf = (struct sp_tgsi_buffer *)buffer;
struct pipe_shader_buffer *bview;
struct softpipe_resource *spr;
unsigned width;
int c, j;
unsigned char *data_ptr;
const struct util_format_description *format_desc = util_format_description(PIPE_FORMAT_R32_UINT);
if (params->unit > PIPE_MAX_SHADER_BUFFERS)
goto fail_write_all_zero;
bview = &sp_buf->sp_bview[params->unit];
spr = softpipe_resource(bview->buffer);
if (!spr)
goto fail_write_all_zero;
if (!get_dimensions(bview, spr, &width))
return;
for (j = 0; j < TGSI_QUAD_SIZE; j++) {
int s_coord;
bool fill_zero = false;
uint32_t sdata[4];
if (!(params->execmask & (1 << j)))
fill_zero = true;
s_coord = s[j];
if (s_coord >= width)
fill_zero = true;
if (fill_zero) {
for (c = 0; c < 4; c++)
rgba[c][j] = 0;
continue;
}
data_ptr = (unsigned char *)spr->data + bview->buffer_offset + s_coord;
for (c = 0; c < 4; c++) {
format_desc->fetch_rgba_uint(sdata, data_ptr, 0, 0);
((uint32_t *)rgba[c])[j] = sdata[0];
data_ptr += 4;
}
}
return;
fail_write_all_zero:
memset(rgba, 0, TGSI_NUM_CHANNELS * TGSI_QUAD_SIZE * 4);
return;
}
/*
* Implement the buffer STORE operation.
*/
static void
sp_tgsi_store(const struct tgsi_buffer *buffer,
const struct tgsi_buffer_params *params,
const int s[TGSI_QUAD_SIZE],
float rgba[TGSI_NUM_CHANNELS][TGSI_QUAD_SIZE])
{
struct sp_tgsi_buffer *sp_buf = (struct sp_tgsi_buffer *)buffer;
struct pipe_shader_buffer *bview;
struct softpipe_resource *spr;
unsigned width;
unsigned char *data_ptr;
int j, c;
const struct util_format_description *format_desc = util_format_description(PIPE_FORMAT_R32_UINT);
if (params->unit > PIPE_MAX_SHADER_BUFFERS)
return;
bview = &sp_buf->sp_bview[params->unit];
spr = softpipe_resource(bview->buffer);
if (!spr)
return;
if (!get_dimensions(bview, spr, &width))
return;
for (j = 0; j < TGSI_QUAD_SIZE; j++) {
int s_coord;
if (!(params->execmask & (1 << j)))
continue;
s_coord = s[j];
if (s_coord >= width)
continue;
data_ptr = (unsigned char *)spr->data + bview->buffer_offset + s_coord;
for (c = 0; c < 4; c++) {
if (params->writemask & (1 << c)) {
unsigned temp[4];
unsigned char *dptr = data_ptr + (c * 4);
temp[0] = ((uint32_t *)rgba[c])[j];
format_desc->pack_rgba_uint(dptr, 0, temp, 0, 1, 1);
}
}
}
}
/*
* Implement atomic operations on unsigned integers.
*/
static void
handle_op_uint(const struct pipe_shader_buffer *bview,
bool just_read,
unsigned char *data_ptr,
uint qi,
unsigned opcode,
unsigned writemask,
float rgba[TGSI_NUM_CHANNELS][TGSI_QUAD_SIZE],
float rgba2[TGSI_NUM_CHANNELS][TGSI_QUAD_SIZE])
{
uint c;
const struct util_format_description *format_desc = util_format_description(PIPE_FORMAT_R32_UINT);
unsigned sdata[4];
for (c = 0; c < 4; c++) {
unsigned temp[4];
unsigned char *dptr = data_ptr + (c * 4);
format_desc->fetch_rgba_uint(temp, dptr, 0, 0);
sdata[c] = temp[0];
}
if (just_read) {
for (c = 0; c < 4; c++) {
((uint32_t *)rgba[c])[qi] = sdata[c];
}
return;
}
switch (opcode) {
case TGSI_OPCODE_ATOMUADD:
for (c = 0; c < 4; 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 < 4; 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 < 4; 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 < 4; 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 < 4; 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 < 4; 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 < 4; 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 < 4; 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 < 4; 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 < 4; 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;
}
for (c = 0; c < 4; c++) {
if (writemask & (1 << c)) {
unsigned temp[4];
unsigned char *dptr = data_ptr + (c * 4);
temp[0] = sdata[c];
format_desc->pack_rgba_uint(dptr, 0, temp, 0, 1, 1);
}
}
}
/*
* Implement atomic buffer operations.
*/
static void
sp_tgsi_op(const struct tgsi_buffer *buffer,
const struct tgsi_buffer_params *params,
unsigned opcode,
const int s[TGSI_QUAD_SIZE],
float rgba[TGSI_NUM_CHANNELS][TGSI_QUAD_SIZE],
float rgba2[TGSI_NUM_CHANNELS][TGSI_QUAD_SIZE])
{
struct sp_tgsi_buffer *sp_buf = (struct sp_tgsi_buffer *)buffer;
struct pipe_shader_buffer *bview;
struct softpipe_resource *spr;
unsigned width;
int j, c;
unsigned char *data_ptr;
if (params->unit > PIPE_MAX_SHADER_BUFFERS)
return;
bview = &sp_buf->sp_bview[params->unit];
spr = softpipe_resource(bview->buffer);
if (!spr)
goto fail_write_all_zero;
if (!get_dimensions(bview, spr, &width))
goto fail_write_all_zero;
for (j = 0; j < TGSI_QUAD_SIZE; j++) {
int s_coord;
bool just_read = false;
s_coord = s[j];
if (s_coord >= width) {
for (c = 0; c < 4; c++) {
rgba[c][j] = 0;
}
continue;
}
/* just readback the value for atomic if execmask isn't set */
if (!(params->execmask & (1 << j))) {
just_read = true;
}
data_ptr = (unsigned char *)spr->data + bview->buffer_offset + s_coord;
/* we should see atomic operations on r32 formats */
handle_op_uint(bview, just_read, data_ptr, j,
opcode, params->writemask, rgba, rgba2);
}
return;
fail_write_all_zero:
memset(rgba, 0, TGSI_NUM_CHANNELS * TGSI_QUAD_SIZE * 4);
return;
}
/*
* return size of the attached buffer for RESQ opcode.
*/
static void
sp_tgsi_get_dims(const struct tgsi_buffer *buffer,
const struct tgsi_buffer_params *params,
int *dim)
{
struct sp_tgsi_buffer *sp_buf = (struct sp_tgsi_buffer *)buffer;
struct pipe_shader_buffer *bview;
struct softpipe_resource *spr;
if (params->unit > PIPE_MAX_SHADER_BUFFERS)
return;
bview = &sp_buf->sp_bview[params->unit];
spr = softpipe_resource(bview->buffer);
if (!spr)
return;
*dim = bview->buffer_size;
}
struct sp_tgsi_buffer *
sp_create_tgsi_buffer(void)
{
struct sp_tgsi_buffer *buf = CALLOC_STRUCT(sp_tgsi_buffer);
if (!buf)
return NULL;
buf->base.load = sp_tgsi_load;
buf->base.store = sp_tgsi_store;
buf->base.op = sp_tgsi_op;
buf->base.get_dims = sp_tgsi_get_dims;
return buf;
};

View file

@ -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_BUFFER_H
#define SP_BUFFER_H
#include "tgsi/tgsi_exec.h"
struct sp_tgsi_buffer
{
struct tgsi_buffer base;
struct pipe_shader_buffer sp_bview[PIPE_MAX_SHADER_BUFFERS];
};
struct sp_tgsi_buffer *
sp_create_tgsi_buffer(void);
#endif

View file

@ -38,6 +38,7 @@
#include "util/u_pstipple.h"
#include "util/u_inlines.h"
#include "tgsi/tgsi_exec.h"
#include "sp_buffer.h"
#include "sp_clear.h"
#include "sp_context.h"
#include "sp_flush.h"
@ -203,6 +204,10 @@ softpipe_create_context(struct pipe_screen *screen,
softpipe->tgsi.image[i] = sp_create_tgsi_image();
}
for (i = 0; i < PIPE_SHADER_TYPES; i++) {
softpipe->tgsi.buffer[i] = sp_create_tgsi_buffer();
}
softpipe->dump_fs = debug_get_bool_option( "SOFTPIPE_DUMP_FS", FALSE );
softpipe->dump_gs = debug_get_bool_option( "SOFTPIPE_DUMP_GS", FALSE );
@ -288,6 +293,16 @@ softpipe_create_context(struct pipe_screen *screen,
(struct tgsi_image *)
softpipe->tgsi.image[PIPE_SHADER_GEOMETRY]);
draw_buffer(softpipe->draw,
PIPE_SHADER_VERTEX,
(struct tgsi_buffer *)
softpipe->tgsi.buffer[PIPE_SHADER_VERTEX]);
draw_buffer(softpipe->draw,
PIPE_SHADER_GEOMETRY,
(struct tgsi_buffer *)
softpipe->tgsi.buffer[PIPE_SHADER_GEOMETRY]);
if (debug_get_bool_option( "SOFTPIPE_NO_RAST", FALSE ))
softpipe->no_rast = TRUE;

View file

@ -84,6 +84,7 @@ struct softpipe_context {
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_shader_buffer buffers[PIPE_SHADER_TYPES][PIPE_MAX_SHADER_BUFFERS];
struct pipe_viewport_state viewports[PIPE_MAX_VIEWPORTS];
struct pipe_vertex_buffer vertex_buffer[PIPE_MAX_ATTRIBS];
struct pipe_index_buffer index_buffer;
@ -174,6 +175,7 @@ struct softpipe_context {
struct {
struct sp_tgsi_sampler *sampler[PIPE_SHADER_TYPES];
struct sp_tgsi_image *image[PIPE_SHADER_TYPES];
struct sp_tgsi_buffer *buffer[PIPE_SHADER_TYPES];
} tgsi;
struct tgsi_exec_machine *fs_machine;

View file

@ -63,14 +63,15 @@ static void
exec_prepare( const struct sp_fragment_shader_variant *var,
struct tgsi_exec_machine *machine,
struct tgsi_sampler *sampler,
struct tgsi_image *image )
struct tgsi_image *image,
struct tgsi_buffer *buffer )
{
/*
* Bind tokens/shader to the interpreter's machine state.
*/
tgsi_exec_machine_bind_shader(machine,
var->tokens,
sampler, image, NULL);
sampler, image, buffer);
}

View file

@ -259,7 +259,6 @@ softpipe_get_param(struct pipe_screen *screen, enum pipe_cap param)
case PIPE_CAP_MULTI_DRAW_INDIRECT_PARAMS:
case PIPE_CAP_TGSI_FS_POSITION_IS_SYSVAL:
case PIPE_CAP_TGSI_FS_FACE_IS_INTEGER_SYSVAL:
case PIPE_CAP_SHADER_BUFFER_OFFSET_ALIGNMENT:
case PIPE_CAP_INVALIDATE_BUFFER:
case PIPE_CAP_GENERATE_MIPMAP:
case PIPE_CAP_STRING_MARKER:
@ -272,6 +271,8 @@ softpipe_get_param(struct pipe_screen *screen, enum pipe_cap param)
case PIPE_CAP_PCI_FUNCTION:
case PIPE_CAP_FRAMEBUFFER_NO_ATTACHMENT:
return 0;
case PIPE_CAP_SHADER_BUFFER_OFFSET_ALIGNMENT:
return 4;
}
/* should only get here on unhandled cases */
debug_printf("Unexpected PIPE_CAP %d query\n", param);

View file

@ -57,6 +57,7 @@
struct tgsi_sampler;
struct tgsi_image;
struct tgsi_buffer;
struct tgsi_exec_machine;
struct vertex_info;
@ -83,7 +84,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_image *image);
struct tgsi_image *image,
struct tgsi_buffer *buffer);
unsigned (*run)(const struct sp_fragment_shader_variant *shader,
struct tgsi_exec_machine *machine,

View file

@ -344,7 +344,8 @@ update_fragment_shader(struct softpipe_context *softpipe, unsigned prim)
softpipe->fs_machine,
(struct tgsi_sampler *) softpipe->
tgsi.sampler[PIPE_SHADER_FRAGMENT],
(struct tgsi_image *)softpipe->tgsi.image[PIPE_SHADER_FRAGMENT]);
(struct tgsi_image *)softpipe->tgsi.image[PIPE_SHADER_FRAGMENT],
(struct tgsi_buffer *)softpipe->tgsi.buffer[PIPE_SHADER_FRAGMENT]);
}
else {
softpipe->fs_variant = NULL;

View file

@ -24,6 +24,7 @@
#include "sp_context.h"
#include "sp_state.h"
#include "sp_image.h"
#include "sp_buffer.h"
static void softpipe_set_shader_images(struct pipe_context *pipe,
unsigned shader,
@ -51,7 +52,34 @@ static void softpipe_set_shader_images(struct pipe_context *pipe,
}
}
static void softpipe_set_shader_buffers(struct pipe_context *pipe,
unsigned shader,
unsigned start,
unsigned num,
struct pipe_shader_buffer *buffers)
{
struct softpipe_context *softpipe = softpipe_context(pipe);
unsigned i;
assert(shader < PIPE_SHADER_TYPES);
assert(start + num <= Elements(softpipe->buffers[shader]));
/* set the new images */
for (i = 0; i < num; i++) {
int idx = start + i;
if (buffers) {
pipe_resource_reference(&softpipe->tgsi.buffer[shader]->sp_bview[idx].buffer, buffers[i].buffer);
softpipe->tgsi.buffer[shader]->sp_bview[idx] = buffers[i];
}
else {
pipe_resource_reference(&softpipe->tgsi.buffer[shader]->sp_bview[idx].buffer, NULL);
memset(&softpipe->tgsi.buffer[shader]->sp_bview[idx], 0, sizeof(struct pipe_shader_buffer));
}
}
}
void softpipe_init_image_funcs(struct pipe_context *pipe)
{
pipe->set_shader_images = softpipe_set_shader_images;
pipe->set_shader_buffers = softpipe_set_shader_buffers;
}