OpenGLPipelineState(OpenGLPipelineState &&) { }
~OpenGLPipelineState();
+ static bool can_bind_tex_level(unsigned l) { return l==0; }
+
void apply() const;
static void clear();
{
t.used = self.shprog->uses_texture_binding(t.binding);
changed_sets |= 1<<(t.binding>>20);
+ if(t.texture && t.level>=0)
+ t.texture->refresh_mip_views();
if(t.sampler)
t.sampler->refresh();
t.changed = false;
result = hash_update<64>(result, t.binding);
result = hash_update<64>(result, reinterpret_cast<uintptr_t>(t.texture));
result = hash_update<64>(result, reinterpret_cast<uintptr_t>(t.sampler));
+ result = hash_update<64>(result, t.level);
}
return result;
if(t.used && (t.binding>>20)==index)
{
image_ptr->sampler = handle_cast<::VkSampler>(t.sampler->handle);
- image_ptr->imageView = handle_cast<::VkImageView>(t.texture->view_handle);
+ if(t.level<0)
+ image_ptr->imageView = handle_cast<::VkImageView>(t.texture->view_handle);
+ else
+ image_ptr->imageView = handle_cast<::VkImageView>(t.texture->mip_view_handles[t.level]);
image_ptr->imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
write_ptr->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
VulkanPipelineState();
VulkanPipelineState(VulkanPipelineState &&);
+ static bool can_bind_tex_level(unsigned) { return true; }
+
void update() const;
void refresh() const { if(changes) update(); }
std::uint64_t compute_hash() const;
+#include <msp/strings/format.h>
#include "device.h"
#include "error.h"
#include "synchronizer.h"
if(view_handle)
dq.destroy(view_handle);
+ if(mip_view_handles.size()>1)
+ {
+ for(VkImageView i: mip_view_handles)
+ dq.destroy(i);
+ }
if(handle)
dq.destroy(handle, memory_id);
}
change_layout(-1, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, true);
}
+ view_handle = create_view(-1);
+
+ if(!debug_name.empty())
+ set_vulkan_object_names();
+}
+
+VkImageView VulkanTexture::create_view(int level) const
+{
+ const Texture &self = *static_cast<const Texture *>(this);
+ const VulkanFunctions &vk = device.get_functions();
+
VkImageViewCreateInfo view_info = { };
view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
view_info.image = handle_cast<::VkImage>(handle);
view_info.viewType = static_cast<VkImageViewType>(view_type);
- view_info.format = image_info.format;
+ view_info.format = static_cast<VkFormat>(get_vulkan_pixelformat(self.storage_fmt));
const unsigned *swizzle_order = get_vulkan_swizzle(self.swizzle);
view_info.components.r = static_cast<VkComponentSwizzle>(swizzle_order[0]);
view_info.components.a = static_cast<VkComponentSwizzle>(swizzle_order[3]);
view_info.subresourceRange.aspectMask = get_vulkan_aspect(get_components(self.storage_fmt));
- view_info.subresourceRange.baseMipLevel = 0;
- view_info.subresourceRange.levelCount = image_info.mipLevels;
+ view_info.subresourceRange.baseMipLevel = max(level, 0);
+ view_info.subresourceRange.levelCount = (level<0 ? VK_REMAINING_MIP_LEVELS : 1);
view_info.subresourceRange.baseArrayLayer = 0;
- view_info.subresourceRange.layerCount = image_info.arrayLayers;
+ view_info.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
- vk.CreateImageView(view_info, view_handle);
+ VkImageView view;
+ vk.CreateImageView(view_info, view);
- if(!debug_name.empty())
- set_vulkan_object_names();
+ return view;
+}
+
+void VulkanTexture::create_mip_views() const
+{
+ const Texture &self = *static_cast<const Texture *>(this);
+
+ if(!mip_view_handles.empty())
+ return;
+
+ mip_view_handles.resize(self.n_levels);
+ if(self.n_levels==1)
+ mip_view_handles[0] = view_handle;
+ else
+ {
+ for(unsigned i=0; i<self.n_levels; ++i)
+ mip_view_handles[i] = create_view(i);
+ }
}
void VulkanTexture::stage_pixels(void *staging, const void *data, size_t count)
name_info.objectHandle = reinterpret_cast<uint64_t>(view_handle);
name_info.pObjectName = view_name.c_str();
vk.SetDebugUtilsObjectName(name_info);
+
+ if(mip_view_handles.size()>1)
+ {
+ for(unsigned i=0; i<mip_view_handles.size(); ++i)
+ {
+ view_name = format("%s/mip%d.view", debug_name, i);
+ name_info.objectHandle = reinterpret_cast<uint64_t>(mip_view_handles[i]);
+ name_info.pObjectName = view_name.c_str();
+ vk.SetDebugUtilsObjectName(name_info);
+ }
+ }
#endif
}
Device &device;
VkImage handle = 0;
VkImageView view_handle = 0;
+ mutable std::vector<VkImageView> mip_view_handles;
unsigned memory_id = 0;
unsigned view_type;
std::string debug_name;
void allocate();
virtual void fill_image_info(void *) const = 0;
+ VkImageView create_view(int) const;
+ void create_mip_views() const;
void require_swizzle() { }
void stage_pixels(void *, const void *, size_t);
void change_layout(int, unsigned, bool) const;
+ void refresh_mip_views() const { if(mip_view_handles.empty()) create_mip_views(); }
+
void set_debug_name(const std::string &);
void set_vulkan_object_names() const;
};
#include <stdexcept>
#include <msp/core/algorithm.h>
+#include "error.h"
#include "pipelinestate.h"
using namespace std;
}
void PipelineState::set_texture(unsigned binding, const Texture *tex, const Sampler *samp)
+{
+ set_texture(binding, tex, -1, samp);
+}
+
+void PipelineState::set_texture(unsigned binding, const Texture *tex, int level, const Sampler *samp)
{
if((tex!=0)!=(samp!=0))
throw invalid_argument("PipelineState::set_texture");
+ if(level>=0 && !can_bind_tex_level(level))
+ throw invalid_operation("PipelineState::set_texture");
auto i = lower_bound_member(textures, binding, &BoundTexture::binding);
if(i==textures.end() || i->binding!=binding)
i = textures.insert(i, BoundTexture(binding));
i->used = (tex && samp);
- if(tex!=i->texture || samp!=i->sampler)
+ if(tex!=i->texture || level!=i->level || samp!=i->sampler)
{
i->texture = tex;
i->sampler = samp;
+ i->level = level;
i->changed = true;
changes |= TEXTURES;
}
mutable bool used = false;
const Texture *texture = 0;
const Sampler *sampler = 0;
+ int level = -1;
BoundTexture(unsigned b): binding(b) { }
};
void set_shader_program(const Program *);
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_vertex_setup(const VertexSetup *);
void set_primitive_type(PrimitiveType);
void set_front_face(FaceWinding);
}
void Renderer::set_texture(Tag tag, const Texture *tex, const Sampler *samp)
+{
+ set_texture(tag, tex, -1, samp);
+}
+
+void Renderer::set_texture(Tag tag, const Texture *tex, int level, const Sampler *samp)
{
State &state = get_state();
bound_tex.tag = tag;
bound_tex.texture = tex;
bound_tex.sampler = samp;
+ bound_tex.level = level;
state.texture_count = texture_stack.size();
}
if(t.binding<0 || shprog_changed)
t.binding = state.shprog->get_uniform_binding(t.tag);
if(t.binding>=0)
- pipeline_state.set_texture(t.binding, t.texture, t.sampler);
+ pipeline_state.set_texture(t.binding, t.texture, t.level, t.sampler);
}
pipeline_state.set_depth_test(state.depth_test);
mutable int binding = -1;
const Texture *texture = 0;
const Sampler *sampler = 0;
+ int level = -1;
int replaced = -1;
};
void add_shader_data(const ProgramData &data);
void set_texture(Tag, const Texture *, const Sampler * = 0);
+ void set_texture(Tag, const Texture *, int, const Sampler * = 0);
private:
void flush_shader_data();