Development of new vsg::StateComposer class

433 views
Skip to first unread message

Robert Osfield

unread,
Mar 15, 2022, 6:55:47 AM3/15/22
to vsg-users : VulkanSceneGraph Developer Discussion Group
Hi All,

Over the last few months I have mentioned my intention to develop a new utility class that can be used by scene builder/loaders to create the all the state required to render a scene - managing the shader composition, creation of Descriptor/DescriptorSets etc. as well as caching the state so that as much state can be shared as possible.

Today I have begun work on a vsg::StateComposer utility class to fulfil this role.  It may be by the end of this work a better name presents itself, and it may be the functionality it provides will evolve as well.  Nothing is set in stone at this point.  I've created a StateComposer branch for this work:


My aim for this week will be to create a proof of concept for this new class that will replace the functionality that is currently built into vsg::Builder for managing state, so vsg::Builder will "have a" StateComposer.

If that goes well then I'll move on to generalizing the class to work with vsgXchange::Assimp loader, at this point it should be possible to load models via Assimp that support the vsg::Light functionality. 

Then the next step will be replacing the state functionality in the vsgXchange::OSG loader and the vsgXchange::GDAL functionality that does the paged tile database.

My thought is that we can assign a StateComposer to the vsg::Options that we pass into the vsg::read(filename, options) call so all the loads can automatically share all the same state.

My plan is to make the StateComposer both extensible and configurable - so you'll be able to subclass from StateComposer and customize it's behaviour if that pipelines it creates suits your application.

For configuring the StateComposer I'm thinking of having a StateConfig class that provides a template of the shaders to use, default stats, bindings etc.  Multiple StateConfig's could be provided by default, as well as user defined StateConfig assigned to the StateComposer to replace the default StateConfig or add to them.  I'm thinking that you could select which StateConfig to use based on a name i.e. "PBR" or "Phong" like the present vsgXchange::Assimp has used.

The StateConfig will be something users can serialize as per other VSG classes, so a StateConfig could be written to .vsgt then edited in a text editor then used in applications. 

While this functionality will be new to the VSG I aware that other APIs, especially game engines, have functionality that overlaps with what I'm trying to achieve.  For over 20 years the OSG and now VSG has been my focus so learning in detail about what others API have used is not something I've not done.  If you've come across concepts or even just naming conventions that make sense to learn from please chip in.

I look forward to hearing everyone's ideas/suggestions,
Robert.

Robert Osfield

unread,
Mar 18, 2022, 7:53:31 AM3/18/22
to vsg-users : VulkanSceneGraph Developer Discussion Group
Hi All,

Progress has been slower on fleshing out the vsg::ShaderComposer so no results to report yet.  The area that I have focused on is on the past day is providing a mechanism for sharing objects, in the context of state this would be sharing vsg::GraphicsPipeline/BindGraphicsPipeline and vsg::BindDescriptorSets, but to know whether you can share state objects or you need to be specify all the settings you want then look for e pre-existing object that matches those settings.  This is easy to write in a sentence less easy to implement in a general purpose way...

The approach I am taking for this is more general purpose than required for the ShaderComposer, as I think one coherent solution for sharing objects with same proprieties can help with geometry, nodes as well as state.  Even within the state side each top level StateCommand like BindGraphicsPipeline and BindDescriptorSets has a whole set of objects nested within them, from the bottom up those objects and their properties need to match. The class that will provide this functionality in a new vsg::SharedObjects class:


The SharedObjects is class that manages a set of default objects that can be shared directly, a pool of objects that can be reused if they are no longer required, and set of shared objects. The type used will be in the form:

    // application setup:
    auto options = vsg::Optons::create();
    options->shadedObjects = vsg::ShaderdObjects::create();

     // then in the scene graph set up/loader code:
     auto& sharedObjects = options->sharedObjects;

     // get an instance of a PhongMaterialValue from the pool or fallback to PhongMaterialValue::create().
     auto material = sharedObjects->get<PhongMaterialValue>();
     materal->value()->diffuse.set(1.0, 0.0, 0.0, 0.0);

     // check for an existing instance that matches material's properties, if so return instance, and place material in the pool
     // otherwise insert material into set of shared objects;
     material = sharedObjects->share(material);
 
I may tweak the share() API, so that it uses the an ref_ptr<> reference and modify the parameter rather than returning as it might feel a bit more logical.

To enable the finding of matching objects I have added a int vsg::Object::compare(const Object& rhs) const method that uses the same return value convention as std::memcmp(..).  I have implementation o the compare done for vsg::Data subclasses, hence I was able to test the above.

My next task is to move up the chain from low level classes like vsg::Data to vsg::BufferInfo and vsg::ImageInfo then vsg::DescriptorBuffer/vsg::DescriptorImage etc.  The actual implementation of vsg::SharedObjects is actually relatively straight-forward, the bulk work will be in adding suitable Object::compare(..) implementations.

I may make sense for me to merge the SharedObjects work separately from the wider StateComposer functionality, as it has usefulness wide that StateComposer and it might free my mind up a bit more to just have one of the building blocks done and dusted.

For OSG users they may be familiar with osgDB::SharedStateManager, conceptually there is an overlap with vsg::SharedObjects but the new class is general purpose - you'll be able to use it for your own classes let alone just geometry, nodes and state.  Also I'm thinking that because it'll be leveraged by StateComposer, which will be used by vsgXchange::Assimp etc,  it'll be much more widely used, rather than a niche feature that few applications utilized.

One last thing or note, vsg::SharedObjects assumes that once created and SharedObjects::share(..) is called the objects public properties will then be immutable, this won't be enforced so there is scope for use/misuse of this feature.  While SharedOjects itself is thread safe if you go modify objects that are shared instances then you could see problems.  If applications are creating data they intend to be dynamic then that data should not be shared via SharedObjects.

Cheers,
Robert.


Robert Osfield

unread,
Mar 18, 2022, 2:39:07 PM3/18/22
to vsg-...@googlegroups.com
I have checked implementation of a int Object::compare(const Object& rhs) in Data, BufferInfo, ImagInfo, Image, ImgeView, DescriptorBuffer, DescriptorImage and DecriptorTexelBufferView which means we can now us the vsg::SharedObjects class to shared uniforms and textures.  The changes are:


I have used a modified version of vsg::Builder to confirm this works, but haven't checked in the changes to Builder checked these changes in as they are just experimental.  With this experimental work I'm testing both the usability of the SharedObjects class and the reliability of the compare/sharing implementation. 

One thing I discussed in my previous post is that SharedObjects has an internal pool of no longer used objects that can be reused - the intention behind this was to avoid new/delete of objects being created just for the purposes of setting the properties.  However, for this scheme to fully work we'll need to implement copy operators and set methods that mirror ones available in constructors.  While possible to add these extra methods to all VSG classes I'm now thinking this is a lot of extra work and extra code we'd need to write, debug and maintain.  So my current inclination is to do away with the pool and just have users use the form:

  auto object = vsg::Class::create(settings);
  object = sharedObjects->share(object);

Or shorthand:

  auto object = sharedObjects->share(vsg::Class::create(settings));

Computational it's not as efficient as using a pool but code wise it's much simpler, and it fits much more closely with existing code base that is just using vsg::Class:create() etc, so retrofitting use of SharedObjects should be more straight-forward.

Cheers,
Robert.

Robert Osfield

unread,
Mar 22, 2022, 10:47:00 AM3/22/22
to vsg-users : VulkanSceneGraph Developer Discussion Group
Hi All,

I have now completed the initial work on vsg::SharedObjects class and all the support required for it across various VSG scene graph classes.  This changes are now checked into VulkanSceneGraph master:


To enable sharing of objects you have introduced a virtual int vsg::Object::compare(const Object& ths) const method with a lots of implementations of this in the various subclasses.  For this initial pass I've focused on vsg::Data subclasses and the state classes.   The return values of int Object::compare(..) follows exactly the same conventio as std::memcmp(..), so negative if lhs is less than rhs, 0 for a match and positive if lhs is greater than rhs.  The is also a new include/vsg/core/compare.h header that has a collection of vsg::compare_*(..) functions to help streamline implementations of Class::compare(..).

There is a fallback implementation provided by vsg::Inherit that a direct memory comparison of the members that a subclass adds which works well for simple member variables like ints, vec4, mat4 etc. For member variables that hold pointers to other memory such as std::string or a vsg::vector<> a comparison will still run but will fail to pick up possible matches that would be found if one compared the contents of the containers - for these cases no match is returned which is a conservative result and safe.  To help match objects that have pointers to other objects that class will need to implement the int Object::compare(cosnt Object& ths) const method. 

Most of the above PR is dedicated to implementations of Object::compare(const Object& ths) so if you are curious about how to implement it then have a read through.

The goal of the vsg::SharedObjects class is to enable scene graph creation code/loaders to share data and especially state between successive creation/load operations.  The vsg::Options class used by vsg::read(..) etc. now has a Options::sharedObjects member so you can assign a vsg::SharedObjects container to the options then the loaders can check for it presence and as it.  By sharing the same SharedObjects container between successive loads you can enable each new model to reuse the same state.  The SharedObjects container is thread safe so it's designed from the start to support multiple threads using and sharing data.

I have bumped the VulkanSceneGraph version number of 0.2.12 to signify the addition of SharedObjects and Object::compare(..).

The SharedObjects work is orthogonal to the StateComposer functionality, so can be used right away without need to wait for StateComposer to be completed - VSG users creating their own scene graphs should find it particularly useful as it will simplify the tasks of implementing sharing.

My work on StateComposer functionality will now resume, it will of course utilize SharedObjects to make sure all what can be shared will be done so efficiently.

Cheers,
Robert.

Sam

unread,
Mar 22, 2022, 4:52:12 PM3/22/22
to vsg-users : VulkanSceneGraph Developer Discussion Group
Hey Robert,

Perhaps you could talk a bit about the SharedObjects vs ObjectCache a bit more? It looks like ObjectCache just operates at a bit of a higher level but is essentially has the same functionality as the ObjectCache. If I'm reading this right, would it make sense to include SharedObjects directly into the ObjectCache? Or would you use them separate from each other.

Thanks, Sam

Robert Osfield

unread,
Mar 22, 2022, 5:50:21 PM3/22/22
to vsg-...@googlegroups.com
Hi Same,

On Tue, 22 Mar 2022 at 20:52, Sam <brk...@gmail.com> wrote:
Perhaps you could talk a bit about the SharedObjects vs ObjectCache a bit more? It looks like ObjectCache just operates at a bit of a higher level but is essentially has the same functionality as the ObjectCache. If I'm reading this right, would it make sense to include SharedObjects directly into the ObjectCache? Or would you use them separate from each other

The vsg::ObjectCache is essentially a cache of already loaded files, so if you load an image multiple times you'll get the same instance each time if you've assigned an ObjectCache to vsg::Options being used for reading.  The ObjectCachje can handle whole models or just single bits of data, so it can be high level or low level.

The vsg::SharedObjects doesn't have any relationship with loaded files, it's just for figuring out whether there is already an object in memory that has the same settings so could be used instead.

It may be we should merge the functionality, or rename one or both to make their relationship a bit clearer.   All this functionality is evolving to meet the challenge of how to provide the required functionality for a modern scene graph, there isn't a completed map to follow. My hope is the work on StateComposer will shake some of this down as it'll touch on several of these areas.  Happy to take suggestions.

Cheers,
Robert.

Robert Osfield

unread,
Mar 22, 2022, 5:52:36 PM3/22/22
to vsg-users : VulkanSceneGraph Developer Discussion Group
Opps Sam, don't know how your name ended up with an e in previous post.  Spell checker gone made?

Robert Osfield

unread,
Mar 24, 2022, 2:04:24 PM3/24/22
to vsg-users : VulkanSceneGraph Developer Discussion Group
The vsg::StateComposer work is continuing it's difficult inception.  My work hasn't yet coalesced onto a particular solution - the challenge is it needs to draw together various aspects to Vulkan state in a coherent and flexible way, so that it's easy to use. I've tried various avenues but still searching for "that" elusive solution.

To help climb out of the quagmire of having too many possible theoretical approaches that might work, I decided to just dive in with a very basic usage case and rebuild bit by bit with various experimental approaches to providing the desired functionality.  The place I've started has been to clone the vsgdraw example I originally wrote at the start of the VSG project when I step by step created the VulkanTutorial in a scene graph.  I've create a StateComposer branch of vsgExamples, and a new vsgstatecomposer example that is essentially vsgdraw.cpp but beginning it's generalization.


The new bit I worked on today was the creation of a vsg::ShaderSet class that encapsulates the task of how we specialize sets of shaders to have the features that we want to use - by setting up the #pragma(tic) shader composition defines that get injected into the shader source to help toggle on/off featuring like texturing etc.  For the code in vsgshadercomposition this simply means defining VSG_DIFFUSE_MAP that maps to the shader that is used, in this case assimp_phong.frag, note the #pragma import_defines (...) and the #ifdef's in the GLSL code:


The public interface to ShaderSet is pretty stright-forward so far:

   class VSG_DECLSPEC ShaderSet : public Inherit<Object, ShaderSet>
    {
    public:

        ShaderSet();
        ShaderSet(const ShaderStages& in_stages);

        /// base ShaderStages that other varients as based on.
        ShaderStages stages;

        /// varients of the rootShaderModule compiled for differen combinations of ShaderCompileSettings
        std::map<ref_ptr<ShaderCompileSettings>, ShaderStages, DerefenceLess> varients;

        /// get the ShaderStages varient that uses specified ShaderCompileSettings.
        ShaderStages getShaderStages(ref_ptr<ShaderCompileSettings> scs = {});

        void read(Input& input) override;
        void write(Output& output) const override;

    protected:
        virtual ~ShaderSet();
    };

And usage is:

    // set up shader hints
    auto shaderHints = vsg::ShaderCompileSettings::create();
    auto& defines = shaderHints->defines;
    defines.push_back("VSG_DIFFUSE_MAP");

    //  use the ShaderSet to configure the ShaderStages we need that match the shaderHints we want:
    auto shaderSet = vsg::ShaderSet::create(vsg::ShaderStages{vertexShader, fragmentShader});
    auto graphicsPipeline = vsg::GraphicsPipeline::create(pipelineLayout, shaderSet->getShaderStages(shaderHints), pipelineStates);

As things are set up right now we have to rely upon vsg::ShaderCompiler that uses glsLang at runtime to compile the GLSL source to SPIRV, but my plan is for a ShaderSet to be compilable then saved with all the required variant then saved to a .vsgt/.vsgb so it can be later reused without any need for recompilation.

vsg::ShaderSet is just a small part of the wider ShaderComposer functionality - it only solves the problem of how to mange ShaderStages/shader compilation in a persistent way.  It may be that vsg::ShaderSet stays as a stand alone class that can be used directly by applications or subsumed in other classes yet to be written.  Given I don't know yet what the final ShaderComosition functional with be "composed" of I have left the vsg::ShaderSet implementation local to the new vsgstatecomposer example.  If it looks solid I'll move it into the core VSG.

Tomorrow I'll look at how to manage the aspects of setting up the Graphics/Compute/RayTracingPipelines so how we may  pipelineLayout & pipelineStates to the respective BindGraphicsPipeline/GraphicsPipeline.  As part of this I'll need to figure out how to reflect the bindings used by the shaders to the bindings set up in the vertex arrays and descriptor set layouts/descriptor sets.

If I can get this all to work in a general purpose way with the vsgdraw derived vsgshadercoposer then I'll have bash at porting vsg::Builder across to whatever classes I created in the vsgstatecomposer experiment, and if that works out well then I'll work on converting the vsgXchange::Assimp loader to use the new scheme.

Phew, been working on this stuff for nearly two weeks and I still feel like I have only nibbled the edges.

Cheers,
Robert.

Robert Osfield

unread,
Mar 25, 2022, 7:59:23 AM3/25/22
to vsg-users : VulkanSceneGraph Developer Discussion Group
This morning I've fleshed out how we might specify the vertex attribute and uniform bindings that need to be used a set of shaders, finally it feels like are starting to coalesce around a workable approach.  I've added AttributeBinding and UnfiromBinding containers to the experimental vsg::ShaderSet class so the interface now looks like:

namespace vsg
{

    struct AttributeBinding
    {
        std::string name;
        std::string define;
        uint32_t location = 0;
        VkFormat foramt = VK_FORMAT_UNDEFINED;
        ref_ptr<Data> data;
    };

    struct UniformBinding
    {
        std::string name;
        std::string define;
        uint32_t set = 0;
        uint32_t binding = 0;
        VkDescriptorType descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
        uint32_t descriptorCount = 0;
        VkShaderStageFlags stageFlags = 0;
        ref_ptr<Data> data;
    };

