From: Mikko Rasa Date: Fri, 10 Dec 2021 13:51:09 +0000 (+0200) Subject: Support binding individual mipmap levels of textures X-Git-Url: http://git.tdb.fi/?p=libs%2Fgl.git;a=commitdiff_plain;h=c89c1fb972fae2cb2f720cb3ec6cf8238ae8d2e1 Support binding individual mipmap levels of textures 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. --- diff --git a/source/backends/opengl/pipelinestate_backend.h b/source/backends/opengl/pipelinestate_backend.h index a8f2d306..9198706c 100644 --- a/source/backends/opengl/pipelinestate_backend.h +++ b/source/backends/opengl/pipelinestate_backend.h @@ -21,6 +21,8 @@ protected: OpenGLPipelineState(OpenGLPipelineState &&) { } ~OpenGLPipelineState(); + static bool can_bind_tex_level(unsigned l) { return l==0; } + void apply() const; static void clear(); diff --git a/source/backends/vulkan/pipelinestate_backend.cpp b/source/backends/vulkan/pipelinestate_backend.cpp index 013e34c2..419a7e4c 100644 --- a/source/backends/vulkan/pipelinestate_backend.cpp +++ b/source/backends/vulkan/pipelinestate_backend.cpp @@ -59,6 +59,8 @@ void VulkanPipelineState::update() const { 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; @@ -280,6 +282,7 @@ uint64_t VulkanPipelineState::compute_descriptor_set_hash(unsigned index) const result = hash_update<64>(result, t.binding); result = hash_update<64>(result, reinterpret_cast(t.texture)); result = hash_update<64>(result, reinterpret_cast(t.sampler)); + result = hash_update<64>(result, t.level); } return result; @@ -334,7 +337,10 @@ unsigned VulkanPipelineState::fill_descriptor_writes(unsigned index, vector>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; diff --git a/source/backends/vulkan/pipelinestate_backend.h b/source/backends/vulkan/pipelinestate_backend.h index d7ef4a20..e675ff33 100644 --- a/source/backends/vulkan/pipelinestate_backend.h +++ b/source/backends/vulkan/pipelinestate_backend.h @@ -23,6 +23,8 @@ protected: 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; diff --git a/source/backends/vulkan/texture_backend.cpp b/source/backends/vulkan/texture_backend.cpp index ec6d5047..237b9845 100644 --- a/source/backends/vulkan/texture_backend.cpp +++ b/source/backends/vulkan/texture_backend.cpp @@ -1,3 +1,4 @@ +#include #include "device.h" #include "error.h" #include "synchronizer.h" @@ -34,6 +35,11 @@ VulkanTexture::~VulkanTexture() 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); } @@ -76,11 +82,22 @@ void VulkanTexture::allocate() 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(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(view_type); - view_info.format = image_info.format; + view_info.format = static_cast(get_vulkan_pixelformat(self.storage_fmt)); const unsigned *swizzle_order = get_vulkan_swizzle(self.swizzle); view_info.components.r = static_cast(swizzle_order[0]); @@ -89,15 +106,32 @@ void VulkanTexture::allocate() view_info.components.a = static_cast(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(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(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[i]); + name_info.pObjectName = view_name.c_str(); + vk.SetDebugUtilsObjectName(name_info); + } + } #endif } diff --git a/source/backends/vulkan/texture_backend.h b/source/backends/vulkan/texture_backend.h index 0907fea0..f7ea99b0 100644 --- a/source/backends/vulkan/texture_backend.h +++ b/source/backends/vulkan/texture_backend.h @@ -18,6 +18,7 @@ protected: Device &device; VkImage handle = 0; VkImageView view_handle = 0; + mutable std::vector mip_view_handles; unsigned memory_id = 0; unsigned view_type; std::string debug_name; @@ -28,6 +29,8 @@ protected: 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); @@ -37,6 +40,8 @@ protected: 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; }; diff --git a/source/core/pipelinestate.cpp b/source/core/pipelinestate.cpp index 076eb333..210a0703 100644 --- a/source/core/pipelinestate.cpp +++ b/source/core/pipelinestate.cpp @@ -1,5 +1,6 @@ #include #include +#include "error.h" #include "pipelinestate.h" using namespace std; @@ -52,18 +53,26 @@ void PipelineState::set_uniform_block(int binding, const UniformBlock *block) } 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; } diff --git a/source/core/pipelinestate.h b/source/core/pipelinestate.h index 89cf1cd7..d0ff3490 100644 --- a/source/core/pipelinestate.h +++ b/source/core/pipelinestate.h @@ -38,6 +38,7 @@ private: mutable bool used = false; const Texture *texture = 0; const Sampler *sampler = 0; + int level = -1; BoundTexture(unsigned b): binding(b) { } }; @@ -91,6 +92,7 @@ public: 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); diff --git a/source/render/renderer.cpp b/source/render/renderer.cpp index 5262d09a..946f0100 100644 --- a/source/render/renderer.cpp +++ b/source/render/renderer.cpp @@ -139,6 +139,11 @@ void Renderer::add_shader_data(const ProgramData &d) } 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(); @@ -170,6 +175,7 @@ void Renderer::set_texture(Tag tag, const Texture *tex, const Sampler *samp) bound_tex.tag = tag; bound_tex.texture = tex; bound_tex.sampler = samp; + bound_tex.level = level; state.texture_count = texture_stack.size(); } @@ -344,7 +350,7 @@ void Renderer::apply_state() 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); diff --git a/source/render/renderer.h b/source/render/renderer.h index 1cbcdc60..fe13a882 100644 --- a/source/render/renderer.h +++ b/source/render/renderer.h @@ -64,6 +64,7 @@ private: mutable int binding = -1; const Texture *texture = 0; const Sampler *sampler = 0; + int level = -1; int replaced = -1; }; @@ -160,6 +161,7 @@ public: 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();