From 5350cc9cce7584837df4e51fbc8613eee26422ed Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Tue, 18 Jul 2023 11:28:40 +0300 Subject: [PATCH] Support using storage blocks for rendering --- extensions/arb_program_interface_query.glext | 1 + .../arb_shader_storage_buffer_object.glext | 1 + source/backends/opengl/device_backend.cpp | 5 +++ source/backends/opengl/device_backend.h | 1 + .../backends/opengl/pipelinestate_backend.cpp | 14 +++++++ source/backends/opengl/program_backend.cpp | 38 ++++++++++++++++++- source/backends/opengl/program_backend.h | 1 + source/backends/vulkan/buffer_backend.cpp | 2 +- .../backends/vulkan/pipelinestate_backend.cpp | 27 +++++++++---- source/backends/vulkan/program_backend.cpp | 5 ++- source/core/device.h | 1 + source/core/module.h | 3 +- source/core/pipelinestate.cpp | 25 +++++++++++- source/core/pipelinestate.h | 3 ++ source/core/program.cpp | 16 +++++--- source/core/reflectdata.cpp | 9 +++-- source/core/reflectdata.h | 1 + source/materials/programdata.cpp | 14 ++++++- source/render/renderer.cpp | 15 ++++++++ source/render/renderer.h | 4 ++ 20 files changed, 163 insertions(+), 23 deletions(-) create mode 100644 extensions/arb_program_interface_query.glext create mode 100644 extensions/arb_shader_storage_buffer_object.glext diff --git a/extensions/arb_program_interface_query.glext b/extensions/arb_program_interface_query.glext new file mode 100644 index 00000000..6939527d --- /dev/null +++ b/extensions/arb_program_interface_query.glext @@ -0,0 +1 @@ +extension ARB_program_interface_query diff --git a/extensions/arb_shader_storage_buffer_object.glext b/extensions/arb_shader_storage_buffer_object.glext new file mode 100644 index 00000000..87a07256 --- /dev/null +++ b/extensions/arb_shader_storage_buffer_object.glext @@ -0,0 +1 @@ +extension ARB_shader_storage_buffer_object diff --git a/source/backends/opengl/device_backend.cpp b/source/backends/opengl/device_backend.cpp index 0fcbe072..c13a7f04 100644 --- a/source/backends/opengl/device_backend.cpp +++ b/source/backends/opengl/device_backend.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -65,6 +66,8 @@ void OpenGLDevice::fill_info() } if(ARB_shader_image_load_store) glGetIntegerv(GL_MAX_IMAGE_UNITS, reinterpret_cast(&lim.max_storage_texture_bindings)); + if(ARB_shader_storage_buffer_object) + glGetIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, reinterpret_cast(&lim.max_storage_buffer_bindings)); if(EXT_framebuffer_object) glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, reinterpret_cast(&lim.max_color_attachments)); if(EXT_framebuffer_multisample) @@ -86,6 +89,7 @@ void OpenGLDevice::fill_info() 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; @@ -95,6 +99,7 @@ void OpenGLDevice::fill_info() 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 diff --git a/source/backends/opengl/device_backend.h b/source/backends/opengl/device_backend.h index cb945b72..ea526e1a 100644 --- a/source/backends/opengl/device_backend.h +++ b/source/backends/opengl/device_backend.h @@ -19,6 +19,7 @@ struct OpenGLDeviceState std::vector bound_tex_targets; std::vector bound_storage_textures; std::vector bound_uniform_blocks; + std::vector bound_storage_buffers; unsigned restart_index = 0; unsigned n_clip_distances = 0; const OpenGLBuffer *scratch_buffer = 0; diff --git a/source/backends/opengl/pipelinestate_backend.cpp b/source/backends/opengl/pipelinestate_backend.cpp index da5f4957..7c41bd36 100644 --- a/source/backends/opengl/pipelinestate_backend.cpp +++ b/source/backends/opengl/pipelinestate_backend.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -130,6 +131,12 @@ void OpenGLPipelineState::apply() const 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) @@ -301,6 +308,13 @@ void OpenGLPipelineState::clear() dev_state.bound_uniform_blocks[i] = 0; } + for(unsigned i=0; i #include #include +#include #include #include +#include #include #include #include @@ -160,6 +162,8 @@ void OpenGLProgram::add_glsl_stages(const GlslModule &mod, const map &block_bindings = compiler.get_uniform_block_bindings(); if(!block_bindings.empty()) { + int first_storage = -1; for(unsigned i=0; isecond); + 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; } } @@ -472,6 +484,30 @@ void OpenGLProgram::query_attributes() sort_member(rd.attributes, &ReflectData::AttributeInfo::name); } +void OpenGLProgram::query_storage_blocks() +{ + ReflectData &rd = static_cast(this)->reflect_data; + + unsigned count = 0; + glGetProgramInterfaceiv(id, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, reinterpret_cast(&count)); + rd.blocks.reserve(rd.blocks.size()+count); + for(unsigned i=0; i(this)->reflect_data; diff --git a/source/backends/opengl/program_backend.h b/source/backends/opengl/program_backend.h index 9f77afdf..3a4c3b43 100644 --- a/source/backends/opengl/program_backend.h +++ b/source/backends/opengl/program_backend.h @@ -58,6 +58,7 @@ protected: void query_uniforms(); void query_uniform_blocks(const std::vector &); void query_attributes(); + void query_storage_blocks(); void finalize_uniforms(); bool is_compute() const { return stage_ids[COMPUTE]; } diff --git a/source/backends/vulkan/buffer_backend.cpp b/source/backends/vulkan/buffer_backend.cpp index bde76bea..e83ccdac 100644 --- a/source/backends/vulkan/buffer_backend.cpp +++ b/source/backends/vulkan/buffer_backend.cpp @@ -39,7 +39,7 @@ void VulkanBuffer::allocate() 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); diff --git a/source/backends/vulkan/pipelinestate_backend.cpp b/source/backends/vulkan/pipelinestate_backend.cpp index 1cd7d2d3..dd3b8347 100644 --- a/source/backends/vulkan/pipelinestate_backend.cpp +++ b/source/backends/vulkan/pipelinestate_backend.cpp @@ -66,7 +66,7 @@ void VulkanPipelineState::update() const 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) { @@ -336,6 +336,8 @@ uint64_t VulkanPipelineState::compute_descriptor_set_hash(unsigned index) const result = hash_update<64>(result, reinterpret_cast(i->block)); result = hash_update<64>(result, reinterpret_cast(i->buffer->handle)); } + else if(i->type==PipelineState::STORAGE_BLOCK) + result = hash_update<64>(result, reinterpret_cast(i->buffer->handle)); else if(i->type==PipelineState::SAMPLED_TEXTURE) { result = hash_update<64>(result, reinterpret_cast(i->texture->handle)); @@ -359,7 +361,7 @@ bool VulkanPipelineState::is_descriptor_set_dynamic(unsigned index) const auto i = lower_bound_member(self.resources, static_cast(index)<<20, &PipelineState::BoundResource::binding); for(; (i!=self.resources.end() && static_cast(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; @@ -381,7 +383,7 @@ unsigned VulkanPipelineState::fill_descriptor_writes(unsigned index, unsigned fr for(auto i=begin; (i!=self.resources.end() && static_cast(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; @@ -402,18 +404,29 @@ unsigned VulkanPipelineState::fill_descriptor_writes(unsigned index, unsigned fr 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; diff --git a/source/backends/vulkan/program_backend.cpp b/source/backends/vulkan/program_backend.cpp index 8c2a5c33..73df3396 100644 --- a/source/backends/vulkan/program_backend.cpp +++ b/source/backends/vulkan/program_backend.cpp @@ -126,7 +126,10 @@ void VulkanProgram::finalize_uniforms() 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; diff --git a/source/core/device.h b/source/core/device.h index b010baaa..518a0d15 100644 --- a/source/core/device.h +++ b/source/core/device.h @@ -18,6 +18,7 @@ struct DeviceLimits 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; diff --git a/source/core/module.h b/source/core/module.h index 3c11f9d4..09d94076 100644 --- a/source/core/module.h +++ b/source/core/module.h @@ -115,7 +115,8 @@ public: INPUT = 1, UNIFORM = 2, OUTPUT = 3, - PUSH_CONSTANT = 9 + PUSH_CONSTANT = 9, + STORAGE_BUFFER = 12 }; enum BuiltinSemantic diff --git a/source/core/pipelinestate.cpp b/source/core/pipelinestate.cpp index dd5b51a3..e971e369 100644 --- a/source/core/pipelinestate.cpp +++ b/source/core/pipelinestate.cpp @@ -84,6 +84,11 @@ void PipelineState::set_uniform_block(int binding, const UniformBlock *block) } } +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); @@ -104,6 +109,24 @@ void PipelineState::set_storage_texture(unsigned binding, const Texture *tex) 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(binding), &BoundResource::binding); + if(i==resources.end() || i->binding!=static_cast(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(binding), &BoundResource::binding); @@ -173,7 +196,7 @@ void PipelineState::check_bound_resources() const { 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()) diff --git a/source/core/pipelinestate.h b/source/core/pipelinestate.h index a7c1c82f..4adc29d1 100644 --- a/source/core/pipelinestate.h +++ b/source/core/pipelinestate.h @@ -36,6 +36,7 @@ private: { NO_RESOURCE, UNIFORM_BLOCK, + STORAGE_BLOCK, SAMPLED_TEXTURE, STORAGE_TEXTURE }; @@ -103,10 +104,12 @@ public: 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 *); diff --git a/source/core/program.cpp b/source/core/program.cpp index 54b58220..bbc420f8 100644 --- a/source/core/program.cpp +++ b/source/core/program.cpp @@ -80,12 +80,13 @@ void Program::collect_uniforms(const SpirVModule &mod) 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; @@ -100,11 +101,14 @@ void Program::collect_uniforms(const SpirVModule &mod) 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)) { diff --git a/source/core/reflectdata.cpp b/source/core/reflectdata.cpp index db583bf7..be4f9400 100644 --- a/source/core/reflectdata.cpp +++ b/source/core/reflectdata.cpp @@ -12,10 +12,11 @@ void ReflectData::update_layout_hash() { 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() diff --git a/source/core/reflectdata.h b/source/core/reflectdata.h index 83b01671..5b913c40 100644 --- a/source/core/reflectdata.h +++ b/source/core/reflectdata.h @@ -55,6 +55,7 @@ struct ReflectData void sort_uniforms(); void update_layout_hash(); + bool is_storage() const { return bind_point>=0 && !data_size; } }; struct AttributeInfo diff --git a/source/materials/programdata.cpp b/source/materials/programdata.cpp index 7a72e1c6..f0214666 100644 --- a/source/materials/programdata.cpp +++ b/source/materials/programdata.cpp @@ -470,8 +470,11 @@ vector::iterator ProgramData::get_program(const Progr return i; const vector &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. */ @@ -483,6 +486,9 @@ vector::iterator ProgramData::get_program(const Progr 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; @@ -631,6 +637,9 @@ vector::const_iterator ProgramData::prepare_program(c 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); @@ -645,6 +654,9 @@ vector::const_iterator ProgramData::prepare_program(c 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) { diff --git a/source/render/renderer.cpp b/source/render/renderer.cpp index 34d18d09..299cdb17 100644 --- a/source/render/renderer.cpp +++ b/source/render/renderer.cpp @@ -160,6 +160,12 @@ void Renderer::add_shader_data(const ProgramData &d) 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); @@ -442,6 +448,15 @@ void Renderer::apply_state() } } + for(const BoundResource &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; diff --git a/source/render/renderer.h b/source/render/renderer.h index 2d60a0c2..e670cafa 100644 --- a/source/render/renderer.h +++ b/source/render/renderer.h @@ -103,6 +103,7 @@ private: 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; @@ -126,6 +127,7 @@ private: State *current_state = 0; ProgramData standard_shdata; std::vector shdata_stack; + std::vector> buffer_stack; std::vector> texture_stack; const Texture &placeholder_texture; const Sampler &default_sampler; @@ -195,6 +197,8 @@ public: 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 *); -- 2.45.2