    class /*VSG_DECLSPEC*/ ShaderSet : public Inherit<Object, ShaderSet>

    {
    public:

        ShaderSet();
        ShaderSet(const ShaderStages& in_stages);

        /// base ShaderStages that other varients as based on.
        ShaderStages stages;

        std::vector<AttributeBinding> attributeBindings;
        std::vector<UniformBinding> uniformBindings;


        /// varients of the rootShaderModule compiled for differen combinations of ShaderCompileSettings
        std::map<ref_ptr<ShaderCompileSettings>, ShaderStages, DerefenceLess> varients;

        void addAttributeBinding(std::string name, std::string define, uint32_t location, VkFormat format, ref_ptr<Data> data)
        {
            attributeBindings.push_back(AttributeBinding{name, define, location, format, data});
        }

        void addUniformBinding(std::string name, std::string define, uint32_t set, uint32_t binding, VkDescriptorType descriptorType, uint32_t descriptorCount, VkShaderStageFlags stageFlags, ref_ptr<Data> data)
        {
            uniformBindings.push_back(UniformBinding{name, define, set, binding, descriptorType, descriptorCount, stageFlags, data});

        }

        /// get the ShaderStages varient that uses specified ShaderCompileSettings.
        ShaderStages getShaderStages(ref_ptr<ShaderCompileSettings> scs = {});

        void read(Input& input) override;
        void write(Output& output) const override;

    protected:
        virtual ~ShaderSet();
    };
}

The setup in the application for the vsgExamples/data/shader/assimp.vert and assim_phong.frag looks like:

   auto shaderSet = vsg::ShaderSet::create(vsg::ShaderStages{vertexShader, fragmentShader});

    shaderSet->addAttributeBinding("vsg_Vertex", "", 0, VK_FORMAT_R32G32B32_SFLOAT, vsg::vec3Array::create(1));
    shaderSet->addAttributeBinding("vsg_Normal", "", 1, VK_FORMAT_R32G32B32_SFLOAT, vsg::vec3Array::create(1));
    shaderSet->addAttributeBinding("vsg_TexCoord0", "", 2, VK_FORMAT_R32G32_SFLOAT, vsg::vec2Array::create(1));
    shaderSet->addAttributeBinding("vsg_Color", "", 3, VK_FORMAT_R32G32B32A32_SFLOAT, vsg::vec4Array::create(1));
    shaderSet->addAttributeBinding("vsg_position", "VSG_INSTANCE_POSITIONS", 3, VK_FORMAT_R32G32B32_SFLOAT, vsg::vec3Array::create(1));

    shaderSet->addUniformBinding("displacementMap", "VSG_DISPLACEMENT_MAP", 0, 6, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_VERTEX_BIT, vsg::vec4Array2D::create(1,1));
    shaderSet->addUniformBinding("diffuseMap", "VSG_DIFFUSE_MAP", 0, 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, vsg::vec4Array2D::create(1,1));
    shaderSet->addUniformBinding("normalMap", "VSG_NORMAL_MAP", 0, 2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, vsg::vec3Array2D::create(1,1));
    shaderSet->addUniformBinding("aoMap", "VSG_LIGHTMAP_MAP", 0, 3, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, vsg::vec4Array2D::create(1,1));
    shaderSet->addUniformBinding("emissiveMap", "VSG_EMISSIVE_MAP", 0, 4, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, vsg::vec4Array2D::create(1,1));
    shaderSet->addUniformBinding("material", "", 0, 10, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, vsg::PhongMaterialValue::create());
    shaderSet->addUniformBinding("lightData", "VSG_VIEW_LIGHT_DATA", 1, 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, vsg::vec4Array::create(64));

What this all does is provide the guidance on how the C++ application/scene graph side needs to be wired up to what the shaders require - both the sets/locations/bindings used and the data types.  The vsg::vec3Array::create() etc. entries provide the data type and a default value to use in case the scene graph doesn't provide it's own.  There is also the optional #pragma(tic) shader composition define i.e. "VSG_DIFFUSE_MAP" that should be used in the vsg::ShaderCompilationSettings to enable a feature if we want it.

The ShaderSet::attributeBindings and ShaderSet::uniformBindings will read/written with the rest of the ShaderSet settings thus encapsulating all data required to specify how a particular set of shaders is intended to be used.  A single .vsgt will be able hold all these details together so you'll be able to do:

  auto shaderSet = vsg::read_cast<vsg::ShaderSet>("phong.vsgt");

This way we can easily set up, share describe everything about the shaders we want to use and how they need to be set up on the scene graph to make them work.  Not only functionally does this wrap up all the details together, I'm now comfortable with the concept and language behind it - I think it'll pass the how easy would it be to teach and use test.

--

My next step will be use all these settings to help create the scene graph elements required, this will really shake down whether this approach really is going to be practical.

Robert Osfield

unread,
Mar 25, 2022, 9:58:14 AM3/25/22
to vsg-...@googlegroups.com
On Fri, 25 Mar 2022 at 11:59, Robert Osfield <robert....@gmail.com> wrote:
My next step will be use all these settings to help create the scene graph elements required, this will really shake down whether this approach really is going to be practical.

I have added the following two methods to ShaderSet:
 
        const AttributeBinding& getAttributeBinding(const std::string& name) const;
        const UniformBinding& getUniformBinding(const std::string& name) const;

And to explicit operator bool() method to AttributeBinding and UniformBinding, which enables the following:


    // read texture image
    if (!textureFile.empty())
    {
        auto textureData = vsg::read_cast<vsg::Data>(textureFile, options);
        if (!textureData)
        {
            std::cout << "Could not read texture file : " << textureFile << std::endl;
            return 1;
        }

        // enable texturing
        if (auto& textureBinding = shaderSet->getUniformBinding("diffuseMap"))
        {
            descriptorBindings.push_back(VkDescriptorSetLayoutBinding{textureBinding.binding, textureBinding.descriptorType, textureBinding.descriptorCount, textureBinding.stageFlags, nullptr});
            if (!textureBinding.define.empty()) defines.push_back(textureBinding.define);

            // create texture image and associated DescriptorSets and binding
            auto texture = vsg::DescriptorImage::create(vsg::Sampler::create(), textureData, textureBinding.binding, 0, textureBinding.descriptorType);
            descriptors.push_back(texture);
        }
    }

    // set up pass of material
    if (auto& materialBinding = shaderSet->getUniformBinding("material"))
    {
        descriptorBindings.push_back(VkDescriptorSetLayoutBinding{materialBinding.binding, materialBinding.descriptorType, materialBinding.descriptorCount, materialBinding.stageFlags, nullptr});
        if (!materialBinding.define.empty()) defines.push_back(materialBinding.define);

#if 0
        // use the default maerialBinding.data
        auto material = vsg::DescriptorBuffer::create(materialBinding.data, materialBinding.binding);
        descriptors.push_back(material);
#else
        // create texture image and associated DescriptorSets and binding
        auto mat = vsg::PhongMaterialValue::create();
        mat->value().diffuse.set(1.0f, 1.0f, 0.0f, 1.0f);
        mat->value().specular.set(1.0f, 0.0f, 0.0f, 1.0f);

        auto material = vsg::DescriptorBuffer::create(mat, materialBinding.binding);
        descriptors.push_back(material);
#endif
    }

The changes checked in vsgExamples illustrate how the code has changed with from the hardwired original code to the new one based on ShaderSet:


There are further opportunities for streamlining the usage - such as generating the VkDescriptorSetLayoutBinding directly from the UniformBinding, I won't get too far ahead on this though as I want to see how all elements of functionality sit together.
 

Robert Osfield

unread,
Mar 25, 2022, 11:16:54 AM3/25/22
to vsg-users : VulkanSceneGraph Developer Discussion Group
The next has been to leverage the ShaderSet::AttributeBindings to help set up the vertex array related setup, this is a bit messy as we're now intwinning the vertex array data set up with the GrahicsPipeline's VertexInputState.  The two aspects always had to be tightly coupled but this could be hidden by two different blocks of code doing it, however, this is kinda fragile because it makes it easy to break if the array data and the GraphicsPipleine configuration end up out of sync.  The code now looks like:

    vsg::VertexInputState::Bindings vertexBindingDescriptions;
    vsg::VertexInputState::Attributes vertexAttributeDescriptions;
    uint32_t baseAttributeBinding = 0;
    uint32_t attributeBinding = baseAttributeBinding;
    vsg::DataList vertexArrays;
    if (auto& vertexBinding = shaderSet->getAttributeBinding("vsg_Vertex"))
    {
        vertexBindingDescriptions.push_back(VkVertexInputBindingDescription{attributeBinding, vertices->getLayout().stride, VK_VERTEX_INPUT_RATE_VERTEX});
        vertexAttributeDescriptions.push_back(VkVertexInputAttributeDescription{vertexBinding.location, attributeBinding, vertexBinding.format, 0});
        vertexArrays.push_back(vertices);
        ++attributeBinding;
    }

    if (auto& normalBinding = shaderSet->getAttributeBinding("vsg_Normal"))
    {
        vertexBindingDescriptions.push_back(VkVertexInputBindingDescription{attributeBinding, normals->getLayout().stride, VK_VERTEX_INPUT_RATE_VERTEX});
        vertexAttributeDescriptions.push_back(VkVertexInputAttributeDescription{normalBinding.location, attributeBinding, normalBinding.format, 0});
        vertexArrays.push_back(normals);
        ++attributeBinding;
    }

    if (auto& texCoordBinding = shaderSet->getAttributeBinding("vsg_TexCoord0"))
    {
        vertexBindingDescriptions.push_back(VkVertexInputBindingDescription{attributeBinding, texcoords->getLayout().stride, VK_VERTEX_INPUT_RATE_VERTEX});
        vertexAttributeDescriptions.push_back(VkVertexInputAttributeDescription{texCoordBinding.location, attributeBinding, texCoordBinding.format, 0});
        vertexArrays.push_back(texcoords);
        ++attributeBinding;
    }

    if (auto& colorBinding = shaderSet->getAttributeBinding("vsg_Color"))
    {
        vertexBindingDescriptions.push_back(VkVertexInputBindingDescription{attributeBinding, colors->getLayout().stride, VK_VERTEX_INPUT_RATE_VERTEX});
        vertexAttributeDescriptions.push_back(VkVertexInputAttributeDescription{colorBinding.location, attributeBinding, colorBinding.format, 0});
        vertexArrays.push_back(colors);
        ++attributeBinding;
    }

    // setup geometry
    auto drawCommands = vsg::Commands::create();
    drawCommands->addChild(vsg::BindVertexBuffers::create(baseAttributeBinding, vertexArrays));
    drawCommands->addChild(vsg::BindIndexBuffer::create(indices));
    drawCommands->addChild(vsg::DrawIndexed::create(12, 1, 0, 0, 0));

The key bit with this is that hardwiring of types and locations is now removed, the ShaderSet settings could change and as long as the required functionality is still available it'll all just wire up correctly.

The code is still long-winded and repetitive, and the same applies the Descriptor setup.  The fact it is now more repetitive is a good thing as it opens the door to use providing high level methods/classes that can do the leg work for us so our application code simplifies. 

We are currently in zone of "It's gets worse before it gets better" as the original vsgdraw.cpp example takes just 193 lines of code, while my new vsgstatecomposer.cpp based very takes 294 lines of code.  Where I want to get to is the new ShaderSet approach taking less code, so I'm now at a point of asking how do a provide high level functionality to hide the complexity and legwork so the application code is simpler, more expressive and takes less code that the vsgdraw example.

Cheers,
Robert.

Robert Osfield

unread,
Mar 25, 2022, 2:25:49 PM3/25/22
to vsg-...@googlegroups.com
I have added push constant range support to ShaderSet and have begun work on serialization support.  One neat aspect to the serialization is that both ascii and binary files are possible just like with other VSG objects, this allows us to pack all the shaders together, source and SPIRV data into a single reusable .vsgt/vsgb file.  Longer term this also opens the door to have a VSG application that allows you to edit and customize shaders all together, including setting up all the bindings and datatypes then serializing it out for use at runtime.

Robert Osfield

unread,
Mar 28, 2022, 9:00:50 AM3/28/22
to vsg-...@googlegroups.com
Hi All,

I have now begun experimenting with a vsg::GraphicsPipelineConfig helper class that provides a high level interface to vsg::ShaderSet and some extra members to help configure the GraphicsPipeline.  Changes are kept with the vsgstatecomposer example, with the new class added as a GraphicsPipelineConfig.h and GraphicsPipelineConfig.cpp

   class GraphicsPipelineConfig : public vsg::Inherit<Object, GraphicsPipelineConfig>
    {
    public:

            GraphicsPipelineConfig(ref_ptr<ShaderSet> in_shaderSet = {});

            // inputs to setup of GraphicsPipeline
            ref_ptr<VertexInputState> vertexInputState;
            ref_ptr<InputAssemblyState> inputAssemblyState;
            ref_ptr<RasterizationState> rasterizationState;
            ref_ptr<ColorBlendState> colorBlendState;
            ref_ptr<MultisampleState> multisampleState;
            ref_ptr<DepthStencilState> depthStencilState;
            uint32_t subpass = 0;
            uint32_t attributeBindingIndex = 0;

            ref_ptr<ShaderCompileSettings> shaderHints;
            vsg::DescriptorSetLayoutBindings descriptorBindings;
            ref_ptr<ShaderSet> shaderSet;

            const AttributeBinding& getAttributeBinding(const std::string& name, ref_ptr<Data> array, VkVertexInputRate vertexInputRate);
            const UniformBinding& getUniformBinding(const std::string& name);

            // setup GraphicsPipeline
            ref_ptr<DescriptorSetLayout> descriptorSetLayout;
            ref_ptr<PipelineLayout> layout;
            ref_ptr<GraphicsPipeline> graphicsPipeline;
            ref_ptr<BindGraphicsPipeline> bindGraphicsPipeline;

            void init();

            int compare(const Object& rhs) const;
    };


The GraphicsPipelineConfig streamlines the set up of the GraphcisPipeline by automatically setting up the vsg::VertexInputState based on the request configuration and the mappings provided by the ShaderSet.  To set up vertex arrays you currently used lke:

    uint32_t baseAttributeBinding = graphicsPipelineConfig->attributeBindingIndex;
    vsg::DataList vertexArrays;
    if (/*auto& vertexBinding = */graphicsPipelineConfig->getAttributeBinding("vsg_Vertex", vertices, VK_VERTEX_INPUT_RATE_VERTEX))
    {
        vertexArrays.push_back(vertices);
    }

    if (/*auto& normalBinding = */graphicsPipelineConfig->getAttributeBinding("vsg_Normal", normals, VK_VERTEX_INPUT_RATE_VERTEX))
    {
        vertexArrays.push_back(normals);
    }

    if (/*auto& texCoordBinding = */graphicsPipelineConfig->getAttributeBinding("vsg_TexCoord0", texcoords, VK_VERTEX_INPUT_RATE_VERTEX))
    {
        vertexArrays.push_back(texcoords);
    }

    if (/*auto& colorBinding = */graphicsPipelineConfig->getAttributeBinding("vsg_Color", colors, VK_VERTEX_INPUT_RATE_VERTEX))
    {
        vertexArrays.push_back(colors);
    }

I may be able to streamline this further, in essence I'm iterating over the various bit of setup code looking for ways to simplify usable whilst still giving applicationslloaders control when they need it.

The code for setting up the textures/uniforms is similar - calling the GraphicsPipelineConfig to help provide mappings:

   // read texture image
    if (!textureFile.empty())
    {
        auto textureData = vsg::read_cast<vsg::Data>(textureFile, options);
        if (!textureData)
        {
            std::cout << "Could not read texture file : " << textureFile << std::endl;
            return 1;
        }

        // enable texturing
        if (auto& textureBinding = graphicsPipelineConfig->getUniformBinding("diffuseMap"))
        {

            // create texture image and associated DescriptorSets and binding
            auto texture = vsg::DescriptorImage::create(vsg::Sampler::create(), textureData, textureBinding.binding, 0, textureBinding.descriptorType);
            descriptors.push_back(texture);
        }
    }

    // set up pass of material
    if (auto& materialBinding = graphicsPipelineConfig->getUniformBinding("material"))
    {

#if 0
        // use the default maerialBinding.data
        auto material = vsg::DescriptorBuffer::create(materialBinding.data, materialBinding.binding);
        descriptors.push_back(material);
#else
        // create texture image and associated DescriptorSets and binding
        auto mat = vsg::PhongMaterialValue::create();
        mat->value().diffuse.set(1.0f, 1.0f, 0.0f, 1.0f);
        mat->value().specular.set(1.0f, 0.0f, 0.0f, 1.0f);

        auto material = vsg::DescriptorBuffer::create(mat, materialBinding.binding);
        descriptors.push_back(material);
#endif
    }

