--- /dev/null
+extension ARB_program_interface_query
--- /dev/null
+extension ARB_shader_storage_buffer_object
#include <msp/gl/extensions/arb_gpu_shader5.h>
#include <msp/gl/extensions/arb_separate_shader_objects.h>
#include <msp/gl/extensions/arb_shader_image_load_store.h>
+#include <msp/gl/extensions/arb_shader_storage_buffer_object.h>
#include <msp/gl/extensions/arb_uniform_buffer_object.h>
#include <msp/gl/extensions/arb_vertex_shader.h>
#include <msp/gl/extensions/ext_framebuffer_multisample.h>
}
if(ARB_shader_image_load_store)
glGetIntegerv(GL_MAX_IMAGE_UNITS, reinterpret_cast<int *>(&lim.max_storage_texture_bindings));
+ if(ARB_shader_storage_buffer_object)
+ glGetIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, reinterpret_cast<int *>(&lim.max_storage_buffer_bindings));
if(EXT_framebuffer_object)
glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, reinterpret_cast<int *>(&lim.max_color_attachments));
if(EXT_framebuffer_multisample)
feat.arb_gpu_shader5 = ARB_gpu_shader5;
feat.arb_separate_shader_objects = ARB_separate_shader_objects;
feat.arb_uniform_buffer_object = ARB_uniform_buffer_object;
+ feat.arb_shader_storage_buffer_object = ARB_shader_storage_buffer_object;
feat.ext_gpu_shader4 = EXT_gpu_shader4;
feat.ext_texture_array = EXT_texture_array;
feat.uniform_binding_range = lim.max_uniform_bindings;
state.bound_tex_targets.resize(lim.max_texture_bindings);
state.bound_storage_textures.resize(lim.max_storage_texture_bindings);
state.bound_uniform_blocks.resize(lim.max_uniform_bindings);
+ state.bound_storage_buffers.resize(lim.max_storage_buffer_bindings);
}
} // namespace GL
std::vector<int> bound_tex_targets;
std::vector<char> bound_storage_textures;
std::vector<char> bound_uniform_blocks;
+ std::vector<char> bound_storage_buffers;
unsigned restart_index = 0;
unsigned n_clip_distances = 0;
const OpenGLBuffer *scratch_buffer = 0;
#include <msp/gl/extensions/arb_sampler_objects.h>
#include <msp/gl/extensions/arb_shader_image_load_store.h>
#include <msp/gl/extensions/arb_shader_objects.h>
+#include <msp/gl/extensions/arb_shader_storage_buffer_object.h>
#include <msp/gl/extensions/arb_uniform_buffer_object.h>
#include <msp/gl/extensions/arb_vertex_array_object.h>
#include <msp/gl/extensions/arb_tessellation_shader.h>
call.func(call.location, call.size, data+call.location*16);
}
}
+ else if(r.type==PipelineState::STORAGE_BLOCK)
+ {
+ static Require _req(ARB_shader_storage_buffer_object);
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, r.binding, r.buffer->id);
+ dev_state.bound_storage_buffers[r.binding] = 1;
+ }
else if(r.type==PipelineState::SAMPLED_TEXTURE)
{
if(ARB_direct_state_access)
dev_state.bound_uniform_blocks[i] = 0;
}
+ for(unsigned i=0; i<dev_state.bound_storage_buffers.size(); ++i)
+ if(dev_state.bound_storage_buffers[i])
+ {
+ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, i, 0);
+ dev_state.bound_storage_buffers[i] = 0;
+ }
+
glDisable(GL_DEPTH_TEST);
glDepthMask(true);
glDisable(GL_STENCIL_TEST);
#include <msp/gl/extensions/arb_fragment_shader.h>
#include <msp/gl/extensions/arb_gl_spirv.h>
#include <msp/gl/extensions/arb_geometry_shader4.h>
+#include <msp/gl/extensions/arb_program_interface_query.h>
#include <msp/gl/extensions/arb_separate_shader_objects.h>
#include <msp/gl/extensions/arb_shader_objects.h>
+#include <msp/gl/extensions/arb_shader_storage_buffer_object.h>
#include <msp/gl/extensions/arb_uniform_buffer_object.h>
#include <msp/gl/extensions/arb_tessellation_shader.h>
#include <msp/gl/extensions/arb_vertex_shader.h>
link(mod);
query_uniforms();
query_attributes();
+ if(ARB_shader_storage_buffer_object && ARB_program_interface_query)
+ query_storage_blocks();
if(is_compute())
{
int wg_size[3];
const map<string, unsigned> &block_bindings = compiler.get_uniform_block_bindings();
if(!block_bindings.empty())
{
+ int first_storage = -1;
for(unsigned i=0; i<rd.blocks.size(); ++i)
{
auto j = block_bindings.find(rd.blocks[i].name);
if(j!=block_bindings.end())
{
- glUniformBlockBinding(id, i, j->second);
+ if(rd.blocks[i].is_storage())
+ {
+ if(first_storage<0)
+ first_storage = i;
+ glShaderStorageBlockBinding(id, i-first_storage, j->second);
+ }
+ else
+ glUniformBlockBinding(id, i, j->second);
rd.blocks[i].bind_point = j->second;
}
}
sort_member(rd.attributes, &ReflectData::AttributeInfo::name);
}
+void OpenGLProgram::query_storage_blocks()
+{
+ ReflectData &rd = static_cast<Program *>(this)->reflect_data;
+
+ unsigned count = 0;
+ glGetProgramInterfaceiv(id, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, reinterpret_cast<int *>(&count));
+ rd.blocks.reserve(rd.blocks.size()+count);
+ for(unsigned i=0; i<count; ++i)
+ {
+ rd.blocks.emplace_back();
+ ReflectData::BlockInfo &info = rd.blocks.back();
+
+ char name[128];
+ int len;
+ glGetProgramResourceName(id, GL_SHADER_STORAGE_BLOCK, i, sizeof(name), &len, name);
+ info.tag = info.name.assign(name, len);
+
+ GLenum prop = GL_BUFFER_BINDING;
+ int value;
+ glGetProgramResourceiv(id, GL_SHADER_STORAGE_BLOCK, i, 1, &prop, 1, 0, &value);
+ info.bind_point = value;
+ }
+}
+
void OpenGLProgram::finalize_uniforms()
{
ReflectData &rd = static_cast<Program *>(this)->reflect_data;
void query_uniforms();
void query_uniform_blocks(const std::vector<ReflectData::UniformInfo *> &);
void query_attributes();
+ void query_storage_blocks();
void finalize_uniforms();
bool is_compute() const { return stage_ids[COMPUTE]; }
buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
buffer_info.size = self.get_total_size();
buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT|VK_BUFFER_USAGE_TRANSFER_DST_BIT|VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT|
- VK_BUFFER_USAGE_INDEX_BUFFER_BIT|VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
+ VK_BUFFER_USAGE_STORAGE_BUFFER_BIT|VK_BUFFER_USAGE_INDEX_BUFFER_BIT|VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
vk.CreateBuffer(buffer_info, handle);
for(const PipelineState::BoundResource &r: self.resources)
if(r.changed || changed_sets==~0U)
{
- if(r.type==PipelineState::UNIFORM_BLOCK)
+ if(r.type==PipelineState::UNIFORM_BLOCK || r.type==PipelineState::STORAGE_BLOCK)
r.used = self.shprog->uses_block_binding(r.binding);
else if(r.type==PipelineState::SAMPLED_TEXTURE || r.type==PipelineState::STORAGE_TEXTURE)
{
result = hash_update<64>(result, reinterpret_cast<uintptr_t>(i->block));
result = hash_update<64>(result, reinterpret_cast<uintptr_t>(i->buffer->handle));
}
+ else if(i->type==PipelineState::STORAGE_BLOCK)
+ result = hash_update<64>(result, reinterpret_cast<uintptr_t>(i->buffer->handle));
else if(i->type==PipelineState::SAMPLED_TEXTURE)
{
result = hash_update<64>(result, reinterpret_cast<uintptr_t>(i->texture->handle));
auto i = lower_bound_member(self.resources, static_cast<int>(index)<<20, &PipelineState::BoundResource::binding);
for(; (i!=self.resources.end() && static_cast<unsigned>(i->binding)>>20==index); ++i)
- if(i->used && i->type==PipelineState::UNIFORM_BLOCK && i->buffer->get_usage()==STREAMING)
+ if(i->used && (i->type==PipelineState::UNIFORM_BLOCK || i->type==PipelineState::STORAGE_BLOCK) && i->buffer->get_usage()==STREAMING)
return true;
return false;
for(auto i=begin; (i!=self.resources.end() && static_cast<unsigned>(i->binding)>>20==index); ++i)
if(i->used)
{
- if(i->type==PipelineState::UNIFORM_BLOCK)
+ if(i->type==PipelineState::UNIFORM_BLOCK || i->type==PipelineState::STORAGE_BLOCK)
++n_buffers;
else if(i->type==PipelineState::SAMPLED_TEXTURE || i->type==PipelineState::STORAGE_TEXTURE)
++n_images;
if(!i->used)
continue;
- if(i->type==PipelineState::UNIFORM_BLOCK)
+ if(i->type==PipelineState::UNIFORM_BLOCK || i->type==PipelineState::STORAGE_BLOCK)
{
buffer_ptr->buffer = handle_cast<::VkBuffer>(i->buffer->handle);
- buffer_ptr->offset = i->block->get_offset();
+ if(i->block)
+ {
+ buffer_ptr->offset = i->block->get_offset();
+ buffer_ptr->range = i->block->get_data_size();
+ }
+ else
+ {
+ buffer_ptr->offset = 0;
+ buffer_ptr->range = i->buffer->get_size();
+ }
if(i->buffer->get_usage()==STREAMING)
buffer_ptr->offset += frame*i->buffer->get_size();
- buffer_ptr->range = i->block->get_data_size();
write_ptr->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write_ptr->dstBinding = i->binding&0xFFFFF;
write_ptr->descriptorCount = 1;
- write_ptr->descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ if(i->type==PipelineState::UNIFORM_BLOCK)
+ write_ptr->descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ else if(i->type==PipelineState::STORAGE_BLOCK)
+ write_ptr->descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
write_ptr->pBufferInfo = buffer_ptr;
++buffer_ptr;
bindings.emplace_back();
VkDescriptorSetLayoutBinding &binding = bindings.back();
binding.binding = b.bind_point&0xFFFFF;
- binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ if(b.is_storage())
+ binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+ else
+ binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
binding.descriptorCount = 1;
binding.stageFlags = stage_flags;
binding.pImmutableSamplers = 0;
unsigned max_vertex_attributes = 16;
unsigned max_texture_bindings = 16;
unsigned max_storage_texture_bindings = 8;
+ unsigned max_storage_buffer_bindings = 8;
unsigned max_color_attachments = 8;
unsigned max_samples = 4;
unsigned max_uniform_bindings = 24;
INPUT = 1,
UNIFORM = 2,
OUTPUT = 3,
- PUSH_CONSTANT = 9
+ PUSH_CONSTANT = 9,
+ STORAGE_BUFFER = 12
};
enum BuiltinSemantic
}
}
+void PipelineState::set_storage_block(unsigned binding, const Buffer *buffer)
+{
+ set_buffer_resource(binding, 0, buffer);
+}
+
void PipelineState::set_texture(unsigned binding, const Texture *tex, const Sampler *samp)
{
set_texture(binding, tex, -1, samp);
set_texture_resource(binding, tex, 0, 0);
}
+void PipelineState::set_buffer_resource(unsigned binding, const UniformBlock *block, const Buffer *buffer)
+{
+ auto i = lower_bound_member(resources, static_cast<int>(binding), &BoundResource::binding);
+ if(i==resources.end() || i->binding!=static_cast<int>(binding))
+ i = resources.insert(i, BoundResource(binding));
+
+ ResourceType type = (buffer ? block ? UNIFORM_BLOCK : STORAGE_BLOCK : NO_RESOURCE);
+ if(i->type!=type || block!=i->block || buffer!=i->buffer)
+ {
+ i->type = type;
+ i->block = block;
+ i->buffer = buffer;
+ i->changed = true;
+ i->used = buffer;
+ changes |= RESOURCES;
+ }
+}
+
void PipelineState::set_texture_resource(unsigned binding, const Texture *tex, int level, const Sampler *samp)
{
auto i = lower_bound_member(resources, static_cast<int>(binding), &BoundResource::binding);
{
auto i = lower_bound_member(resources, b.bind_point, &PipelineState::BoundResource::binding);
if(i==resources.end() || i->binding!=b.bind_point)
- IO::print(IO::cerr, "Warning: No resource present for uniform block binding %d:%d (%s)\n", b.bind_point>>20, b.bind_point&0xFFFFF, b.name);
+ IO::print(IO::cerr, "Warning: No resource present for block binding %d:%d (%s)\n", b.bind_point>>20, b.bind_point&0xFFFFF, b.name);
}
for(const ReflectData::UniformInfo &u: shprog->get_uniforms())
{
NO_RESOURCE,
UNIFORM_BLOCK,
+ STORAGE_BLOCK,
SAMPLED_TEXTURE,
STORAGE_TEXTURE
};
void set_scissor(const Rect &);
void set_shader_program(const Program *);
void set_uniform_block(int, const UniformBlock *);
+ void set_storage_block(unsigned, const Buffer *);
void set_texture(unsigned, const Texture *, const Sampler *);
void set_texture(unsigned, const Texture *, int, const Sampler *);
void set_storage_texture(unsigned, const Texture *);
private:
+ void set_buffer_resource(unsigned, const UniformBlock *, const Buffer *);
void set_texture_resource(unsigned, const Texture *, int, const Sampler *);
public:
void set_vertex_setup(const VertexSetup *);
for(const SpirVModule::Variable &v: mod.get_variables())
{
- if((v.storage==SpirVModule::UNIFORM || v.storage==SpirVModule::PUSH_CONSTANT) && v.struct_type)
+ if((v.storage==SpirVModule::UNIFORM || v.storage==SpirVModule::PUSH_CONSTANT || v.storage==SpirVModule::STORAGE_BUFFER) && v.struct_type)
{
reflect_data.blocks.emplace_back();
ReflectData::BlockInfo &info = reflect_data.blocks.back();
info.tag = info.name = v.struct_type->name;
- info.data_size = v.struct_type->size;
+ if(v.storage!=SpirVModule::STORAGE_BUFFER)
+ info.data_size = v.struct_type->size;
if(v.storage==SpirVModule::PUSH_CONSTANT)
{
info.bind_point = ReflectData::PUSH_CONSTANT;
reflect_data.n_descriptor_sets = max(reflect_data.n_descriptor_sets, v.descriptor_set+1);
}
- string prefix;
- if(!v.name.empty())
- prefix = v.struct_type->name+".";
block_uniform_names.emplace_back();
- collect_block_uniforms(*v.struct_type, prefix, 0, block_uniform_names.back());
+ if(!info.is_storage())
+ {
+ string prefix;
+ if(!v.name.empty())
+ prefix = v.struct_type->name+".";
+ collect_block_uniforms(*v.struct_type, prefix, 0, block_uniform_names.back());
+ }
}
else if(v.storage==SpirVModule::UNIFORM_CONSTANT && (v.location>=0 || v.binding>=0))
{
{
layout_hash = hash<32>(blocks.size());
for(const BlockInfo &b: blocks)
- {
- layout_hash = hash_update<32>(layout_hash, b.bind_point);
- layout_hash = hash_update<32>(layout_hash, b.layout_hash);
- }
+ if(!b.is_storage())
+ {
+ layout_hash = hash_update<32>(layout_hash, b.bind_point);
+ layout_hash = hash_update<32>(layout_hash, b.layout_hash);
+ }
}
void ReflectData::update_used_bindings()
void sort_uniforms();
void update_layout_hash();
+ bool is_storage() const { return bind_point>=0 && !data_size; }
};
struct AttributeInfo
return i;
const vector<ReflectData::BlockInfo> &block_infos = prog.get_blocks();
+ unsigned count = 0;
+ for(const ReflectData::BlockInfo &b: block_infos)
+ count += !b.is_storage();
unsigned index = i-programs.begin();
- programs.insert(i, 1+block_infos.size(), ProgramBlock(prog_hash));
+ programs.insert(i, 1+count, ProgramBlock(prog_hash));
/* Block indices may change if new shared blocks need to be inserted. Store
the hashes so they can be matched up later. */
unsigned j = index+1;
for(const ReflectData::BlockInfo &b: block_infos)
{
+ if(b.is_storage())
+ continue;
+
block_hashes[j] = b.layout_hash;
programs[j].bind_point = b.bind_point;
++j;
auto j = prog_begin+1;
for(const ReflectData::BlockInfo &b: block_infos)
{
+ if(b.is_storage())
+ continue;
+
SharedBlock &shared = blocks[j->block_index];
if(shared.dirty==ALL_ONES)
update_block_uniform_indices(shared, b);
auto j = prog_begin+1;
for(const ReflectData::BlockInfo &b: block_infos)
{
+ if(b.is_storage())
+ continue;
+
SharedBlock &shared = blocks[j->block_index];
if(shared.dirty)
{
changed |= SHADER_DATA;
}
+void Renderer::set_storage_buffer(Tag tag, const Buffer *buf)
+{
+ State &state = get_state();
+ set_resource(buffer_stack, state.buffer_count, tag, buf);
+}
+
void Renderer::set_texture(Tag tag, const Texture *tex, const Sampler *samp)
{
set_texture(tag, tex, -1, samp);
}
}
+ for(const BoundResource<const Buffer *> &b: buffer_stack)
+ if(b.resource && b.replaced<0)
+ {
+ if(b.binding<0 || shprog_changed)
+ b.binding = state.shprog->get_block_binding(b.tag);
+ if(b.binding>=0)
+ ps.set_storage_block(b.binding, b.resource);
+ }
+
static const DepthTest default_depth_test;
ps.set_depth_test(state.depth_test ? *state.depth_test : default_depth_test);
static const StencilTest default_stencil_test;
unsigned texture_count = 0;
const Program *shprog = 0;
unsigned shdata_count = 0;
+ unsigned buffer_count = 0;
const VertexSetup *vertex_setup = 0;
FaceWinding front_face = NON_MANIFOLD;
CullMode face_cull = NO_CULL;
State *current_state = 0;
ProgramData standard_shdata;
std::vector<BoundProgramData> shdata_stack;
+ std::vector<BoundResource<const Buffer *>> buffer_stack;
std::vector<BoundResource<SampledTexture>> texture_stack;
const Texture &placeholder_texture;
const Sampler &default_sampler;
last will be used. */
void add_shader_data(const ProgramData &data);
+ void set_storage_buffer(Tag, const Buffer *);
+
void set_texture(Tag, const Texture *, const Sampler * = 0);
void set_texture(Tag, const Texture *, int, const Sampler * = 0);
void set_storage_texture(Tag, const Texture *);