blob: 585f0875ce68e2e7e98c0b017a6408022b81f77f [file] [log] [blame]
/*
* 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/format/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;
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;
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;
}
uint32_t *src = (uint32_t *)((unsigned char *)spr->data +
bview->buffer_offset + s_coord);
for (c = 0; c < 4; c++) {
memcpy(&rgba[c][j], &src[c], 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;
int j, c;
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;
uint32_t *dst = (uint32_t *)((unsigned char *)spr->data +
bview->buffer_offset + s_coord);
for (c = 0; c < 4; c++) {
if (params->writemask & (1 << c))
memcpy(&dst[c], &rgba[c][j], 4);
}
}
}
/*
* Implement atomic operations on unsigned integers.
*/
static void
handle_op_atomic(const struct pipe_shader_buffer *bview,
bool just_read,
unsigned char *data_ptr,
uint qi,
enum tgsi_opcode opcode,
unsigned writemask,
float rgba[TGSI_NUM_CHANNELS][TGSI_QUAD_SIZE],
float rgba2[TGSI_NUM_CHANNELS][TGSI_QUAD_SIZE])
{
uint c;
unsigned sdata[4];
for (c = 0; c < 4; c++) {
memcpy(&sdata[c], data_ptr + (c * 4), 4);
}
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;
case TGSI_OPCODE_ATOMFADD:
for (c = 0; c < 4; c++) {
float temp = uif(sdata[c]);
sdata[c] = fui(temp + rgba[c][qi]);
rgba[c][qi] = temp;
}
break;
default:
assert(!"Unexpected TGSI opcode in sp_tgsi_op");
break;
}
for (c = 0; c < 4; c++) {
if (writemask & (1 << c)) {
memcpy(data_ptr + (c * 4), &sdata[c], 4);
}
}
}
/*
* Implement atomic buffer operations.
*/
static void
sp_tgsi_op(const struct tgsi_buffer *buffer,
const struct tgsi_buffer_params *params,
enum tgsi_opcode 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_atomic(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;
};