Finally we need to create the DescirptorSetLayout, PipelineLayout and GraphicsPipeline based on arrays and uniforms setup, to do this we call GraphicsPipelineConfig::init() and then use the created objects:

    // set up the DescriptorSetLayout, PipelineLayout and GraphicsPipeline
    graphicsPipelineConfig->init();

    auto descriptorSet = vsg::DescriptorSet::create(graphicsPipelineConfig->descriptorSetLayout, descriptors);
    auto bindDescriptorSet = vsg::BindDescriptorSet::create(VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipelineConfig->layout, 0, descriptorSet);

    // create StateGroup as the root of the scene/command graph to hold the GraphicsProgram, and binding of Descriptors to decorate the whole graph
    auto scenegraph = vsg::StateGroup::create();
    scenegraph->add(graphicsPipelineConfig->bindGraphicsPipeline);
    scenegraph->add(bindDescriptorSet);

The introduction of vsg::GraphicsPipelineConfig simplifies the vsgstatecomposer.cpp so ~50 lines of code are saved, so it's now 288 lines of code.  However, vsgdraw.cpp that I used as the initial base is just 183 lines of code.  The shaders used are far more sophisticated and the code is more general purpose but still has some way to go.

I also would like to make it easier to share instances of GraphcsPipeline etc. which will bring me back to utilization of the vsg::SharedObjects class within GraphicsPipelineConfig.  My hope is that I can provide a set of interfaces that work together to simplify the task of setting up efficient scene graphs.  At present it kinda looks like I won't actually have a StateComposer class, instead this overall functionality will be wrapped up in vsg::SharedObjects, vsg::ShaderSet and various *PipelineConfig classes.

I still need to work out all the interface and implementation details to make it work well, but at least I'm now on a path that looks like it should deliver on my original goals for this block of work.

Cheers,
Robert.

Robert Osfield

unread,
Mar 28, 2022, 11:36:50 AM3/28/22
to vsg-...@googlegroups.com
I have refined the GraphicsPipelineConfig class a bit further introducing three convenience methods for setting up the vertex arrays and uniform/texture descriptors:

            bool assignArray(DataList& arrays, const std::string& name, VkVertexInputRate vertexInputRate, ref_ptr<Data> array);
            bool assignTexture(Descriptors& descriptors, const std::string& name, ref_ptr<Data> textureData = {}, ref_ptr<Sampler> sampler = {});
            bool assignUniform(Descriptors& descriptors, const std::string& name, ref_ptr<Data> data = {});

Usage for arrays looks like:

    vsg::DataList vertexArrays;
    graphicsPipelineConfig->assignArray(vertexArrays, "vsg_Vertex", VK_VERTEX_INPUT_RATE_VERTEX, vertices);
    graphicsPipelineConfig->assignArray(vertexArrays, "vsg_Normal", VK_VERTEX_INPUT_RATE_VERTEX, normals);
    graphicsPipelineConfig->assignArray(vertexArrays, "vsg_TexCoord0", VK_VERTEX_INPUT_RATE_VERTEX, texcoords);
    graphicsPipelineConfig->assignArray(vertexArrays, "vsg_Color", VK_VERTEX_INPUT_RATE_VERTEX, colors);

Textures/uniforms:

    vsg::Descriptors descriptors;


    // read texture image
    if (!textureFile.empty())
    {
        auto textureData = vsg::read_cast<vsg::Data>(textureFile, options);
        if (!textureData)
        {
            std::cout << "Could not read texture file : " << textureFile << std::endl;
            return 1;
        }

        // enable texturing
        graphicsPipelineConfig->assignTexture(descriptors, "diffuseMap", textureData);

    }

    // set up pass of material
    auto mat = vsg::PhongMaterialValue::create();
    mat->value().diffuse.set(1.0f, 1.0f, 0.0f, 1.0f);
    mat->value().specular.set(1.0f, 0.0f, 0.0f, 1.0f);

    graphicsPipelineConfig->assignUniform(descriptors, "material", mat);

Which is all starting to feel pretty expressive and straight forward, with the combination of ShaderSet and GraphicsPipelineConfig doing the heavy lifting of coordinating the setup of graphics pipeline, arrays and descriptors.

Note, I've introduced default values for the uniform setup so you can automatically use the default values provided by vsg::ShaderSet i.e.

    graphicsPipelineConfig->assignTexture(descriptors, "diffuseTexture"); // assign a default white texture
    graphicsPipelineConfig->assignUniform(descriptors, "material"); // assign a default white material

For the phong ShaderSet the shader doesn't need a diffuseTexture as you can toggle this on/off via the associated VSG_DIFFUSE_MAP define so it's not required to pass a default.  However, the assim_phone.frag shader requires the PhongMaterial to be provided so this line will be required.

I am considering adding a method in GraphicsPipelineConfig to add in required defaults is non have assigned for them, I'll ponder on this before adding it, and will need to see how the code looks in action.

Another possible feature would be to check the data types being assigned via GraphicsPipelineConfig::assign*() to make sure they match the type specified in the ShaderSet so that if the shaders says it expects a vec3 it doesn't get  a vec2Array.  This is another topic I'll need to ponder - should it throw an exception or just emit a console warning etc.

When I am a bit further along I also want to see about generalizing the pipeline configuration to include support for RayTracingPipeline and ComputePipeline.  For instance if you are loading a model from vsgXchange and you want to use the scene exclusively for ray tracing then having GraphicsPipeline built for you would be inappropriate, here you'd want to tell the vsgXchange loader that you want to use a particular type of pipeline creation. 

Perhaps the ShaderSet could provide what types of pipelines to generate for it as the shaders are all written specifically for compute, graphics or ray tracing pipelines.  Or perhaps have vsg::Options have the ability to provide the preferences for ShaderSet to use, and a GraphicsPipelienConfig/RayTrayingConfig/ComputeConfig to use for each shader type.

Next up my focus is now going to move to reusing the GraphicsPipeline/PipelineLayout/DescriptorSetLayout down the Bind/DescriptorSet.  The port of vsgdraw to use the new functionality found in vsgstatecomposer won't stress this part of the functionality that I'm developing as there is only one geometry, one textures etc.  Rather than keep working on this example it might be time for me to move the ShaderSet and GraphicsPipelineConfig classes out from vsgExamples into a branch of the VSG and see about porting vsg::Builder to use this new functionality.

Consider all this as ideas at this point, I'm not following a map of how to do this, I'm exploring new territory.  If there is a region that I stumble into of relevance to your work see how this work might help/hinder your work and let me know issues you can see, or opportunities to solve things in better ways.

Cheers,
Robert.

Robert Osfield

unread,
Mar 28, 2022, 12:50:37 PM3/28/22
to vsg-...@googlegroups.com
I have now moved the vsg::ShaderSet and vsg::GraphicsPipelineConfig classes from vsgstatecomposer example into the VulkanSceneGraph StateComposer branch:


These classes aren't complete yet, but not far enough along to feel comfortable using them as base for refactoring vsg::Builder to use these new classes, and then use that as a testbed for the sharing of state within vsg::Builder.

I am also considering putting the vsg::createPhongShaderSet(),  createPbrShaderSet() and createFlatShadedShaderSet() functions and associated shaders built into core VSG library, these would build off the present assimp.vert + assimp_pbr.frag etc. shaders used by vsg::Builder and vsgXchange::Assimp loader. 

These built in ShaderSet would provide basic functionality that the loaders can use by default, but also pave the way from ShaderSet to be passed in via vsg::Options so that loaders can use a user defined set of shaders and associated mappings.

This work has taken a number of twists and turns over the past few weeks but now feels like things are solidifying nicely on a coherent and flexible approach, or at least that's how it feels for me, I hope it'll make sense for others.

Cheers,
Robert.



role...@gmail.com

unread,
Mar 29, 2022, 3:21:24 AM3/29/22
to vsg-users : VulkanSceneGraph Developer Discussion Group
Hi Robert,

I'm concerned about using defines to control shader options. Maybe I have it wrong.

Consider the case where your application reads a control file to open various datasets for display. I then create a thread for each dataset to build its own 'branch' of the scene graph and then add them all to a root group node in the frame loop when they are ready. They will all need different shader setups and defines. Will setting a define in one thread contaminate another thread? My immediate thought is that I would prefer to set the shader components in vsg::Options.

Regards,
Roland

role...@gmail.com

unread,
Mar 29, 2022, 3:27:48 AM3/29/22
to vsg-users : VulkanSceneGraph Developer Discussion Group
Just thinking, those defines are all done at compile time, so shouldn't be a problem as long as you set and unset them all appropriately. It still seams fraught with peril to me and my vote would be for Options. I've already gone down the Options path a little way myself for my own shader factory, but would prefer to integrate with your's once you're finished.

Roland

Robert Osfield

unread,
Mar 29, 2022, 5:06:25 AM3/29/22
to vsg-...@googlegroups.com
Hi Roland,

On Tue, 29 Mar 2022 at 08:21, role...@gmail.com <role...@gmail.com> wrote:
Consider the case where your application reads a control file to open various datasets for display. I then create a thread for each dataset to build its own 'branch' of the scene graph and then add them all to a root group node in the frame loop when they are ready. They will all need different shader setups and defines. Will setting a define in one thread contaminate another thread? My immediate thought is that I would prefer to set the shader components in vsg::Options.

The concept of the ShaderSet is that you have a master set of shaders (vertex + fragment etc.) and then variants of these with different combinations of defines that specialize them for the needs of each combination of defines.  It's possible to precompile combinations of variants so that at runtime there isn't an overhead.

I haven't yet made ShaderSet's::getShaderStages() method thread safe, but I'll before this work is complete so you'll be able to share a single ShaderSet between loader threads and each one will be able to access the variants safely.  SharedObjects is already thread safe.

As I've briefly mentioned in this thread my plan is also to assign the preferred ShaderSet's into vsg::Option, either via named meta data or a field.  The named meta data route would be:

    options->setObject("pbr", pbrShaderSet);
    options->setObject("phong", phongShaderSet);
    options->setObject("flatShaded", flatShadedShaderSet);

This way loaders can share their ShaderSet or have their preferred instance own.

The main focus for me today is further developing the GraphicsPipelineConfig so it can share descriptors/graphics pipeline where possible, again I'll be doing with multi-threading in mind though the first cut will assume single threaded. 

Making the sharing of state straight forward is important for load, compile and runtime performance, sharing across the different multi-threaded loads is part of this - there won't be any special case that it'll work, it's intended to be a general purpose solution that is used by most loaders/scene graph construction routines.

This work should also make it much easier to handle things like points, lines and triangle meshes in the loaders as they are currently hardwired around triangle meshes - Vulkan make such things rather more complicated to manage than OpenGL, so it's been a case of iteratively building out the codebase without biting off too much complexity at one time.  If I can achieve my goals for this work then much of this complexity will be moved out of loaders.

I hope this makes sense.  This work is evolving and I've tried to write down my thoughts and intentions but until it's near complete it will likely remain a bit more difficult to follow then once we have the classes finalized and implementation that use it are done.  This week I expect I'll make good progress towards this goal so the period of uncertainty should be over soon.

Cheers,
Robert.

werner.m...@modenbach-ac.de

unread,
Mar 29, 2022, 5:10:24 AM3/29/22
to vsg-...@googlegroups.com

Hi Robert,
as you know I'm still with OSG and following your VSG development loosely.
Please don't forget the efforts of mesh shaders and textures working on buffer arrays. In my opinion based on my experiences with mesh shades in OSG this is the future of graphics development. In my case 10 times performance on graphics cards and the endless flexibility IS the future.
Thanks
Werner

Robert Osfield

unread,
Mar 29, 2022, 5:25:30 AM3/29/22
to vsg-...@googlegroups.com
Hi Werner,

On Tue, 29 Mar 2022 at 10:10, Werner.M...@texion.eu <werner.m...@modenbach-ac.de> wrote:

as you know I'm still with OSG and following your VSG development loosely.
Please don't forget the efforts of mesh shaders and textures working on buffer arrays. In my opinion based on my experiences with mesh shades in OSG this is the future of graphics development. In my case 10 times performance on graphics cards and the endless flexibility IS the future

The StateComposer (ShaderSet + SharedObjects) functionality I'm writing should be mostly agnostic of shader type and should work with ray tracing and compute shaders.  Mesh shaders will fall under the graphics pipeline family of shaders so should work with the GraphicsPipelineConfig convenience class that is tailored for graphics. 

 A good test would be to rewrite the vsgmeshshaders example to use the new classes to build the scene graph.  I'd need to pop back in my Nvidia card though as not all mesh shaders features are supported on the AMD5700G that I'm presently using.   A quick run of vsgmeshshaders:

$ vsgmeshshader  
nv_features.meshShader = 1
nv_features.taskShader = 0
Mesh shaders not supported.

Which suggests partial support, I haven't looked into what can be achieved with the support that is available - I'm rather stretched a bit thin just trying to complete the shader composer functionality at this time so will keep focused on the core task.  Happy for others to dive in and see what is achievable.

Cheers,
Robert


Robert Osfield

unread,
Mar 29, 2022, 6:18:09 AM3/29/22
to vsg-...@googlegroups.com
On Tue, 29 Mar 2022 at 10:06, Robert Osfield <robert....@gmail.com> wrote:
I haven't yet made ShaderSet's::getShaderStages() method thread safe, but I'll before this work is complete so you'll be able to share a single ShaderSet between loader threads and each one will be able to access the variants safely.  SharedObjects is already thread safe.

I have now checked into the StateComposer branch a thread safe ShaderSet::getShaderStages(..) method.

Robert Osfield

unread,
Mar 29, 2022, 11:08:48 AM3/29/22
to vsg-...@googlegroups.com
I have been working on sharing descriptors, arrays, BindDescriptorSet, BindGraphicsPipeline, BindVertexBuffers, BindIndexBuffers and Commands and have adapted the vsgstatecomposer example to create a grid of textured quad pair subgraphs (the original vsgdraw repeared).  With a 1000 repeated quad pairs subgraphs without sharing I'm getting 533fps, with vsg::SharedObjects usage I see 1930fps. 

The binary file is about 1/4 of the size for the shared version.

This is very early results but even at this stage it illustrates nicely when sharing is so important, and worthwhile jumping through some extra hoops to make it happen.

I still have plenty of work to do with the new classes, but these tests do further affirm that I'm on the right track.   I will do a review of all the changes I've made today and if they look solid will check them in.

Next up I'll port vsg::Builder to use the new classes.

Robert Osfield

unread,
Mar 30, 2022, 1:42:24 PM3/30/22
to vsg-...@googlegroups.com
Hi All,

> Next up I'll port vsg::Builder to use the new classes.

I have now ported vsg::Builder across to use the new vsg::SharedObjects, vsg::ShaderSet and vsg::GraphicsPipelineConfig classes.  I still have the old code sitting alongside it for testing purposes so the code isn't yet ready to merge.

Tomorrow I'll clean up my vsg::Builder changes and get these checked in.  It'll be interesting to compare the two approaches, something that will be easier once we have a clean old Builder.cpp implementation and a new clean Builder.cpp implementation.

Once the Builder changes are settled I'll move on to looking at vsgXchange::Assimp, which will be where the big challenges and opportunities begin to be settled.  It needn't be a huge mount of work. might even get something working this week, but... there could be dragons awaiting...

Robert Osfield

unread,
Mar 31, 2022, 3:44:07 AM3/31/22
to vsg-...@googlegroups.com
On Wed, 30 Mar 2022 at 18:42, Robert Osfield <robert....@gmail.com> wrote:
Tomorrow I'll clean up my vsg::Builder changes and get these checked in.  It'll be interesting to compare the two approaches, something that will be easier once we have a clean old Builder.cpp implementation and a new clean Builder.cpp implementation.

I've now cleaned up vsg::Builder and checked it into the StateComposer branch.  The Builder.cpp changes add 95 lines, but remove 155.  The Builder.h is smaller as well.  The code will shrink further once I've created the dedicated vsg::createPhongShaderSet() function for setting up the ShaderSet.

It's a huge relief to see the code footprint going down, I was hopeful that it would be the case but until you've changed some real code you don't know.  We are probably looking at about half the code required to provide the same functionality, with the added flexibility for customizing with your own shader sets.

Today I'll move on to tackling vsgXchange::Assimp.

Robert Osfield

