]> git.tdb.fi Git - libs/gl.git/commitdiff
Support binding individual mipmap levels of textures
authorMikko Rasa <tdb@tdb.fi>
Fri, 10 Dec 2021 13:51:09 +0000 (15:51 +0200)
committerMikko Rasa <tdb@tdb.fi>
Fri, 10 Dec 2021 23:32:45 +0000 (01:32 +0200)
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.

source/backends/opengl/pipelinestate_backend.h
source/backends/vulkan/pipelinestate_backend.cpp
source/backends/vulkan/pipelinestate_backend.h
source/backends/vulkan/texture_backend.cpp
source/backends/vulkan/texture_backend.h
source/core/pipelinestate.cpp
source/core/pipelinestate.h
source/render/renderer.cpp
source/render/renderer.h

index a8f2d306109109f440661b066410bb947973940f..9198706c7344d7f23f4369f3ddc8208598dcfbd5 100644 (file)
@@ -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();
index 013e34c2636a9e995ab64d9815bc53e14dc80f41..419a7e4c149545b3de9ce19cd211da7bef5e563d 100644 (file)
@@ -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<uintptr_t>(t.texture));
                        result = hash_update<64>(result, reinterpret_cast<uintptr_t>(t.sampler));
+                       result = hash_update<64>(result, t.level);
                }
 
        return result;
@@ -334,7 +337,10 @@ unsigned VulkanPipelineState::fill_descriptor_writes(unsigned index, vector<char
                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;
index d7ef4a20bf97483ade9b2c818228d59352e2628e..e675ff335c8cf311ad59cc9fe013e27804a4b0c0 100644 (file)
@@ -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;
index ec6d504732f9f0a13055eff1f42086d2fe4858a7..237b984558bf1dd3fd484009a1b06cd5b2bc24c9 100644 (file)
@@ -1,3 +1,4 @@
+#include <msp/strings/format.h>
 #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<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]);
@@ -89,15 +106,32 @@ void VulkanTexture::allocate()
        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)
@@ -206,6 +240,17 @@ void VulkanTexture::set_vulkan_object_names() const
        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
 }
 
index 0907fea058000fe849167ae1b36d385dff479dbc..f7ea99b0ecd4c13b605c0bc0bfc54a5b75fe1edf 100644 (file)
@@ -18,6 +18,7 @@ protected:
        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;
@@ -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;
 };
index 076eb333d29b127a0d08ef38ec158223fb957179..210a0703868035dc69572fc56cc50eaf67b9ff08 100644 (file)
@@ -1,5 +1,6 @@
 #include <stdexcept>
 #include <msp/core/algorithm.h>
+#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;
        }
index 89cf1cd7261df3c29f20cb769eddc6e6c6c93bb7..d0ff349069cee04e69992d2c94fa76233b9f6f0d 100644 (file)
@@ -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);
index 5262d09a90ee2862e6c0db07dcd02e71044241fd..946f0100bbbf0a5467877e451343d643a0f13670 100644 (file)
@@ -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);
index 1cbcdc60546b93f0cccc2db6ee5cb95a27107b39..fe13a882430f5407b4175d1aa481f776df86d009 100644 (file)
@@ -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();