metaforce/hecl/include/hecl/PipelineBase.hpp

223 lines
9.2 KiB
C++

#pragma once
#include <cstddef>
#include <cstdint>
#include <string_view>
#include <type_traits>
#include <vector>
#include "hecl/Compilers.hpp"
#include <boo/graphicsdev/IGraphicsDataFactory.hpp>
#include <xxhash/xxhash.h>
#define HECL_RUNTIME 1
namespace hecl {
using AdditionalPipelineInfo = boo::AdditionalPipelineInfo;
enum class StageTargetType { SourceText, Binary, Runtime };
enum class PipelineTargetType {
StageSourceTextCollection,
StageBinaryCollection,
StageRuntimeCollection,
FinalPipeline
};
template <typename P, typename S>
class StageConverter;
template <typename P>
class PipelineConverter;
#if HECL_RUNTIME
using FactoryCtx = boo::IGraphicsDataFactory::Context;
#else
struct FactoryCtx {};
#endif
template <typename P, typename S>
class StageRep {
public:
using Platform = P;
using Stage = S;
};
template <typename P>
class PipelineRep {
public:
using Platform = P;
};
class GeneralShader : public hecl::PipelineRep<hecl::PlatformType::Null> {};
class TessellationShader : public hecl::PipelineRep<hecl::PlatformType::Null> {};
template <typename P, typename S>
class StageSourceText : public StageRep<P, S> {
std::string_view m_text;
uint64_t m_hash;
public:
static constexpr StageTargetType TargetType = StageTargetType::SourceText;
static constexpr PipelineTargetType PipelineTarget = PipelineTargetType::StageSourceTextCollection;
static constexpr bool HasHash = true;
uint64_t Hash() const { return m_hash; }
explicit StageSourceText(std::string_view text) : m_text(text), m_hash(XXH64(m_text.data(), m_text.size(), 0)) {}
std::string_view text() const { return m_text; }
};
template <typename P, typename S>
class StageBinary : public StageRep<P, S> {
StageBinaryData m_ownedData;
const uint8_t* m_data = nullptr;
size_t m_size = 0;
uint64_t m_hash = 0;
public:
static constexpr StageTargetType TargetType = StageTargetType::Binary;
static constexpr PipelineTargetType PipelineTarget = PipelineTargetType::StageBinaryCollection;
static constexpr bool HasHash = true;
uint64_t Hash() const { return m_hash; }
StageBinary(const uint8_t* data, size_t size) : m_data(data), m_size(size) { m_hash = XXH64(m_data, m_size, 0); }
StageBinary(StageBinaryData data, size_t size)
: m_ownedData(std::move(data)), m_data(m_ownedData.get()), m_size(size) {
m_hash = XXH64(m_data, m_size, 0);
}
explicit StageBinary(std::pair<StageBinaryData, size_t> data) : StageBinary(data.first, data.second) {}
StageBinary(StageConverter<P, S>& conv, FactoryCtx& ctx, const StageSourceText<P, S>& in)
: StageBinary(CompileShader<P, S>(in.text())) {}
const uint8_t* data() const { return m_data; }
size_t size() const { return m_size; }
};
template <typename P>
class FinalPipeline;
template <class T>
using __IsStageSubclass =
typename std::disjunction<std::is_base_of<StageRep<typename T::Platform, PipelineStage::Vertex>, T>,
std::is_base_of<StageRep<typename T::Platform, PipelineStage::Fragment>, T>,
std::is_base_of<StageRep<typename T::Platform, PipelineStage::Geometry>, T>,
std::is_base_of<StageRep<typename T::Platform, PipelineStage::Control>, T>,
std::is_base_of<StageRep<typename T::Platform, PipelineStage::Evaluation>, T>>;
template <class T>
inline constexpr bool __IsStageSubclass_v = __IsStageSubclass<T>::value;
template <typename T>
class StageCollection;
template <template <typename, typename> class T, typename P, typename... Rest>
class StageCollection<T<P, Rest...>> : public PipelineRep<P> {
using base = PipelineRep<P>;
friend class FinalPipeline<P>;
static_assert(__IsStageSubclass_v<T<P, PipelineStage::Vertex>>,
"Stage Collection may only be specialized with StageRep subclasses");
T<P, PipelineStage::Vertex> m_vertex;
T<P, PipelineStage::Fragment> m_fragment;
T<P, PipelineStage::Geometry> m_geometry;
T<P, PipelineStage::Control> m_control;
T<P, PipelineStage::Evaluation> m_evaluation;
AdditionalPipelineInfo m_additionalInfo;
std::vector<boo::VertexElementDescriptor> m_vtxFmtData;
boo::VertexFormatInfo m_vtxFmt;
uint64_t m_hash;
public:
static constexpr PipelineTargetType TargetType = T<P, PipelineStage::Vertex>::PipelineTarget;
static constexpr bool HasHash = T<P, PipelineStage::Vertex>::HasHash;
template <typename U = StageCollection<T<P, Rest...>>>
std::enable_if_t<U::HasHash, uint64_t> Hash() const {
return m_hash;
}
template <typename U = StageCollection<T<P, Rest...>>>
void MakeHash(std::enable_if_t<!U::HasHash>* = 0) {}
template <typename U = StageCollection<T<P, Rest...>>>
void MakeHash(std::enable_if_t<U::HasHash>* = 0) {
m_hash = 0;
m_hash ^= m_vertex.Hash();
m_hash ^= m_fragment.Hash();
m_hash ^= m_geometry.Hash();
m_hash ^= m_control.Hash();
m_hash ^= m_evaluation.Hash();
m_hash ^= XXH64(&m_additionalInfo, sizeof(m_additionalInfo), 0);
}
template <typename I>
StageCollection(PipelineConverter<P>& conv, FactoryCtx& ctx, const I& in,
typename std::enable_if_t<std::is_base_of_v<GeneralShader, I>>* = 0) {
m_vertex = conv.getVertexConverter().convert(ctx, in);
m_fragment = conv.getFragmentConverter().convert(ctx, in);
m_vtxFmt = in.VtxFmt;
m_additionalInfo = in.PipelineInfo;
MakeHash();
}
template <typename I>
StageCollection(
PipelineConverter<P>& conv, FactoryCtx& ctx, const I& in,
typename std::enable_if_t<std::conjunction_v<std::is_base_of<TessellationShader, I>,
std::negation<std::is_same<P, PlatformType::Metal>>>>* = 0) {
m_vertex = conv.getVertexConverter().convert(ctx, in);
m_fragment = conv.getFragmentConverter().convert(ctx, in);
if (in.HasTessellation) {
m_control = conv.getControlConverter().convert(ctx, in);
m_evaluation = conv.getEvaluationConverter().convert(ctx, in);
}
m_vtxFmt = in.VtxFmt;
m_additionalInfo = in.PipelineInfo;
MakeHash();
}
template <typename I>
StageCollection(PipelineConverter<P>& conv, FactoryCtx& ctx, const I& in,
typename std::enable_if_t<std::conjunction_v<std::is_base_of<TessellationShader, I>,
std::is_same<P, PlatformType::Metal>>>* = 0) {
if (in.HasTessellation) {
m_control = conv.getControlConverter().convert(ctx, in);
m_evaluation = conv.getEvaluationConverter().convert(ctx, in);
} else {
m_vertex = conv.getVertexConverter().convert(ctx, in);
}
m_fragment = conv.getFragmentConverter().convert(ctx, in);
m_vtxFmt = in.VtxFmt;
m_additionalInfo = in.PipelineInfo;
MakeHash();
}
StageCollection(const T<P, PipelineStage::Vertex>& vertex, const T<P, PipelineStage::Fragment>& fragment,
const T<P, PipelineStage::Geometry>& geometry, const T<P, PipelineStage::Control>& control,
const T<P, PipelineStage::Evaluation>& evaluation, const AdditionalPipelineInfo& info,
const boo::VertexFormatInfo& vtxFmt)
: m_vertex(vertex)
, m_fragment(fragment)
, m_geometry(geometry)
, m_control(control)
, m_evaluation(evaluation)
, m_additionalInfo(info)
, m_vtxFmt(vtxFmt) {}
};
} // namespace hecl
#ifndef _WIN32
#define _STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, P) \
template <> \
const hecl::StageBinary<P, hecl::PipelineStage::Vertex> T<P, hecl::PipelineStage::Vertex>::Prototype; \
template <> \
const hecl::StageBinary<P, hecl::PipelineStage::Fragment> T<P, hecl::PipelineStage::Fragment>::Prototype; \
template <> \
const hecl::StageBinary<P, hecl::PipelineStage::Geometry> T<P, hecl::PipelineStage::Geometry>::Prototype; \
template <> \
const hecl::StageBinary<P, hecl::PipelineStage::Control> T<P, hecl::PipelineStage::Control>::Prototype; \
template <> \
const hecl::StageBinary<P, hecl::PipelineStage::Evaluation> T<P, hecl::PipelineStage::Evaluation>::Prototype;
#else
#define _STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, P)
#endif
#define STAGEOBJECT_PROTOTYPE_DECLARATIONS(T) \
_STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, hecl::PlatformType::OpenGL) \
_STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, hecl::PlatformType::Vulkan) \
_STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, hecl::PlatformType::D3D11) \
_STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, hecl::PlatformType::Metal) \
_STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, hecl::PlatformType::NX)