unread,
Mar 31, 2022, 6:44:32 AM3/31/22
to vsg-...@googlegroups.com
I have now checked three convenience methods for creating standard vsg::ShaderSet, that can be found in include/vsg/utils/ShaderSet.h:

    /// create a ShaderSet for unlit, flat shaded rendering
    extern VSG_DECLSPEC ref_ptr<ShaderSet> createFlatShadedShaderSet(ref_ptr<Options> options = {});

    /// create a ShaderSet for phong shaded rendering
    extern VSG_DECLSPEC ref_ptr<ShaderSet> createPhongShaderSet(ref_ptr<Options> options = {});

    /// create a ShaderSet for Physics Based Rendering rendering
    extern VSG_DECLSPEC ref_ptr<ShaderSet> createPhysicsBasedRenderingShaderSet(ref_ptr<Options> options = {});

These currently use the vsgExamples/data/shaders/assimp.vert, assimp_phong.frag etc. shaders with built in fallbacks for each case.  Once the shaders settle down I'll rename them to something more appropriate rather than having the assimp prefix - this prefix is just inherited from the original shaders they were based on when vsgXchage::Assimp was first written.

With these changes vsg::Builder is now 101 lines small, with about half the code required to do the state setup.  There are a few small areas that can be cleaned up further, but even now it's in a state I'm pretty happy with.

Next up I'll port vsgXchange::Assimp across to use vsg::ShaderSet/GraphicsPipelineConfig/SharedObjects.  This is the final big test of how well this new functionality is hanging together.

Robert Osfield

unread,
Mar 31, 2022, 1:02:10 PM3/31/22
to vsg-...@googlegroups.com
On Thu, 31 Mar 2022 at 11:44, Robert Osfield <robert....@gmail.com> wrote:
Next up I'll port vsgXchange::Assimp across to use vsg::ShaderSet/GraphicsPipelineConfig/SharedObjects.  This is the final big test of how well this new functionality is hanging together.

I've done a small refactor of the vsgXchange::Assimp to prepare the way for the using the new StateComposition functionality, this is checked into vsgXchange master.  I've also created a StateComposer branch of vsgXchange based on this.

I have now got into a code review of the old code and am figuring out how best to map across to the new functionality.  The old code is a bit crude in places and hard-wires how bits of the state and geometries are handled to make it simpler to implement, but in doing so will not do the right thing when handling point and lines geometries - it'll treat them all as triangle meshes! 

I need to add support for points and lines, thankfully the design of GraphicsPipelineConfig/ShaderSet should make this easier to handle. Part of me is inclined to just rip out a big chunk of the old code and just build it up from scratch as this would make it easier to add support for features not currently supported. but another part of me knows that debugging code that diverges significantly from original mostly working code is harder.

It's the end of the working day here so won't try to decide what should be the next step, instead I'll come at it fresh tomorrow having a night for background processes to do their thing.  Hopefully I'll wake up with clarity.

I'm optimistic that I should have something working by the end of tomorrow and check in so others can test it out.

Cheers,
Robert.

Robert Osfield

unread,
Apr 1, 2022, 12:13:15 PM4/1/22
to vsg-...@googlegroups.com
On Thu, 31 Mar 2022 at 18:01, Robert Osfield <robert....@gmail.com> wrote:
I'm optimistic that I should have something working by the end of tomorrow and check in so others can test it out.

Took my this morning to settle upon an approach to take, but it's an approach where I'm writing most of the key code from scratch rather than just adapting the original code.  For now I'm leaving the old code in place and can switch between the new code and the original code by using the --original_converter command line parameter.

Writing the code more from the ground up is enabling me to handle a wide range of cases and to enable optimizations that aren't possible with the original code.  The downside is it'll take a bit longer to write and debug, so my optimism about having something working today was unfounded.  Realistically I'm another day's work away from being able to load and render models that the original code can handle.

Might do some tinkering over the weekend as I'm keen to see some results.

 

Sam

unread,
Apr 1, 2022, 2:06:03 PM4/1/22
to vsg-users : VulkanSceneGraph Developer Discussion Group
Hey Robert,

Just taking a look at the changes so far and man this is looking much easier. There is a macro in Builder that is throwing a warning for me for VSG_COMPARE_PARAMETERS. Can that be removed in favor of the work you did in compare.h for sharing? Looking forward to diving into this.

Thanks, Sam

Sam

unread,
Apr 1, 2022, 2:34:32 PM4/1/22
to vsg-users : VulkanSceneGraph Developer Discussion Group
Hi All,

I had a chance to port some easy code over to the StateComposer to give it a try. Currently I have setup the GraphicsPipelineConfig as follows:

auto shaderSet = vsg::createFlatShadedShaderSet();
auto graphicsPipelineConfig = vsg::GraphicsPipelineConfig::create(shaderSet);

graphicsPipelineConfig->enableArray("vsg_Vertex", VK_VERTEX_INPUT_RATE_VERTEX, 12);
graphicsPipelineConfig->enableArray("vsg_Normal", VK_VERTEX_INPUT_RATE_VERTEX, 12);
graphicsPipelineConfig->enableArray("vsg_TexCoord0", VK_VERTEX_INPUT_RATE_VERTEX, 8);

graphicsPipelineConfig->init();

// accessed by the ReaderWriters to setup pipelines and layouts
options->setObject("SiegeNodeGraphicsPipeline", graphicsPipelineConfig->bindGraphicsPipeline);
options->setObject("SiegeNodeLayout", graphicsPipelineConfig->bindGraphicsPipeline->pipeline->layout);

This crashed due to the texture missing from the dataset. With the current implementation you have to assignTexture right on the PipelineConfig. This isn't that flexible as in my loaders I read the layout and assign textures right in the ReaderWriter. This allows me to have the GraphicsPipeline at the top of my graph and then not repeat the pipeline over and over. With the above approach Ill have to a config and do an init() for every unique node that gets loaded.

If I'm offbase - help would be appreciated.

Thanks, Sam

Robert Osfield

unread,
Apr 1, 2022, 3:17:52 PM4/1/22
to vsg-...@googlegroups.com
HI Sam,

I'm away from my dev system so just a quick reply from the top of my head.

> Just taking a look at the changes so far and man this is looking much easier. There is a macro in Builder that is throwing a warning
> for me for VSG_COMPARE_PARAMETERS. Can that be removed in favor of the work you did in compare.h for sharing?

We can probably remove the compare code completely now as I don't think it'll be used anymore - instead the state sharing provided by vsg::SharedObjects will do this for us.

On Fri, 1 Apr 2022 at 19:34, Sam <brk...@gmail.com> wrote:
I had a chance to port some easy code over to the StateComposer to give it a try. Currently I have setup the GraphicsPipelineConfig as follows:

auto shaderSet = vsg::createFlatShadedShaderSet();
auto graphicsPipelineConfig = vsg::GraphicsPipelineConfig::create(shaderSet);

graphicsPipelineConfig->enableArray("vsg_Vertex", VK_VERTEX_INPUT_RATE_VERTEX, 12);
graphicsPipelineConfig->enableArray("vsg_Normal", VK_VERTEX_INPUT_RATE_VERTEX, 12);
graphicsPipelineConfig->enableArray("vsg_TexCoord0", VK_VERTEX_INPUT_RATE_VERTEX, 8);

graphicsPipelineConfig->init();

// accessed by the ReaderWriters to setup pipelines and layouts
options->setObject("SiegeNodeGraphicsPipeline", graphicsPipelineConfig->bindGraphicsPipeline);
options->setObject("SiegeNodeLayout", graphicsPipelineConfig->bindGraphicsPipeline->pipeline->layout);

This crashed due to the texture missing from the dataset. With the current implementation you have to assignTexture right on the PipelineConfig. This isn't that flexible as in my loaders I read the layout and assign textures right in the ReaderWriter. This allows me to have the GraphicsPipeline at the top of my graph and then not repeat the pipeline over and over. With the above approach Ill have to a config and do an init() for every unique node that gets loaded.

If I'm offbase - help would be appreciated.

My intention has been for vsg::SharedObjects container to be the way of sharing data, including GraphicsPipelinConfig objects between multiple loads.  SharedObjects has a init() function support that can be used to make sure that an instance is only initialized once, so any duplicates (as far as compare(..) is concerned) are matched but original match is returned, without invoking the init(). 

This init trick allows you to create GraphicsPipelinConfig on the fly and then share it to get any instance that's already been created, without needing to reinitialize.  For your loaded code this means you'll be able to just stick all the scene graph setup code in the loader and have it configure all the arrays and textures you need for that mesh and then get the GraphicsPipeline etc. that suits it, with reuse provided by SharedObjects without you needing to worry about explictly sharing across loads like you are doing right now.

This approach might seem heavy weight, but the heavy lifting is mostly done by the GraphicsPipelineConfig::init() method, and this is something that will only be done once if the configuration is the same all the time.

For the vsgXchange::Assimp work will shake down this approach, so there may be things I change in SharedObjects/ShaderSet/GraphicsPipelineConfig that will help your usage case as well.  It should also provide a bit of an example of how it's done.

Cheers,
Robert.

Robert Osfield

unread,
Apr 2, 2022, 8:37:02 AM4/2/22
to vsg-...@googlegroups.com
On Fri, 1 Apr 2022 at 20:17, Robert Osfield <robert....@gmail.com> wrote:
> Just taking a look at the changes so far and man this is looking much easier. There is a macro in Builder that is throwing a warning
> for me for VSG_COMPARE_PARAMETERS. Can that be removed in favor of the work you did in compare.h for sharing?

We can probably remove the compare code completely now as I don't think it'll be used anymore - instead the state sharing provided by vsg::SharedObjects will do this for us.

I've changed the comparison code Builder.h, removing the old marco and replacing with vsg::compare_*() versions where comparisons were still needed.  The changes are now checked in StateComposer branch:

Robert Osfield

unread,
Apr 4, 2022, 12:23:21 PM4/4/22
to vsg-users : VulkanSceneGraph Developer Discussion Group
I have the new vsgXchange::Assimp loader creating reasonable looking scene graphs using the new vsg::ShaderSet/GraphicsPipelineConfig functionality, it's still just preliminary with more work to complete, but it's probably 60% done now.  Changes are checked into the StateComposer branch of vsgXchange:


The new code does a better job of mapping vertex arrays across than the original code, using bind per instance rather bind per vertex for cases where no array is provide by the model/Assimp.  This differs from the original loader code that would just duplicate the same default value for 1:! for num of vertices.  On the FlightHelmet.gltf test model this results in a 33% smaller scene graph and 9% improvement in performance.  I haven't implemented use of SharedObjects yet which should help further reduce model size and improve performance, especially once we load multiple models as they'll be able shared data across each model.

I'm not yet happy with the internals of how GraphicsPipelineConfig is used.  The coupling of descriptor setup and geometry setup and how this impacts the graphics pipeline that needs to get created needs a cleaner means for managing these usage case.  Right now it can be made to work but it's more awkward that it needs to be.  Trying to make something as flexible as ShaderSet be usable for setting up pipelines and other state in easy to use and coherent way isn't easy.

I'm pretty happy with ShaderSet and SharedObjects so at least that part should need major refactoring.  API might need to be a refined a bit more but they are mostly working pretty nicely.

Tomorrow I'll integrate SharedObjects and try to tease on a way to make GraphicsPipelineConfig functionality more user friendly.  Eventually I'd like to generalize it as well, so RayTacingConfig and custom state/geometry building will be possible.  Perhaps a PipelineCofig/PipelineBuilder class will be needed.  Though might have to leave this generalization till after this phase of work. perhaps even after 1.0.

Cheers,
Robert.

Robert Osfield

unread,
Apr 6, 2022, 12:09:16 PM4/6/22
to vsg-...@googlegroups.com
HI All,

I'm continuing to work on vsgXchange::Assimp utilization of vsg::ShaderSet etc.  I've struggled a bit to resolve how best to manage the set up of DescriptorSet, Geometries and GraohcsPipeline using GraphicsPipelineConfig. 

Using just GraphicsPipelineConfig has shown up awkwardness with having Assimp materials and mesh being loosely coupled and handling the coupling cleanly when final scene graph StateGroup and VertexIndexDraw nodes are created.   As an experiment I've introduced a vsg::DescriptorConfig helper class that takes on a subset of GraphcsPipelineConfig did, focusing on the texturing/uniform side.

The changes for this are now checked in the StateComposer branches of the VSG and vsgXchange:


The new vsg::DescriptorConfig class name needn't be final.  I did contemplate calling it DescriptorBundle but flip flop and what the best name for it is.

The relationship with GraphicsiPipelineConfig is also not finalized, for now the new class is independent, I will likely refactor this relationship at some point.  I have also considered creating an ArrayConfig class that is like DescriptorConfig but focuses on the setup of the vertex arrays.

With this functionality there are many different ways of going with various pros and cons so finding the best path forward still isn't clear.

The next step for me is to implement support for vsg::SharedObjects to the vsgXchange::Assimp plugin.

Cheers.
Robert.

Sam

unread,
Apr 6, 2022, 12:13:45 PM4/6/22
to vsg-users : VulkanSceneGraph Developer Discussion Group
Hey Robert,

Sounds similar to what I was brushing up against. I'll take a look at this latest pass with DescriptorConfig and see how it feels in my code base. Will you also be moving over the vsg::Text techniques over prior to the merge to master?

Thanks, Sam

Robert Osfield

unread,
Apr 6, 2022, 12:30:24 PM4/6/22
to vsg-...@googlegroups.com
Hi Sam,

On Wed, 6 Apr 2022 at 17:13, Sam <brk...@gmail.com> wrote:
Sounds similar to what I was brushing up against. I'll take a look at this latest pass with DescriptorConfig and see how it feels in my code base. Will you also be moving over the vsg::Text techniques over prior to the merge to master?

I haven't contemplated vsg::Text and ShaderSet functionality yet, just biting off as much as I can chew at this point.

After vsgXchange::Assimp my plan is to look at vsgXchange::OSG as it has it's own cludying shader composition that kinda works, but in a limited way and without support for lines and points.

After I've got vsg::SharedObjects working with vsgXchange::Assimp I'll add support for lines and points which is a bit of precursor for the OSG stuff.

This work has been a bit stop-start, I'll hit a junction with many paths, try a few, finally make some progress, then come to the next junction with no clear path forward, then go hacking around in the undergrowth to find a good way forward.

The DescriptorConfig/GraphicsPipelineConfig functionality don't yet feel like a solid path, while ShaderSet and SharedObjects feel solid.

Attacking real tasks like vsgXchange::Assimp and OSG loaders will be a good way of shaking down this functionality.  Testing out in the community will also help - the functionality needs to work but also needs to be clear and natural to use.

Cheers,
Robert.

Robert Osfield

unread,
Apr 6, 2022, 12:39:51 PM4/6/22
to vsg-...@googlegroups.com
I should add that my intention for these classes is that they are all utilities that you can use when you feel they help, but if you want to create scene graphs without using them then that's perfectly fine.

These classes are fundamentally about providing flexibility in creation of scene graph by providing loosely coupled functionality, that flexibility does have overheads in looking up and connecting various bits of the scene graph. 

The final scene graph should be efficient but creation might be a bit slower than hard-wired creation of scene graph where you know exactly what relates to what.  If you have a fixed configuration then these new classes may well not be the tool for you, and just using more hard-wired code will be more efficient.

Robert Osfield

unread,
Apr 6, 2022, 3:21:25 PM4/6/22
to vsg-...@googlegroups.com
First pass of adding use of vsg::ShaderObjects to vsgXchange::Assimp is now checked into the vsgXchange StateComposer branch. Frame rate now 32% faster than the original vsgXchange::Assimp loader when rendering the FlightHelment.gltf. 

vsg::GraphicsPipeline's aren't yet sharing when I thought they might, so still need to do some more work.  If I can get the sharing of pipelines working it will help both initial compile performance and runtime performance.

Robert Osfield

unread,
Apr 7, 2022, 1:19:24 PM4/7/22
to vsg-...@googlegroups.com
On Wed, 6 Apr 2022 at 20:21, Robert Osfield <robert....@gmail.com> wrote:
vsg::GraphicsPipeline's aren't yet sharing when I thought they might, so still need to do some more work.  If I can get the sharing of pipelines working it will help both initial compile performance and runtime performance.

I have now got GraphicsPipeline sharing, which is working well for standalone loads of models. when loading multiple models together and sharing objects between the successive loads isn't yet working reliably.

Changes are checked into VSG and vsgXchange StateComposer branches.

Cheers,
Robert.
 

Robert Osfield

unread,
Apr 8, 2022, 8:14:33 AM4/8/22
to vsg-...@googlegroups.com
On Thu, 7 Apr 2022 at 18:19, Robert Osfield <robert....@gmail.com> wrote:
I have now got GraphicsPipeline sharing, which is working well for standalone loads of models. when loading multiple models together and sharing objects between the successive loads isn't yet working reliably.

Issues with sharing of objects between successively loads looks to be resolved now, so I'm able to load multiple models at the same time without any issues with state being inappropriately shared.  These fixes also address problems in ShaderSet where the shaderHints were wrongly matched.  Fixes are checked into VSG and vsgXchanges StateComposer branches.

This afternoon I'll add support for models with points and lines to vsgXchange::Assimp.

Robert Osfield

unread,
Apr 8, 2022, 12:40:20 PM4/8/22
to vsg-...@googlegroups.com
Hi All,

I have just checked in preliminary support in vsgXchange::Assimp for points and lines.  Handling this in Vulkan is a bit more convoluted than in OpenGL as you have to change the graphics pipeline state to match the topology you want, rather than just passing the primitive type when you call the draw command.

The vsgXhange::Assimp loader in the StateComposer branch has now surpassed the functionality of the original loader and can load a wider range of models.  There is still more work for me to do before merging the StateComposer branch with master.

At this point 4 weeks in I'm feeling a bit overwhelmed by the size/complexity/awkwardness of this task.  I know it'll be an important feature for the VSG/VSG users but rather looking forward to being on the other side of this block of work...

Cheers,
Robert.

Sam

unread,
Apr 8, 2022, 4:36:37 PM4/8/22
to vsg-users : VulkanSceneGraph Developer Discussion Group
Hey Robert,

Had a chance to dig into this a bit today. Since you are loading a single assimp scene, you can just store the graphics pipeline at the top of the group. This make sense for this use case. How would you handle if you wanted to load the same scene twice and share the graphics pipeline across both scenes? You wouldn't want to have a second pipeline swap for no reason. Is VSG smart enough to pick this up and just ignore the pipeline swap? I know not the best example but it illustrates what I'm trying to convey within the scope of work you have completed.

Right now I have a GraphicsPipeline at the top for my entire scene and nodes get loaded in on the fly in relation to that pipeline. So really all I need in my loader is the layout to make sure I can setup the right descriptors. I do this by creating the pipeline and then passing it into the loader but this causes me to have a const_cast on the layout: 

auto bindDescriptorSets = vsg::BindDescriptorSets::create(VK_PIPELINE_BIND_POINT_GRAPHICS, const_cast<vsg::PipelineLayout*>(layout), 0, vsg::DescriptorSets{descriptorSet});

Thanks, Sam

Robert Osfield

unread,
Apr 9, 2022, 12:08:47 AM4/9/22
to vsg-...@googlegroups.com
HI Sam,

THe vsgXchange::Assimp loader localize the StateGroup above each VertexIndexDraw without attempting to decorate the whole scene with a single StateGroup.  THis is done as there are just so many different combinations of state that you need to handle across different model types that you have to do this.

However, the vsg::SharedObjects utility enables sharing of various state objects that are within each StateGroup, and this can now work across subsequent loads if they share the same SharedObjects.

One optimization I have in mind is to post processA loaded models looking for opportunities to move state up the scene graph where it's shared in the subgraph below.

Another approach is to pass in the inherited state to the loader so it can decide whether it needs to apply local state or not. So if there is a shared tBindGraphicsPipe;oe then it can not place one in the newly loaded subgraph.

For custom loaders one could use vsg::Options to pass in data like inherited state - exactly as you are doing.  Doing a const cast should be perfectly safe in this instance.

As a general note, the VSG and Vulkan both cope far better with fine grained state changes than the OSG/OpenGL do so you don't need to work so hard to create an application with decent performance.  THis extra performance headroom will mean you don't need to dedicate so much time up front trying to create an optimal scene graph - just get it working, and it doesn't perform then optimize.

Robert. 

Robert Osfield

unread,
Apr 10, 2022, 6:11:08 AM4/10/22
to vsg-...@googlegroups.com
Hi All,

I added support for vsg::ViewDependentState to the vsgXchange::Assimp loader and have checked this into the vsgXchange StateComposer branch, this enables full support for vsg::Light(s).  Changes are:


I would like to make the addition of vsg:ViewDependentState a user controllable option, this could be done several ways via a meta data value in vsg::Options, a dedicated vsg::Option member variable through to an addition to vsg::ShaderSet in the form of controls for which default #define's and DescriptorSets that should get added.

I think the best solution will be to add defaults support to vsg::ShaderSet and add support for prototype vsg::ShaderSet that can be assigned to the vsg::Options.

I will look at these next week.  I feel I'm pretty close to making the new ShaderSet code paths in vsgXchange::Assimp the only implementation as the old loader is slower and less capable.

Cheers.
Robert.

Robert Osfield

unread,
Apr 11, 2022, 6:07:54 AM4/11/22
to vsg-...@googlegroups.com
Hi All,

I have cleaned up the vsgXchange::Assimp code so that it now only has the new vsg::ShaderSet based code paths, this means that the loader no longer needs the built in assimp.vert & frag shaders that used to be compiled into vsgXchange library.   The changes are:


Thanks to removal of the shaders and the old conversion code we now can get a handle on the amount of code required to implement a loader using the new StateComposer features, the diffs for vsgXchange::Assimp according to github are:

     721 additions and 1,346 deletions.

Which is nice win, especially considering that the vsgXchange::Assimp loader now supports ViewDependetState (with it's lighting support),  points, lines, state sharing within a single load and across subsequent loads so results in smaller and faster scene graphs.

I still have some further changes to vsg::Options and vsgXchange::Assimp to implement support for user control of the ShaderSet used by the loader, which I'll tackle next, but feel we are now pretty close to having a form that is ready for wider testing.

I am also going to look at how easy it would be to combine vsg::ObjectCache and vsg:ShadredObjects as there is functionality and conceptual overlap between the two that will just confuse VSG users.  Which should be subsubseemed into the other?

After this, I'm torn whether to merge the respective VSG.vsgXchange/vsgExamples StateComposer branches with master then port the vsgXchange::OSG loader across to use ShaderSet etc, or to the the vsgXchange::OSG port first.

Cheers,
Robert.


Robert Osfield

unread,
Apr 11, 2022, 7:19:57 AM4/11/22
to vsg-...@googlegroups.com

HI All,

I have checked into the addition of a map<std::string, ref_ptr<ShaderSet> to vsg::Options, this allows you to override the the vsg::createPhysicsBasedRenderingShaderSet(..), createPhongShaderSet(..) and createFlatShadedShaderSet(..) calls so they use your ShaderSet.  The usage is:

  options-> shaderSets["pbr"] = createMyCustomPBRShaderSet();
  auto model = vsg::read_cast<vsg::Node>("mymodel.glt", options);  // any pbr shader based code will use  MyCustomPBRShaderSet(

I haven't yet fully implemented the ShaderSet serialization support, but once that's done you'll be able to read/writer completed ShaderSet to disk and use:

  options-> shaderSets["pbr"] = vsg::read_cast<vsg::ShaderSet>("myCustomShaderSet.vsgt", options);
  auto model = vsg::read_cast<vsg::Node>("mymodel.glt", options);  // any pbr shader based code will use  MyCustomPBRShaderSet(

When you substitute a ShaderSet like this it'll need to provide the same bindings and use the same basic uniform data as the original, otherwise when the loader looks up the bindings of arrays, textures and uniforms it'll not get a required match.  We'll eventually need to provide a graceful failure mode for this, but for now that fact it's possible at all is a major step forward.

For now if you are planning to try providing custom ShaderSet then you'll be entering the advanced VSG user realm so I'll assume you'll read/re-read this thread and be able to step through the code to figure out if stuff breaks :-)

Oh, I should mention vsg::Builder will automatically leverage this functionality - you just need to set the vsg::Options assigned to the builder.

Changes t add this support (note it's just the VSG that I had to change):



Cheers,
Robert.

Robert Osfield

unread,
Apr 11, 2022, 10:41:42 AM4/11/22
to vsg-...@googlegroups.com
I have coded the renaming vsg::ShaderSet serialization support and am about to put together some testing of the functionality, so thoughts have also moved to the possibilities of utility programs that take shaders and build ShaderSets from them.  Poteitally you could parse GLSL files and figure out all the #define's they support, and the bindings/locations of uniforms and vertex attributes etc,  There is also 3rd party libs like SPIRV-Reflect:


One could potentially have GUI tool for editing vertex/fragment etc. shaders. testing runtime output of those shaders and then when finished compile to SPIR-V and output a complete ShaderSet.vsgt which could then be used when loading model etc.

While I can see value in all these possibilities I also can't just keep working on these areas as they are ancillary to VSG-1.0 itself, and 1.0 has to be my main focus as I'm currently self funding my work on the VSG..  However, if members of the VSG community fancy tracking some of these possible solutions then that would be great, or perhaps one of the companies using the VSG sees value in such tools and could fund myself or others to develop them.

For today I just need to test the serialization side so will just throw something crude together to make sure the basic IO for ShaderSet works, then I'll get it checked in.

Cheers,
Robert

Denis Taniguchi

unread,
Apr 11, 2022, 10:46:21 AM4/11/22
to vsg-users : VulkanSceneGraph Developer Discussion Group
Hi Robert,

Would be good to have those ideas in one place, so that contributors could pick one up. Like a spin-off TODO list.
Cheers,

Denis

Robert Osfield

unread,
Apr 11, 2022, 11:13:07 AM4/11/22
to vsg-...@googlegroups.com
On Mon, 11 Apr 2022 at 15:46, 'Denis Taniguchi' via vsg-users : VulkanSceneGraph Developer Discussion Group <vsg-...@googlegroups.com> wrote:
Would be good to have those ideas in one place, so that contributors could pick one up. Like a spin-off TODO list.

I haven't yet created a Wiki or used Projects support for the VSG project, but these might be the best place for this type of thing.

Right now I have to concentrate on just getting StateComposer done, more than happy for others to help with insights on use of Github Wiki and Projects, thought this would be best done starting a new vsg-users thread.

Cheers,
Robert.

Robert Osfield

unread,
Apr 11, 2022, 12:32:12 PM4/11/22
to vsg-...@googlegroups.com
I have implemented and tested the serialization support for vsg::ShaderSet, changes to VSG are now checked into StateComposer branch:


To test serialization I extend the vsgstatecomposer example to have --pbr, --phong and --flat command line options that call the respective vsg::create*ShaderSet() functions, and pexisting -s shaderset.vsgt and --os output_shaderset.vsgt command line options allow us to complete a loop of creating a specified ShaderSet, outputting it .vsgt, modifying the file and then reloading it to confirm that everything works :-)

Changes to vsgExample StateCompooser branch are:



With these changes vsg::ShaderSet is coming close to be fully functional, so it would now be a good time for members of the community to start checking out the StateComposer branches of the VSG, vsgXchange and vsgExamples to make sure everything compiles on your platform and tools and you can load all the original models you could load, etc.

--

There will be still a few loose ends so if you spot usage cases that don't seem well catered for let me know so we can discuss whether they are already supported, or need some further refinement of ShaderSet/GraphicsPipelineConfig etc.

One loose end might to add support for the vsg::SaderSet to provide a way of providing custom vsg::ArrayState that can be used to enable utilities like the LineSegmentIntersector to handle shaders that modified the vertex coordinates so that normal treatment of modelview matrix and vertex values don't work correctly.  Currently applications have to explicitly attach the ArraySet to the StateGroup that provides the BindGraphicsPipleine with the novel vertex shader treatment, the StateGroup::prototypeArrayState member is what does this:

        /// if the shaders associated with GraphicsPipeline don't treat the array 0 as xyz vertex then provide a ArrayState prototype to provide custom mapping of arrays to vertices.
        ref_ptr<ArrayState> prototypeArrayState;

I'm thinking that perhaps vsg::ShaderSet could have this same member variable, with the same naming. and then have the scene graph creation code that is setting up the vsg::StateGroup to assign this member.

When serializing the ArrayState one would need to implement the read/write support and register this with the vsg::ObjectFactory to get everything to work when serializing the shader sets.

Um... I think I'll add this and wrap it up for the day, tomorrow I'll look at combining vsg::ObjectCache, that handles sharing loaded models/textures, and the new vsg::SharedOjbects.  Once that's done my inclination is to push the StateComposer branches to their respective  masters.

Cheers,
Robert.

Robert Osfield

unread,
Apr 11, 2022, 12:39:32 PM4/11/22
to vsg-...@googlegroups.com
On Mon, 11 Apr 2022 at 17:31, Robert Osfield <robert....@gmail.com> wrote:

One loose end might to add support for the vsg::SaderSet to provide a way of providing custom vsg::ArrayState that can be used to enable utilities like the LineSegmentIntersector to handle shaders that modified the vertex coordinates so that normal treatment of modelview matrix and vertex values don't work correctly.  Currently applications have to explicitly attach the ArraySet to the StateGroup that provides the BindGraphicsPipleine with the novel vertex shader treatment, the StateGroup::prototypeArrayState member is what does this:

        /// if the shaders associated with GraphicsPipeline don't treat the array 0 as xyz vertex then provide a ArrayState prototype to provide custom mapping of arrays to vertices.
        ref_ptr<ArrayState> prototypeArrayState;

I'm thinking that perhaps vsg::ShaderSet could have this same member variable, with the same naming. and then have the scene graph creation code that is setting up the vsg::StateGroup to assign this member.

When serializing the ArrayState one would need to implement the read/write support and register this with the vsg::ObjectFactory to get everything to work when serializing the shader sets.

Um... I think I'll add this and wrap it up for the day

Ummm^2

Just looked at the possibility of adding  ref_ptr<ArrayState> prototypeArrayState; member variable to vsg::ShaderSet but you potentially need multiple ArrayState classes to handle different variants of the shaders as the different #define's can select different vertex shader code paths - for instance VSG_INSTANCE_POSITIONS and VSG_DISPLACEMENT_MAP defines in the standard assim.vert shader add their own requirements for how to map the vertex/texture data to final vertex positions...  Ahhhh.. Programmable graphics are great... except when you need to replicate functionality on the CPU....
 

Rainer Gericke

unread,
Apr 12, 2022, 3:33:25 AM4/12/22
to vsg-users : VulkanSceneGraph Developer Discussion Group
Hi Robert,

I just made build and run tests with the vsgstatecomposer demo. It works well on MacOS 12 and Windows 10, when the Phong shader set is used. PBR needs special treatment I guess.

Best,
Rainer

Robert Osfield

unread,
Apr 12, 2022, 4:00:34 AM4/12/22
to vsg-...@googlegroups.com
Hi Rainer.

On Tue, 12 Apr 2022 at 08:33, Rainer Gericke <dr.ratz...@gmail.com> wrote:
I just made build and run tests with the vsgstatecomposer demo. It works well on MacOS 12 and Windows 10, when the Phong shader set is used. PBR needs special treatment I guess.

 Thanks for the testing.  Good to hear it's working. 

vsgstatecomposer doesn't render with all features with the PBR ShaderSet because it has different bindings and material types so when the code queries for them that don't have a mapping to assign them.

Different ShaderSet will be compatible enough to be swapped in for others, while other ShaderSet will not be compatible and it won't be possible to substitute them.  I haven't resolved how best to handle treatment of these incompatibilities, partly it'll need to be an education issue, but also it may be possible to refactoring ShaderSet (and the shaders that encapsulate) so they have broader compatibility.

In the case of Phong and Flat ShaderSets they already use the same PhongMaterial struct, and reasonable overlap of bindings/defines.  The PBR ShaderSet has some level of overlap, especially w.r.t vertex shader, but diverge more with bindings for the fragment shader and use PbrMaterial struct.  I've been wondering about the possibility of coming up with a single material struct that can be used for all three of these ShaderSet to make them more compatible.  Unfortunately, they don't use all the same member variables, naming and meaning behind it so it won't be a straight forward rename and reuse.

The other possible approach is to make the scene graph creation code more flexible with it more proactively getting the material class that is compatible - the uniform bindings have the default data already so this is possible right now, but it's obviously more complicated for the scene graph creation code.

When I convert the vsgXchange::OSG loader across to use ShaderSet I will hit upon this issue as it currently uses it's own set of shaders that aren't compatible at all with the ones derived from the Assimp loader.  It might be that we'll need to add another ShaderSet implementation, perhaps something like a FixedFunctionStyleShaderSet as it'll need to be closer to what is needed to map the OpenGL fixed function pipeline.  There is also a FBX code path in the present vsgXchange::OSG loader that will also need mapping across in some way.

Cheers,
Robert.

Rainer Gericke

unread,
Apr 12, 2022, 4:28:40 AM4/12/22
to vsg-users : VulkanSceneGraph Developer Discussion Group
Hi Robert,

thanks for the explanation. The new classes will ease the use of vsg a lot, especially for non-experts. I like it.

Best,

Rainer

Robert Osfield

unread,
Apr 12, 2022, 5:09:30 AM4/12/22
to vsg-...@googlegroups.com
Hi Rainer,

On Tue, 12 Apr 2022 at 09:28, Rainer Gericke <dr.ratz...@gmail.com> wrote:
thanks for the explanation. The new classes will ease the use of vsg a lot, especially for non-experts. I like it.

Thanks.  Making the VSG/Vulkan easier to use is a key part of vsg::ShaderSet and associated functionality, it's hard to know what is actually achieving that rather than just adding yet another class, level of indirection and complexity.

I'm getting the same internal vibe with vsg::ShaderSet as I did when I came up with #pragma(tic) shader composition, it just feels right, helps orders one understanding of how to put things together as well as offering flexibility.  ShaderSet is of course layered upon #pramga(tic) shader composition so it's some ways a continuation of that work, building upon it to provide a more complete integration between developing shaders and applications.

Which is a bit ironic as I came up with #pramga(tic) shader composition so help with shader composition for the OpenSceneGraph and then attempted to build upon this, but ever after many months on higher level shader composition functionality for the OSG it never really gelled.  Now 5 years on I've come at the problem from a completely different direction on a completely new scene graph and finally come up with a coherent way to provide the C++ glue on top #pramga(tic) shader composition.

I don't get this same feeling about vsg::DescriptorConfig+GraphicsPipelineConfig, they work and they make it easier to create the scene graph guided by vsg::ShaderSet but it doesn't yet feel as coherent and solid.  It might be that some more time needs to pass and to come back to this problem to come up with a better solution.  I'd also be very happy if others come along and beat me to making these improvements :-)

Cheers,
Robert.

Robert Osfield

unread,
Apr 12, 2022, 6:35:02 AM4/12/22
to vsg-...@googlegroups.com
This morning I'm working on adding the vsg::ObjectCache functionality for caching loaded files into vsg::SharedObjects, I'm not aiming to be API compatible, just to replicate the broad functionality. 

I already have a quick and dirty test done that leverages the existing vsg::SharedObjects::share(object, init_function) method and it worked pretty well out of the box.  Kinda neat when a feature written for something else entirely makes it easier to do something that used to require more code.  The problem I've come across is that SharedObjects used to use a std::mutex for thread safety, but now SharedObjects can do instigate loads, and them loads can utilize exactly the same SharedObjects you end up with potential recursion and a deadlock.  The solution is simply to use a std::recursive_mutex in SharedOjbects.

I now need to decide whether we want to retain the time limit functionality of the ObjectCache which enables us to keep references in the SharedOjects cache after they have been orphaned for a specified duration.  This extra complexity is something that will only ever be very niche for VSG users.  The OSG has similar functionality and I suspect a couple of end users have used it.

I do think a SharedObjects::prune() method would be a good complement to the current clear() that removes all the cached references.  The prune() would only remove references that have a Object::referenceCount() of 1 which indicates that the Object is effectively orphaned save for the SharedObjects reference.

The prune() is something applications would explicitly call to free up space, without clearing the whole cache of references within vsg::SharedObjects.

For most application that only need to use the SharedObjects container to optimize loading then you can simply assign the SharedObjects to the vsg::Options then once loading is done let the options object go out of scope and the SharedObjects and all the internal references it holds would disappear, leaving on the loaded scene as the remaining reference to the loaded data.





Robert Osfield

unread,
Apr 12, 2022, 12:33:14 PM4/12/22
to vsg-...@googlegroups.com
 I have merged the vsg::ObjectCache functionality into vsg::SharedObjects, but have streamlined it a little so that the time stamp related code no longer exists, instead developers will need to call sharedOjbects->prune()..

The SharedObjects::prune() method trims away objects that no longer have any external references.  Implementation of prune() is a bit more complicated than just checking Object::referenceCount()==1 as SharedObjects can contain objects that contain other objects that are held in the SharedObjects cache.  To handle this I had to leverage vsg::oberserver_ptr<> and iterate on the clean up code to ensure everything that can be cleaned is cleaned up.

Now that SharedObjects manages the task of caching loaded models and images there is no longer a need for vsg::ObjectCache so I have removed it from the StateComposer branch that holds the new SharedObjects utility class.  I have updated vsgXchange and vsgExamples StateComposer branches to replace lines referencing ObjectCache to SharedObjects - it's simply a rename so pretty straight forward.

The changes to vsgExamples are:

My current thought is to make use of vsg::SharedObjects for sharing data between subsequent loads something that uses will enable by assigning a SharedObjects to vsg::Options prior to loading their files, and have the vsgXchange::Assimp and vsgXchange::OSG loaders use that SharedObjects object when it's assigned, but if one isn't assign create a SharedObjects object for the during of each individual Assimp or OSG model load.  This gives some high level control over sharing to the application developer when they want it, but doesn't leave loading of models with now state etc. sharing when the app developer hasn't assigned a SharedObjects.

As SharedObjects is reference counted and thread safe you can share a single instance between multiple loads even when running in mutli-threaded, you simply assign the a single SharedObjects to each of the vsg::Options you use when calling vsg::read/vsg::write.  Typically you'd want to share a single SharedObjects for all your loads, but there may be cases where you want to keep things more ring fenced - for instance if you application have multiple independent viewers with entirely different scene graph.

With ObjectCache now replaced by SharedObjects I can tick off another of TODO items for the StateComposer work, so getting pretty close to looking at merging with master :-)

