Skip to content

Rework pipeline shader spec info #871

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 26 commits into
base: stagesless_shaders
Choose a base branch
from

Conversation

kevyuu
Copy link

@kevyuu kevyuu commented Apr 28, 2025

Rework SShaderSpecInfo for ICPUPipeline to be more mutable:
IPipelineBase::SShaderSpecInfo should be templated on a boolean being mutable or not, such that members are conditional_t, and for CPU:

  • shader is smart pointer
  • entryPoint is a string and not a string view
  • entries are an actual unordered_map instead of a pointer to one (this is bit complicated cause you need to make IPipelineBase::SShaderSpecInfo::SSpecConstantValue hold a vector of uint8_t instead of a const void* + size

@devshgraphicsprogramming devshgraphicsprogramming changed the base branch from master to stagesless_shaders April 28, 2025 14:03
Comment on lines 33 to 41
if (shader)
{
auto stageInfo = m_stages[i].info;
core::smart_refctd_ptr<IShader> newShader;
if (_depth>0u)
{
newShader = core::smart_refctd_ptr_static_cast<IShader>(shader->clone(_depth-1u));
stageInfo.shader = newShader.get();
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make a method which handles cloning a single SpecInfo in a pipeline given this clone depth, make it final and protected in this class

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my bad, make it a method of the SpecInfo

Comment on lines 74 to 81
inline bool setSpecInfo(const IPipelineBase::SShaderSpecInfo<true>& info)
{
return stage!=hlsl::ShaderStage::ESS_COMPUTE ? (-1):0;
const auto specSize = info.valid();
if (specSize < 0) return false;
if (info.stage != hlsl::ESS_COMPUTE) return false;
m_specInfo = info;
return true;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need public by-reference getters for the spec info, one const and one non-const which checks/asserts if the asset is mutable

I think we can check validity of spec constants and spec info later when hashing and optionally also add a bool valid() const which tells you if the spec infos you have make sense

Comment on lines 20 to 24
explicit ICPUComputePipeline(const ICPUPipelineLayout* layout):
base_t(core::smart_refctd_ptr<ICPUPipelineLayout>(layout))
{}

static core::smart_refctd_ptr<ICPUComputePipeline> create(const ICPUPipelineLayout* layout)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

have one, either the create factory or a public constructor, make the constructor private

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

return core::smart_refctd_ptr<ICPUComputePipeline>(retval,core::dont_grab);
}

inline core::smart_refctd_ptr<IAsset> clone(uint32_t _depth = ~0u) const override final
inline base_t* clone_impl(core::smart_refctd_ptr<const ICPUPipelineLayout>&& layout, uint32_t depth) const override final
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think an override can change the return type of a function, it needs to return core::smart_refctd_ptr<IAsset>

also clone_impl spawns a new object which needs to be reference counted, therefore it needs to return a smart pointer and not a raw pointer

ok I see that clone_impl is a new function you added to ICPUPipeline, but I would not prioritise keeping code DRY and abstracting away a smart pointer assign, over readability so please return smartpointer

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw clone_impl shall be private or protected

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also you can mark the whole ICPUComputePipeline class as final

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment on lines -35 to +31
enum class FLAGS : uint64_t
{
NONE = 0, // disallowed in maintanance5
DISABLE_OPTIMIZATIONS = 1<<0,
ALLOW_DERIVATIVES = 1<<1,

// I can just derive this
//DERIVATIVE = 1<<2,
public:
enum class CreationFlags : uint64_t

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you want an alias of FLAGS = CreationFlags so you con't need to refactor lots of code

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment on lines 34 to 46
struct SSpecConstantValue
{
core::vector<uint8_t> data;
inline operator bool() const { return data.size(); }
inline size_t size() const { return data.size(); }
};

inline SSpecConstantValue* getSpecializationByteValue(const spec_constant_id_t _specConstID)
{
const auto found = entries.find(_specConstID);
if (found != entries.end() && bool(found->second)) return &found->second;
else return nullptr;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not just alias/typedef SSpecConstantValue = cover::vector<uint8_t> ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

core::smart_refctd_ptr<IShader> shader = nullptr;
std::string entryPoint = "";
IPipelineBase::SUBGROUP_SIZE requiredSubgroupSize : 3 = IPipelineBase::SUBGROUP_SIZE::UNKNOWN; //!< Default value of 8 means no requirement
uint8_t requireFullSubgroups : 1 = false;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm maybe we should have a ICPUComputePipeline::SShaderSpecInfo : ICPUPipeline::SShaderSpecInfo to enforce even more strong typing (no requireFullSubgroup in pipelines not using the compute or mesh stage)

Comment on lines 124 to 129
core::smart_refctd_ptr<ICPUPipelineLayout> layout;
if (_depth>0u && getLayout())
layout = core::smart_refctd_ptr_static_cast<ICPUPipelineLayout>(getLayout->clone(_depth-1u));

auto* newPipeline = clone_impl(std::move(layout), _depth);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

geenerally speaking you can return nullptr right away if there isn't a valid layout

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment on lines 94 to 96
// Common Base class for pipelines
template<typename PipelineNonAssetBase>
class IGPUPipeline : public IBackendObject, public PipelineNonAssetBase, public IGPUPipelineBase

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment about PipelineNonAssetBase

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should probably be called PipelineNonBackendObjectBase and have a constraint like I suggested

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

// If you set this, then we don't take `basePipelineIndex` into account, the pointer takes precedence

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't touch my comment

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -51,7 +50,7 @@ class IGPUComputePipeline : public IBackendObject, public asset::IPipeline<const
if (dataSize<0)
return {};
// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkComputePipelineCreateInfo.html#VUID-VkComputePipelineCreateInfo-stage-00701
if (!layout || shader.stage!=hlsl::ShaderStage::ESS_COMPUTE)
if (!layout)
return {};

uint32_t count = 0;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't we already have the entries validation somwhere ?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I swear I just say it while reviewing

Comment on lines 67 to 66
inline std::span<const spec_info_t> getShaders() const {return {&shader,1}; }
inline std::span<const SShaderSpecInfo> getShaders() const {return {&shader,1}; }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do we need getShaders in GPU Pipeline Creation parameters for ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment on lines 96 to 107
const auto stageIx = hlsl::findLSB(stage);
if (stageIx < 0 || stageIx >= GRAPHICS_SHADER_STAGE_COUNT || hlsl::bitCount(stage)!=1)
return -1;
return stageIx;
}

inline const SCachedCreationParams& getCachedCreationParams() const {return m_params;}
static inline hlsl::ShaderStage indexToStage(const int8_t index)
{
if (index < 0 || index > GRAPHICS_SHADER_STAGE_COUNT)
return hlsl::ShaderStage::ESS_UNKNOWN;
return static_cast<hlsl::ShaderStage>(hlsl::ShaderStage::ESS_VERTEX + index);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these stage-to-index functions we don't need anymore, CPU class and GPU creation params will have 5 separate named members each

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that it makes your life easier in ICPUGraphicsPipeline, but that sort of mapping shouldn't be used in IGPUGraphicsPipeline::SCreationParams, so you can move these static methods to ICPUGraphicsPipeline

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


inline const renderpass_t* getRenderpass() const {return m_renderpass.get();}
static inline bool isValidStagePresence(const core::bitflag<hlsl::ShaderStage>& stagePresence, E_PRIMITIVE_TOPOLOGY primitiveType)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe name the method hasRequiredStages

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -61,8 +81,9 @@ class IGPURayTracingPipeline : public IBackendObject, public asset::IRayTracingP
return retval;
}

inline std::span<const asset::IPipelineBase::SShaderSpecInfo> getShaders() const { return shaders; }
inline std::span<const spec_info_t> getShaders() const { return shaders; }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the creation params shouldn't need a getter, you need 6 different members (5 spans and 1 raygen)

Comment on lines 22 to 27
explicit ICPUGraphicsPipeline(const ICPUPipelineLayout* layout)
: base_t(layout, {}, {})
{}

static core::smart_refctd_ptr<ICPUGraphicsPipeline> create(const ICPUPipelineLayout* layout)
{

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again, constructor, private, create factory public

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment on lines +78 to +80
inline virtual bool valid() const override final
{
if (!m_layout) return false;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe valid() should be in IAsset and ICPUPipeline should already override and check the layout presence

Comment on lines 68 to 75
inline virtual std::span<SShaderSpecInfo> getSpecInfo(hlsl::ShaderStage stage) override final
{
const auto stageIndex = stageToIndex(stage);
if (isMutable() && stageIndex != -1)
{
return { &m_specInfos[stageIndex], 1 };
}
return {};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you also need a const flavour which doesn't check for mutability (maybe implement non-const in terms of const + isMutable() check)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

basically you got the DRY implementation backwards in ICPUPipeline

Comment on lines 50 to 53
inline virtual bool valid() const override final
{
return m_specInfo.valid();
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CPUGraphicsPipeline checks the layout, you don't check here ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

}

virtual bool valid() const = 0;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move to IAsset, I'm thinking more and more classes will need this

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants