From: Ilia Mirkin Date: Mon, 18 Jan 2016 05:08:24 +0000 (-0500) Subject: st/mesa: add support for SSBO binding and GLSL intrinsics X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=688003669469d787847d58572956e9d3fcecaf3c;p=mesa.git st/mesa: add support for SSBO binding and GLSL intrinsics Signed-off-by: Ilia Mirkin v1 -> v2: some 80 char reformatting --- diff --git a/src/mesa/Makefile.sources b/src/mesa/Makefile.sources index c5379e87310..5d6f2d0784d 100644 --- a/src/mesa/Makefile.sources +++ b/src/mesa/Makefile.sources @@ -410,6 +410,7 @@ STATETRACKER_FILES = \ state_tracker/st_atom_shader.c \ state_tracker/st_atom_shader.h \ state_tracker/st_atom_stipple.c \ + state_tracker/st_atom_storagebuf.c \ state_tracker/st_atom_tess.c \ state_tracker/st_atom_texture.c \ state_tracker/st_atom_viewport.c \ diff --git a/src/mesa/state_tracker/st_atom.c b/src/mesa/state_tracker/st_atom.c index 0676b087eea..4b89ade1b15 100644 --- a/src/mesa/state_tracker/st_atom.c +++ b/src/mesa/state_tracker/st_atom.c @@ -80,6 +80,11 @@ static const struct st_tracked_state *atoms[] = &st_bind_tes_atomics, &st_bind_fs_atomics, &st_bind_gs_atomics, + &st_bind_vs_ssbos, + &st_bind_tcs_ssbos, + &st_bind_tes_ssbos, + &st_bind_fs_ssbos, + &st_bind_gs_ssbos, &st_update_pixel_transfer, &st_update_tess, diff --git a/src/mesa/state_tracker/st_atom.h b/src/mesa/state_tracker/st_atom.h index 7cbd52e1335..3a9153c80cb 100644 --- a/src/mesa/state_tracker/st_atom.h +++ b/src/mesa/state_tracker/st_atom.h @@ -83,6 +83,11 @@ extern const struct st_tracked_state st_bind_vs_atomics; extern const struct st_tracked_state st_bind_gs_atomics; extern const struct st_tracked_state st_bind_tcs_atomics; extern const struct st_tracked_state st_bind_tes_atomics; +extern const struct st_tracked_state st_bind_fs_ssbos; +extern const struct st_tracked_state st_bind_vs_ssbos; +extern const struct st_tracked_state st_bind_gs_ssbos; +extern const struct st_tracked_state st_bind_tcs_ssbos; +extern const struct st_tracked_state st_bind_tes_ssbos; extern const struct st_tracked_state st_update_pixel_transfer; extern const struct st_tracked_state st_update_tess; diff --git a/src/mesa/state_tracker/st_atom_storagebuf.c b/src/mesa/state_tracker/st_atom_storagebuf.c new file mode 100644 index 00000000000..a581818d8c0 --- /dev/null +++ b/src/mesa/state_tracker/st_atom_storagebuf.c @@ -0,0 +1,194 @@ +/************************************************************************** + * + * Copyright 2014 Ilia Mirkin. All Rights Reserved. + * + * 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 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 VMWARE AND/OR ITS 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 "main/imports.h" +#include "program/prog_parameter.h" +#include "program/prog_print.h" +#include "compiler/glsl/ir_uniform.h" + +#include "pipe/p_context.h" +#include "pipe/p_defines.h" +#include "util/u_inlines.h" +#include "util/u_surface.h" + +#include "st_debug.h" +#include "st_cb_bufferobjects.h" +#include "st_context.h" +#include "st_atom.h" +#include "st_program.h" + +static void +st_bind_ssbos(struct st_context *st, struct gl_shader *shader, + unsigned shader_type) +{ + unsigned i; + struct pipe_shader_buffer buffers[MAX_SHADER_STORAGE_BUFFERS]; + struct gl_program_constants *c = &st->ctx->Const.Program[shader->Stage]; + + if (!shader || !st->pipe->set_shader_buffers) + return; + + for (i = 0; i < shader->NumShaderStorageBlocks; i++) { + struct gl_shader_storage_buffer_binding *binding; + struct st_buffer_object *st_obj; + struct pipe_shader_buffer *sb = &buffers[i]; + + binding = &st->ctx->ShaderStorageBufferBindings[ + shader->ShaderStorageBlocks[i]->Binding]; + st_obj = st_buffer_object(binding->BufferObject); + + sb->buffer = st_obj->buffer; + + if (sb->buffer) { + sb->buffer_offset = binding->Offset; + sb->buffer_size = sb->buffer->width0 - binding->Offset; + + /* AutomaticSize is FALSE if the buffer was set with BindBufferRange. + * Take the minimum just to be sure. + */ + if (!binding->AutomaticSize) + sb->buffer_size = MIN2(sb->buffer_size, (unsigned) binding->Size); + } + else { + sb->buffer_offset = 0; + sb->buffer_size = 0; + } + } + st->pipe->set_shader_buffers(st->pipe, shader_type, c->MaxAtomicBuffers, + shader->NumShaderStorageBlocks, buffers); + /* clear out any stale shader buffers */ + if (shader->NumShaderStorageBlocks < c->MaxShaderStorageBlocks) + st->pipe->set_shader_buffers( + st->pipe, shader_type, + c->MaxAtomicBuffers + shader->NumShaderStorageBlocks, + c->MaxShaderStorageBlocks - shader->NumShaderStorageBlocks, + NULL); +} + +static void bind_vs_ssbos(struct st_context *st) +{ + struct gl_shader_program *prog = + st->ctx->_Shader->CurrentProgram[MESA_SHADER_VERTEX]; + + if (!prog) + return; + + st_bind_ssbos(st, prog->_LinkedShaders[MESA_SHADER_VERTEX], + PIPE_SHADER_VERTEX); +} + +const struct st_tracked_state st_bind_vs_ssbos = { + "st_bind_vs_ssbos", + { + 0, + ST_NEW_VERTEX_PROGRAM | ST_NEW_STORAGE_BUFFER, + }, + bind_vs_ssbos +}; + +static void bind_fs_ssbos(struct st_context *st) +{ + struct gl_shader_program *prog = + st->ctx->_Shader->CurrentProgram[MESA_SHADER_FRAGMENT]; + + if (!prog) + return; + + st_bind_ssbos(st, prog->_LinkedShaders[MESA_SHADER_FRAGMENT], + PIPE_SHADER_FRAGMENT); +} + +const struct st_tracked_state st_bind_fs_ssbos = { + "st_bind_fs_ssbos", + { + 0, + ST_NEW_FRAGMENT_PROGRAM | ST_NEW_STORAGE_BUFFER, + }, + bind_fs_ssbos +}; + +static void bind_gs_ssbos(struct st_context *st) +{ + struct gl_shader_program *prog = + st->ctx->_Shader->CurrentProgram[MESA_SHADER_GEOMETRY]; + + if (!prog) + return; + + st_bind_ssbos(st, prog->_LinkedShaders[MESA_SHADER_GEOMETRY], + PIPE_SHADER_GEOMETRY); +} + +const struct st_tracked_state st_bind_gs_ssbos = { + "st_bind_gs_ssbos", + { + 0, + ST_NEW_GEOMETRY_PROGRAM | ST_NEW_STORAGE_BUFFER, + }, + bind_gs_ssbos +}; + +static void bind_tcs_ssbos(struct st_context *st) +{ + struct gl_shader_program *prog = + st->ctx->_Shader->CurrentProgram[MESA_SHADER_TESS_CTRL]; + + if (!prog) + return; + + st_bind_ssbos(st, prog->_LinkedShaders[MESA_SHADER_TESS_CTRL], + PIPE_SHADER_TESS_CTRL); +} + +const struct st_tracked_state st_bind_tcs_ssbos = { + "st_bind_tcs_ssbos", + { + 0, + ST_NEW_TESSCTRL_PROGRAM | ST_NEW_STORAGE_BUFFER, + }, + bind_tcs_ssbos +}; + +static void bind_tes_ssbos(struct st_context *st) +{ + struct gl_shader_program *prog = + st->ctx->_Shader->CurrentProgram[MESA_SHADER_TESS_EVAL]; + + if (!prog) + return; + + st_bind_ssbos(st, prog->_LinkedShaders[MESA_SHADER_TESS_EVAL], + PIPE_SHADER_TESS_EVAL); +} + +const struct st_tracked_state st_bind_tes_ssbos = { + "st_bind_tes_ssbos", + { + 0, + ST_NEW_TESSEVAL_PROGRAM | ST_NEW_STORAGE_BUFFER, + }, + bind_tes_ssbos +}; diff --git a/src/mesa/state_tracker/st_cb_bufferobjects.c b/src/mesa/state_tracker/st_cb_bufferobjects.c index 0c4c98911b1..1951366e5e5 100644 --- a/src/mesa/state_tracker/st_cb_bufferobjects.c +++ b/src/mesa/state_tracker/st_cb_bufferobjects.c @@ -238,6 +238,7 @@ st_bufferobj_data(struct gl_context *ctx, bind = PIPE_BIND_COMMAND_ARGS_BUFFER; break; case GL_ATOMIC_COUNTER_BUFFER: + case GL_SHADER_STORAGE_BUFFER: bind = PIPE_BIND_SHADER_BUFFER; break; default: diff --git a/src/mesa/state_tracker/st_context.c b/src/mesa/state_tracker/st_context.c index 40779f962ee..ff9135f0e6e 100644 --- a/src/mesa/state_tracker/st_context.c +++ b/src/mesa/state_tracker/st_context.c @@ -351,6 +351,7 @@ static void st_init_driver_flags(struct gl_driver_flags *f) f->NewDefaultTessLevels = ST_NEW_TESS_STATE; f->NewTextureBuffer = ST_NEW_SAMPLER_VIEWS; f->NewAtomicBuffer = ST_NEW_ATOMIC_BUFFER; + f->NewShaderStorageBuffer = ST_NEW_STORAGE_BUFFER; } struct st_context *st_create_context(gl_api api, struct pipe_context *pipe, diff --git a/src/mesa/state_tracker/st_context.h b/src/mesa/state_tracker/st_context.h index ee27533912c..9c6980fc425 100644 --- a/src/mesa/state_tracker/st_context.h +++ b/src/mesa/state_tracker/st_context.h @@ -63,6 +63,7 @@ struct u_upload_mgr; #define ST_NEW_TESSEVAL_PROGRAM (1 << 10) #define ST_NEW_SAMPLER_VIEWS (1 << 11) #define ST_NEW_ATOMIC_BUFFER (1 << 12) +#define ST_NEW_STORAGE_BUFFER (1 << 13) struct st_state_flags { diff --git a/src/mesa/state_tracker/st_extensions.c b/src/mesa/state_tracker/st_extensions.c index 016d07d3411..5695f2718b0 100644 --- a/src/mesa/state_tracker/st_extensions.c +++ b/src/mesa/state_tracker/st_extensions.c @@ -220,7 +220,8 @@ void st_init_limits(struct pipe_screen *screen, pc->MaxAtomicCounters = MAX_ATOMIC_COUNTERS; pc->MaxAtomicBuffers = screen->get_shader_param( - screen, sh, PIPE_SHADER_CAP_MAX_SHADER_BUFFERS); + screen, sh, PIPE_SHADER_CAP_MAX_SHADER_BUFFERS) / 2; + pc->MaxShaderStorageBlocks = pc->MaxAtomicBuffers; /* Gallium doesn't really care about local vs. env parameters so use the * same limits. @@ -350,6 +351,17 @@ void st_init_limits(struct pipe_screen *screen, if (c->MaxCombinedAtomicBuffers > 0) extensions->ARB_shader_atomic_counters = GL_TRUE; + + c->MaxCombinedShaderOutputResources = c->MaxDrawBuffers; + c->ShaderStorageBufferOffsetAlignment = + screen->get_param(screen, PIPE_CAP_SHADER_BUFFER_OFFSET_ALIGNMENT); + if (c->ShaderStorageBufferOffsetAlignment) { + c->MaxCombinedShaderStorageBlocks = c->MaxShaderStorageBufferBindings = + c->MaxCombinedAtomicBuffers; + c->MaxCombinedShaderOutputResources += + c->MaxCombinedShaderStorageBlocks; + c->MaxShaderStorageBlockSize = 1 << 27; + } } diff --git a/src/mesa/state_tracker/st_glsl_to_tgsi.cpp b/src/mesa/state_tracker/st_glsl_to_tgsi.cpp index f7e0bb81604..03c7660063b 100644 --- a/src/mesa/state_tracker/st_glsl_to_tgsi.cpp +++ b/src/mesa/state_tracker/st_glsl_to_tgsi.cpp @@ -267,6 +267,7 @@ public: int dead_mask; /**< Used in dead code elimination */ st_src_reg buffer; /**< buffer register */ + unsigned buffer_access; /**< buffer access type */ class function_entry *function; /* Set on TGSI_OPCODE_CAL or TGSI_OPCODE_BGNSUB */ const struct tgsi_opcode_info *info; @@ -447,6 +448,7 @@ public: /*@}*/ void visit_atomic_counter_intrinsic(ir_call *); + void visit_ssbo_intrinsic(ir_call *); st_src_reg result; @@ -687,8 +689,6 @@ glsl_to_tgsi_visitor::emit_asm(ir_instruction *ir, unsigned op, } } - this->instructions.push_tail(inst); - /* * This section contains the double processing. * GLSL just represents doubles as single channel values, @@ -724,7 +724,7 @@ glsl_to_tgsi_visitor::emit_asm(ir_instruction *ir, unsigned op, int initial_src_swz[4], initial_src_idx[4]; int initial_dst_idx[2], initial_dst_writemask[2]; /* select the writemask for dst0 or dst1 */ - unsigned writemask = inst->dst[0].file == PROGRAM_UNDEFINED ? inst->dst[1].writemask : inst->dst[0].writemask; + unsigned writemask = inst->dst[1].file == PROGRAM_UNDEFINED ? inst->dst[0].writemask : inst->dst[1].writemask; /* copy out the writemask, index and swizzles for all src/dsts. */ for (j = 0; j < 2; j++) { @@ -741,10 +741,22 @@ glsl_to_tgsi_visitor::emit_asm(ir_instruction *ir, unsigned op, * scan all the components in the dst writemask * generate an instruction for each of them if required. */ + st_src_reg addr; while (writemask) { int i = u_bit_scan(&writemask); + /* before emitting the instruction, see if we have to adjust store + * address */ + if (i > 1 && inst->op == TGSI_OPCODE_STORE && + addr.file == PROGRAM_UNDEFINED) { + /* We have to advance the buffer address by 16 */ + addr = get_temp(glsl_type::uint_type); + emit_asm(ir, TGSI_OPCODE_UADD, st_dst_reg(addr), + inst->src[0], st_src_reg_for_int(16)); + } + + /* first time use previous instruction */ if (dinst == NULL) { dinst = inst; @@ -754,16 +766,21 @@ glsl_to_tgsi_visitor::emit_asm(ir_instruction *ir, unsigned op, *dinst = *inst; dinst->next = NULL; dinst->prev = NULL; - this->instructions.push_tail(dinst); } + this->instructions.push_tail(dinst); /* modify the destination if we are splitting */ for (j = 0; j < 2; j++) { if (dst_is_double[j]) { dinst->dst[j].writemask = (i & 1) ? WRITEMASK_ZW : WRITEMASK_XY; dinst->dst[j].index = initial_dst_idx[j]; - if (i > 1) + if (i > 1) { + if (dinst->op == TGSI_OPCODE_STORE) { + dinst->src[0] = addr; + } else { dinst->dst[j].index++; + } + } } else { /* if we aren't writing to a double, just get the bit of the initial writemask for this channel */ @@ -799,6 +816,8 @@ glsl_to_tgsi_visitor::emit_asm(ir_instruction *ir, unsigned op, } } inst = dinst; + } else { + this->instructions.push_tail(inst); } @@ -833,7 +852,9 @@ glsl_to_tgsi_visitor::get_opcode(ir_instruction *ir, unsigned op, assert(src1.type != GLSL_TYPE_ARRAY); assert(src1.type != GLSL_TYPE_STRUCT); - if (src0.type == GLSL_TYPE_DOUBLE || src1.type == GLSL_TYPE_DOUBLE) + if (is_resource_instruction(op)) + type = src1.type; + else if (src0.type == GLSL_TYPE_DOUBLE || src1.type == GLSL_TYPE_DOUBLE) type = GLSL_TYPE_DOUBLE; else if (src0.type == GLSL_TYPE_FLOAT || src1.type == GLSL_TYPE_FLOAT) type = GLSL_TYPE_FLOAT; @@ -917,6 +938,9 @@ glsl_to_tgsi_visitor::get_opcode(ir_instruction *ir, unsigned op, case3fid(FLR, FLR, DFLR); case3fid(ROUND, ROUND, DROUND); + case2iu(ATOMIMAX, ATOMUMAX); + case2iu(ATOMIMIN, ATOMUMIN); + default: break; } @@ -3148,6 +3172,117 @@ glsl_to_tgsi_visitor::visit_atomic_counter_intrinsic(ir_call *ir) } } +void +glsl_to_tgsi_visitor::visit_ssbo_intrinsic(ir_call *ir) +{ + const char *callee = ir->callee->function_name(); + exec_node *param = ir->actual_parameters.get_head(); + + ir_rvalue *block = ((ir_instruction *)param)->as_rvalue(); + + param = param->get_next(); + ir_rvalue *offset = ((ir_instruction *)param)->as_rvalue(); + + ir_constant *const_block = block->as_constant(); + + st_src_reg buffer( + PROGRAM_BUFFER, + ctx->Const.Program[shader->Stage].MaxAtomicBuffers + + (const_block ? const_block->value.u[0] : 0), + GLSL_TYPE_UINT); + + if (!const_block) { + block->accept(this); + emit_arl(ir, sampler_reladdr, this->result); + buffer.reladdr = ralloc(mem_ctx, st_src_reg); + memcpy(buffer.reladdr, &sampler_reladdr, sizeof(sampler_reladdr)); + } + + /* Calculate the surface offset */ + offset->accept(this); + st_src_reg off = this->result; + + st_dst_reg dst = undef_dst; + if (ir->return_deref) { + ir->return_deref->accept(this); + dst = st_dst_reg(this->result); + dst.writemask = (1 << ir->return_deref->type->vector_elements) - 1; + } + + glsl_to_tgsi_instruction *inst; + + if (!strcmp("__intrinsic_load_ssbo", callee)) { + inst = emit_asm(ir, TGSI_OPCODE_LOAD, dst, off); + if (dst.type == GLSL_TYPE_BOOL) + emit_asm(ir, TGSI_OPCODE_USNE, dst, st_src_reg(dst), st_src_reg_for_int(0)); + } else if (!strcmp("__intrinsic_store_ssbo", callee)) { + param = param->get_next(); + ir_rvalue *val = ((ir_instruction *)param)->as_rvalue(); + val->accept(this); + + param = param->get_next(); + ir_constant *write_mask = ((ir_instruction *)param)->as_constant(); + assert(write_mask); + dst.writemask = write_mask->value.u[0]; + + dst.type = this->result.type; + inst = emit_asm(ir, TGSI_OPCODE_STORE, dst, off, this->result); + } else { + param = param->get_next(); + ir_rvalue *val = ((ir_instruction *)param)->as_rvalue(); + val->accept(this); + + st_src_reg data = this->result, data2 = undef_src; + unsigned opcode; + if (!strcmp("__intrinsic_atomic_add_ssbo", callee)) + opcode = TGSI_OPCODE_ATOMUADD; + else if (!strcmp("__intrinsic_atomic_min_ssbo", callee)) + opcode = TGSI_OPCODE_ATOMIMIN; + else if (!strcmp("__intrinsic_atomic_max_ssbo", callee)) + opcode = TGSI_OPCODE_ATOMIMAX; + else if (!strcmp("__intrinsic_atomic_and_ssbo", callee)) + opcode = TGSI_OPCODE_ATOMAND; + else if (!strcmp("__intrinsic_atomic_or_ssbo", callee)) + opcode = TGSI_OPCODE_ATOMOR; + else if (!strcmp("__intrinsic_atomic_xor_ssbo", callee)) + opcode = TGSI_OPCODE_ATOMXOR; + else if (!strcmp("__intrinsic_atomic_exchange_ssbo", callee)) + opcode = TGSI_OPCODE_ATOMXCHG; + else if (!strcmp("__intrinsic_atomic_comp_swap_ssbo", callee)) { + opcode = TGSI_OPCODE_ATOMCAS; + param = param->get_next(); + val = ((ir_instruction *)param)->as_rvalue(); + val->accept(this); + data2 = this->result; + } else { + assert(!"Unexpected intrinsic"); + return; + } + + inst = emit_asm(ir, opcode, dst, off, data, data2); + } + + param = param->get_next(); + ir_constant *access = NULL; + if (!param->is_tail_sentinel()) { + access = ((ir_instruction *)param)->as_constant(); + assert(access); + } + + /* The emit_asm() might have actually split the op into pieces, e.g. for + * double stores. We have to go back and fix up all the generated ops. + */ + unsigned op = inst->op; + do { + inst->buffer = buffer; + if (access) + inst->buffer_access = access->value.u[0]; + inst = (glsl_to_tgsi_instruction *)inst->get_prev(); + if (inst->op == TGSI_OPCODE_UADD) + inst = (glsl_to_tgsi_instruction *)inst->get_prev(); + } while (inst && inst->buffer.file == PROGRAM_UNDEFINED && inst->op == op); +} + void glsl_to_tgsi_visitor::visit(ir_call *ir) { @@ -3165,6 +3300,20 @@ glsl_to_tgsi_visitor::visit(ir_call *ir) return; } + if (!strcmp("__intrinsic_load_ssbo", callee) || + !strcmp("__intrinsic_store_ssbo", callee) || + !strcmp("__intrinsic_atomic_add_ssbo", callee) || + !strcmp("__intrinsic_atomic_min_ssbo", callee) || + !strcmp("__intrinsic_atomic_max_ssbo", callee) || + !strcmp("__intrinsic_atomic_and_ssbo", callee) || + !strcmp("__intrinsic_atomic_or_ssbo", callee) || + !strcmp("__intrinsic_atomic_xor_ssbo", callee) || + !strcmp("__intrinsic_atomic_exchange_ssbo", callee) || + !strcmp("__intrinsic_atomic_comp_swap_ssbo", callee)) { + visit_ssbo_intrinsic(ir); + return; + } + entry = get_function_signature(sig); /* Process in parameters. */ foreach_two_lists(formal_node, &sig->parameters, @@ -4999,7 +5148,18 @@ compile_tgsi_instruction(struct st_translate *t, src[0] = t->buffers[inst->buffer.index]; if (inst->buffer.reladdr) src[0] = ureg_src_indirect(src[0], ureg_src(t->address[2])); - ureg_insn(ureg, inst->op, dst, num_dst, src, num_src); + assert(src[0].File != TGSI_FILE_NULL); + ureg_memory_insn(ureg, inst->op, dst, num_dst, src, num_src, + inst->buffer_access); + break; + + case TGSI_OPCODE_STORE: + dst[0] = ureg_writemask(ureg_dst(t->buffers[inst->buffer.index]), inst->dst[0].writemask); + if (inst->buffer.reladdr) + dst[0] = ureg_dst_indirect(dst[0], ureg_src(t->address[2])); + assert(dst[0].File != TGSI_FILE_NULL); + ureg_memory_insn(ureg, inst->op, dst, num_dst, src, num_src, + inst->buffer_access); break; case TGSI_OPCODE_SCS: @@ -5639,6 +5799,15 @@ st_translate_program( } } + for (; i < frag_const->MaxAtomicBuffers + frag_const->MaxShaderStorageBlocks; + i++) { + if (program->buffers_used & (1 << i)) { + t->buffers[i] = ureg_DECL_buffer(ureg, i, false); + } + } + + + /* Emit each instruction in turn: */ foreach_in_list(glsl_to_tgsi_instruction, inst, &program->instructions) {