Robert Osfield

unread,
Apr 13, 2022, 8:13:50 AM4/13/22
to vsg-...@googlegroups.com
I have checked in the first step toward vsg::ShaderSet's providing a means of providing a suitable vsg::ArrayState that can be assigned to a vsg::StateGroup so that traversals like vsg::LineSegmentIntersector or vsg::ComputeBounds can handle custom treatment of the vertex positions such as when a vertex shader using instancing or displacement maps.  The changes are checked in the StateComposer branch.


I have utilized this new functionality in the vsg::Builder class, and vsgXchange::Assimp loader.  The changes are simply:

    // assign any custom ArrayState that may be required.
    stateGroup->prototypeArrayState = config->shaderSet->getSuitableArrayState(config->shaderHints->defines);

This decouples the scene graph builders/loaders from having to explicitly tie the C++ implementation with the shader configuration one is using.  One still needs some C++ glue in the form of a custom ArrayState, but this can be localized to the configuration of the ShaderSet.  The vsg::create*ShaderSet() convenience functions set things up thus, for instance the vsg::createPhysicsBasedRenderingShaderSet(ref_ptr<const Options> options) implementation now has the lines:

    shaderSet->definesArrayStates.push_back(DefinesArrayState{{"VSG_INSTANCE_POSITIONS", "VSG_DISPLACEMENT_MAP"}, PositionAndDisplacementMapArrayState::create()});
    shaderSet->definesArrayStates.push_back(DefinesArrayState{{"VSG_INSTANCE_POSITIONS"}, PositionArrayState::create()});
    shaderSet->definesArrayStates.push_back(DefinesArrayState{{"VSG_DISPLACEMENT_MAP"}, DisplacementMapArrayState::create()});

After you create your scene graph the StateGroup will have it's prototypeArrayState member set to the appropriate custom ArrayState and will be serialized with StateGroup, so once you've created your scene graph with the help of a ShaderSet/GraphicsPipelineConfig there is no coupling left to the original ShaderSet and the new serialized scene graph will work happily.

I haven't yet implemented the internals of the PositionAndDisplacementMapArrayState, PositionArrayState and DisplacementMapArrayState classes used above, so for now vsgintersection can't yet intersection scene graphs generated with instance or displacement maps. This afternoon I'll tackle this and am hoping to finally get vsgintersection to work with all the different combinations of vsgbuilder usage.  Once I achieve this I will have finally provided a solution for a long standard challenge with handling vertex shaders in a general purpose way - how to handle vertex shaders that change the vertex positions on the C++ side.

Cheers,
Robet.

Robert Osfield

unread,
Apr 13, 2022, 1:38:42 PM4/13/22
to vsg-...@googlegroups.com
On Wed, 13 Apr 2022 at 13:13, Robert Osfield <robert....@gmail.com> wrote:
I haven't yet implemented the internals of the PositionAndDisplacementMapArrayState, PositionArrayState and DisplacementMapArrayState classes used above, so for now vsgintersection can't yet intersection scene graphs generated with instance or displacement maps. This afternoon I'll tackle this and am hoping to finally get vsgintersection to work with all the different combinations of vsgbuilder usage.  Once I achieve this I will have finally provided a solution for a long standard challenge with handling vertex shaders in a general purpose way - how to handle vertex shaders that change the vertex positions on the C++ side.

I was out for most of the afternoon so haven't been able to complete the implementation of the custom ArrayState class to fully support the flat, pbr and phong shader sets.  Have to admit to being rusty on the details of how to implement a custom ArrayState as it's nearly 2 years since I wrote this class, and up till now that hasn't been a public example of it being used...

The good news is why I'm rusty on ArrayState, once I'm done with these custom ArrayState implementations we'll have a nice public example for us (including me) to help guide how you go about implementing them :-)

 

Robert Osfield

unread,
Apr 14, 2022, 6:53:24 AM4/14/22
to vsg-...@googlegroups.com
I'm now heading down another rabbit whole, this time in how to handle instancing with vsg::ArrayState, vsg::Intersector and vsg::LineSegmentInersector.  Up till now I haven't attempted to tackle intersection of scene graphs using instancing so I haven't had to deep dive on this simple on the GPU, but awkward on the CPU problem.  With the new vsg::ShaderSet provided in the core the supported vertex shader has a instance code path built in so to fully support this variant of the ShaderSet's I have to implement support for instancing within the ArrayState and Intersector base classes as well as the custom PositionArrayState class that adapts these vertex shaders with instancing enabled.

An example of instancing in action using the built in Phong vsg::ShaderSet is when you run vsgbuilder with -n numInstances command line option i.e

    vsgbuilder -n 10

vsgbuilder_n_10.png
You can write the created scene graph to a file and then load in vsgintersection:

   vsgbuilder -n 10 -o instanced.vsgt
   vsgintersection instanced.vsgt

The visualization and serialization of this instanced scene graph all works perfectly, but vsgintersecton uses CPU intersections with vsg::LineSegmentIntersector which needs to handle the instancing in order to work.  My goal with this portion of the StateComposer work is to have the ShaderSet provide the custom PositionArrayState handle the correct vertex mapping for each instance so that vsgintersection will just work out of the box.  This is where having everything loosely coupled makes getting things to work hard.

Feels like this block of work just keeps throwing up more problems to solve the closer I think I'm to wrapping it up.

Robert Osfield

unread,
Apr 14, 2022, 1:20:10 PM4/14/22
to vsg-...@googlegroups.com
I have a custom PositionArrayState class working with LineSegmentIntersector so with vsgintersection have been able to insert white shapes into an instanced scene created by vsgshapes. All the intersections are in the correct place.  The code has loads of debug messages and dynamically creates vertex arrays that are used to place the geometry in the correct offset position based on instanced positions.  But it works, which is a big step towards providing a means for handling scenes with novel ways of positioning vertex data in the vertex shader.  The white shapes in this scenes are the ones I've inserted interactively:

vsgintersection_n_10.png
Tomorrow I'll clean up my changes and get them checked into the StateComposer branch. 

I'll then tackle supporting the displacement mapping in the vertex shader, again this is something I haven't attempted before so don't know how awkward it'll be.

With these custom ArrayState handling vertex shaders that move vertex positions dynamically there is a CPU overhead you have to pay each time you do an intersection traversal, so with this brute force approach it won't be super fast.  Longer term I think the right way to tackle that would be having additional data structures that exist purely for optimization of mesh intersections.  This is something for after the VSG-1.0 release.

Cheers,
Robert.

Robert Osfield

unread,
Apr 15, 2022, 6:37:25 AM4/15/22
to vsg-...@googlegroups.com
I have now cleaned up and checked in PositionalArrayState implementation:


This enables instanced geometries to be handled by vsg::ComputeBounds and vsg::LineSegmentIntersector.  There will be efficiency improvements we'll be able to make, but for now I'm happy to just get something like this working, something I've pondered about right back to when we added GLSL support to the OSG - that's 15 years ago!

Robert Osfield

unread,
Apr 15, 2022, 1:21:19 PM4/15/22
to vsg-...@googlegroups.com
Today I've been working on vsg::DisplacementMapArrayState so when you create a ShaderSet with displacement mapping enabled tools like intersection testing and compute bounds will account for the displacement map.  This image is for work in progress with vsgintersection now able to intersect with a terrain created with displacement mapping:
vsgintersection_dm.png
The code is experimental/proof of concept at this point.  I had hoped to wrap it up today but still need to do more work to get it to handle all the ways that displacement mapping can work.  The displacement mapping shader code itself also needs more work to address issues with normal computation.

So... will need to return to another day, and so StateComposer work is now sleeping into yet another week. Ahhhhh....

Sam

unread,
Apr 15, 2022, 7:28:01 PM4/15/22
to vsg-users : VulkanSceneGraph Developer Discussion Group
Hi Robert,

Giving this some more kicks of the can, I created a very minimal example - it doesn't draw anything as it's more of a logic example. You can see the program flow here: https://gist.github.com/sbrkopac/5a185961f4aee28cac0aaf130a59c6c0 I guess my biggest issue with getting this to work this way was setting up the pipeline layout. I don't technically want to configure anything when my program first runs - I just want to setup the GraphicsPipelineConfig and be done with it. But I realized without the following:

vsg::DescriptorConfig config2(graphicsPipelineConfig->shaderSet);
config2.assignTexture("diffuseMap", {}); // required to ensure the descriptorSetLayout can be configured properly
config2.init();
graphicsPipelineConfig->descriptorSetLayout = config2.descriptorSet->setLayout;
graphicsPipelineConfig->init();

I was unable to get the loader to load the mesh because the PipelineLayout wasn't initialized yet. So adding this "dummy" config let me set everything up in my InitState and then let me use my loader code appropriately. I'm not sure I have a solution for you but thought I'd post my thoughts here for now so it can percolate.

Thanks, Sam

Robert Osfield

unread,
Apr 16, 2022, 9:50:03 AM4/16/22
to vsg-...@googlegroups.com
Hi Sam,

I personally wouldn't try to split the GraphicsPipelineConfig set up across global and local scale, I'd just stick it in the ReaderWriter and let the SharedObjects figure out what can be shared.  You are very likely to need multiple GraphicsPipeline in your application - for instance opaque objects are best rendered with blending disabled, while transparent objects need blending enabled so you'll need at least two GraphicsPipeline in your application. 

Whether a subgraph that is being loaded required blending or not will depend upon what is being drawn - a loaded subgraph requires blending will depend upon what is it that subgraph - for instance if you have a tile with terrain and trees, then the terrain would be rendered with blending disabled and trees rendered with blending enabled.  To do this you'll need to decorate the opaque and blended subgraphs under a separate StateGroup's with appropriate BindGraphicsPipeline assigned. 

For performance you'd want to share the GraphcsPipeline across separate loads, which is where SharedState comes in.  You can store the GraphicsPipelineConfig in the SharedState and even use it to call the init() only on the first type that GraphicsPipelineConfig configuration is required.   This is illustrated in the vsgXchange/src/assimp/assimp.cpp code:
 
    auto config = vsg::GraphicsPipelineConfig::create(material.shaderSet);
 ...
    if (sharedObjects) sharedObjects->share(config, [](auto gpc) { gpc->init(); });
    else config->init();

If the particular combination of GraphicsPipelienConfig settings has been assigned to the SharedObjects container before then the compare(..) functionality will match against it and return that version placing this value in the config ref_ptr<>, if there isn't a compatible config already it'll call the init lambda function.

These scheme means that the local code can just worry about making sure the configuration exactly matches the need of that geometry being created, without concerning itself with some global state, with the SharedObjects usage resolving the global state for you.

In your example you are trying to be clever and manage the global and local state in a coherent way, while technically possible, it's more complicated to implement and far more fragile and less flexible.  For instance the global approach fails to handle a newly loaded subgraph needing to be blended if the global state doesn't support it.

My recommendation is forget about the global state, just move the whole GraphicsPipelineConfig local to each load, and let SharedObjects manage the sharing for you.

Another little note, in your example you don't assign a SharedObjects object to the vsg::Options, you'll need to do this in order to leverage the use of SharedObjects.

