From 2a70fecfbbe8708be2bdaa75d222dd5a889a8ed3 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Fri, 15 Apr 2022 23:10:37 +0300 Subject: [PATCH] Add support for storage images in Renderer and PipelineState --- extensions/arb_shader_image_load_store.glext | 1 + source/backends/opengl/device_backend.cpp | 1 + source/backends/opengl/device_backend.h | 1 + .../backends/opengl/pipelinestate_backend.cpp | 18 ++++++++- source/backends/vulkan/device_backend.cpp | 1 - .../backends/vulkan/pipelinestate_backend.cpp | 37 +++++++++++++++---- .../backends/vulkan/pipelinestate_backend.h | 1 + source/backends/vulkan/program_backend.cpp | 5 ++- source/backends/vulkan/texture_backend.cpp | 9 ++++- source/core/datatype.h | 1 + source/core/device.h | 1 + source/core/module.cpp | 6 ++- source/core/pipelinestate.cpp | 14 ++++++- source/core/pipelinestate.h | 7 +++- source/render/renderer.cpp | 13 ++++++- source/render/renderer.h | 1 + 16 files changed, 99 insertions(+), 18 deletions(-) create mode 100644 extensions/arb_shader_image_load_store.glext diff --git a/extensions/arb_shader_image_load_store.glext b/extensions/arb_shader_image_load_store.glext new file mode 100644 index 00000000..591da5d5 --- /dev/null +++ b/extensions/arb_shader_image_load_store.glext @@ -0,0 +1 @@ +extension ARB_shader_image_load_store diff --git a/source/backends/opengl/device_backend.cpp b/source/backends/opengl/device_backend.cpp index db711e51..0fcbe072 100644 --- a/source/backends/opengl/device_backend.cpp +++ b/source/backends/opengl/device_backend.cpp @@ -93,6 +93,7 @@ void OpenGLDevice::fill_info() feat.storage_texture_binding_range = lim.max_storage_texture_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); } diff --git a/source/backends/opengl/device_backend.h b/source/backends/opengl/device_backend.h index 491f1b31..2583d582 100644 --- a/source/backends/opengl/device_backend.h +++ b/source/backends/opengl/device_backend.h @@ -16,6 +16,7 @@ struct OpenGLDeviceState { const OpenGLPipelineState *last_pipeline = 0; std::vector bound_tex_targets; + std::vector bound_storage_textures; std::vector bound_uniform_blocks; unsigned restart_index = 0; unsigned n_clip_distances = 0; diff --git a/source/backends/opengl/pipelinestate_backend.cpp b/source/backends/opengl/pipelinestate_backend.cpp index 3b8c6a5a..a2b8ab32 100644 --- a/source/backends/opengl/pipelinestate_backend.cpp +++ b/source/backends/opengl/pipelinestate_backend.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -128,7 +129,7 @@ void OpenGLPipelineState::apply() const call.func(call.location, call.size, data+call.location*16); } } - else if(r.type==PipelineState::TEXTURE) + else if(r.type==PipelineState::SAMPLED_TEXTURE) { if(ARB_direct_state_access) glBindTextureUnit(r.binding, r.texture->id); @@ -145,6 +146,14 @@ void OpenGLPipelineState::apply() const glBindSampler(r.binding, r.sampler->id); r.sampler->refresh(); } + else if(r.type==PipelineState::STORAGE_TEXTURE) + { + static Require _req(ARB_shader_image_load_store); + GLenum gl_format = get_gl_pixelformat(r.texture->get_format()); + glBindImageTexture(r.binding, r.texture->id, 0, true, 0, GL_READ_WRITE, gl_format); + + dev_state.bound_storage_textures[r.binding] = 1; + } } r.changed = false; @@ -260,6 +269,13 @@ void OpenGLPipelineState::clear() dev_state.bound_tex_targets[i] = 0; } + for(unsigned i=0; iuses_uniform_block_binding(r.binding); - else if(r.type==PipelineState::TEXTURE) + else if(r.type==PipelineState::SAMPLED_TEXTURE || r.type==PipelineState::STORAGE_TEXTURE) { r.used = self.shprog->uses_texture_binding(r.binding); if(r.mip_level>=0) r.texture->refresh_mip_views(); - r.sampler->refresh(); + if(r.type==PipelineState::SAMPLED_TEXTURE) + r.sampler->refresh(); } if(r.binding>=0) changed_sets |= 1<<(r.binding>>20); @@ -285,12 +286,14 @@ 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::TEXTURE) + else if(i->type==PipelineState::SAMPLED_TEXTURE) { result = hash_update<64>(result, reinterpret_cast(i->texture->handle)); result = hash_update<64>(result, reinterpret_cast(i->sampler->handle)); result = hash_update<64>(result, i->mip_level); } + else if(i->type==PipelineState::STORAGE_TEXTURE) + result = hash_update<64>(result, reinterpret_cast(i->texture->handle)); empty = false; } @@ -330,7 +333,7 @@ unsigned VulkanPipelineState::fill_descriptor_writes(unsigned index, unsigned fr { if(i->type==PipelineState::UNIFORM_BLOCK) ++n_buffers; - else if(i->type==PipelineState::TEXTURE) + else if(i->type==PipelineState::SAMPLED_TEXTURE || i->type==PipelineState::STORAGE_TEXTURE) ++n_images; } unsigned n_writes = n_buffers+n_images; @@ -365,19 +368,28 @@ unsigned VulkanPipelineState::fill_descriptor_writes(unsigned index, unsigned fr ++buffer_ptr; } - else if(i->type==PipelineState::TEXTURE) + else if(i->type==PipelineState::SAMPLED_TEXTURE || i->type==PipelineState::STORAGE_TEXTURE) { - image_ptr->sampler = handle_cast<::VkSampler>(i->sampler->handle); if(i->mip_level<0) image_ptr->imageView = handle_cast<::VkImageView>(i->texture->view_handle); else image_ptr->imageView = handle_cast<::VkImageView>(i->texture->mip_view_handles[i->mip_level]); - image_ptr->imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + if(i->type==PipelineState::SAMPLED_TEXTURE) + { + image_ptr->sampler = handle_cast<::VkSampler>(i->sampler->handle); + image_ptr->imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + write_ptr->descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + } + else if(i->type==PipelineState::STORAGE_TEXTURE) + { + image_ptr->imageLayout = VK_IMAGE_LAYOUT_GENERAL; + write_ptr->descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + } 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_COMBINED_IMAGE_SAMPLER; write_ptr->pImageInfo = image_ptr; ++image_ptr; @@ -389,6 +401,15 @@ unsigned VulkanPipelineState::fill_descriptor_writes(unsigned index, unsigned fr return n_writes; } +void VulkanPipelineState::synchronize_resources(bool discard_fb_contents) const +{ + const PipelineState &self = *static_cast(this); + + for(const PipelineState::BoundResource &r: self.resources) + if(r.type==PipelineState::STORAGE_TEXTURE) + r.texture->change_layout(-1, VK_IMAGE_LAYOUT_GENERAL, false); +} + void VulkanPipelineState::apply(const VulkanCommandRecorder &vkCmd, const VulkanPipelineState *last, unsigned frame, bool negative_viewport) const { const PipelineState &self = *static_cast(this); diff --git a/source/backends/vulkan/pipelinestate_backend.h b/source/backends/vulkan/pipelinestate_backend.h index 892d8b3a..37c96a4e 100644 --- a/source/backends/vulkan/pipelinestate_backend.h +++ b/source/backends/vulkan/pipelinestate_backend.h @@ -39,6 +39,7 @@ protected: VkDescriptorSetLayout get_descriptor_set_layout(unsigned) const; unsigned fill_descriptor_writes(unsigned, unsigned, std::vector &) const; + void synchronize_resources(bool) const; void apply(const VulkanCommandRecorder &, const VulkanPipelineState *, unsigned, bool) const; }; diff --git a/source/backends/vulkan/program_backend.cpp b/source/backends/vulkan/program_backend.cpp index 9402226b..a962a78e 100644 --- a/source/backends/vulkan/program_backend.cpp +++ b/source/backends/vulkan/program_backend.cpp @@ -138,7 +138,10 @@ void VulkanProgram::finalize_uniforms() bindings.emplace_back(); VkDescriptorSetLayoutBinding &binding = bindings.back(); binding.binding = u.binding&0xFFFFF; - binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + if(is_sampled_image(u.type)) + binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + else + binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; binding.descriptorCount = 1; binding.stageFlags = stage_flags; binding.pImmutableSamplers = 0; diff --git a/source/backends/vulkan/texture_backend.cpp b/source/backends/vulkan/texture_backend.cpp index 5b737498..1367cc8d 100644 --- a/source/backends/vulkan/texture_backend.cpp +++ b/source/backends/vulkan/texture_backend.cpp @@ -49,9 +49,13 @@ void VulkanTexture::allocate() const Texture &self = *static_cast(this); const VulkanFunctions &vk = device.get_functions(); + VkFormat vk_format = static_cast(get_vulkan_pixelformat(self.storage_fmt)); + VkFormatProperties props; + vk.GetPhysicalDeviceFormatProperties(vk_format, props); + VkImageCreateInfo image_info = { }; image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - image_info.format = static_cast(get_vulkan_pixelformat(self.storage_fmt)); + image_info.format = vk_format; image_info.extent.width = 1; image_info.extent.height = 1; image_info.extent.depth = 1; @@ -63,6 +67,9 @@ void VulkanTexture::allocate() image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; image_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT|VK_IMAGE_USAGE_TRANSFER_DST_BIT|VK_IMAGE_USAGE_SAMPLED_BIT; + if(props.optimalTilingFeatures&VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) + image_info.usage |= VK_IMAGE_USAGE_STORAGE_BIT; + PixelComponents comp = get_components(self.storage_fmt); if(comp==DEPTH_COMPONENT || comp==STENCIL_INDEX) image_info.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; diff --git a/source/core/datatype.h b/source/core/datatype.h index d2739c49..29789907 100644 --- a/source/core/datatype.h +++ b/source/core/datatype.h @@ -101,6 +101,7 @@ inline bool is_float(DataType t) { return t&0x200; } inline bool is_matrix(DataType t) { return t&0xC000; } inline bool is_vector(DataType t) { return !is_matrix(t) && (t&0x3000); } inline bool is_image(DataType t) { return t&0x70000; } +inline bool is_sampled_image(DataType t) { return t&0x100000; } inline DataType get_matrix_column_type(DataType t) { diff --git a/source/core/device.h b/source/core/device.h index 97a28e73..8c7e92a9 100644 --- a/source/core/device.h +++ b/source/core/device.h @@ -17,6 +17,7 @@ struct DeviceLimits unsigned max_clip_planes = 6; unsigned max_vertex_attributes = 16; unsigned max_texture_bindings = 16; + unsigned max_storage_texture_bindings = 8; unsigned max_color_attachments = 8; unsigned max_samples = 4; unsigned max_uniform_bindings = 24; diff --git a/source/core/module.cpp b/source/core/module.cpp index 5475e434..0870c478 100644 --- a/source/core/module.cpp +++ b/source/core/module.cpp @@ -623,11 +623,13 @@ void SpirVModule::Reflection::reflect_matrix_type(CodeIterator op) void SpirVModule::Reflection::reflect_image_type(CodeIterator op) { TypeInfo &type = types[*(op+1)]; - DataType sample = types[*(op+2)].type; + DataType sample_type = types[*(op+2)].type; unsigned dimensions = *(op+3); bool depth = *(op+4)==1; bool array = *(op+5); - type.type = static_cast((depth*0x200000) | (array*0x80000) | ((dimensions+1)<<16) | sample); + bool sampled = *(op+7)==1; + type.type = static_cast((depth*0x200000) | (sampled*0x100000) | (array*0x80000) | + ((dimensions+1)<<16) | sample_type); } void SpirVModule::Reflection::reflect_sampled_image_type(CodeIterator op) diff --git a/source/core/pipelinestate.cpp b/source/core/pipelinestate.cpp index 5ad51502..4dc54e24 100644 --- a/source/core/pipelinestate.cpp +++ b/source/core/pipelinestate.cpp @@ -70,11 +70,21 @@ void PipelineState::set_texture(unsigned binding, const Texture *tex, int level, if(level>=0 && !can_bind_tex_level(level)) throw invalid_operation("PipelineState::set_texture"); + set_texture_resource(binding, tex, level, samp); +} + +void PipelineState::set_storage_texture(unsigned binding, const Texture *tex) +{ + set_texture_resource(binding, tex, 0, 0); +} + +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); if(i==resources.end() || i->binding!=static_cast(binding)) i = resources.insert(i, BoundResource(binding)); - ResourceType type = (tex ? TEXTURE : NO_RESOURCE); + ResourceType type = (tex ? samp ? SAMPLED_TEXTURE : STORAGE_TEXTURE : NO_RESOURCE); if(i->type!=type || tex!=i->texture || level!=i->mip_level || samp!=i->sampler) { i->type = type; @@ -82,7 +92,7 @@ void PipelineState::set_texture(unsigned binding, const Texture *tex, int level, i->sampler = samp; i->mip_level = level; i->changed = true; - i->used = (tex && samp); + i->used = tex; changes |= RESOURCES; } } diff --git a/source/core/pipelinestate.h b/source/core/pipelinestate.h index e7fdfdc8..965bc15f 100644 --- a/source/core/pipelinestate.h +++ b/source/core/pipelinestate.h @@ -36,7 +36,8 @@ private: { NO_RESOURCE, UNIFORM_BLOCK, - TEXTURE + SAMPLED_TEXTURE, + STORAGE_TEXTURE }; struct BoundResource @@ -98,6 +99,10 @@ public: void set_uniform_block(int, const UniformBlock *); 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_texture_resource(unsigned, const Texture *, int, const Sampler *); +public: void set_vertex_setup(const VertexSetup *); void set_primitive_type(PrimitiveType); void set_front_face(FaceWinding); diff --git a/source/render/renderer.cpp b/source/render/renderer.cpp index f2fb002e..828efd70 100644 --- a/source/render/renderer.cpp +++ b/source/render/renderer.cpp @@ -181,6 +181,12 @@ void Renderer::set_texture(Tag tag, const Texture *tex, int level, const Sampler set_resource(texture_stack, state.texture_count, tag, { tex, samp, level }); } +void Renderer::set_storage_texture(Tag tag, const Texture *tex) +{ + State &state = get_state(); + set_resource(texture_stack, state.texture_count, tag, { tex, 0, 0 }); +} + template void Renderer::set_resource(vector> &stack, unsigned &count, Tag tag, const T &res) { @@ -413,7 +419,12 @@ void Renderer::apply_state() if(t.binding<0 || shprog_changed) t.binding = state.shprog->get_uniform_binding(t.tag); if(t.binding>=0) - ps.set_texture(t.binding, t.resource.texture, t.resource.level, t.resource.sampler); + { + if(t.resource.sampler) + ps.set_texture(t.binding, t.resource.texture, t.resource.level, t.resource.sampler); + else + ps.set_storage_texture(t.binding, t.resource.texture); + } } static const DepthTest default_depth_test; diff --git a/source/render/renderer.h b/source/render/renderer.h index 9335164b..fb460cf0 100644 --- a/source/render/renderer.h +++ b/source/render/renderer.h @@ -197,6 +197,7 @@ public: 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 *); private: template -- 2.43.0