Vulkan requires different layouts for sampling a texture and rendering
into, so reading from one mip level and rendering into another requires
separate views.
The OpenGL backend only allows binding level 0, since nothing currently
needs binding higher levels. Full support requires the ARB_texture_view
extension.
OpenGLPipelineState(OpenGLPipelineState &&) { }
~OpenGLPipelineState();
OpenGLPipelineState(OpenGLPipelineState &&) { }
~OpenGLPipelineState();
+ static bool can_bind_tex_level(unsigned l) { return l==0; }
+
void apply() const;
static void clear();
void apply() const;
static void clear();
{
t.used = self.shprog->uses_texture_binding(t.binding);
changed_sets |= 1<<(t.binding>>20);
{
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;
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.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);
if(t.used && (t.binding>>20)==index)
{
image_ptr->sampler = handle_cast<::VkSampler>(t.sampler->handle);
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;
image_ptr->imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
write_ptr->sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
VulkanPipelineState();
VulkanPipelineState(VulkanPipelineState &&);
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;
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"
#include "device.h"
#include "error.h"
#include "synchronizer.h"
if(view_handle)
dq.destroy(view_handle);
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);
}
if(handle)
dq.destroy(handle, memory_id);
}
change_layout(-1, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, true);
}
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);
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]);
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.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.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)
}
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);
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);
+ }
+ }
Device &device;
VkImage handle = 0;
VkImageView view_handle = 0;
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;
unsigned memory_id = 0;
unsigned view_type;
std::string debug_name;
void allocate();
virtual void fill_image_info(void *) const = 0;
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 require_swizzle() { }
void stage_pixels(void *, const void *, size_t);
void change_layout(int, unsigned, bool) const;
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;
};
void set_debug_name(const std::string &);
void set_vulkan_object_names() const;
};
#include <stdexcept>
#include <msp/core/algorithm.h>
#include <stdexcept>
#include <msp/core/algorithm.h>
#include "pipelinestate.h"
using namespace std;
#include "pipelinestate.h"
using namespace std;
}
void PipelineState::set_texture(unsigned binding, const Texture *tex, const Sampler *samp)
}
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((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);
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->texture = tex;
i->sampler = samp;
i->changed = true;
changes |= TEXTURES;
}
i->changed = true;
changes |= TEXTURES;
}
mutable bool used = false;
const Texture *texture = 0;
const Sampler *sampler = 0;
mutable bool used = false;
const Texture *texture = 0;
const Sampler *sampler = 0;
BoundTexture(unsigned b): binding(b) { }
};
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_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 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)
}
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();
{
State &state = get_state();
bound_tex.tag = tag;
bound_tex.texture = tex;
bound_tex.sampler = samp;
bound_tex.tag = tag;
bound_tex.texture = tex;
bound_tex.sampler = samp;
+ bound_tex.level = level;
state.texture_count = texture_stack.size();
}
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)
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);
}
pipeline_state.set_depth_test(state.depth_test);
mutable int binding = -1;
const Texture *texture = 0;
const Sampler *sampler = 0;
mutable int binding = -1;
const Texture *texture = 0;
const Sampler *sampler = 0;
void add_shader_data(const ProgramData &data);
void set_texture(Tag, const Texture *, const Sampler * = 0);
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();
private:
void flush_shader_data();