My hope is that if you just fully embrace the ShaderSet/SharedObects/GraphicsPipekienConfig in usage model I describe it should be more straightforward to implement, easier to understand and will have all the flexibility to handle all the different combinations of state you'll need as your project progresses.  To dive in fully you have to let go of some previous ideas of how to do things - trying to mix and match approaches is probably what is holding you back at this point.

Cheers,
Robert.

Robert Osfield

unread,
Apr 20, 2022, 6:26:53 AM4/20/22
to vsg-...@googlegroups.com
Hi All,

I'm still half way down yet another rabbit hole - this time following the the features required to be able to implement a DisplacementMapArrayState, in particular how to sample data from a vsg::Array/Array2D/Array3D using the vsg::vk/Sampler settings as a guide. Essentially it's an issue of how do we replicate the functionality found in GLSL on the GPU in C++ on the CPU.

As this work has progressed I've experimented with, then built usable helper functions with clamping texture coords and casting of colors between types.  I've put these helper functions into two new headers:


I feel that I've diverted off the task of StateComposer at this point, it is loosely related because it's required for the custom ArrayState that createPbrShaderSet, createPhongShaderSet() provide, but it I'm a bit torn between wanting to get StateComposer branch checked into master and completing all the various threads of functionality that are entwined with it.

Today I'll aim to wrap up a set of vsg::sample(vsg::Sampler& sampler, vsg::Data& data, float/vec2/3 coord) template functions required to process image data, but it might slip into another day as it's another one of this tasks that is non trivial once you start digging into it...

Also be another important VSG merge that could happen today.... and it has nothing to do with ShaderComposer work! :-)

Cheers,
Robert.

vsguy

unread,
Apr 20, 2022, 7:43:18 AM4/20/22
to vsg-users : VulkanSceneGraph Developer Discussion Group
I'm probably out of my depth on this, but I remember a few years ago when I first started experimenting with Vulkan I wrote a small program that used texture2D (as an array) and a sampler in GLSL, but it was very hacky (and static). Maybe it could be helpful... if relevant - maybe not. The code is like this (global):
uniform texture2D textures[1024]
uniform sampler texsampler
vec4 color = texture(sampler2D(textures[texid], texsampler), texcoord)
where the 'texid' was from the vertex shader (vertex attribute) 
For specific pipeline:
fragment:
I then hardcoded shaders and modified the text where: "outColor = inColor*texture(sampler2D(textures[" --some indexer as a string -- "], texsampler), inUV)" 
Then on launch all of my textures get loaded into a global array, using a function that loads an image from file and fills the texture struct containing vk image, vk device memory, and vk image view
The function also indexes each texture (to an id that corresponds to textures[id] in the shaders). Per vertex texture id is maybe not the right way, but there's always another way. 
Not sure if that little hack is helpful.
I also just wanted to say I really have been enjoying using VSG for my small project. It's appreciated since it saves me a lot of time and hard work. I've been working on a small game engine and hopefully to become a full game. I had been keeping up with StateComposer branch specifically since it's very useful when it comes to a resource loader/manager in a game engine. 
Well... good luck with the work today - and I don't want to lie, I don't fully understand what's going on under the hood, nor do I want to - I trust your design and everything seems well-crafted - I guess that was the intention right? Thanks for VSG. I'll probably be releasing some of my work soon, specifically my VSG SDL integration, which might be sought after by anyone who likes SDL! Again, thanks for this library. 

Robert Osfield

unread,
Apr 20, 2022, 12:50:02 PM4/20/22
to vsg-...@googlegroups.com
Hi Stephen,

Welcome to the VSG community :-)

On Wed, 20 Apr 2022 at 12:43, vsguy <stephe...@gmail.com> wrote:
I'm probably out of my depth on this, but I remember a few years ago when I first started experimenting with Vulkan I wrote a small program that used texture2D (as an array) and a sampler in GLSL, but it was very hacky (and static). Maybe it could be helpful... if relevant - maybe not. The code is like this (global):
uniform texture2D textures[1024]
uniform sampler texsampler
vec4 color = texture(sampler2D(textures[texid], texsampler), texcoord)
where the 'texid' was from the vertex shader (vertex attribute) 
For specific pipeline:
fragment:
I then hardcoded shaders and modified the text where: "outColor = inColor*texture(sampler2D(textures[" --some indexer as a string -- "], texsampler), inUV)" 
Then on launch all of my textures get loaded into a global array, using a function that loads an image from file and fills the texture struct containing vk image, vk device memory, and vk image view
The function also indexes each texture (to an id that corresponds to textures[id] in the shaders). Per vertex texture id is maybe not the right way, but there's always another way. 
Not sure if that little hack is helpful.

You should be able implement something similar with the VSG, the texture array would map to vsg::DescriptorImage object with a list of images, rather than just a single one.

Whether this approach is useful depends upon the application.  The new vsg::ShaderSet/vsg::SharedObjects/vsg::GraphicsPipeline should be pretty orthogonal to these types of choices.
 
I also just wanted to say I really have been enjoying using VSG for my small project. It's appreciated since it saves me a lot of time and hard work.

Thanks, the VSG exists to save folks time and help them write robust and well performing applications.  As time goes on it should mature and become easier for users to pick and and run with, if it doesn't folks will need to kick my butt and provide help in addressing any shortfalls :-)
 
I've been working on a small game engine and hopefully to become a full game. I had been keeping up with StateComposer branch specifically since it's very useful when it comes to a resource loader/manager in a game engine. 
Well... good luck with the work today - and I don't want to lie, I don't fully understand what's going on under the hood, nor do I want to - I trust your design and everything seems well-crafted - I guess that was the intention right? Thanks for VSG. I'll probably be releasing some of my work soon, specifically my VSG SDL integration, which might be sought after by anyone who likes SDL! Again, thanks for this library.

Sounds like a vsgSDL library!

FY, I've used SDL with the OSG when working with joysticks and sound, but not for windowing.  SDL does support a few Windows options that the VSG doesn't yet support out of the box so would be an interesting addition.  I haven't yet had a chance to try SDL with Vulkan

If you want to chat more about your plans and general questions it would be best to start a new thread here on vsg-users.

Cheers
Robert.

Robert Osfield

unread,
Apr 20, 2022, 12:59:51 PM4/20/22
to vsg-...@googlegroups.com
I've checked in the vsg::sample(..) template function to help with getting samples from images, currently it's limited to Array2D subclasses, but my plan is to add support for Array and Array3D later.

Changes to add use of the new vsg::sample(..) function in the DisplaceMapArrayState:

These implementations are written for convenience when needing to get the occasional value rather than an implementation for a high bandwidth sample of images, if you need high performance then you might be just doing the work in Vulkan/on the GPU!

Robert Osfield

unread,
Apr 20, 2022, 2:24:11 PM4/20/22
to vsg-...@googlegroups.com
I have checked in vsg::PositionAndDisplacementMapArrayState that handles the case where you are using instancing and displacement maps at the same time - something supported by the built in flat, phong and pbr  shaders.  To test the functionality I generated a set of 10 instanced terrain meshes that use displacement mapping to render a height field, and then test it with vsgintersection and it WORKS!
instanced_displacement_maps.png
The testing showed that the vsg::ComputeBounds traversal doesn't yet handle displacement mapping and instancing that the vsg::LineSegmentIntersector can handle so I'll need to adapt ComputeBouns to utilize the full capabilities of vsg::ArrayState.

Robert Osfield

unread,
Apr 26, 2022, 11:30:40 AM4/26/22
to vsg-...@googlegroups.com
Hi All,

On Wed, 20 Apr 2022 at 19:23, Robert Osfield:
The testing showed that the vsg::ComputeBounds traversal doesn't yet handle displacement mapping and instancing that the vsg::LineSegmentIntersector can handle so I'll need to adapt ComputeBouns to utilize the full capabilities of vsg::ArrayState.

Yesterday I completed the refactor of vsg::ComputeBounds so that it fully utilise vsg::ArrayState newly extended ability to handle instanced geometries, this means that when you load a dataset that has a vertex shader that uses techniques like displacement mapping and instancing they can be handled correctly by utilities like ComputeBounds and LineSegmentIntersector.

These changes are checked into the StateComposer branch.

--

I then moved on to adding the ability to specify default values of the GraphisPipelineStates that the vsg::GraphicsPipelineConfig utility uses when setting up the pipeline states used to create the final vsg::GraphicsPipeline.  These default values are passed in via a new vsg::ShaderSet::defaultGraphicsPipelineStates member that is a list of ref_ptr<GrahcsPipelineState>.

This feature allows you to do things like select polygon mode to render a scene as wireframe, for instance to test this I added the following to the vsgstatecomposer example.

    if (arguments.read("--wireframe"))
    {
        auto rasterizationState = vsg::RasterizationState::create();
        rasterizationState->polygonMode = VK_POLYGON_MODE_LINE;
        shaderSet->defaultGraphicsPipelineStates.push_back(rasterizationState);
    }

So when you run:

> vsgstatecomposer -n 1000 --wireframe

You now get:
Wireframe.png
You can provide any of the GrahicsPipelineState as defaults, one that may be particularly will be passing in vsg::DynamicState to enable the GraphicsPipeline to be overridden for doing lines like adjust line widths (using vsg::SetLineWith) etc without having to rebuild GraphicsPpeline.

I haven't tried it yet, but this should also work for the vsgXchange::Assimp loader - just pass in your custom ShaderSet configuration via vsg::Options so I'll need to create an example for this. 

--

Feature wise SharedObjects/ShaderSet/GraphicsPpelineConfig are all now pretty close to complete and achieve all the encapsulation and application level control over that I was hoping to achieve with the StateComposer branch, so now I'm focus more of testing to help shake down the interface and implementation.  To this end I'll now look at rewriting the vsg::Text backend to use vsg::ShaderSet, all going well this will enable much more control over how text is rendered.

Robert Osfield

unread,
Apr 28, 2022, 7:04:49 AM4/28/22
to vsg-...@googlegroups.com
Hi All,

I have now refactored vsg::Text's GpuLayoutTechnique and CpuLayoutTechniques so they use vsg::ShaderSet/SharedObjects/GraphicsPilineConfig,  These changes are checked into the StateComposer branch of the VSG:


The end when running vsgtext looks the same, but it's now far more extensible as you can substitute your own shaders, by assigning the shaderSet to vsg::Options::shaderSets using the "cpuTextLayout" or "gpuTextLayout" keys.
vsgtext.png
This means I have now completed all the key features required for the StateComposer work, so I'm now ready prep merging with VSG master.

Before I merged with master I'd like feedback from the community about how the StateComposer branch is compiling and running on your platform.  vsgbuilder, vsgtext and loading any models using vsgXchange::Assimp will all use the new functionality.

While the StateComposer is broadly feature complete there are still things to be refined, but these are best done with testing it in the real world finding out what works well/what is still awkward and then refining what needs to be improved.

There are also further opportunities to leverage vsg::ShaderSet to stream line the VSG code base and shaders.  For instance I think it would be good to merge the Cpu and GpuLayoutTechinques ShaderSets and use #pragma(tic) shader composition to specialize the vertex shaders.  I also would like to refine the naming of standard vertex attributes and uniforms in the shaders so it feels less adhoc.  I would also like to see if we can merge the material uniform struct so rather than having three variants we just have one. 

This further work needn't hold back merging the StateComposer branch with VSG master.

Cheers,
Robert.



Rainer Gericke

unread,
Apr 28, 2022, 12:02:02 PM4/28/22
to vsg-users : VulkanSceneGraph Developer Discussion Group
Hi Robert,

I just tried to build vsg and vsgExchange, both in the StateComposer branch. Vsg build, but vsgXchange doesn't, due to missing header files:

-- Build files have been written to: /Volumes/Ramdisk/vsgXchange

[16/32] Building CXX object src/CMakeFiles/vsgXchange.dir/Release/stbi/stbi.cpp.o

FAILED: src/CMakeFiles/vsgXchange.dir/Release/stbi/stbi.cpp.o 

/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++ -DASSIMP_VERSION_MAJOR=5 -DASSIMP_VERSION_MINOR=2 -DASSIMP_VERSION_PATCH=0 -DBASISD_SUPPORT_FXT1=0 -DBASISU_NO_ITERATOR_DEBUG_LEVEL -DKHRONOS_STATIC -DKTX_FEATURE_KTX1 -DKTX_FEATURE_KTX2 -DLIBKTX -DUSE_FREETYPE -DCMAKE_INTDIR=\"Release\" -I/Users/rainerge/Documents/GitHub/vsgXchange/include -I/Volumes/Ramdisk/vsgXchange/include -I/Users/rainerge/Documents/GitHub/vsgXchange/src/ktx/libktx -I/opt/homebrew/include/freetype2 -isystem /Users/rainerge/Soft/Packs/VSG/include -isystem /usr/local/include -isystem /Users/rainerge/Soft/Packs/Deps/include -O3 -DNDEBUG -arch arm64 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk -fPIC -std=gnu++17 -MD -MT src/CMakeFiles/vsgXchange.dir/Release/stbi/stbi.cpp.o -MF src/CMakeFiles/vsgXchange.dir/Release/stbi/stbi.cpp.o.d -o src/CMakeFiles/vsgXchange.dir/Release/stbi/stbi.cpp.o -c /Users/rainerge/Documents/GitHub/vsgXchange/src/stbi/stbi.cpp

/Users/rainerge/Documents/GitHub/vsgXchange/src/stbi/stbi.cpp:16:10: fatal error: 'vsg/io/ObjectCache.h' file not found

#include <vsg/io/ObjectCache.h>

         ^~~~~~~~~~~~~~~~~~~~~~

1 error generated.

[17/32] Building CXX object src/CMakeFiles/vsgXchange.dir/Release/dds/dds.cpp.o

FAILED: src/CMakeFiles/vsgXchange.dir/Release/dds/dds.cpp.o 

/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++ -DASSIMP_VERSION_MAJOR=5 -DASSIMP_VERSION_MINOR=2 -DASSIMP_VERSION_PATCH=0 -DBASISD_SUPPORT_FXT1=0 -DBASISU_NO_ITERATOR_DEBUG_LEVEL -DKHRONOS_STATIC -DKTX_FEATURE_KTX1 -DKTX_FEATURE_KTX2 -DLIBKTX -DUSE_FREETYPE -DCMAKE_INTDIR=\"Release\" -I/Users/rainerge/Documents/GitHub/vsgXchange/include -I/Volumes/Ramdisk/vsgXchange/include -I/Users/rainerge/Documents/GitHub/vsgXchange/src/ktx/libktx -I/opt/homebrew/include/freetype2 -isystem /Users/rainerge/Soft/Packs/VSG/include -isystem /usr/local/include -isystem /Users/rainerge/Soft/Packs/Deps/include -O3 -DNDEBUG -arch arm64 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk -fPIC -std=gnu++17 -MD -MT src/CMakeFiles/vsgXchange.dir/Release/dds/dds.cpp.o -MF src/CMakeFiles/vsgXchange.dir/Release/dds/dds.cpp.o.d -o src/CMakeFiles/vsgXchange.dir/Release/dds/dds.cpp.o -c /Users/rainerge/Documents/GitHub/vsgXchange/src/dds/dds.cpp

/Users/rainerge/Documents/GitHub/vsgXchange/src/dds/dds.cpp:16:10: fatal error: 'vsg/io/ObjectCache.h' file not found

#include <vsg/io/ObjectCache.h>

         ^~~~~~~~~~~~~~~~~~~~~~

1 error generated.

[25/32] Building CXX object src/CMakeFiles/vsgXchange.dir/Release/ktx/ktx.cpp.o

ninja: build stopped: subcommand failed.


ptrfun

unread,
Apr 28, 2022, 12:18:11 PM4/28/22
to vsg-users : VulkanSceneGraph Developer Discussion Group

I think he merged those features into another header and class. I commented out the include and it compiled so I can only guess nothing depends on it. I'm not sure if that was the right call, so I'd wait for Robert's word on it

Robert Osfield

unread,
Apr 28, 2022, 12:48:16 PM4/28/22
to vsg-...@googlegroups.com
Hi Rainer,

Thanks for the testing.  Looks like my local header install still has the ObjectCache.h which has no been removed, so vsgXchange has kept compiling even though it shouldn't have.  I'll scrub my include install and do a fresh build of everything.

Cheers,
Robert.

Robert Osfield

unread,
Apr 28, 2022, 12:59:26 PM4/28/22
to vsg-...@googlegroups.com
HI Rainer,

