--- /dev/null
+extension ARB_shader_image_load_store
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);
}
{
const OpenGLPipelineState *last_pipeline = 0;
std::vector<int> bound_tex_targets;
+ std::vector<char> bound_storage_textures;
std::vector<char> bound_uniform_blocks;
unsigned restart_index = 0;
unsigned n_clip_distances = 0;
#include <msp/gl/extensions/arb_direct_state_access.h>
#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_uniform_buffer_object.h>
#include <msp/gl/extensions/arb_vertex_array_object.h>
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);
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;
dev_state.bound_tex_targets[i] = 0;
}
+ for(unsigned i=0; i<dev_state.bound_storage_textures.size(); ++i)
+ if(dev_state.bound_storage_textures[i])
+ {
+ glBindImageTexture(i, 0, 0, true, 0, GL_READ_ONLY, GL_RGBA8);
+ dev_state.bound_storage_textures[i] = 0;
+ }
+
for(unsigned i=0; i<dev_state.bound_uniform_blocks.size(); ++i)
if(dev_state.bound_uniform_blocks[i])
{
limits.max_clip_planes = props.limits.maxClipDistances;
limits.max_vertex_attributes = props.limits.maxVertexInputAttributes;
limits.max_texture_bindings = props.limits.maxDescriptorSetSampledImages;
- limits.max_storage_texture_bindings = props.limits.maxDescriptorSetStorageImages;
limits.max_color_attachments = props.limits.maxColorAttachments;
unsigned samples = props.limits.framebufferColorSampleCounts&props.limits.framebufferDepthSampleCounts&props.limits.framebufferStencilSampleCounts;
if(samples&VK_SAMPLE_COUNT_64_BIT)
{
if(r.type==PipelineState::UNIFORM_BLOCK)
r.used = self.shprog->uses_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);
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::TEXTURE)
+ else if(i->type==PipelineState::SAMPLED_TEXTURE)
{
result = hash_update<64>(result, reinterpret_cast<uintptr_t>(i->texture->handle));
result = hash_update<64>(result, reinterpret_cast<uintptr_t>(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<uintptr_t>(i->texture->handle));
empty = false;
}
{
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;
++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;
return n_writes;
}
+void VulkanPipelineState::synchronize_resources(bool discard_fb_contents) const
+{
+ const PipelineState &self = *static_cast<const PipelineState *>(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<const PipelineState *>(this);
VkDescriptorSetLayout get_descriptor_set_layout(unsigned) const;
unsigned fill_descriptor_writes(unsigned, unsigned, std::vector<char> &) const;
+ void synchronize_resources(bool) const;
void apply(const VulkanCommandRecorder &, const VulkanPipelineState *, unsigned, bool) const;
};
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;
const Texture &self = *static_cast<const Texture *>(this);
const VulkanFunctions &vk = device.get_functions();
+ VkFormat vk_format = static_cast<VkFormat>(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<VkFormat>(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;
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;
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)
{
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;
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<DataType>((depth*0x200000) | (array*0x80000) | ((dimensions+1)<<16) | sample);
+ bool sampled = *(op+7)==1;
+ type.type = static_cast<DataType>((depth*0x200000) | (sampled*0x100000) | (array*0x80000) |
+ ((dimensions+1)<<16) | sample_type);
}
void SpirVModule::Reflection::reflect_sampled_image_type(CodeIterator op)
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<int>(binding), &BoundResource::binding);
if(i==resources.end() || i->binding!=static_cast<int>(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;
i->sampler = samp;
i->mip_level = level;
i->changed = true;
- i->used = (tex && samp);
+ i->used = tex;
changes |= RESOURCES;
}
}
{
NO_RESOURCE,
UNIFORM_BLOCK,
- TEXTURE
+ SAMPLED_TEXTURE,
+ STORAGE_TEXTURE
};
struct BoundResource
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);
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<typename T>
void Renderer::set_resource(vector<BoundResource<T>> &stack, unsigned &count, Tag tag, const T &res)
{
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;
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<typename T>