On Thu, 28 Apr 2022 at 17:48, Robert Osfield <robert....@gmail.com> wrote:
Thanks for the testing.  Looks like my local header install still has the ObjectCache.h which has no been removed, so vsgXchange has kept compiling even though it shouldn't have.  I'll scrub my include install and do a fresh build of everything.

I've now done this and found the vsgXchage::stbi and vsgXchage::dds loaders both include ObjectCache.h, neither actually used the ObjectCache so there was no need for them to include it.  This fixes are checked into vsgXchange StateComposer branch.

Cheers,
Robert.
 

Robert Osfield

unread,
Apr 28, 2022, 1:11:53 PM4/28/22
to vsg-...@googlegroups.com
On Thu, 28 Apr 2022 at 12:04, Robert Osfield <robert....@gmail.com> wrote:
There are also further opportunities to leverage vsg::ShaderSet to stream line the VSG code base and shaders.  For instance I think it would be good to merge the Cpu and GpuLayoutTechinques ShaderSets and use #pragma(tic) shader composition to specialize the vertex shaders. 

I have gone ahead and merged the "cpuTextLayout" and "gpuTextLayout" ShaderSets into a single "text" ShaderSet that uses #pramat(ic) shader composition to switch between the two sets of code paths in the text.vert shader.

Githib reports "139 additions and 504 deletions" for this commit which is NICE :-)

By combining into a single ShaderSet dedicated to rendering text it makes it easier to substitute your own ShaderSet.  You custom ShaderSet will still need to provide the same array and uniform bindings as the existing text ShaderSet to ensure that the CpuLayoutTechnique and GpuLayoutTechnique can still find what they need. 

To provide your own ShaderSet you'd do something like:

   font->options = vsg::Options::create();
   font->options->shaderSets["text"] = createMyTextShaderSet();

Then when the text setup function is automatically called the appropriate CpuLayoutTechnique or GpuLayoutTechnique will use your custom ShaderSet instead of the built in one.

Cheers,
Robert.

Rainer Gericke

unread,
Apr 28, 2022, 1:30:48 PM4/28/22
to vsg-users : VulkanSceneGraph Developer Discussion Group
Hi Robert,

thanks, I tried it and it builds and works now.

Best

Rainer

Robert Osfield

unread,
Apr 28, 2022, 4:59:00 PM4/28/22
to vsg-...@googlegroups.com
On Thu, 28 Apr 2022 at 18:30, Rainer Gericke <dr.ratz...@gmail.com> wrote:
thanks, I tried it and it builds and works now.

Good to hear, thanks for the testing.
 

Roland Hill

unread,
Apr 28, 2022, 9:54:28 PM4/28/22
to vsg-...@googlegroups.com
Hi Robert,

Just pulled vsg, vsgXchange and vsgImgui. Everything builds and runs using StateComposer branches on Kubuntu 20.04. I've only tested using my application, but it uses many StateComposer features.

The only possible problem I've noted is that I open a DXF file via vsgXchange and the TwoSided option doesn't appear to be working with StateComposer branch. I'm fairly certain this used to come in OK in the past. I'll investigate it more thoroughly - I'm in git branch disarray at present.

Cheers,
Roland


--
You received this message because you are subscribed to the Google Groups "vsg-users : VulkanSceneGraph Developer Discussion Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to vsg-users+...@googlegroups.com.
To view this discussion on the web, visit https://groups.google.com/d/msgid/vsg-users/CAFN7Y%2BUBq9XdnS-GS5ntyfb94pfJKrPFERueV84LPAb_AQdj9w%40mail.gmail.com.

Robert Osfield

unread,
Apr 29, 2022, 3:44:47 AM4/29/22
to vsg-users : VulkanSceneGraph Developer Discussion Group
Hi Roland,

On Friday, 29 April 2022 at 02:54:28 UTC+1 role...@gmail.com wrote:
Just pulled vsg, vsgXchange and vsgImgui. Everything builds and runs using StateComposer branches on Kubuntu 20.04. I've only tested using my application, but it uses many StateComposer features.

Good to hear it's working fine.
 
The only possible problem I've noted is that I open a DXF file via vsgXchange and the TwoSided option doesn't appear to be working with StateComposer branch. I'm fairly certain this used to come in OK in the past. I'll investigate it more thoroughly - I'm in git branch disarray at present.

Is there a DXF model that I could test? The assimp repo has some:

~/3rdParty/assimp/test/models/DXF$ ls
issue_2229.dxf  lineTest.dxf  PinkEggFromLW.dxf  wuson.dxf

I've just tried these and don't know what I should be expecting.  The look to render without proper normal assignment, I'll look into what we should expect.

--

My plan for today is to do a code review of the respective StateComposer branches and clean up loose ends with the aim to merge with VSG, vsgXchange and vsgExamples master today, after 6 weeks working on it I'll be relieved to get this big update merged.  Hopefully this will reduce the juggling of branches that users are currently needing to do to play with the new work.

Robert Osfield

unread,
Apr 29, 2022, 11:01:48 AM4/29/22
to vsg-...@googlegroups.com
Hi All,

With some help from Roland I have been able to reproduce the problem he was seeing, and have now fixed the issues checking into VSG, vsgXchange and vsgExample StateComposer branches.  The problem related to the handling of the two_sided hint and how it affects the back face culling and handling of two sided lighting in the shader.

With those fixes checked in I'll now restart my general review of StateComposer changes.

Cheers,
Robert.

Robert Osfield

unread,
Apr 29, 2022, 12:38:32 PM4/29/22
to vsg-...@googlegroups.com
I have now completed my code review, clean ups and one final addition to ShaderSet - a std::vector<std::string> optionalDefines member that enables ShaderSet's to declare what optional defines can be used to enable features in the shader, beyond the ones associated the array and uniform defines:

 
The intention is to use this list of defines for reference so you know what capabilities there are in a ShaderSet, rather for it to be automatically applied to the shaders, so you'll need to explicitly enables these defines - the vsg::Text, vsg::Builder classes and vsgXchange::assimp loader both do this, explicitly adding the extra defines to enable specific features.

You'll see code in the form:

   auto& defines = graphicsPipelineConfig->shaderHints->defines;
   if (stateInfo.greyscale) defines.push_back("VSG_GREYSACLE_DIFFUSE_MAP");

Do this prior to the graphicsPipelineConfig->init() call so that these extra defines are utilized.

This final feature means that StateComposer work is now feature complete :-)

Robert Osfield

unread,
Apr 29, 2022, 2:02:45 PM4/29/22
to vsg-...@googlegroups.com
Hi All,

6 Weeks work on state composition is now completed, and merged with VulkanSceneGraph, vsgXchange and vsgExamples masters. I've bumped the VulkanSceneGraph version to 0.3.0 to signify when this work was added.  The respective changes are:

Changes to VulkanSceneGraph:

Changes to vsgXchange:

Changes to vsgExamples:

This is the final major block of functionality I have planned before 1.0, so we're getting pretty close to feature complete.  There are still a few items left to do but fingers crossed none will take me 6 weeks to complete!  I'll start a separate thread here on vsg-users on Monday about remaining work.

Thanks to all those that have helped with testing and constructive suggestions, pretty chuffed with how this work came together.

Cheers,
Robert.




Roland Hill

unread,
Apr 30, 2022, 5:49:54 AM4/30/22
to vsg-...@googlegroups.com
Hi Robert,

I pulled fresh copies of everything today, built the master branches and it all appears to be working. Congratulations on getting StateComposer to this point, there is a lot of functionality for us all to explore.

Cheers,
Roland


--
You received this message because you are subscribed to the Google Groups "vsg-users : VulkanSceneGraph Developer Discussion Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to vsg-users+...@googlegroups.com.

Roland Hill

unread,
Apr 30, 2022, 7:54:24 AM4/30/22
to vsg-...@googlegroups.com
Hi Robert,

The back face culling is fixed when using two_sided hint, however there is still an issue with the illumination on the side facing away from the light - it works as it should using vsgviewer, but not in my own application. It's been tricky to pin down. The following all use master branches after the StateComposer merge.

Calling /vsgviewer /home/roland/Source/si/Test/cs.dxf produces a flat looking image because we need two_sided and generate_sharp_normals, so I modified vsgviewer as follows:

auto options = vsg::Options::create();
options->fileCache = vsg::getEnv("VSG_FILE_CACHE");
// options->paths = vsg::getEnvPaths("VSG_FILE_PATH");
options->setValue("generate_sharp_normals", true);
options->setValue("two_sided", true);


I also commented out VSG_FILE_PATH to force vsgviewer to use assimp_pbr_frag.cpp instead of finding assimp_pbr.frag file to ensure there is no difference there (they are the same anyway as I compared them).

Running /vsgviewer /home/roland/Source/si/Test/cs.dxf now shows as image as expected with shading on both sides as expected.

Then I load the same image in my application and the side away from the light looks flat while the side facing the light is properly shaded (just like it was using vsgviewer). You can see both sides in the image below. If I move the light to the other side, then the flat and shaded sides switch.
image.png
Both applications are using the VSG_VIEW_LIGHT_DATA code path of the PBR shader. I showed this by adding outColor = vec4(1.0, 0.0, 1.0, 1.0) at the end of that code path and the images were pink in both applications.

The only difference I can think of is that I set my lights up at the top of the scene graph as follows:
_directionalLight = vsg::DirectionalLight::create();
_directionalLight->name = "Sun Directional";
_directionalLight->color.set(1.0, 1.0, 1.0);
_directionalLight->intensity = 0.6;
_directionalLight->direction.set(-0.58, -0.58, -0.58);

_ambientLight = vsg::AmbientLight::create();
_ambientLight->name = "Sun Ambient";
_ambientLight->color.set(1.0, 1.0, 1.0);
_ambientLight->intensity = 0.25;

I can't see where vsgviewer sets up its lights, but it does use the same VSG_VIEW_LIGHT_DATA code path in the pbr shader, so I guess there is a default somewhere, probably in vsgXchange. Could vsgXchange be modifying my existing lights?  

Cheers,
Roland
--
You received this message because you are subscribed to the Google Groups "vsg-users : VulkanSceneGraph Developer Discussion Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to vsg-users+...@googlegroups.com.

Roland Hill

unread,
Apr 30, 2022, 8:05:34 AM4/30/22
to vsg-...@googlegroups.com
Hi Robert,

Just trying something else. If I add lights manually to vsgviewer as follows
auto group = vsg::Group::create();

auto directionalLight = vsg::DirectionalLight::create();
directionalLight->name = "Sun Directional";
directionalLight->color.set(1.0, 1.0, 1.0);
directionalLight->intensity = 0.6;
directionalLight->direction.set(-0.58, -0.58, -0.58);

auto ambientLight = vsg::AmbientLight::create();
ambientLight->name = "Sun Ambient";
ambientLight->color.set(1.0, 1.0, 1.0);
ambientLight->intensity = 0.25;

group->addChild(directionalLight);
group->addChild(ambientLight);

vsg::Path path;

// read any vsg files
for (int i = 1; i < argc; ++i)
{
vsg::Path filename = arguments[i];
path = vsg::filePath(filename);


Then the image looks correctly shaded, but saturates, so vsgXchange appears to be adding additional lights on top of those already specified. This doesn't really explain the lack of shading that I observed, but does pose the question whether adding lights in vsgXchange can be disabled.

Cheers,
Roland

On Sat, 30 Apr 2022 at 01:01, Robert Osfield <robert....@gmail.com> wrote:
--
You received this message because you are subscribed to the Google Groups "vsg-users : VulkanSceneGraph Developer Discussion Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to vsg-users+...@googlegroups.com.

Robert Osfield

unread,
Apr 30, 2022, 8:56:38 AM4/30/22
to vsg-...@googlegroups.com
Hi Roland,

A quick reply as I'm "not" working today...

vsgXchange::assimp doesn't add any lights to the scene graph so this won't be the source of the issues.

The vsg::createRenderGraph..(..) method does optionally invoke assignment of a directional headlight and ambient light, this exists to ensure there is at least something to light a scene graph that leverages ViewDependentState related shaders - otherwise everything you load would be black unless you explicitly added a light.

When setting up the viewer yourself, if you are adding your own lights you should disable the default creation of lights.  Have a look at the vsglights example as it does this.

Cheers,
Robert.

Roland Hill

unread,
May 1, 2022, 6:45:52 PM5/1/22
to vsg-...@googlegroups.com
Hi Robert,

Thanks for the tip. I've modified vsgviewer further so that it doesn't add its own lights and only uses the ones specified. This now replicates the flat shading that I see in my application using a static diffuse light rather than the view dependant headlight. Please see attached modified vsgviewer.

The modifications are:
  1. Set two_sided and generate_sharp_normals. Disable VSG_FILE_PATH
  2. Manually add 1 ambient light and 1 directional light
  3. Disable createCommandGraphForView and manually set up view/rendergraph/commandgraph.
Just call ./vsgviewer cs.dxf

Cheers,
Roland


--
You received this message because you are subscribed to the Google Groups "vsg-users : VulkanSceneGraph Developer Discussion Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to vsg-users+...@googlegroups.com.
vsgviewer.cpp

Robert Osfield

unread,
May 2, 2022, 4:06:12 AM5/2/22
to vsg-...@googlegroups.com
Hi Roland,

Thanks for the example. 

FYI, the createCommandGraphForView(..) function allows you to switch off the lights that are added by default by passing in false to override the default of true.  This is a bit more streamlined than setting the RenderGraph explicitly:

        auto commandGraph = vsg::createCommandGraphForView(window, camera, vsg_scene, VK_SUBPASS_CONTENTS_INLINE, false);

I'm not entirely happy with having the lights added in by default, so may make it something done explicitly.  I went with the default approach to avoid everyone suddenly getting black scenes.  Not adding by default would require us all to explicitly add the lighting we want.  The pros and cons of how to go about this is probably best kept for a dedicated thread.

I'm now investigating why the lights aren't being utilized as expected, at this point I'm mostly just stretching my head why it doesn't work as expected...

Cheers,
Robert.

Robert Osfield

unread,
May 2, 2022, 4:25:52 AM5/2/22
to vsg-...@googlegroups.com
Hi Roland,

Things weren't working for me because I hadn't done a git pull on my VSG after checking out master....  What a rookie...

So now I'm getting your model lit on the one side and shadow on the other, despite the VSG_TWO_SIDED_LIGHTING #define's being enabled in the shader.  I'll dig into the shaders.

Robert.

Robert Osfield

unread,
May 2, 2022, 4:52:40 AM5/2/22
to vsg-...@googlegroups.com
On Mon, 2 May 2022 at 09:25, Robert Osfield <robert....@gmail.com> wrote:
So now I'm getting your model lit on the one side and shadow on the other, despite the VSG_TWO_SIDED_LIGHTING #define's being enabled in the shader.  I'll dig into the shaders.

I have dug into the PBR shaders and my quick change to support two sided lighting was modifying the normal orientation based on the wrong light direction.  There was a light direction value set by the old pre vsg::Light code paths that I was used and this old code pat is hardwired for a headlight direction.  I didn't see the problem because I was using the default headlight in testing.

I am going to rip out the old hardwired lighting code to avoid these potential pitfalls of mixing and matching old and new.  I should be able to resolve this all today.
 


 

Robert Osfield

unread,
May 2, 2022, 11:11:18 AM5/2/22
to vsg-...@googlegroups.com
On Mon, 2 May 2022 at 09:52, Robert Osfield <robert....@gmail.com> wrote:
I have dug into the PBR shaders and my quick change to support two sided lighting was modifying the normal orientation based on the wrong light direction.  There was a light direction value set by the old pre vsg::Light code paths that I was used and this old code pat is hardwired for a headlight direction.  I didn't see the problem because I was using the default headlight in testing.

I am going to rip out the old hardwired lighting code to avoid these potential pitfalls of mixing and matching old and new.  I should be able to resolve this all today.

I have just rewritten the handling of two sided lighting in the Phong and PBR shaders, I have also standardized on the vsg::Light code paths in the shaders so there aren't any of the old hardwired shader code paths.

These changes are all checked into VSG master and vsgExamples master.

Roland, could you test these changes out?

Cheers,
Robert

Roland Hill

unread,
May 2, 2022, 8:06:40 PM5/2/22
to vsg-...@googlegroups.com
Hi Robert,

I tested the updated version and it all looks good to me. My dodgy dxf renders perfectly with two_sided and generate_sharp_normals set and Builder generated spheres work perfectly. I've also got a textured surface with my own shader and ShaderSet which continues to work as before.

Thanks again for your efforts and great support, Robert.

Cheers,
Roland


--
You received this message because you are subscribed to the Google Groups "vsg-users : VulkanSceneGraph Developer Discussion Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to vsg-users+...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages