diff --git a/hecl/.gitignore b/hecl/.gitignore index f7e5c090f..ff0ebdf01 100644 --- a/hecl/.gitignore +++ b/hecl/.gitignore @@ -1,2 +1,3 @@ DataSpecRegistry.hpp +include/hecl/ApplicationReps.hpp .DS_Store diff --git a/hecl/ApplicationTools.cmake b/hecl/ApplicationTools.cmake new file mode 100644 index 000000000..94f3d9b88 --- /dev/null +++ b/hecl/ApplicationTools.cmake @@ -0,0 +1,60 @@ +include_guard(GLOBAL) + +unset(HECL_APPLICATION_REPS_TARGETS_LIST CACHE) +unset(HECL_APPLICATION_REPS_INCLUDES_LIST CACHE) +unset(HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL CACHE) +unset(HECL_APPLICATION_PIPELINE_REPS CACHE) +unset(HECL_APPLICATION_STAGE_REPS CACHE) + +# add_pipeline_rep(my::fully::qualified::class my_class_header.hpp [UNIVERSAL]) +function(add_pipeline_rep name header) + if(IS_ABSOLUTE ${header}) + set(theHeader ${header}) + else() + set(theHeader ${CMAKE_CURRENT_SOURCE_DIR}/${header}) + endif() + if (NOT ${theHeader} IN_LIST HECL_APPLICATION_REPS_INCLUDES_LIST) + set(HECL_APPLICATION_REPS_INCLUDES_LIST "${HECL_APPLICATION_REPS_INCLUDES_LIST};${theHeader}" CACHE INTERNAL "") + endif() + if ("${ARGV2}" STREQUAL "UNIVERSAL") + set(HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL "${HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL};${name}" CACHE INTERNAL "") + else() + set(HECL_APPLICATION_PIPELINE_REPS "${HECL_APPLICATION_PIPELINE_REPS};${name}" CACHE INTERNAL "") + endif() +endfunction() + +# add_stage_rep(my::fully::qualified::class my_class_header.hpp) +function(add_stage_rep name header) + if(IS_ABSOLUTE ${header}) + set(theHeader ${header}) + else() + set(theHeader ${CMAKE_CURRENT_SOURCE_DIR}/${header}) + endif() + if (NOT ${theHeader} IN_LIST HECL_APPLICATION_REPS_INCLUDES_LIST) + set(HECL_APPLICATION_REPS_INCLUDES_LIST "${HECL_APPLICATION_REPS_INCLUDES_LIST};${theHeader}" CACHE INTERNAL "") + endif() + set(HECL_APPLICATION_STAGE_REPS "${HECL_APPLICATION_STAGE_REPS};${name}" CACHE INTERNAL "") +endfunction() + +function(add_shader_target target) + if (NOT ${target} IN_LIST HECL_APPLICATION_REPS_TARGETS_LIST) + set(HECL_APPLICATION_REPS_TARGETS_LIST "${HECL_APPLICATION_REPS_TARGETS_LIST};${target}" CACHE INTERNAL "") + endif() +endfunction() + +function(add_shader file) + get_filename_component(name ${file} NAME) + get_filename_component(dir ${file} DIRECTORY) + shaderc(${CMAKE_CURRENT_BINARY_DIR}/${dir}/shader_${name} ${file}.shader) + add_stage_rep(shader_${name} ${CMAKE_CURRENT_BINARY_DIR}/${dir}/shader_${name}.hpp) + add_pipeline_rep(shader_${name} ${CMAKE_CURRENT_BINARY_DIR}/${dir}/shader_${name}.hpp UNIVERSAL) + add_library(shader_${name} ${CMAKE_CURRENT_BINARY_DIR}/${dir}/shader_${name}.hpp ${CMAKE_CURRENT_BINARY_DIR}/${dir}/shader_${name}.cpp) + add_shader_target(shader_${name}) +endfunction() + +function(add_special_shader name) + add_stage_rep(${name} ${name}.hpp) + add_pipeline_rep(${name} ${name}.hpp UNIVERSAL) + add_library(${name} ${name}.hpp ${ARGN}) + add_shader_target(${name}) +endfunction() diff --git a/hecl/CMakeLists.txt b/hecl/CMakeLists.txt index 5e272366d..64ffbcee3 100644 --- a/hecl/CMakeLists.txt +++ b/hecl/CMakeLists.txt @@ -14,14 +14,53 @@ else() endif() endif() +include(ApplicationTools.cmake) +add_shader(test/test) + configure_file(DataSpecRegistry.hpp.in ${CMAKE_CURRENT_SOURCE_DIR}/DataSpecRegistry.hpp @ONLY) +unset(HECL_APPLICATION_REPS_INCLUDES_LOCAL) +foreach(theHeader ${HECL_APPLICATION_REPS_INCLUDES_LIST}) + set(HECL_APPLICATION_REPS_INCLUDES_LOCAL "${HECL_APPLICATION_REPS_INCLUDES_LOCAL}#include \"${theHeader}\"\n") +endforeach() +unset(HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL_LOCAL) +foreach(name ${HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL}) + set(HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL_LOCAL "${HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL_LOCAL}UNIVERSAL_PIPELINES_${name} \\\n") +endforeach() +unset(HECL_APPLICATION_PIPELINE_REPS_OPENGL_LOCAL) +unset(HECL_APPLICATION_PIPELINE_REPS_VULKAN_LOCAL) +unset(HECL_APPLICATION_PIPELINE_REPS_D3D11_LOCAL) +unset(HECL_APPLICATION_PIPELINE_REPS_METAL_LOCAL) +unset(HECL_APPLICATION_PIPELINE_REPS_NX_LOCAL) +foreach(name ${HECL_APPLICATION_PIPELINE_REPS}) + set(HECL_APPLICATION_PIPELINE_REPS_OPENGL_LOCAL "${HECL_APPLICATION_PIPELINE_REPS_OPENGL_LOCAL}OPENGL_PIPELINES_${name} \\\n") + set(HECL_APPLICATION_PIPELINE_REPS_VULKAN_LOCAL "${HECL_APPLICATION_PIPELINE_REPS_VULKAN_LOCAL}VULKAN_PIPELINES_${name} \\\n") + set(HECL_APPLICATION_PIPELINE_REPS_D3D11_LOCAL "${HECL_APPLICATION_PIPELINE_REPS_D3D11_LOCAL}D3D11_PIPELINES_${name} \\\n") + set(HECL_APPLICATION_PIPELINE_REPS_METAL_LOCAL "${HECL_APPLICATION_PIPELINE_REPS_METAL_LOCAL}METAL_PIPELINES_${name} \\\n") + set(HECL_APPLICATION_PIPELINE_REPS_NX_LOCAL "${HECL_APPLICATION_PIPELINE_REPS_NX_LOCAL}NX_PIPELINES_${name} \\\n") +endforeach() + +unset(HECL_APPLICATION_STAGE_REPS_OPENGL_LOCAL) +unset(HECL_APPLICATION_STAGE_REPS_VULKAN_LOCAL) +unset(HECL_APPLICATION_STAGE_REPS_D3D11_LOCAL) +unset(HECL_APPLICATION_STAGE_REPS_METAL_LOCAL) +unset(HECL_APPLICATION_STAGE_REPS_NX_LOCAL) +foreach(name ${HECL_APPLICATION_STAGE_REPS}) + set(HECL_APPLICATION_STAGE_REPS_OPENGL_LOCAL "${HECL_APPLICATION_STAGE_REPS_OPENGL_LOCAL}OPENGL_STAGES_${name} \\\n") + set(HECL_APPLICATION_STAGE_REPS_VULKAN_LOCAL "${HECL_APPLICATION_STAGE_REPS_VULKAN_LOCAL}VULKAN_STAGES_${name} \\\n") + set(HECL_APPLICATION_STAGE_REPS_D3D11_LOCAL "${HECL_APPLICATION_STAGE_REPS_D3D11_LOCAL}D3D11_STAGES_${name} \\\n") + set(HECL_APPLICATION_STAGE_REPS_METAL_LOCAL "${HECL_APPLICATION_STAGE_REPS_METAL_LOCAL}METAL_STAGES_${name} \\\n") + set(HECL_APPLICATION_STAGE_REPS_NX_LOCAL "${HECL_APPLICATION_STAGE_REPS_NX_LOCAL}NX_STAGES_${name} \\\n") +endforeach() + +configure_file(include/hecl/ApplicationReps.hpp.in ${CMAKE_CURRENT_SOURCE_DIR}/include/hecl/ApplicationReps.hpp @ONLY) + set(ATHENA_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/extern/athena/include) set(ATHENA_INCLUDE_DIR ${ATHENA_INCLUDE_DIR} PARENT_SCOPE) set(SQUISH_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/extern/libSquish) set(SQUISH_INCLUDE_DIR ${SQUISH_INCLUDE_DIR} PARENT_SCOPE) -set(BOO_INCLUDE_DIR extern/boo/include) +set(BOO_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/extern/boo/include) add_subdirectory(bintoc) @@ -41,7 +80,7 @@ if(NOT TARGET atdna) endif() add_definitions(${BOO_SYS_DEFINES}) -include_directories(include blender ${LOGVISOR_INCLUDE_DIR} ${ATHENA_INCLUDE_DIR} +include_directories(include blender shaderc ${LOGVISOR_INCLUDE_DIR} ${ATHENA_INCLUDE_DIR} ${BOO_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR} ${BOO_SYS_INCLUDES}) add_subdirectory(lib) add_subdirectory(blender) diff --git a/hecl/bintoc/CMakeLists.txt b/hecl/bintoc/CMakeLists.txt index 1c4df5356..be4f4c422 100644 --- a/hecl/bintoc/CMakeLists.txt +++ b/hecl/bintoc/CMakeLists.txt @@ -1,6 +1,6 @@ if(NOT CMAKE_CROSSCOMPILING) add_executable(bintoc bintoc.c) -macro(bintoc out in sym) +function(bintoc out in sym) if(IS_ABSOLUTE ${out}) set(theOut ${out}) else() @@ -16,7 +16,7 @@ macro(bintoc out in sym) add_custom_command(OUTPUT ${theOut} COMMAND $ ARGS ${theIn} ${theOut} ${sym} DEPENDS ${theIn} bintoc) -endmacro() +endfunction() ################## # Package Export # diff --git a/hecl/bintoc/hecl-bintocConfig.cmake.in b/hecl/bintoc/hecl-bintocConfig.cmake.in index dc01430ca..903e678f3 100644 --- a/hecl/bintoc/hecl-bintocConfig.cmake.in +++ b/hecl/bintoc/hecl-bintocConfig.cmake.in @@ -8,7 +8,7 @@ if(NOT TARGET bintoc AND NOT bintoc_BINARY_DIR) include("${BINTOC_CMAKE_DIR}/hecl-bintocTargets.cmake") endif() -macro(bintoc out in sym) +function(bintoc out in sym) if(IS_ABSOLUTE ${out}) set(theOut ${out}) else() @@ -24,4 +24,4 @@ macro(bintoc out in sym) add_custom_command(OUTPUT ${theOut} COMMAND $ ARGS ${theIn} ${theOut} ${sym} DEPENDS ${theIn}) -endmacro() +endfunction() diff --git a/hecl/extern/athena b/hecl/extern/athena index 488acc867..5f2611702 160000 --- a/hecl/extern/athena +++ b/hecl/extern/athena @@ -1 +1 @@ -Subproject commit 488acc867523ddce51bdf8e6975a4271997363dc +Subproject commit 5f2611702d4989097a850d8a5705184392d8510c diff --git a/hecl/extern/boo b/hecl/extern/boo index 08d632a8b..c29d837ab 160000 --- a/hecl/extern/boo +++ b/hecl/extern/boo @@ -1 +1 @@ -Subproject commit 08d632a8bd88b9a79884c2fe1cb36c00ddc400a8 +Subproject commit c29d837ab58b9dd8be67a7f2358b84e94f6ee7f9 diff --git a/hecl/include/hecl/ApplicationReps.hpp.in b/hecl/include/hecl/ApplicationReps.hpp.in new file mode 100644 index 000000000..7db7f500c --- /dev/null +++ b/hecl/include/hecl/ApplicationReps.hpp.in @@ -0,0 +1,35 @@ +/* CMake-curated application reps header */ + +#define STAGE_SPECIALIZATIONS(T, P) \ +T, \ +T, \ +T, \ +T, \ +T, + +@HECL_APPLICATION_REPS_INCLUDES_LOCAL@ + +#define HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL \ +@HECL_APPLICATION_PIPELINE_REPS_UNIVERSAL_LOCAL@ + +#define HECL_APPLICATION_PIPELINE_REPS_OPENGL \ +@HECL_APPLICATION_PIPELINE_REPS_OPENGL_LOCAL@ +#define HECL_APPLICATION_PIPELINE_REPS_VULKAN \ +@HECL_APPLICATION_PIPELINE_REPS_VULKAN_LOCAL@ +#define HECL_APPLICATION_PIPELINE_REPS_D3D11 \ +@HECL_APPLICATION_PIPELINE_REPS_D3D11_LOCAL@ +#define HECL_APPLICATION_PIPELINE_REPS_METAL \ +@HECL_APPLICATION_PIPELINE_REPS_METAL_LOCAL@ +#define HECL_APPLICATION_PIPELINE_REPS_NX \ +@HECL_APPLICATION_PIPELINE_REPS_NX_LOCAL@ + +#define HECL_APPLICATION_STAGE_REPS_OPENGL \ +@HECL_APPLICATION_STAGE_REPS_OPENGL_LOCAL@ +#define HECL_APPLICATION_STAGE_REPS_VULKAN \ +@HECL_APPLICATION_STAGE_REPS_VULKAN_LOCAL@ +#define HECL_APPLICATION_STAGE_REPS_D3D11 \ +@HECL_APPLICATION_STAGE_REPS_D3D11_LOCAL@ +#define HECL_APPLICATION_STAGE_REPS_METAL \ +@HECL_APPLICATION_STAGE_REPS_METAL_LOCAL@ +#define HECL_APPLICATION_STAGE_REPS_NX \ +@HECL_APPLICATION_STAGE_REPS_NX_LOCAL@ diff --git a/hecl/include/hecl/Backend/Backend.hpp b/hecl/include/hecl/Backend/Backend.hpp index e3d8e8fbd..8f6433798 100644 --- a/hecl/include/hecl/Backend/Backend.hpp +++ b/hecl/include/hecl/Backend/Backend.hpp @@ -75,6 +75,167 @@ public: virtual void reset(const IR& ir, Diagnostics& diag)=0; }; +/** + * @brief Hash subclass for identifying shaders and their metadata + */ +class ShaderTag : public Hash +{ + union + { + uint64_t m_meta = 0; + struct + { + uint8_t m_colorCount; + uint8_t m_uvCount; + uint8_t m_weightCount; + uint8_t m_skinSlotCount; + uint8_t m_primitiveType; + uint8_t m_reflectionType; + bool m_depthTest:1; + bool m_depthWrite:1; + bool m_backfaceCulling:1; + }; + }; +public: + ShaderTag() = default; + ShaderTag(std::string_view source, uint8_t c, uint8_t u, uint8_t w, uint8_t s, boo::Primitive pt, + Backend::ReflectionType reflectionType, bool depthTest, bool depthWrite, bool backfaceCulling) + : Hash(source), m_colorCount(c), m_uvCount(u), m_weightCount(w), m_skinSlotCount(s), + m_primitiveType(uint8_t(pt)), m_reflectionType(uint8_t(reflectionType)), + m_depthTest(depthTest), m_depthWrite(depthWrite), m_backfaceCulling(backfaceCulling) + {hash ^= m_meta;} + ShaderTag(const hecl::Frontend::IR& ir, uint8_t c, uint8_t u, uint8_t w, uint8_t s, boo::Primitive pt, + Backend::ReflectionType reflectionType, bool depthTest, bool depthWrite, bool backfaceCulling) + : Hash(ir.m_hash), m_colorCount(c), m_uvCount(u), m_weightCount(w), m_skinSlotCount(s), + m_primitiveType(uint8_t(pt)), m_reflectionType(uint8_t(reflectionType)), + m_depthTest(depthTest), m_depthWrite(depthWrite), m_backfaceCulling(backfaceCulling) + {hash ^= m_meta;} + ShaderTag(uint64_t hashin, uint8_t c, uint8_t u, uint8_t w, uint8_t s, boo::Primitive pt, + Backend::ReflectionType reflectionType, bool depthTest, bool depthWrite, bool backfaceCulling) + : Hash(hashin), m_colorCount(c), m_uvCount(u), m_weightCount(w), m_skinSlotCount(s), + m_primitiveType(uint8_t(pt)), m_reflectionType(uint8_t(reflectionType)), + m_depthTest(depthTest), m_depthWrite(depthWrite), m_backfaceCulling(backfaceCulling) + {hash ^= m_meta;} + ShaderTag(uint64_t comphashin, uint64_t meta) + : Hash(comphashin), m_meta(meta) {} + ShaderTag(const ShaderTag& other) : Hash(other), m_meta(other.m_meta) {} + uint8_t getColorCount() const {return m_colorCount;} + uint8_t getUvCount() const {return m_uvCount;} + uint8_t getWeightCount() const {return m_weightCount;} + uint8_t getSkinSlotCount() const {return m_skinSlotCount;} + boo::Primitive getPrimType() const {return boo::Primitive(m_primitiveType);} + Backend::ReflectionType getReflectionType() const {return Backend::ReflectionType(m_reflectionType);} + bool getDepthTest() const {return m_depthTest;} + bool getDepthWrite() const {return m_depthWrite;} + bool getBackfaceCulling() const {return m_backfaceCulling;} + uint64_t getMetaData() const {return m_meta;} + + std::vector vertexFormat() const + { + std::vector ret; + size_t elemCount = 2 + m_colorCount + m_uvCount + m_weightCount; + ret.resize(elemCount); + + ret[0].semantic = boo::VertexSemantic::Position3; + ret[1].semantic = boo::VertexSemantic::Normal3; + size_t e = 2; + + for (size_t i=0 ; i struct hash +{ + size_t operator()(const hecl::Backend::ShaderTag& val) const noexcept + {return val.valSizeT();} +}; } #endif // HECLBACKEND_HPP diff --git a/hecl/include/hecl/Backend/GLSL.hpp b/hecl/include/hecl/Backend/GLSL.hpp index 8c7f9db57..2f911a723 100644 --- a/hecl/include/hecl/Backend/GLSL.hpp +++ b/hecl/include/hecl/Backend/GLSL.hpp @@ -12,15 +12,15 @@ namespace hecl::Backend struct GLSL : ProgrammableCommon { void reset(const IR& ir, Diagnostics& diag); - std::string makeVert(const char* glslVer, unsigned col, unsigned uv, unsigned w, + std::string makeVert(unsigned col, unsigned uv, unsigned w, unsigned skinSlots, size_t extTexCount, const TextureInfo* extTexs, ReflectionType reflectionType) const; - std::string makeFrag(const char* glslVer, bool alphaTest, ReflectionType reflectionType, - const ShaderFunction& lighting=ShaderFunction()) const; - std::string makeFrag(const char* glslVer, bool alphaTest, + std::string makeFrag(bool alphaTest, ReflectionType reflectionType, + const Function& lighting=Function()) const; + std::string makeFrag(bool alphaTest, ReflectionType reflectionType, - const ShaderFunction& lighting, - const ShaderFunction& post, + const Function& lighting, + const Function& post, size_t extTexCount, const TextureInfo* extTexs) const; private: diff --git a/hecl/include/hecl/Backend/HLSL.hpp b/hecl/include/hecl/Backend/HLSL.hpp index ab9570f41..39ea3ddc1 100644 --- a/hecl/include/hecl/Backend/HLSL.hpp +++ b/hecl/include/hecl/Backend/HLSL.hpp @@ -13,10 +13,10 @@ struct HLSL : ProgrammableCommon unsigned skinSlots, size_t extTexCount, const TextureInfo* extTexs, ReflectionType reflectionType) const; std::string makeFrag(bool alphaTest, ReflectionType reflectionType, - const ShaderFunction& lighting=ShaderFunction()) const; + const Function& lighting=Function()) const; std::string makeFrag(bool alphaTest, ReflectionType reflectionType, - const ShaderFunction& lighting, - const ShaderFunction& post, size_t extTexCount, + const Function& lighting, + const Function& post, size_t extTexCount, const TextureInfo* extTexs) const; private: diff --git a/hecl/include/hecl/Backend/Metal.hpp b/hecl/include/hecl/Backend/Metal.hpp index ed51b3532..dd7f9d2d6 100644 --- a/hecl/include/hecl/Backend/Metal.hpp +++ b/hecl/include/hecl/Backend/Metal.hpp @@ -1,8 +1,6 @@ #ifndef HECLBACKEND_METAL_HPP #define HECLBACKEND_METAL_HPP -#if BOO_HAS_METAL - #include "ProgrammableCommon.hpp" namespace hecl::Backend @@ -16,10 +14,10 @@ struct Metal : ProgrammableCommon const TextureInfo* extTexs, ReflectionType reflectionType) const; std::string makeFrag(size_t blockCount, const char** blockNames, bool alphaTest, ReflectionType reflectionType, - const ShaderFunction& lighting=ShaderFunction()) const; + const Function& lighting=Function()) const; std::string makeFrag(size_t blockCount, const char** blockNames, bool alphaTest, - ReflectionType reflectionType, const ShaderFunction& lighting, - const ShaderFunction& post, size_t extTexCount, + ReflectionType reflectionType, const Function& lighting, + const Function& post, size_t extTexCount, const TextureInfo* extTexs) const; private: @@ -46,5 +44,4 @@ private: } -#endif // BOO_HAS_METAL #endif // HECLBACKEND_METAL_HPP diff --git a/hecl/include/hecl/Backend/ProgrammableCommon.hpp b/hecl/include/hecl/Backend/ProgrammableCommon.hpp index a2322d3c6..8caa5b74a 100644 --- a/hecl/include/hecl/Backend/ProgrammableCommon.hpp +++ b/hecl/include/hecl/Backend/ProgrammableCommon.hpp @@ -13,8 +13,6 @@ namespace hecl::Backend struct ProgrammableCommon : IBackend { - using ShaderFunction = Runtime::ShaderCacheExtensions::Function; - std::string m_colorExpr; std::string m_alphaExpr; BlendFactor m_blendSrc; diff --git a/hecl/include/hecl/Compilers.hpp b/hecl/include/hecl/Compilers.hpp new file mode 100644 index 000000000..370261cbe --- /dev/null +++ b/hecl/include/hecl/Compilers.hpp @@ -0,0 +1,67 @@ +#pragma once +#include "boo/graphicsdev/IGraphicsDataFactory.hpp" +#include "boo/graphicsdev/GL.hpp" +#include "boo/graphicsdev/Vulkan.hpp" +#include "boo/graphicsdev/D3D.hpp" +#include "boo/graphicsdev/Metal.hpp" + +namespace hecl +{ + +namespace PlatformType +{ +using PlatformEnum = boo::IGraphicsDataFactory::Platform; +struct Null {}; +struct OpenGL { static constexpr PlatformEnum Enum = PlatformEnum::OpenGL; static const char* Name; +#if BOO_HAS_GL +using Context = boo::GLDataFactory::Context; +#endif +}; +inline const char* OpenGL::Name = "OpenGL"; +struct D3D11 { static constexpr PlatformEnum Enum = PlatformEnum::D3D11; static const char* Name; +#if _WIN32 +using Context = boo::D3DDataFactory::Context; +#endif +}; +inline const char* D3D11::Name = "D3D11"; +struct Metal { static constexpr PlatformEnum Enum = PlatformEnum::Metal; static const char* Name; +#if BOO_HAS_METAL +using Context = boo::MetalDataFactory::Context; +#endif +}; +inline const char* Metal::Name = "Metal"; +struct Vulkan { static constexpr PlatformEnum Enum = PlatformEnum::Vulkan; static const char* Name; +#if BOO_HAS_VULKAN +using Context = boo::VulkanDataFactory::Context; +#endif +}; +inline const char* Vulkan::Name = "Vulkan"; +struct NX { static constexpr PlatformEnum Enum = PlatformEnum::NX; static const char* Name; +#if BOO_HAS_NX +using Context = boo::NXDataFactory::Context; +#endif +}; +inline const char* NX::Name = "NX"; +} + +namespace PipelineStage +{ +using StageEnum = boo::PipelineStage; +struct Null { static constexpr StageEnum Enum = StageEnum::Null; static const char* Name; }; +inline const char* Null::Name = "Null"; +struct Vertex { static constexpr StageEnum Enum = StageEnum::Vertex; static const char* Name; }; +inline const char* Vertex::Name = "Vertex"; +struct Fragment { static constexpr StageEnum Enum = StageEnum::Fragment; static const char* Name; }; +inline const char* Fragment::Name = "Fragment"; +struct Geometry { static constexpr StageEnum Enum = StageEnum::Geometry; static const char* Name; }; +inline const char* Geometry::Name = "Geometry"; +struct Control { static constexpr StageEnum Enum = StageEnum::Control; static const char* Name; }; +inline const char* Control::Name = "Control"; +struct Evaluation { static constexpr StageEnum Enum = StageEnum::Evaluation; static const char* Name; }; +inline const char* Evaluation::Name = "Evaluation"; +} + +template +std::pair, size_t> CompileShader(std::string_view text); + +} \ No newline at end of file diff --git a/hecl/include/hecl/Pipeline.hpp b/hecl/include/hecl/Pipeline.hpp new file mode 100644 index 000000000..564b020bf --- /dev/null +++ b/hecl/include/hecl/Pipeline.hpp @@ -0,0 +1,592 @@ +#pragma once + +#include +#include +#include "hecl/hecl.hpp" +#include "hecl/Backend/GLSL.hpp" +#include "hecl/Backend/HLSL.hpp" +#include "hecl/Backend/Metal.hpp" +#include "PipelineBase.hpp" + +/* CMake-curated rep classes for the application */ +#include "ApplicationReps.hpp" + +namespace hecl +{ + +#if HECL_RUNTIME +template +class StageRuntimeObject : public StageRep +{ + boo::ObjToken m_stage; +public: + static constexpr StageTargetType TargetType = StageTargetType::Runtime; + static constexpr PipelineTargetType PipelineTarget = PipelineTargetType::StageRuntimeCollection; + static constexpr bool HasHash = false; + StageRuntimeObject() = default; + StageRuntimeObject(StageConverter& conv, FactoryCtx& ctx, const StageBinary& in) + { + m_stage = static_cast(ctx).newShaderStage(in.data(), in.size(), S::Enum); + } + boo::ObjToken stage() const { return m_stage; } +}; +#endif + +class HECLIR : public PipelineRep +{ + const hecl::Backend::IR& m_ir; + const hecl::Backend::ShaderTag& m_tag; + const hecl::Backend::ExtensionSlot& m_extension; + uint64_t m_hash; +public: + HECLIR(const hecl::Backend::IR& ir, const hecl::Backend::ShaderTag& tag, + const hecl::Backend::ExtensionSlot& extension) + : m_ir(ir), m_tag(tag), m_extension(extension) + { + m_hash = tag.val64(); + m_hash ^= extension.hash(); + } + static constexpr bool HasHash = true; + uint64_t Hash() const { return m_hash; } + + const hecl::Backend::IR& ir() const { return m_ir; } + const hecl::Backend::ShaderTag& tag() const { return m_tag; } + const hecl::Backend::ExtensionSlot& extension() const { return m_extension; } +}; + +template +class HECLBackendImpl : public PipelineRep

+{ + hecl::Backend::ShaderTag m_tag; + BackendTp m_backend; + const hecl::Backend::ExtensionSlot& m_extension; +public: + static constexpr bool HasHash = false; + HECLBackendImpl(PipelineConverter

& conv, FactoryCtx& ctx, const HECLIR& in) + : m_tag(in.tag()), m_extension(in.extension()) + { + hecl::Backend::Diagnostics diag; + m_backend.reset(in.ir(), diag); + } + std::string makeVert() const + { + return m_backend.makeVert( + m_tag.getColorCount(), m_tag.getUvCount(), m_tag.getWeightCount(), + m_tag.getSkinSlotCount(), m_extension.texCount, + m_extension.texs, m_tag.getReflectionType()); + } + std::string makeFrag() const + { + return m_backend.makeFrag(m_tag.getDepthWrite() && + m_backend.m_blendDst == hecl::Backend::BlendFactor::InvSrcAlpha, + m_tag.getReflectionType(), m_extension.lighting, m_extension.post, + m_extension.texCount, m_extension.texs); + } + const hecl::Backend::ShaderTag& getTag() const { return m_tag; } +}; + +template +class HECLBackend : public PipelineRep

+{ +public: + static constexpr bool HasHash = false; +}; + +template<> +class HECLBackend : public HECLBackendImpl +{ +public: + using HECLBackendImpl::HECLBackendImpl; +}; + +template<> +class HECLBackend : public HECLBackendImpl +{ +public: + using HECLBackendImpl::HECLBackendImpl; +}; + +template<> +class HECLBackend : public HECLBackendImpl +{ +public: + using HECLBackendImpl::HECLBackendImpl; +}; + +template<> +class HECLBackend : public HECLBackendImpl +{ +public: + using HECLBackendImpl::HECLBackendImpl; +}; + +template<> +class HECLBackend : public HECLBackendImpl +{ +public: + using HECLBackendImpl::HECLBackendImpl; +}; + +template class T, typename P, typename... Rest> +StageCollection>::StageCollection(PipelineConverter

& conv, FactoryCtx& ctx, const HECLBackend

& in) +{ + m_vertex = conv.getVertexConverter().convert(ctx, StageSourceText(in.makeVert())); + m_fragment = conv.getFragmentConverter().convert(ctx, StageSourceText(in.makeFrag())); + m_vtxFmtData = in.getTag().vertexFormat(); + m_vtxFmt = boo::VertexFormatInfo(m_vtxFmtData.size(), m_vtxFmtData.data()); + MakeHash(); +} + +#if HECL_RUNTIME +template +class FinalPipeline : public PipelineRep

+{ + boo::ObjToken m_pipeline; +public: + static constexpr PipelineTargetType TargetType = PipelineTargetType::FinalPipeline; + static constexpr bool HasHash = false; + FinalPipeline(PipelineConverter

& conv, FactoryCtx& ctx, + const StageCollection>& in) + { + m_pipeline = static_cast(ctx).newShaderPipeline( + in.m_vertex.stage(), in.m_fragment.stage(), in.m_geometry.stage(), + in.m_control.stage(), in.m_evaluation.stage(), in.m_vtxFmt, in.m_additionalInfo); + } + boo::ObjToken pipeline() const { return m_pipeline; } +}; +#endif + +#define STAGE_COLLECTION_SPECIALIZATIONS(T, P) StageCollection>, + +template struct pack {}; +struct null_t {}; +template struct TypeDB {}; +template<> struct TypeDB +{ + using PipelineTypes = pack< +#if HECL_RUNTIME +#if BOO_HAS_GL + HECLBackend, + STAGE_COLLECTION_SPECIALIZATIONS(StageSourceText, PlatformType::OpenGL) + STAGE_COLLECTION_SPECIALIZATIONS(StageBinary, PlatformType::OpenGL) + STAGE_COLLECTION_SPECIALIZATIONS(StageRuntimeObject, PlatformType::OpenGL) + FinalPipeline, +#endif +#else + HECLBackend, + STAGE_COLLECTION_SPECIALIZATIONS(StageSourceText, PlatformType::OpenGL) +#endif + null_t + >; + using StageTypes = pack< +#if HECL_RUNTIME +#if BOO_HAS_GL + STAGE_SPECIALIZATIONS(StageBinary, PlatformType::OpenGL) + HECL_APPLICATION_STAGE_REPS_OPENGL + STAGE_SPECIALIZATIONS(StageRuntimeObject, PlatformType::OpenGL) +#endif +#else + HECL_APPLICATION_STAGE_REPS_OPENGL +#endif + null_t + >; +}; +template<> struct TypeDB +{ + using PipelineTypes = pack< +#if HECL_RUNTIME +#if BOO_HAS_VULKAN + HECLBackend, + STAGE_COLLECTION_SPECIALIZATIONS(StageSourceText, PlatformType::Vulkan) + STAGE_COLLECTION_SPECIALIZATIONS(StageBinary, PlatformType::Vulkan) + STAGE_COLLECTION_SPECIALIZATIONS(StageRuntimeObject, PlatformType::Vulkan) + FinalPipeline, +#endif +#else + HECLBackend, + STAGE_COLLECTION_SPECIALIZATIONS(StageSourceText, PlatformType::Vulkan) + STAGE_COLLECTION_SPECIALIZATIONS(StageBinary, PlatformType::Vulkan) +#endif + null_t + >; + using StageTypes = pack< +#if HECL_RUNTIME +#if BOO_HAS_VULKAN + STAGE_SPECIALIZATIONS(StageBinary, PlatformType::Vulkan) + HECL_APPLICATION_STAGE_REPS_VULKAN + STAGE_SPECIALIZATIONS(StageRuntimeObject, PlatformType::Vulkan) +#endif +#else + HECL_APPLICATION_STAGE_REPS_VULKAN +#endif + null_t + >; +}; + +class ShaderCacheZipStream; + +template +class StageConverter +{ + friend class PipelineConverter

; +#if HECL_RUNTIME + using StageTargetTp = StageRuntimeObject; +#else + using StageTargetTp = StageBinary; +#endif + std::unordered_map m_stageCache; + +#if 0 /* Horrible compiler memory explosion - DO NOT USE! */ + template + static constexpr bool is_stage_constructible_v = + std::is_constructible&, FactoryCtx&, FromTp>::value; + + template + struct is_eventually_constructible : std::false_type {}; + template + struct _is_eventually_constructible + : std::conditional_t, + std::true_type, + std::conditional_t, + is_eventually_constructible, + _is_eventually_constructible>> {}; + template + struct _is_eventually_constructible : std::false_type {}; + template + struct is_eventually_constructible> + : _is_eventually_constructible {}; + template + static constexpr bool is_eventually_constructible_v = + is_eventually_constructible::value; + + template + struct _next_type { using type = std::conditional_t && + is_eventually_constructible_v, + ToTp, + typename _next_type::type>; }; + template + struct _next_type { using type = null_t; }; + template + struct next_type { using type = null_t; }; + template + struct next_type> : _next_type {}; + template + using next_type_t = typename next_type::type; + + template + std::enable_if_t, ToTp> + _Do(FactoryCtx& ctx, const FromTp& in) + { + using NextTp = next_type_t; + return ToTp(*this, ctx, _Do(ctx, in)); + } + + template + std::enable_if_t, ToTp> + _Do(FactoryCtx& ctx, const FromTp& in) + { + return ToTp(*this, ctx, in); + } +#endif + + using StageTypes = typename TypeDB

::StageTypes; + + template + static constexpr bool is_stage_constructible_v = + std::is_constructible&, FactoryCtx&, FromTp>::value; + + template + struct _next_type { using type = std::conditional_t, + ToTp, + typename _next_type::type>; }; + template + struct _next_type { using type = null_t; }; + template + struct next_type { using type = null_t; }; + template + struct next_type> : _next_type {}; + template + using next_type_t = typename next_type::type; + + /* StageSourceText derivative -> StageBinary */ + template + std::enable_if_t> && + std::is_base_of_v, NextTp>, StageBinary> + _DoDerivative(FactoryCtx& ctx, const FromTp& in) + { + return StageBinary(*this, ctx, NextTp(*this, ctx, in)); + } + + /* StageBinary derivative -> StageBinary */ + template + std::enable_if_t> && + std::is_base_of_v, NextTp>, StageBinary> + _DoDerivative(FactoryCtx& ctx, const FromTp& in) + { + return NextTp(*this, ctx, in); + } + + /* Non-StageSourceText derivative -> StageBinary */ + template + std::enable_if_t> && + !std::is_base_of_v, FromTp>, StageBinary> + _Do(FactoryCtx& ctx, const FromTp& in) + { + using NextTp = next_type_t; + static_assert(!std::is_same_v, + "Unable to resolve StageBinary or StageSourceText derivative"); + return _DoDerivative(ctx, in); + } + + /* StageSourceText derivative -> StageBinary */ + template + std::enable_if_t> && + std::is_base_of_v, FromTp>, StageBinary> + _Do(FactoryCtx& ctx, const FromTp& in) + { + return StageBinary(*this, ctx, in); + } + + /* Non-StageBinary derivative -> StageRuntimeObject */ + template + std::enable_if_t> && + !std::is_base_of_v, FromTp>, StageRuntimeObject> + _Do(FactoryCtx& ctx, const FromTp& in) + { + return StageRuntimeObject(*this, ctx, _Do, FromTp>(ctx, in)); + } + + /* StageBinary derivative -> StageRuntimeObject */ + template + std::enable_if_t> && + std::is_base_of_v, FromTp>, StageRuntimeObject> + _Do(FactoryCtx& ctx, const FromTp& in) + { + return StageRuntimeObject(*this, ctx, in); + } + + template + ToTp Do(FactoryCtx& ctx, const FromTp& in) + { + return _Do(ctx, in); + } + +public: +#if HECL_RUNTIME + void loadFromStream(FactoryCtx& ctx, ShaderCacheZipStream& r); +#endif + + template + StageTargetTp convert(FactoryCtx& ctx, const FromTp& in) + { + if (FromTp::HasHash) + { + uint64_t hash = in.Hash(); + auto search = m_stageCache.find(hash); + if (search != m_stageCache.end()) + return search->second; + return m_stageCache.insert(std::make_pair(hash, Do(ctx, in))).first->second; + } + return Do(ctx, in); + } +}; + +class PipelineConverterBase +{ + boo::IGraphicsDataFactory* m_gfxF; + boo::IGraphicsDataFactory::Platform m_platform; +protected: + PipelineConverterBase(boo::IGraphicsDataFactory* gfxF, boo::IGraphicsDataFactory::Platform platform) + : m_gfxF(gfxF), m_platform(platform) {} +public: + virtual ~PipelineConverterBase() = default; +#if HECL_RUNTIME + template + boo::ObjToken convert(FactoryCtx& ctx, const FromTp& in); + template + boo::ObjToken convert(const FromTp& in); +#endif +}; + +template +class PipelineConverter : public PipelineConverterBase +{ +#if HECL_RUNTIME + using PipelineTargetTp = FinalPipeline

; +#else + using PipelineTargetTp = StageCollection>; +#endif + std::unordered_map m_pipelineCache; + StageConverter m_vertexConverter; + StageConverter m_fragmentConverter; + StageConverter m_geometryConverter; + StageConverter m_controlConverter; + StageConverter m_evaluationConverter; + + using PipelineTypes = typename TypeDB

::PipelineTypes; + + template + static constexpr bool is_pipeline_constructible_v = + std::is_constructible&, FactoryCtx&, FromTp>::value; + + template + struct is_eventually_constructible : std::false_type {}; + template + struct _is_eventually_constructible + : std::conditional_t, + std::true_type, + std::conditional_t, + is_eventually_constructible, + _is_eventually_constructible>> {}; + template + struct _is_eventually_constructible : std::false_type {}; + template + struct is_eventually_constructible> + : _is_eventually_constructible {}; + template + static constexpr bool is_eventually_constructible_v = + is_eventually_constructible::value; + + template + struct _next_type { using type = std::conditional_t && + is_eventually_constructible_v, + ToTp, + typename _next_type::type>; }; + template + struct _next_type { using type = null_t; }; + template + struct next_type { using type = null_t; }; + template + struct next_type> : _next_type {}; + template + using next_type_t = typename next_type::type; + + template + std::enable_if_t, ToTp> + _Do(FactoryCtx& ctx, const FromTp& in) + { + using NextTp = next_type_t; + return ToTp(*this, ctx, _Do(ctx, in)); + } + + template + std::enable_if_t, ToTp> + _Do(FactoryCtx& ctx, const FromTp& in) + { + return ToTp(*this, ctx, in); + } + + template + ToTp Do(FactoryCtx& ctx, const FromTp& in) + { + /* No idea why this fails; it works fine with manual template arguments (clang bug?) */ + //static_assert(is_eventually_constructible_v, "Unable to resolve pipeline conversion chain"); + return _Do(ctx, in); + } + +public: + PipelineConverter(boo::IGraphicsDataFactory* gfxF) : PipelineConverterBase(gfxF, P::Enum) {} +#if HECL_RUNTIME + bool loadFromFile(FactoryCtx& ctx, const hecl::SystemChar* path); +#endif + + template + PipelineTargetTp convert(FactoryCtx& ctx, const FromTp& in) + { + if (FromTp::HasHash) + { + uint64_t hash = in.Hash(); + auto search = m_pipelineCache.find(hash); + if (search != m_pipelineCache.end()) + return search->second; + return m_pipelineCache.insert(std::make_pair(hash, Do(ctx, in))).first->second; + } + return Do(ctx, in); + } + + StageConverter& getVertexConverter() { return m_vertexConverter; } + StageConverter& getFragmentConverter() { return m_fragmentConverter; } + StageConverter& getGeometryConverter() { return m_geometryConverter; } + StageConverter& getControlConverter() { return m_controlConverter; } + StageConverter& getEvaluationConverter() { return m_evaluationConverter; } +}; + +#if HECL_RUNTIME + +template +inline boo::ObjToken PipelineConverterBase::convert(FactoryCtx& ctx, const FromTp& in) +{ + assert(ctx.platform() == m_platform && "PipelineConverterBase platform mismatch"); + switch (m_platform) + { +#if BOO_HAS_GL + case boo::IGraphicsDataFactory::Platform::OpenGL: + return static_cast&>(*this).convert(ctx, in).pipeline(); +#endif +#if BOO_HAS_VULKAN + case boo::IGraphicsDataFactory::Platform::Vulkan: + return static_cast&>(*this).convert(ctx, in).pipeline(); +#endif +#if _WIN32 + case boo::IGraphicsDataFactory::Platform::D3D11: + return static_cast&>(*this).convert(ctx, in).pipeline(); +#endif +#if BOO_HAS_METAL + case boo::IGraphicsDataFactory::Platform::Metal: + return static_cast&>(*this).convert(ctx, in).pipeline(); +#endif +#if BOO_HAS_NX + case boo::IGraphicsDataFactory::Platform::NX: + return static_cast&>(*this).convert(ctx, in).pipeline(); +#endif + default: + return {}; + } +} + +template +inline boo::ObjToken PipelineConverterBase::convert(const FromTp& in) +{ + boo::ObjToken ret; + m_gfxF->commitTransaction([this, &ret, &in](boo::IGraphicsDataFactory::Context& ctx) + { + ret = convert(ctx, in); + return true; + } BooTrace); + return ret; +} + +inline std::unique_ptr NewPipelineConverter(boo::IGraphicsDataFactory* gfxF) +{ + switch (gfxF->platform()) + { +#if BOO_HAS_GL + case boo::IGraphicsDataFactory::Platform::OpenGL: + return std::make_unique>(gfxF); +#endif +#if BOO_HAS_VULKAN + case boo::IGraphicsDataFactory::Platform::Vulkan: + return std::make_unique>(gfxF); +#endif +#if _WIN32 + case boo::IGraphicsDataFactory::Platform::D3D11: + return std::make_unique>(gfxF); +#endif +#if BOO_HAS_METAL + case boo::IGraphicsDataFactory::Platform::Metal: + return std::make_unique>(gfxF); +#endif +#if BOO_HAS_NX + case boo::IGraphicsDataFactory::Platform::NX: + return std::make_unique>(gfxF); +#endif + default: + return {}; + } +} + +extern PipelineConverterBase* conv; + +#endif + +} diff --git a/hecl/include/hecl/PipelineBase.hpp b/hecl/include/hecl/PipelineBase.hpp new file mode 100644 index 000000000..6700e4329 --- /dev/null +++ b/hecl/include/hecl/PipelineBase.hpp @@ -0,0 +1,211 @@ +#pragma once +#include "Compilers.hpp" +extern "C" unsigned long long XXH64 (const void* input, size_t length, unsigned long long seed); + +#define HECL_RUNTIME 1 + +namespace hecl +{ + +using AdditionalPipelineInfo = boo::AdditionalPipelineInfo; + +enum class StageTargetType +{ + SourceText, + Binary, + Runtime +}; + +enum class PipelineTargetType +{ + StageSourceTextCollection, + StageBinaryCollection, + StageRuntimeCollection, + FinalPipeline +}; + +template +class StageConverter; + +template +class PipelineConverter; + +#if HECL_RUNTIME +using FactoryCtx = boo::IGraphicsDataFactory::Context; +#else +struct FactoryCtx {}; +#endif + +template +class StageRep +{ +public: + using Platform = P; + using Stage = S; +}; + +template +class PipelineRep +{ +public: + using Platform = P; +}; + +class GeneralShader : public hecl::PipelineRep {}; +class TessellationShader : public hecl::PipelineRep {}; + +template +class StageSourceText : public StageRep +{ + 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_hash = XXH64(m_text.data(), m_text.size(), 0); + } + std::string_view text() const { return m_text; } +}; + +template +class StageBinary : public StageRep +{ + std::shared_ptr 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(std::shared_ptr 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, size_t> data) + : StageBinary(data.first, data.second) {} + StageBinary(StageConverter& conv, FactoryCtx& ctx, const StageSourceText& in) + : StageBinary(CompileShader(in.text())) {} + const uint8_t* data() const { return m_data; } + size_t size() const { return m_size; } +}; + +template +class FinalPipeline; + +template +class HECLBackend; + +template +using __IsStageSubclass = typename std::disjunction< + std::is_base_of, T>, + std::is_base_of, T>, + std::is_base_of, T>, + std::is_base_of, T>, + std::is_base_of, T>>; +template +inline constexpr bool __IsStageSubclass_v = __IsStageSubclass::value; + +template class StageCollection; +template class T, typename P, typename... Rest> +class StageCollection> : public PipelineRep

+{ + using base = PipelineRep

; + friend class FinalPipeline

; + static_assert(__IsStageSubclass_v>, + "Stage Collection may only be specialized with StageRep subclasses"); + T m_vertex; + T m_fragment; + T m_geometry; + T m_control; + T m_evaluation; + AdditionalPipelineInfo m_additionalInfo; + std::vector m_vtxFmtData; + boo::VertexFormatInfo m_vtxFmt; + uint64_t m_hash; +public: + static constexpr PipelineTargetType TargetType = T::PipelineTarget; + static constexpr bool HasHash = T::HasHash; + template>> + std::enable_if_t Hash() const { return m_hash; } + template>> + void MakeHash(std::enable_if_t* = 0) {} + template>> + void MakeHash(std::enable_if_t* = 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); + } + StageCollection(PipelineConverter

& conv, FactoryCtx& ctx, const HECLBackend

& in); + template + StageCollection(PipelineConverter

& conv, FactoryCtx& ctx, const I& in, + typename std::enable_if_t>* = 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 + StageCollection(PipelineConverter

& conv, FactoryCtx& ctx, const I& in, + typename std::enable_if_t>* = 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(); + } + StageCollection(const T& vertex, + const T& fragment, + const T& geometry, + const T& control, + const T& 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) {} +}; + +} + +#define _STAGEOBJECT_PROTOTYPE_DECLARATIONS(T, P) \ +template <> const hecl::StageBinary \ +T::Prototype; \ +template <> const hecl::StageBinary \ +T::Prototype; \ +template <> const hecl::StageBinary \ +T::Prototype; \ +template <> const hecl::StageBinary \ +T::Prototype; \ +template <> const hecl::StageBinary \ +T::Prototype; + +#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) diff --git a/hecl/include/hecl/Runtime.hpp b/hecl/include/hecl/Runtime.hpp index 7d478be7f..fadb4628a 100644 --- a/hecl/include/hecl/Runtime.hpp +++ b/hecl/include/hecl/Runtime.hpp @@ -2,11 +2,7 @@ #define HECLRUNTIME_HPP #include "hecl.hpp" -#include "Frontend.hpp" -#include "Backend/Backend.hpp" #include "boo/graphicsdev/IGraphicsDataFactory.hpp" -#include "athena/DNA.hpp" -#include "athena/FileReader.hpp" #include namespace hecl @@ -33,256 +29,6 @@ public: SystemStringView getStoreRoot() const {return m_storeRoot;} }; -/** - * @brief Hash subclass for identifying shaders and their metadata - */ -class ShaderTag : public Hash -{ - union - { - uint64_t m_meta = 0; - struct - { - uint8_t m_colorCount; - uint8_t m_uvCount; - uint8_t m_weightCount; - uint8_t m_skinSlotCount; - uint8_t m_primitiveType; - uint8_t m_reflectionType; - bool m_depthTest:1; - bool m_depthWrite:1; - bool m_backfaceCulling:1; - }; - }; -public: - ShaderTag() = default; - ShaderTag(std::string_view source, uint8_t c, uint8_t u, uint8_t w, uint8_t s, boo::Primitive pt, - Backend::ReflectionType reflectionType, bool depthTest, bool depthWrite, bool backfaceCulling) - : Hash(source), m_colorCount(c), m_uvCount(u), m_weightCount(w), m_skinSlotCount(s), - m_primitiveType(uint8_t(pt)), m_reflectionType(uint8_t(reflectionType)), - m_depthTest(depthTest), m_depthWrite(depthWrite), m_backfaceCulling(backfaceCulling) - {hash ^= m_meta;} - ShaderTag(const hecl::Frontend::IR& ir, uint8_t c, uint8_t u, uint8_t w, uint8_t s, boo::Primitive pt, - Backend::ReflectionType reflectionType, bool depthTest, bool depthWrite, bool backfaceCulling) - : Hash(ir.m_hash), m_colorCount(c), m_uvCount(u), m_weightCount(w), m_skinSlotCount(s), - m_primitiveType(uint8_t(pt)), m_reflectionType(uint8_t(reflectionType)), - m_depthTest(depthTest), m_depthWrite(depthWrite), m_backfaceCulling(backfaceCulling) - {hash ^= m_meta;} - ShaderTag(uint64_t hashin, uint8_t c, uint8_t u, uint8_t w, uint8_t s, boo::Primitive pt, - Backend::ReflectionType reflectionType, bool depthTest, bool depthWrite, bool backfaceCulling) - : Hash(hashin), m_colorCount(c), m_uvCount(u), m_weightCount(w), m_skinSlotCount(s), - m_primitiveType(uint8_t(pt)), m_reflectionType(uint8_t(reflectionType)), - m_depthTest(depthTest), m_depthWrite(depthWrite), m_backfaceCulling(backfaceCulling) - {hash ^= m_meta;} - ShaderTag(uint64_t comphashin, uint64_t meta) - : Hash(comphashin), m_meta(meta) {} - ShaderTag(const ShaderTag& other) : Hash(other), m_meta(other.m_meta) {} - uint8_t getColorCount() const {return m_colorCount;} - uint8_t getUvCount() const {return m_uvCount;} - uint8_t getWeightCount() const {return m_weightCount;} - uint8_t getSkinSlotCount() const {return m_skinSlotCount;} - boo::Primitive getPrimType() const {return boo::Primitive(m_primitiveType);} - Backend::ReflectionType getReflectionType() const {return Backend::ReflectionType(m_reflectionType);} - bool getDepthTest() const {return m_depthTest;} - bool getDepthWrite() const {return m_depthWrite;} - bool getBackfaceCulling() const {return m_backfaceCulling;} - uint64_t getMetaData() const {return m_meta;} - - /* For shader constructors that require vertex format up-front (HLSL) */ - boo::ObjToken newVertexFormat(boo::IGraphicsDataFactory::Context& ctx) const; -}; - -/** - * @brief Simple binary data and tag container for cache interaction - */ -class ShaderCachedData -{ - friend class ShaderCacheManager; - ShaderCachedData() = default; -public: - ShaderCachedData(const ShaderTag& tag, size_t decompSz) - : m_tag(tag), m_data(new uint8_t[decompSz]), m_sz(decompSz) {} - ShaderTag m_tag; - std::unique_ptr m_data; - size_t m_sz; - operator bool() const {return m_tag.operator bool();} -}; - -/** - * @brief Optional cache extensions allowing the client to specify shader transformations in bulk - */ -class ShaderCacheExtensions -{ - friend class ShaderCacheManager; - boo::IGraphicsDataFactory::Platform m_plat; - ShaderCacheExtensions() : m_plat(boo::IGraphicsDataFactory::Platform::Null) {} - - uint64_t hashExtensions() const; -public: - struct Function - { - const char* m_source = nullptr; - const char* m_entry = nullptr; - Function() = default; - Function(const char* source, const char* entry) - : m_source(source), m_entry(entry) {} - }; - - struct ExtensionSlot - { - Function lighting; - Function post; - size_t blockCount = 0; - const char** blockNames = nullptr; - size_t texCount = 0; - const Backend::TextureInfo* texs = nullptr; - Backend::BlendFactor srcFactor = Backend::BlendFactor::Original; - Backend::BlendFactor dstFactor = Backend::BlendFactor::Original; - Backend::ZTest depthTest = Backend::ZTest::Original; - Backend::CullMode cullMode = Backend::CullMode::Backface; - bool noDepthWrite = false; - bool noColorWrite = false; - bool noAlphaWrite = false; - bool noAlphaOverwrite = false; - bool noReflection = false; - }; - std::vector m_extensionSlots; - - ShaderCacheExtensions(boo::IGraphicsDataFactory::Platform plat) : m_plat(plat) - { - /* Index-0 has special default-meaning */ - m_extensionSlots.emplace_back(); - } - operator bool() const {return m_plat != boo::IGraphicsDataFactory::Platform::Null;} - - /* Strings must remain resident!! (intended to be stored static const) */ - unsigned registerExtensionSlot(Function lighting, Function post, - size_t blockCount, const char** blockNames, - size_t texCount, const Backend::TextureInfo* texs, - Backend::BlendFactor srcFactor, Backend::BlendFactor dstFactor, - Backend::ZTest depthTest = Backend::ZTest::Original, - Backend::CullMode cullMode = Backend::CullMode::Backface, - bool noDepthWrite = false, - bool noColorWrite = false, - bool noAlphaWrite = false, - bool noAlphaOverwrite = false, - bool noReflection = false) - { - m_extensionSlots.emplace_back(); - ExtensionSlot& slot = m_extensionSlots.back(); - slot.lighting = lighting; - slot.post = post; - slot.blockCount = blockCount; - slot.blockNames = blockNames; - slot.texCount = texCount; - slot.texs = texs; - slot.srcFactor = srcFactor; - slot.dstFactor = dstFactor; - slot.depthTest = depthTest; - slot.cullMode = cullMode; - slot.noDepthWrite = noDepthWrite; - slot.noColorWrite = noColorWrite; - slot.noAlphaWrite = noAlphaWrite; - slot.noAlphaOverwrite = noAlphaOverwrite; - slot.noReflection = noReflection; - return m_extensionSlots.size() - 1; - } -}; - -/** - * @brief Interface for binding HECL backends to the ShaderCacheManager - */ -class IShaderBackendFactory -{ - friend class ShaderCacheManager; -protected: - using FReturnExtensionShader = std::function&)>; - virtual ShaderCachedData buildShaderFromIR(const ShaderTag& tag, - const hecl::Frontend::IR& ir, - hecl::Frontend::Diagnostics& diag, - boo::IGraphicsDataFactory::Context& ctx, - boo::ObjToken& objOut)=0; - virtual boo::ObjToken buildShaderFromCache(const ShaderCachedData& data, - boo::IGraphicsDataFactory::Context& ctx)=0; - virtual ShaderCachedData buildExtendedShaderFromIR(const ShaderTag& tag, - const hecl::Frontend::IR& ir, - hecl::Frontend::Diagnostics& diag, - const std::vector& extensionSlots, - boo::IGraphicsDataFactory::Context& ctx, - FReturnExtensionShader returnFunc)=0; - virtual bool buildExtendedShaderFromCache(const ShaderCachedData& data, - const std::vector& extensionSlots, - boo::IGraphicsDataFactory::Context& ctx, - FReturnExtensionShader returnFunc)=0; -public: - virtual ~IShaderBackendFactory() = default; -}; - -/** - * @brief Stores token and pipeline set for sharing with ref-counting - */ -struct ShaderPipelines -{ - std::vector> m_pipelines; -}; - -/** - * @brief Maintains index/data file pair containing platform-dependent cached shader data - */ -class ShaderCacheManager -{ - const FileStoreManager& m_storeMgr; - ShaderCacheExtensions m_extensions; - uint64_t m_extensionsHash = 0; - std::unique_ptr m_factory; - athena::io::FileReader m_idxFr; - athena::io::FileReader m_datFr; - hecl::Frontend::Frontend FE; - struct IndexEntry : athena::io::DNA - { - AT_DECL_DNA - Value m_hash; - Value m_meta; - Value m_compOffset; - Value m_compSize; - Value m_decompSize; - }; - std::vector m_entries; - std::unordered_map m_entryLookup; - std::unordered_map> m_pipelineLookup; - - uint64_t m_timeHash = 0; - void bootstrapIndex(); - ShaderCachedData lookupData(const Hash& hash); - bool addData(const ShaderCachedData& data); - boo::ObjToken buildFromCache(const ShaderCachedData& foundData, - boo::IGraphicsDataFactory::Context& ctx); - std::vector> buildExtendedFromCache(const ShaderCachedData& foundData, - boo::IGraphicsDataFactory::Context& ctx); -public: - ShaderCacheManager(const FileStoreManager& storeMgr, - boo::IGraphicsDataFactory* gfxFactory, - ShaderCacheExtensions&& extension); - ShaderCacheManager(const FileStoreManager& storeMgr, - boo::IGraphicsDataFactory* gfxFactory) - : ShaderCacheManager(storeMgr, gfxFactory, ShaderCacheExtensions()) {} - void reload(); - void clearCachedPipelines() { m_pipelineLookup.clear(); } - - std::shared_ptr buildShader(const ShaderTag& tag, std::string_view source, - std::string_view diagName, - boo::IGraphicsDataFactory& factory); - std::shared_ptr buildShader(const ShaderTag& tag, const hecl::Frontend::IR& ir, - std::string_view diagName, - boo::IGraphicsDataFactory& factory); - std::shared_ptr buildExtendedShader(const ShaderTag& tag, std::string_view source, - std::string_view diagName, - boo::IGraphicsDataFactory& factory); - std::shared_ptr buildExtendedShader(const ShaderTag& tag, const hecl::Frontend::IR& ir, - std::string_view diagName, - boo::IGraphicsDataFactory& factory); -}; - /** * @brief Integrated reader/constructor/container for HMDL data */ @@ -290,24 +36,19 @@ struct HMDLData { boo::ObjToken m_vbo; boo::ObjToken m_ibo; - boo::ObjToken m_vtxFmt; + std::unique_ptr m_vtxFmtData; + boo::VertexFormatInfo m_vtxFmt; HMDLData(boo::IGraphicsDataFactory::Context& ctx, const void* metaData, const void* vbo, const void* ibo); - /* For binding constructors that require vertex format up front (GLSL) */ - static boo::ObjToken - NewVertexFormat(boo::IGraphicsDataFactory::Context& ctx, const HMDLMeta& meta, - const boo::ObjToken& vbo={}, - const boo::ObjToken& ibo={}); - boo::ObjToken newShaderDataBindng(boo::IGraphicsDataFactory::Context& ctx, const boo::ObjToken& shader, size_t ubufCount, const boo::ObjToken* ubufs, const boo::PipelineStage* ubufStages, size_t texCount, const boo::ObjToken* texs) - {return ctx.newShaderDataBinding(shader, m_vtxFmt, m_vbo.get(), nullptr, m_ibo.get(), + {return ctx.newShaderDataBinding(shader, m_vbo.get(), nullptr, m_ibo.get(), ubufCount, ubufs, ubufStages, nullptr, nullptr, texCount, texs, nullptr, nullptr);} }; @@ -315,13 +56,4 @@ struct HMDLData } } -namespace std -{ -template <> struct hash -{ - size_t operator()(const hecl::Runtime::ShaderTag& val) const noexcept - {return val.valSizeT();} -}; -} - #endif // HECLRUNTIME_HPP diff --git a/hecl/lib/Backend/CMakeLists.txt b/hecl/lib/Backend/CMakeLists.txt index 028ec2c04..54422bead 100644 --- a/hecl/lib/Backend/CMakeLists.txt +++ b/hecl/lib/Backend/CMakeLists.txt @@ -1,11 +1,7 @@ -if(WIN32) - set(PLAT_SRCS HLSL.cpp) -endif() -if(APPLE) - set(PLAT_SRCS Metal.cpp) -endif() if(NOT WINDOWS_STORE) - list(APPEND PLAT_SRCS GLSL.cpp) + list(APPEND PLAT_SRCS GLSL.cpp HLSL.cpp Metal.cpp) +else() + list(APPEND PLAT_SRCS HLSL.cpp) endif() set(BACKEND_SOURCES diff --git a/hecl/lib/Backend/GLSL.cpp b/hecl/lib/Backend/GLSL.cpp index 080bc8f05..02e141fa7 100644 --- a/hecl/lib/Backend/GLSL.cpp +++ b/hecl/lib/Backend/GLSL.cpp @@ -1,9 +1,8 @@ #include "hecl/Backend/GLSL.hpp" #include "hecl/Runtime.hpp" -#include -#include -#include -#include +#include "athena/MemoryReader.hpp" +#include "athena/MemoryWriter.hpp" +#include "boo/graphicsdev/GLSLMacros.hpp" static logvisor::Module Log("hecl::Backend::GLSL"); @@ -150,12 +149,12 @@ void GLSL::reset(const IR& ir, Diagnostics& diag) ProgrammableCommon::reset(ir, diag, "GLSL"); } -std::string GLSL::makeVert(const char* glslVer, unsigned col, unsigned uv, unsigned w, +std::string GLSL::makeVert(unsigned col, unsigned uv, unsigned w, unsigned s, size_t extTexCount, const TextureInfo* extTexs, ReflectionType reflectionType) const { extTexCount = std::min(int(extTexCount), BOO_GLSL_MAX_TEXTURE_COUNT - int(m_tcgs.size())); - std::string retval = std::string(glslVer) + "\n" BOO_GLSL_BINDING_HEAD + + std::string retval = GenerateVertInStruct(col, uv, w) + "\n" + GenerateVertToFragStruct(extTexCount, reflectionType != ReflectionType::None) + "\n" + GenerateVertUniformStruct(s, reflectionType != ReflectionType::None) + @@ -221,11 +220,11 @@ std::string GLSL::makeVert(const char* glslVer, unsigned col, unsigned uv, unsig return retval + "}\n"; } -std::string GLSL::makeFrag(const char* glslVer, bool alphaTest, - ReflectionType reflectionType, const ShaderFunction& lighting) const +std::string GLSL::makeFrag(bool alphaTest, + ReflectionType reflectionType, const Function& lighting) const { std::string lightingSrc; - if (lighting.m_source) + if (!lighting.m_source.empty()) lightingSrc = lighting.m_source; else lightingSrc = "const vec4 colorReg0 = vec4(1.0);\n" @@ -245,8 +244,8 @@ std::string GLSL::makeFrag(const char* glslVer, bool alphaTest, texMapDecl += hecl::Format("TBINDING%u uniform sampler2D reflectionTex;\n", m_texMapEnd); - std::string retval = std::string(glslVer) + - "\n#extension GL_ARB_shader_image_load_store: enable\n" BOO_GLSL_BINDING_HEAD + + std::string retval = + std::string("#extension GL_ARB_shader_image_load_store: enable\n") + GenerateVertToFragStruct(0, reflectionType != ReflectionType::None) + (!alphaTest ? "#ifdef GL_ARB_shader_image_load_store\n" @@ -261,8 +260,9 @@ std::string GLSL::makeFrag(const char* glslVer, bool alphaTest, if (m_lighting) { - if (lighting.m_entry) - retval += hecl::Format(" vec4 lighting = %s(vtf.mvPos.xyz, normalize(vtf.mvNorm.xyz));\n", lighting.m_entry); + if (!lighting.m_entry.empty()) + retval += hecl::Format(" vec4 lighting = %s(vtf.mvPos.xyz, normalize(vtf.mvNorm.xyz));\n", + lighting.m_entry.data()); else retval += " vec4 lighting = vec4(1.0,1.0,1.0,1.0);\n"; } @@ -282,14 +282,14 @@ std::string GLSL::makeFrag(const char* glslVer, bool alphaTest, return retval + (alphaTest ? GenerateAlphaTest() : "") + "}\n"; } -std::string GLSL::makeFrag(const char* glslVer, bool alphaTest, +std::string GLSL::makeFrag(bool alphaTest, ReflectionType reflectionType, - const ShaderFunction& lighting, - const ShaderFunction& post, + const Function& lighting, + const Function& post, size_t extTexCount, const TextureInfo* extTexs) const { std::string lightingSrc; - if (lighting.m_source) + if (!lighting.m_source.empty()) lightingSrc = lighting.m_source; else lightingSrc = "const vec4 colorReg0 = vec4(1.0);\n" @@ -299,11 +299,11 @@ std::string GLSL::makeFrag(const char* glslVer, bool alphaTest, "\n"; std::string postSrc; - if (post.m_source) + if (!post.m_source.empty()) postSrc = post.m_source; std::string postEntry; - if (post.m_entry) + if (!post.m_entry.empty()) postEntry = post.m_entry; std::string texMapDecl; @@ -324,8 +324,8 @@ std::string GLSL::makeFrag(const char* glslVer, bool alphaTest, extTex.mapIdx, extTex.mapIdx); } - std::string retval = std::string(glslVer) + - "\n#extension GL_ARB_shader_image_load_store: enable\n" BOO_GLSL_BINDING_HEAD + + std::string retval = + std::string("#extension GL_ARB_shader_image_load_store: enable\n") + GenerateVertToFragStruct(extTexCount, reflectionType != ReflectionType::None) + (!alphaTest ? "\n#ifdef GL_ARB_shader_image_load_store\n" @@ -340,8 +340,9 @@ std::string GLSL::makeFrag(const char* glslVer, bool alphaTest, if (m_lighting) { - if (lighting.m_entry) - retval += hecl::Format(" vec4 lighting = %s(vtf.mvPos.xyz, normalize(vtf.mvNorm.xyz));\n", lighting.m_entry); + if (!lighting.m_entry.empty()) + retval += hecl::Format(" vec4 lighting = %s(vtf.mvPos.xyz, normalize(vtf.mvNorm.xyz));\n", + lighting.m_entry.data()); else retval += " vec4 lighting = vec4(1.0,1.0,1.0,1.0);\n"; } @@ -363,599 +364,3 @@ std::string GLSL::makeFrag(const char* glslVer, bool alphaTest, } -namespace hecl::Runtime -{ - -static const char* STD_BLOCKNAMES[] = {HECL_GLSL_VERT_UNIFORM_BLOCK_NAME, - HECL_GLSL_TEXMTX_UNIFORM_BLOCK_NAME}; - -static const char* STD_TEXNAMES[] = -{ - "tex0", - "tex1", - "tex2", - "tex3", - "tex4", - "tex5", - "tex6", - "tex7" -}; - -static const char* EXT_TEXNAMES[] = -{ - "extTex0", - "extTex1", - "extTex2", - "extTex3", - "extTex4", - "extTex5", - "extTex6", - "extTex7" -}; - -struct GLSLBackendFactory : IShaderBackendFactory -{ - Backend::GLSL m_backend; - - ShaderCachedData buildShaderFromIR(const ShaderTag& tag, - const hecl::Frontend::IR& ir, - hecl::Frontend::Diagnostics& diag, - boo::IGraphicsDataFactory::Context& ctx, - boo::ObjToken& objOut) - { - m_backend.reset(ir, diag); - size_t cachedSz = 3; - - std::string vertSource = - m_backend.makeVert("#version 330", - tag.getColorCount(), tag.getUvCount(), tag.getWeightCount(), - tag.getSkinSlotCount(), 0, nullptr, tag.getReflectionType()); - cachedSz += vertSource.size() + 1; - - std::string fragSource = m_backend.makeFrag("#version 330", - tag.getDepthWrite() && m_backend.m_blendDst == hecl::Backend::BlendFactor::InvSrcAlpha, - tag.getReflectionType()); - cachedSz += fragSource.size() + 1; - - if (m_backend.m_texMapEnd > 8) - Log.report(logvisor::Fatal, "maximum of 8 texture maps supported"); - - objOut = - static_cast(ctx). - newShaderPipeline(vertSource.c_str(), fragSource.c_str(), - m_backend.m_texMapEnd, STD_TEXNAMES, - 2, STD_BLOCKNAMES, - boo::BlendFactor(m_backend.m_blendSrc), - boo::BlendFactor(m_backend.m_blendDst), - tag.getPrimType(), tag.getDepthTest() ? boo::ZTest::LEqual : boo::ZTest::None, - tag.getDepthWrite(), true, false, - tag.getBackfaceCulling() ? boo::CullMode::Backface : boo::CullMode::None); - if (!objOut) - Log.report(logvisor::Fatal, "unable to build shader"); - - ShaderCachedData dataOut(tag, cachedSz); - athena::io::MemoryWriter w(dataOut.m_data.get(), dataOut.m_sz); - w.writeUByte(m_backend.m_texMapEnd); - w.writeUByte(atUint8(m_backend.m_blendSrc)); - w.writeUByte(atUint8(m_backend.m_blendDst)); - w.writeString(vertSource); - w.writeString(fragSource); - - return dataOut; - } - - boo::ObjToken buildShaderFromCache(const ShaderCachedData& data, - boo::IGraphicsDataFactory::Context& ctx) - { - const ShaderTag& tag = data.m_tag; - athena::io::MemoryReader r(data.m_data.get(), data.m_sz, false, false); - atUint8 texMapEnd = r.readUByte(); - boo::BlendFactor blendSrc = boo::BlendFactor(r.readUByte()); - boo::BlendFactor blendDst = boo::BlendFactor(r.readUByte()); - std::string vertSource = r.readString(); - std::string fragSource = r.readString(); - - if (r.hasError()) - return nullptr; - - if (texMapEnd > 8) - Log.report(logvisor::Fatal, "maximum of 8 texture maps supported"); - - auto ret = - static_cast(ctx). - newShaderPipeline(vertSource.c_str(), fragSource.c_str(), - texMapEnd, STD_TEXNAMES, - 2, STD_BLOCKNAMES, - blendSrc, blendDst, tag.getPrimType(), - tag.getDepthTest() ? boo::ZTest::LEqual : boo::ZTest::None, - tag.getDepthWrite(), true, false, - tag.getBackfaceCulling() ? boo::CullMode::Backface : boo::CullMode::None); - if (!ret) - Log.report(logvisor::Fatal, "unable to build shader"); - return ret; - } - - ShaderCachedData buildExtendedShaderFromIR(const ShaderTag& tag, - const hecl::Frontend::IR& ir, - hecl::Frontend::Diagnostics& diag, - const std::vector& extensionSlots, - boo::IGraphicsDataFactory::Context& ctx, - FReturnExtensionShader returnFunc) - { - m_backend.reset(ir, diag); - size_t cachedSz = 3; - - if (m_backend.m_texMapEnd > 8) - Log.report(logvisor::Fatal, "maximum of 8 texture maps supported"); - - std::vector> sources; - sources.reserve(extensionSlots.size()); - for (const ShaderCacheExtensions::ExtensionSlot& slot : extensionSlots) - { - size_t bc = 2; - const char** bn = STD_BLOCKNAMES; - if (slot.blockCount) - { - bc = slot.blockCount; - bn = slot.blockNames; - } - - sources.emplace_back(m_backend.makeVert("#version 330", - tag.getColorCount(), tag.getUvCount(), tag.getWeightCount(), - tag.getSkinSlotCount(), slot.texCount, - slot.texs, tag.getReflectionType()), - m_backend.makeFrag("#version 330", - tag.getDepthWrite() && m_backend.m_blendDst == hecl::Backend::BlendFactor::InvSrcAlpha, - tag.getReflectionType(), slot.lighting, slot.post, slot.texCount, slot.texs)); - cachedSz += sources.back().first.size() + 1; - cachedSz += sources.back().second.size() + 1; - - boo::ZTest zTest; - switch (slot.depthTest) - { - case hecl::Backend::ZTest::Original: - default: - zTest = tag.getDepthTest() ? boo::ZTest::LEqual : boo::ZTest::None; - break; - case hecl::Backend::ZTest::None: - zTest = boo::ZTest::None; - break; - case hecl::Backend::ZTest::LEqual: - zTest = boo::ZTest::LEqual; - break; - case hecl::Backend::ZTest::Greater: - zTest = boo::ZTest::Greater; - break; - case hecl::Backend::ZTest::Equal: - zTest = boo::ZTest::Equal; - break; - case hecl::Backend::ZTest::GEqual: - zTest = boo::ZTest::GEqual; - break; - } - - const char* ExtTexnames[8]; - for (int i=0 ; i<8 ; ++i) - ExtTexnames[i] = STD_TEXNAMES[i]; - for (int i=0 ; i(ctx). - newShaderPipeline(sources.back().first.c_str(), sources.back().second.c_str(), - 8, ExtTexnames, bc, bn, - boo::BlendFactor((slot.srcFactor == hecl::Backend::BlendFactor::Original) ? m_backend.m_blendSrc : slot.srcFactor), - boo::BlendFactor((slot.dstFactor == hecl::Backend::BlendFactor::Original) ? m_backend.m_blendDst : slot.dstFactor), - tag.getPrimType(), zTest, slot.noDepthWrite ? false : tag.getDepthWrite(), !slot.noColorWrite, !slot.noAlphaWrite, - (slot.cullMode == hecl::Backend::CullMode::Original) ? - (tag.getBackfaceCulling() ? boo::CullMode::Backface : boo::CullMode::None) : - boo::CullMode(slot.cullMode), !slot.noAlphaOverwrite); - if (!ret) - Log.report(logvisor::Fatal, "unable to build shader"); - returnFunc(ret); - } - - ShaderCachedData dataOut(tag, cachedSz); - athena::io::MemoryWriter w(dataOut.m_data.get(), dataOut.m_sz); - w.writeUByte(m_backend.m_texMapEnd); - w.writeUByte(atUint8(m_backend.m_blendSrc)); - w.writeUByte(atUint8(m_backend.m_blendDst)); - for (const std::pair& pair : sources) - { - w.writeString(pair.first); - w.writeString(pair.second); - } - - return dataOut; - } - - bool buildExtendedShaderFromCache(const ShaderCachedData& data, - const std::vector& extensionSlots, - boo::IGraphicsDataFactory::Context& ctx, - FReturnExtensionShader returnFunc) - { - const ShaderTag& tag = data.m_tag; - athena::io::MemoryReader r(data.m_data.get(), data.m_sz, false, false); - atUint8 texMapEnd = r.readUByte(); - hecl::Backend::BlendFactor blendSrc = hecl::Backend::BlendFactor(r.readUByte()); - hecl::Backend::BlendFactor blendDst = hecl::Backend::BlendFactor(r.readUByte()); - - if (r.hasError()) - return false; - - if (texMapEnd > 8) - Log.report(logvisor::Fatal, "maximum of 8 texture maps supported"); - - for (const ShaderCacheExtensions::ExtensionSlot& slot : extensionSlots) - { - size_t bc = 2; - const char** bn = STD_BLOCKNAMES; - if (slot.blockCount) - { - bc = slot.blockCount; - bn = slot.blockNames; - } - - std::string vertSource = r.readString(); - std::string fragSource = r.readString(); - - if (r.hasError()) - return false; - - boo::ZTest zTest; - switch (slot.depthTest) - { - case hecl::Backend::ZTest::Original: - default: - zTest = tag.getDepthTest() ? boo::ZTest::LEqual : boo::ZTest::None; - break; - case hecl::Backend::ZTest::None: - zTest = boo::ZTest::None; - break; - case hecl::Backend::ZTest::LEqual: - zTest = boo::ZTest::LEqual; - break; - case hecl::Backend::ZTest::Greater: - zTest = boo::ZTest::Greater; - break; - case hecl::Backend::ZTest::Equal: - zTest = boo::ZTest::Equal; - break; - case hecl::Backend::ZTest::GEqual: - zTest = boo::ZTest::GEqual; - break; - } - - const char* ExtTexnames[8]; - for (int i=0 ; i<8 ; ++i) - ExtTexnames[i] = STD_TEXNAMES[i]; - for (int i=0 ; i(ctx). - newShaderPipeline(vertSource.c_str(), fragSource.c_str(), - 8, ExtTexnames, bc, bn, - boo::BlendFactor((slot.srcFactor == hecl::Backend::BlendFactor::Original) ? blendSrc : slot.srcFactor), - boo::BlendFactor((slot.dstFactor == hecl::Backend::BlendFactor::Original) ? blendDst : slot.dstFactor), - tag.getPrimType(), zTest, slot.noDepthWrite ? false : tag.getDepthWrite(), !slot.noColorWrite, !slot.noAlphaWrite, - (slot.cullMode == hecl::Backend::CullMode::Original) ? - (tag.getBackfaceCulling() ? boo::CullMode::Backface : boo::CullMode::None) : - boo::CullMode(slot.cullMode), !slot.noAlphaOverwrite); - if (!ret) - Log.report(logvisor::Fatal, "unable to build shader"); - returnFunc(ret); - } - - return true; - } -}; - -IShaderBackendFactory* _NewGLSLBackendFactory() -{ - return new struct GLSLBackendFactory(); -} - -#if BOO_HAS_VULKAN - -struct SPIRVBackendFactory : IShaderBackendFactory -{ - Backend::GLSL m_backend; - - ShaderCachedData buildShaderFromIR(const ShaderTag& tag, - const hecl::Frontend::IR& ir, - hecl::Frontend::Diagnostics& diag, - boo::IGraphicsDataFactory::Context& ctx, - boo::ObjToken& objOut) - { - m_backend.reset(ir, diag); - - std::string vertSource = - m_backend.makeVert("#version 330", - tag.getColorCount(), tag.getUvCount(), tag.getWeightCount(), - tag.getSkinSlotCount(), 0, nullptr, - tag.getReflectionType()); - - std::string fragSource = m_backend.makeFrag("#version 330", - tag.getDepthWrite() && m_backend.m_blendDst == hecl::Backend::BlendFactor::InvSrcAlpha, - tag.getReflectionType()); - - std::vector vertBlob; - std::vector fragBlob; - std::vector pipelineBlob; - - objOut = - static_cast(ctx). - newShaderPipeline(vertSource.c_str(), fragSource.c_str(), - &vertBlob, &fragBlob, &pipelineBlob, tag.newVertexFormat(ctx), - boo::BlendFactor(m_backend.m_blendSrc), boo::BlendFactor(m_backend.m_blendDst), - tag.getPrimType(), tag.getDepthTest() ? boo::ZTest::LEqual : boo::ZTest::None, - tag.getDepthWrite(), true, false, - tag.getBackfaceCulling() ? boo::CullMode::Backface : boo::CullMode::None); - if (!objOut) - Log.report(logvisor::Fatal, "unable to build shader"); - - - atUint32 vertSz = vertBlob.size() * sizeof(unsigned int); - atUint32 fragSz = fragBlob.size() * sizeof(unsigned int); - atUint32 pipelineSz = pipelineBlob.size(); - - size_t cachedSz = 15 + vertSz + fragSz + pipelineSz; - - ShaderCachedData dataOut(tag, cachedSz); - athena::io::MemoryWriter w(dataOut.m_data.get(), dataOut.m_sz); - w.writeUByte(atUint8(m_backend.m_texMapEnd)); - w.writeUByte(atUint8(m_backend.m_blendSrc)); - w.writeUByte(atUint8(m_backend.m_blendDst)); - - if (vertBlob.size()) - { - w.writeUint32Big(vertSz); - w.writeUBytes((atUint8*)vertBlob.data(), vertSz); - } - else - w.writeUint32Big(0); - - if (fragBlob.size()) - { - w.writeUint32Big(fragSz); - w.writeUBytes((atUint8*)fragBlob.data(), fragSz); - } - else - w.writeUint32Big(0); - - if (pipelineBlob.size()) - { - w.writeUint32Big(pipelineSz); - w.writeUBytes((atUint8*)pipelineBlob.data(), pipelineSz); - } - else - w.writeUint32Big(0); - - return dataOut; - } - - boo::ObjToken - buildShaderFromCache(const ShaderCachedData& data, - boo::IGraphicsDataFactory::Context& ctx) - { - const ShaderTag& tag = data.m_tag; - athena::io::MemoryReader r(data.m_data.get(), data.m_sz, false, false); - size_t texCount = size_t(r.readByte()); - boo::BlendFactor blendSrc = boo::BlendFactor(r.readUByte()); - boo::BlendFactor blendDst = boo::BlendFactor(r.readUByte()); - - atUint32 vertSz = r.readUint32Big(); - std::vector vertBlob(vertSz / sizeof(unsigned int)); - if (vertSz) - r.readUBytesToBuf(vertBlob.data(), vertSz); - - atUint32 fragSz = r.readUint32Big(); - std::vector fragBlob(fragSz / sizeof(unsigned int)); - if (fragSz) - r.readUBytesToBuf(fragBlob.data(), fragSz); - - atUint32 pipelineSz = r.readUint32Big(); - std::vector pipelineBlob(pipelineSz); - if (pipelineSz) - r.readUBytesToBuf(pipelineBlob.data(), pipelineSz); - - if (r.hasError()) - return nullptr; - - boo::ObjToken ret = - static_cast(ctx). - newShaderPipeline(nullptr, nullptr, - &vertBlob, &fragBlob, &pipelineBlob, - tag.newVertexFormat(ctx), - blendSrc, blendDst, tag.getPrimType(), - tag.getDepthTest() ? boo::ZTest::LEqual : boo::ZTest::None, - tag.getDepthWrite(), true, false, - tag.getBackfaceCulling() ? boo::CullMode::Backface : boo::CullMode::None); - if (!ret) - Log.report(logvisor::Fatal, "unable to build shader"); - return ret; - } - - ShaderCachedData buildExtendedShaderFromIR(const ShaderTag& tag, - const hecl::Frontend::IR& ir, - hecl::Frontend::Diagnostics& diag, - const std::vector& extensionSlots, - boo::IGraphicsDataFactory::Context& ctx, - FReturnExtensionShader returnFunc) - { - m_backend.reset(ir, diag); - - struct Blobs - { - std::vector vert; - std::vector frag; - std::vector pipeline; - }; - std::vector pipeBlobs; - pipeBlobs.reserve(extensionSlots.size()); - - size_t cachedSz = 3 + 12 * extensionSlots.size(); - for (const ShaderCacheExtensions::ExtensionSlot& slot : extensionSlots) - { - std::string vertSource = - m_backend.makeVert("#version 330", - tag.getColorCount(), tag.getUvCount(), tag.getWeightCount(), - tag.getSkinSlotCount(), slot.texCount, slot.texs, - tag.getReflectionType()); - - std::string fragSource = m_backend.makeFrag("#version 330", - tag.getDepthWrite() && m_backend.m_blendDst == hecl::Backend::BlendFactor::InvSrcAlpha, - tag.getReflectionType(), slot.lighting, slot.post, slot.texCount, slot.texs); - pipeBlobs.emplace_back(); - Blobs& pipeBlob = pipeBlobs.back(); - boo::ObjToken ret = - static_cast(ctx). - newShaderPipeline(vertSource.c_str(), fragSource.c_str(), - &pipeBlob.vert, &pipeBlob.frag, &pipeBlob.pipeline, - tag.newVertexFormat(ctx), - boo::BlendFactor((slot.srcFactor == hecl::Backend::BlendFactor::Original) ? - m_backend.m_blendSrc : slot.srcFactor), - boo::BlendFactor((slot.dstFactor == hecl::Backend::BlendFactor::Original) ? - m_backend.m_blendDst : slot.dstFactor), - tag.getPrimType(), tag.getDepthTest() ? boo::ZTest::LEqual : boo::ZTest::None, - slot.noDepthWrite ? false : tag.getDepthWrite(), - !slot.noColorWrite, !slot.noAlphaWrite, - (slot.cullMode == hecl::Backend::CullMode::Original) ? - (tag.getBackfaceCulling() ? boo::CullMode::Backface : boo::CullMode::None) : - boo::CullMode(slot.cullMode), !slot.noAlphaOverwrite); - if (!ret) - Log.report(logvisor::Fatal, "unable to build shader"); - cachedSz += pipeBlob.vert.size() * sizeof(unsigned int); - cachedSz += pipeBlob.frag.size() * sizeof(unsigned int); - cachedSz += pipeBlob.pipeline.size(); - returnFunc(ret); - } - - ShaderCachedData dataOut(tag, cachedSz); - athena::io::MemoryWriter w(dataOut.m_data.get(), dataOut.m_sz); - w.writeUByte(atUint8(m_backend.m_texMapEnd)); - w.writeUByte(atUint8(m_backend.m_blendSrc)); - w.writeUByte(atUint8(m_backend.m_blendDst)); - - for (const Blobs& pipeBlob : pipeBlobs) - { - size_t vertBlobSz = pipeBlob.vert.size() * sizeof(unsigned int); - size_t fragBlobSz = pipeBlob.frag.size() * sizeof(unsigned int); - size_t pipeBlobSz = pipeBlob.pipeline.size(); - - if (vertBlobSz) - { - w.writeUint32Big(vertBlobSz); - w.writeUBytes((atUint8*)pipeBlob.vert.data(), vertBlobSz); - } - else - w.writeUint32Big(0); - - if (fragBlobSz) - { - w.writeUint32Big(fragBlobSz); - w.writeUBytes((atUint8*)pipeBlob.frag.data(), fragBlobSz); - } - else - w.writeUint32Big(0); - - if (pipeBlobSz) - { - w.writeUint32Big(pipeBlobSz); - w.writeUBytes((atUint8*)pipeBlob.pipeline.data(), pipeBlobSz); - } - else - w.writeUint32Big(0); - } - - return dataOut; - } - - bool buildExtendedShaderFromCache(const ShaderCachedData& data, - const std::vector& extensionSlots, - boo::IGraphicsDataFactory::Context& ctx, - FReturnExtensionShader returnFunc) - { - const ShaderTag& tag = data.m_tag; - athena::io::MemoryReader r(data.m_data.get(), data.m_sz); - size_t texCount = size_t(r.readByte()); - hecl::Backend::BlendFactor blendSrc = hecl::Backend::BlendFactor(r.readUByte()); - hecl::Backend::BlendFactor blendDst = hecl::Backend::BlendFactor(r.readUByte()); - - if (r.hasError()) - return false; - - for (const ShaderCacheExtensions::ExtensionSlot& slot : extensionSlots) - { - atUint32 vertSz = r.readUint32Big(); - std::vector vertBlob(vertSz / sizeof(unsigned int)); - if (vertSz) - r.readUBytesToBuf(vertBlob.data(), vertSz); - - atUint32 fragSz = r.readUint32Big(); - std::vector fragBlob(fragSz / sizeof(unsigned int)); - if (fragSz) - r.readUBytesToBuf(fragBlob.data(), fragSz); - - atUint32 pipelineSz = r.readUint32Big(); - std::vector pipelineBlob(pipelineSz); - if (pipelineSz) - r.readUBytesToBuf(pipelineBlob.data(), pipelineSz); - - if (r.hasError()) - return false; - - boo::ZTest zTest; - switch (slot.depthTest) - { - case hecl::Backend::ZTest::Original: - default: - zTest = tag.getDepthTest() ? boo::ZTest::LEqual : boo::ZTest::None; - break; - case hecl::Backend::ZTest::None: - zTest = boo::ZTest::None; - break; - case hecl::Backend::ZTest::LEqual: - zTest = boo::ZTest::LEqual; - break; - case hecl::Backend::ZTest::Greater: - zTest = boo::ZTest::Greater; - break; - case hecl::Backend::ZTest::Equal: - zTest = boo::ZTest::Equal; - break; - case hecl::Backend::ZTest::GEqual: - zTest = boo::ZTest::GEqual; - break; - } - - boo::ObjToken ret = - static_cast(ctx). - newShaderPipeline(nullptr, nullptr, - &vertBlob, &fragBlob, &pipelineBlob, - tag.newVertexFormat(ctx), - boo::BlendFactor((slot.srcFactor == hecl::Backend::BlendFactor::Original) ? blendSrc : slot.srcFactor), - boo::BlendFactor((slot.dstFactor == hecl::Backend::BlendFactor::Original) ? blendDst : slot.dstFactor), - tag.getPrimType(), zTest, slot.noDepthWrite ? false : tag.getDepthWrite(), - !slot.noColorWrite, !slot.noAlphaWrite, - (slot.cullMode == hecl::Backend::CullMode::Original) ? - (tag.getBackfaceCulling() ? boo::CullMode::Backface : boo::CullMode::None) : - boo::CullMode(slot.cullMode), !slot.noAlphaOverwrite); - if (!ret) - Log.report(logvisor::Fatal, "unable to build shader"); - returnFunc(ret); - } - - return true; - } -}; - -IShaderBackendFactory* _NewSPIRVBackendFactory() -{ - return new struct SPIRVBackendFactory(); -} - -#endif - -} diff --git a/hecl/lib/Backend/HLSL.cpp b/hecl/lib/Backend/HLSL.cpp index 5c28d4c90..81da87373 100644 --- a/hecl/lib/Backend/HLSL.cpp +++ b/hecl/lib/Backend/HLSL.cpp @@ -215,10 +215,10 @@ std::string HLSL::makeVert(unsigned col, unsigned uv, unsigned w, } std::string HLSL::makeFrag(bool alphaTest, ReflectionType reflectionType, - const ShaderFunction& lighting) const + const Function& lighting) const { std::string lightingSrc; - if (lighting.m_source) + if (!lighting.m_source.empty()) lightingSrc = lighting.m_source; else lightingSrc = "static const float4 colorReg0 = float4(1.0, 1.0, 1.0, 1.0);\n" @@ -248,8 +248,9 @@ std::string HLSL::makeFrag(bool alphaTest, ReflectionType reflectionType, if (m_lighting) { - if (lighting.m_entry) - retval += hecl::Format(" float4 lighting = %s(vtf.mvPos.xyz, normalize(vtf.mvNorm.xyz), vtf);\n", lighting.m_entry); + if (!lighting.m_entry.empty()) + retval += hecl::Format(" float4 lighting = %s(vtf.mvPos.xyz, normalize(vtf.mvNorm.xyz), vtf);\n", + lighting.m_entry.data()); else retval += " float4 lighting = float4(1.0,1.0,1.0,1.0);\n"; } @@ -271,12 +272,12 @@ std::string HLSL::makeFrag(bool alphaTest, ReflectionType reflectionType, } std::string HLSL::makeFrag(bool alphaTest, ReflectionType reflectionType, - const ShaderFunction& lighting, - const ShaderFunction& post, size_t extTexCount, + const Function& lighting, + const Function& post, size_t extTexCount, const TextureInfo* extTexs) const { std::string lightingSrc; - if (lighting.m_source) + if (!lighting.m_source.empty()) lightingSrc = lighting.m_source; else lightingSrc = "static const float4 colorReg0 = float4(1.0, 1.0, 1.0, 1.0);\n" @@ -285,11 +286,11 @@ std::string HLSL::makeFrag(bool alphaTest, ReflectionType reflectionType, "static const float4 mulColor = float4(1.0, 1.0, 1.0, 1.0);\n"; std::string postSrc; - if (post.m_source) + if (!post.m_source.empty()) postSrc = post.m_source; std::string postEntry; - if (post.m_entry) + if (!post.m_entry.empty()) postEntry = post.m_entry; std::string texMapDecl; @@ -323,8 +324,9 @@ std::string HLSL::makeFrag(bool alphaTest, ReflectionType reflectionType, if (m_lighting) { - if (lighting.m_entry) - retval += hecl::Format(" float4 lighting = %s(vtf.mvPos.xyz, normalize(vtf.mvNorm.xyz), vtf);\n", lighting.m_entry); + if (!lighting.m_entry.empty()) + retval += hecl::Format(" float4 lighting = %s(vtf.mvPos.xyz, normalize(vtf.mvNorm.xyz), vtf);\n", + lighting.m_entry.data()); else retval += " float4 lighting = float4(1.0,1.0,1.0,1.0);\n"; } @@ -346,344 +348,3 @@ std::string HLSL::makeFrag(bool alphaTest, ReflectionType reflectionType, } } - -namespace hecl::Runtime -{ - -struct HLSLBackendFactory : IShaderBackendFactory -{ - Backend::HLSL m_backend; - - ShaderCachedData buildShaderFromIR(const ShaderTag& tag, - const hecl::Frontend::IR& ir, - hecl::Frontend::Diagnostics& diag, - boo::IGraphicsDataFactory::Context& ctx, - boo::ObjToken& objOut) - { - m_backend.reset(ir, diag); - - std::string vertSource = - m_backend.makeVert(tag.getColorCount(), tag.getUvCount(), tag.getWeightCount(), - tag.getSkinSlotCount(), 0, nullptr, - tag.getReflectionType()); - - std::string fragSource = m_backend.makeFrag(tag.getDepthWrite() && m_backend.m_blendDst == hecl::Backend::BlendFactor::InvSrcAlpha, - tag.getReflectionType()); - ComPtr vertBlob; - ComPtr fragBlob; - ComPtr pipelineBlob; - objOut = - static_cast(ctx). - newShaderPipeline(vertSource.c_str(), fragSource.c_str(), - ReferenceComPtr(vertBlob), ReferenceComPtr(fragBlob), ReferenceComPtr(pipelineBlob), - tag.newVertexFormat(ctx), - boo::BlendFactor(m_backend.m_blendSrc), - boo::BlendFactor(m_backend.m_blendDst), - tag.getPrimType(), - tag.getDepthTest() ? boo::ZTest::LEqual : boo::ZTest::None, tag.getDepthWrite(), true, false, - tag.getBackfaceCulling() ? boo::CullMode::Backface : boo::CullMode::None); - if (!objOut) - Log.report(logvisor::Fatal, "unable to build shader"); - - atUint32 vertSz = 0; - atUint32 fragSz = 0; - atUint32 pipelineSz = 0; - if (vertBlob) - vertSz = vertBlob->GetBufferSize(); - if (fragBlob) - fragSz = fragBlob->GetBufferSize(); - if (pipelineBlob) - pipelineSz = pipelineBlob->GetBufferSize(); - - size_t cachedSz = 14 + vertSz + fragSz + pipelineSz; - - ShaderCachedData dataOut(tag, cachedSz); - athena::io::MemoryWriter w(dataOut.m_data.get(), dataOut.m_sz); - w.writeUByte(atUint8(m_backend.m_blendSrc)); - w.writeUByte(atUint8(m_backend.m_blendDst)); - - if (vertBlob) - { - w.writeUint32Big(vertSz); - w.writeUBytes((atUint8*)vertBlob->GetBufferPointer(), vertSz); - } - else - w.writeUint32Big(0); - - if (fragBlob) - { - w.writeUint32Big(fragSz); - w.writeUBytes((atUint8*)fragBlob->GetBufferPointer(), fragSz); - } - else - w.writeUint32Big(0); - - if (pipelineBlob) - { - w.writeUint32Big(pipelineSz); - w.writeUBytes((atUint8*)pipelineBlob->GetBufferPointer(), pipelineSz); - } - else - w.writeUint32Big(0); - - return dataOut; - } - - boo::ObjToken - buildShaderFromCache(const ShaderCachedData& data, - boo::IGraphicsDataFactory::Context& ctx) - { - const ShaderTag& tag = data.m_tag; - athena::io::MemoryReader r(data.m_data.get(), data.m_sz, false, false); - boo::BlendFactor blendSrc = boo::BlendFactor(r.readUByte()); - boo::BlendFactor blendDst = boo::BlendFactor(r.readUByte()); - - if (r.hasError()) - return nullptr; - - atUint32 vertSz = r.readUint32Big(); - ComPtr vertBlob; - if (vertSz) - { - D3DCreateBlobPROC(vertSz, &vertBlob); - r.readUBytesToBuf(vertBlob->GetBufferPointer(), vertSz); - } - - atUint32 fragSz = r.readUint32Big(); - ComPtr fragBlob; - if (fragSz) - { - D3DCreateBlobPROC(fragSz, &fragBlob); - r.readUBytesToBuf(fragBlob->GetBufferPointer(), fragSz); - } - - atUint32 pipelineSz = r.readUint32Big(); - ComPtr pipelineBlob; - if (pipelineSz) - { - D3DCreateBlobPROC(pipelineSz, &pipelineBlob); - r.readUBytesToBuf(pipelineBlob->GetBufferPointer(), pipelineSz); - } - - if (r.hasError()) - return nullptr; - - boo::ObjToken ret = - static_cast(ctx). - newShaderPipeline(nullptr, nullptr, - ReferenceComPtr(vertBlob), ReferenceComPtr(fragBlob), ReferenceComPtr(pipelineBlob), - tag.newVertexFormat(ctx), - blendSrc, blendDst, tag.getPrimType(), - tag.getDepthTest() ? boo::ZTest::LEqual : boo::ZTest::None, tag.getDepthWrite(), true, false, - tag.getBackfaceCulling() ? boo::CullMode::Backface : boo::CullMode::None); - if (!ret) - Log.report(logvisor::Fatal, "unable to build shader"); - return ret; - } - - ShaderCachedData buildExtendedShaderFromIR(const ShaderTag& tag, - const hecl::Frontend::IR& ir, - hecl::Frontend::Diagnostics& diag, - const std::vector& extensionSlots, - boo::IGraphicsDataFactory::Context& ctx, - FReturnExtensionShader returnFunc) - { - m_backend.reset(ir, diag); - - struct Blobs - { - ComPtr vert; - ComPtr frag; - ComPtr pipeline; - }; - std::vector pipeBlobs; - pipeBlobs.reserve(extensionSlots.size()); - - size_t cachedSz = 2 + 12 * extensionSlots.size(); - for (const ShaderCacheExtensions::ExtensionSlot& slot : extensionSlots) - { - std::string vertSource = - m_backend.makeVert(tag.getColorCount(), tag.getUvCount(), tag.getWeightCount(), - tag.getSkinSlotCount(), slot.texCount, slot.texs, - tag.getReflectionType()); - - std::string fragSource = m_backend.makeFrag(tag.getDepthWrite() && m_backend.m_blendDst == hecl::Backend::BlendFactor::InvSrcAlpha, - tag.getReflectionType(), slot.lighting, slot.post, slot.texCount, slot.texs); - pipeBlobs.emplace_back(); - Blobs& thisPipeBlobs = pipeBlobs.back(); - - boo::ZTest zTest; - switch (slot.depthTest) - { - case hecl::Backend::ZTest::Original: - default: - zTest = tag.getDepthTest() ? boo::ZTest::LEqual : boo::ZTest::None; - break; - case hecl::Backend::ZTest::None: - zTest = boo::ZTest::None; - break; - case hecl::Backend::ZTest::LEqual: - zTest = boo::ZTest::LEqual; - break; - case hecl::Backend::ZTest::Greater: - zTest = boo::ZTest::Greater; - break; - case hecl::Backend::ZTest::Equal: - zTest = boo::ZTest::Equal; - break; - case hecl::Backend::ZTest::GEqual: - zTest = boo::ZTest::GEqual; - break; - } - - boo::ObjToken ret = - static_cast(ctx). - newShaderPipeline(vertSource.c_str(), fragSource.c_str(), - ReferenceComPtr(thisPipeBlobs.vert), ReferenceComPtr(thisPipeBlobs.frag), ReferenceComPtr(thisPipeBlobs.pipeline), - tag.newVertexFormat(ctx), - boo::BlendFactor((slot.srcFactor == hecl::Backend::BlendFactor::Original) ? m_backend.m_blendSrc : slot.srcFactor), - boo::BlendFactor((slot.dstFactor == hecl::Backend::BlendFactor::Original) ? m_backend.m_blendDst : slot.dstFactor), - tag.getPrimType(), zTest, slot.noDepthWrite ? false : tag.getDepthWrite(), - !slot.noColorWrite, !slot.noAlphaWrite, - (slot.cullMode == hecl::Backend::CullMode::Original) ? - (tag.getBackfaceCulling() ? boo::CullMode::Backface : boo::CullMode::None) : - boo::CullMode(slot.cullMode), !slot.noAlphaOverwrite); - if (!ret) - Log.report(logvisor::Fatal, "unable to build shader"); - if (thisPipeBlobs.vert) - cachedSz += thisPipeBlobs.vert->GetBufferSize(); - if (thisPipeBlobs.frag) - cachedSz += thisPipeBlobs.frag->GetBufferSize(); - if (thisPipeBlobs.pipeline) - cachedSz += thisPipeBlobs.pipeline->GetBufferSize(); - returnFunc(ret); - } - - ShaderCachedData dataOut(tag, cachedSz); - athena::io::MemoryWriter w(dataOut.m_data.get(), dataOut.m_sz); - w.writeUByte(atUint8(m_backend.m_blendSrc)); - w.writeUByte(atUint8(m_backend.m_blendDst)); - - for (const Blobs& blobs : pipeBlobs) - { - if (blobs.vert) - { - w.writeUint32Big(blobs.vert->GetBufferSize()); - w.writeUBytes((atUint8*)blobs.vert->GetBufferPointer(), blobs.vert->GetBufferSize()); - } - else - w.writeUint32Big(0); - - if (blobs.frag) - { - w.writeUint32Big(blobs.frag->GetBufferSize()); - w.writeUBytes((atUint8*)blobs.frag->GetBufferPointer(), blobs.frag->GetBufferSize()); - } - else - w.writeUint32Big(0); - - if (blobs.pipeline) - { - w.writeUint32Big(blobs.pipeline->GetBufferSize()); - w.writeUBytes((atUint8*)blobs.pipeline->GetBufferPointer(), blobs.pipeline->GetBufferSize()); - } - else - w.writeUint32Big(0); - } - - return dataOut; - } - - bool buildExtendedShaderFromCache(const ShaderCachedData& data, - const std::vector& extensionSlots, - boo::IGraphicsDataFactory::Context& ctx, - FReturnExtensionShader returnFunc) - { - const ShaderTag& tag = data.m_tag; - athena::io::MemoryReader r(data.m_data.get(), data.m_sz, false, false); - hecl::Backend::BlendFactor blendSrc = hecl::Backend::BlendFactor(r.readUByte()); - hecl::Backend::BlendFactor blendDst = hecl::Backend::BlendFactor(r.readUByte()); - - if (r.hasError()) - return false; - - for (const ShaderCacheExtensions::ExtensionSlot& slot : extensionSlots) - { - atUint32 vertSz = r.readUint32Big(); - ComPtr vertBlob; - if (vertSz) - { - D3DCreateBlobPROC(vertSz, &vertBlob); - r.readUBytesToBuf(vertBlob->GetBufferPointer(), vertSz); - } - - atUint32 fragSz = r.readUint32Big(); - ComPtr fragBlob; - if (fragSz) - { - D3DCreateBlobPROC(fragSz, &fragBlob); - r.readUBytesToBuf(fragBlob->GetBufferPointer(), fragSz); - } - - atUint32 pipelineSz = r.readUint32Big(); - ComPtr pipelineBlob; - if (pipelineSz) - { - D3DCreateBlobPROC(pipelineSz, &pipelineBlob); - r.readUBytesToBuf(pipelineBlob->GetBufferPointer(), pipelineSz); - } - - if (r.hasError()) - return false; - - boo::ZTest zTest; - switch (slot.depthTest) - { - case hecl::Backend::ZTest::Original: - default: - zTest = tag.getDepthTest() ? boo::ZTest::LEqual : boo::ZTest::None; - break; - case hecl::Backend::ZTest::None: - zTest = boo::ZTest::None; - break; - case hecl::Backend::ZTest::LEqual: - zTest = boo::ZTest::LEqual; - break; - case hecl::Backend::ZTest::Greater: - zTest = boo::ZTest::Greater; - break; - case hecl::Backend::ZTest::Equal: - zTest = boo::ZTest::Equal; - break; - case hecl::Backend::ZTest::GEqual: - zTest = boo::ZTest::GEqual; - break; - } - - boo::ObjToken ret = - static_cast(ctx). - newShaderPipeline(nullptr, nullptr, - ReferenceComPtr(vertBlob), ReferenceComPtr(fragBlob), ReferenceComPtr(pipelineBlob), - tag.newVertexFormat(ctx), - boo::BlendFactor((slot.srcFactor == hecl::Backend::BlendFactor::Original) ? blendSrc : slot.srcFactor), - boo::BlendFactor((slot.dstFactor == hecl::Backend::BlendFactor::Original) ? blendDst : slot.dstFactor), - tag.getPrimType(), zTest, slot.noDepthWrite ? false : tag.getDepthWrite(), - !slot.noColorWrite, !slot.noAlphaWrite, - (slot.cullMode == hecl::Backend::CullMode::Original) ? - (tag.getBackfaceCulling() ? boo::CullMode::Backface : boo::CullMode::None) : - boo::CullMode(slot.cullMode), !slot.noAlphaOverwrite); - if (!ret) - Log.report(logvisor::Fatal, "unable to build shader"); - returnFunc(ret); - } - - return true; - } -}; - -IShaderBackendFactory* _NewHLSLBackendFactory() -{ - return new struct HLSLBackendFactory(); -} - -} diff --git a/hecl/lib/Backend/Metal.cpp b/hecl/lib/Backend/Metal.cpp index c48b7bb7d..8c7a29a2e 100644 --- a/hecl/lib/Backend/Metal.cpp +++ b/hecl/lib/Backend/Metal.cpp @@ -1,5 +1,4 @@ #include "hecl/Backend/Metal.hpp" -#if BOO_HAS_METAL #include #include #include @@ -223,10 +222,10 @@ std::string Metal::makeVert(unsigned col, unsigned uv, unsigned w, } std::string Metal::makeFrag(size_t blockCount, const char** blockNames, bool alphaTest, - ReflectionType reflectionType, const ShaderFunction& lighting) const + ReflectionType reflectionType, const Function& lighting) const { std::string lightingSrc; - if (lighting.m_source) + if (!lighting.m_source.empty()) lightingSrc = lighting.m_source; std::string texMapDecl; @@ -259,7 +258,7 @@ std::string Metal::makeFrag(size_t blockCount, const char** blockNames, bool alp "{\n" " FragOut out;\n"; - if (lighting.m_source) + if (!lighting.m_source.empty()) { retval += " float4 colorReg0 = block0.colorReg0;\n" " float4 colorReg1 = block0.colorReg1;\n" @@ -276,8 +275,9 @@ std::string Metal::makeFrag(size_t blockCount, const char** blockNames, bool alp if (m_lighting) { - if (lighting.m_entry) - retval += hecl::Format(" float4 lighting = %s(%s, vtf.mvPos.xyz, normalize(vtf.mvNorm.xyz), vtf);\n", lighting.m_entry, blockCall.c_str()); + if (!lighting.m_entry.empty()) + retval += hecl::Format(" float4 lighting = %s(%s, vtf.mvPos.xyz, normalize(vtf.mvNorm.xyz), vtf);\n", + lighting.m_entry.data(), blockCall.c_str()); else retval += " float4 lighting = float4(1.0,1.0,1.0,1.0);\n"; } @@ -301,24 +301,24 @@ std::string Metal::makeFrag(size_t blockCount, const char** blockNames, bool alp } std::string Metal::makeFrag(size_t blockCount, const char** blockNames, bool alphaTest, - ReflectionType reflectionType, const ShaderFunction& lighting, - const ShaderFunction& post, size_t extTexCount, + ReflectionType reflectionType, const Function& lighting, + const Function& post, size_t extTexCount, const TextureInfo* extTexs) const { std::string lightingSrc; - if (lighting.m_source) + if (!lighting.m_source.empty()) lightingSrc = lighting.m_source; std::string postSrc; - if (post.m_source) + if (!post.m_source.empty()) postSrc = post.m_source; std::string lightingEntry; - if (lighting.m_entry) + if (!lighting.m_entry.empty()) lightingEntry = lighting.m_entry; std::string postEntry; - if (post.m_entry) + if (!post.m_entry.empty()) postEntry = post.m_entry; int extTexBits = 0; @@ -371,7 +371,7 @@ std::string Metal::makeFrag(size_t blockCount, const char** blockNames, bool alp "{\n" " FragOut out;\n"; - if (lighting.m_source) + if (!lighting.m_source.empty()) { retval += " float4 colorReg0 = block0.colorReg0;\n" " float4 colorReg1 = block0.colorReg1;\n" @@ -388,10 +388,10 @@ std::string Metal::makeFrag(size_t blockCount, const char** blockNames, bool alp if (m_lighting) { - if (lighting.m_entry) + if (!lighting.m_entry.empty()) { retval += " float4 lighting = " + lightingEntry + "(" + blockCall + ", vtf.mvPos.xyz, normalize(vtf.mvNorm.xyz), vtf" + - (!strncmp(lighting.m_entry, "EXT", 3) ? (extTexCall.size() ? (", samp, clampSamp," + extTexCall) : "") : "") + ");\n"; + (!strncmp(lighting.m_entry.data(), "EXT", 3) ? (extTexCall.size() ? (", samp, clampSamp," + extTexCall) : "") : "") + ");\n"; } else retval += " float4 lighting = float4(1.0,1.0,1.0,1.0);\n"; @@ -408,14 +408,14 @@ std::string Metal::makeFrag(size_t blockCount, const char** blockNames, bool alp { retval += " out.color = " + postEntry + "(" + (postEntry.size() ? ("vtf, " + (blockCall.size() ? (blockCall + ", ") : "") + - (!strncmp(post.m_entry, "EXT", 3) ? (extTexCall.size() ? ("samp, clampSamp," + extTexCall + ", ") : "") : "")) : "") + + (!strncmp(post.m_entry.data(), "EXT", 3) ? (extTexCall.size() ? ("samp, clampSamp," + extTexCall + ", ") : "") : "")) : "") + "float4(" + m_colorExpr + " + " + reflectionExpr + ", " + m_alphaExpr + ")) * mulColor;\n"; } else { retval += " out.color = " + postEntry + "(" + (postEntry.size() ? ("vtf, " + (blockCall.size() ? (blockCall + ", ") : "") + - (!strncmp(post.m_entry, "EXT", 3) ? (extTexCall.size() ? ("samp, clampSamp," + extTexCall + ", ") : "") : "")) : "") + + (!strncmp(post.m_entry.data(), "EXT", 3) ? (extTexCall.size() ? ("samp, clampSamp," + extTexCall + ", ") : "") : "")) : "") + "float4(" + m_colorExpr + " + " + reflectionExpr + ", 1.0)) * mulColor;\n"; } @@ -427,265 +427,3 @@ std::string Metal::makeFrag(size_t blockCount, const char** blockNames, bool alp } -namespace hecl::Runtime -{ - -struct MetalBackendFactory : IShaderBackendFactory -{ - Backend::Metal m_backend; - - ShaderCachedData buildShaderFromIR(const ShaderTag& tag, - const hecl::Frontend::IR& ir, - hecl::Frontend::Diagnostics& diag, - boo::IGraphicsDataFactory::Context& ctx, - boo::ObjToken& objOut) - { - m_backend.reset(ir, diag); - size_t cachedSz = 2; - - std::string vertSource = - m_backend.makeVert(tag.getColorCount(), tag.getUvCount(), tag.getWeightCount(), - tag.getSkinSlotCount(), 0, nullptr, tag.getReflectionType()); - - std::string fragSource = m_backend.makeFrag(0, nullptr, - tag.getDepthWrite() && m_backend.m_blendDst == hecl::Backend::BlendFactor::InvSrcAlpha, - tag.getReflectionType()); - - std::vector vertBlob; - std::vector fragBlob; - objOut = - static_cast(ctx). - newShaderPipeline(vertSource.c_str(), fragSource.c_str(), - &vertBlob, &fragBlob, - tag.newVertexFormat(ctx), - boo::BlendFactor(m_backend.m_blendSrc), - boo::BlendFactor(m_backend.m_blendDst), - tag.getPrimType(), - tag.getDepthTest() ? boo::ZTest::LEqual : boo::ZTest::None, tag.getDepthWrite(), true, true, - tag.getBackfaceCulling() ? boo::CullMode::Backface : boo::CullMode::None); - if (!objOut) - Log.report(logvisor::Fatal, "unable to build shader"); - - cachedSz += vertBlob.size() + 4; - cachedSz += fragBlob.size() + 4; - - ShaderCachedData dataOut(tag, cachedSz); - athena::io::MemoryWriter w(dataOut.m_data.get(), dataOut.m_sz); - w.writeUByte(atUint8(m_backend.m_blendSrc)); - w.writeUByte(atUint8(m_backend.m_blendDst)); - w.writeUint32Big(vertBlob.size()); - w.writeUBytes(vertBlob.data(), vertBlob.size()); - w.writeUint32Big(fragBlob.size()); - w.writeUBytes(fragBlob.data(), fragBlob.size()); - - return dataOut; - } - - boo::ObjToken buildShaderFromCache(const ShaderCachedData& data, - boo::IGraphicsDataFactory::Context& ctx) - { - const ShaderTag& tag = data.m_tag; - athena::io::MemoryReader r(data.m_data.get(), data.m_sz, false, false); - boo::BlendFactor blendSrc = boo::BlendFactor(r.readUByte()); - boo::BlendFactor blendDst = boo::BlendFactor(r.readUByte()); - std::vector vertBlob; - std::vector fragBlob; - atUint32 vertLen = r.readUint32Big(); - if (vertLen) - { - vertBlob.resize(vertLen); - r.readUBytesToBuf(&vertBlob[0], vertLen); - } - atUint32 fragLen = r.readUint32Big(); - if (fragLen) - { - fragBlob.resize(fragLen); - r.readUBytesToBuf(&fragBlob[0], fragLen); - } - - if (r.hasError()) - return nullptr; - - auto ret = - static_cast(ctx). - newShaderPipeline(nullptr, nullptr, - &vertBlob, &fragBlob, - tag.newVertexFormat(ctx), - blendSrc, blendDst, tag.getPrimType(), - tag.getDepthTest() ? boo::ZTest::LEqual : boo::ZTest::None, tag.getDepthWrite(), true, true, - tag.getBackfaceCulling() ? boo::CullMode::Backface : boo::CullMode::None); - if (!ret) - Log.report(logvisor::Fatal, "unable to build shader"); - return ret; - } - - ShaderCachedData buildExtendedShaderFromIR(const ShaderTag& tag, - const hecl::Frontend::IR& ir, - hecl::Frontend::Diagnostics& diag, - const std::vector& extensionSlots, - boo::IGraphicsDataFactory::Context& ctx, - FReturnExtensionShader returnFunc) - { - m_backend.reset(ir, diag); - size_t cachedSz = 2; - - std::vector, std::vector>> blobs; - blobs.reserve(extensionSlots.size()); - for (const ShaderCacheExtensions::ExtensionSlot& slot : extensionSlots) - { - std::string vertSource = - m_backend.makeVert(tag.getColorCount(), tag.getUvCount(), tag.getWeightCount(), - tag.getSkinSlotCount(), slot.texCount, slot.texs, - slot.noReflection ? Backend::ReflectionType::None : tag.getReflectionType()); - std::string fragSource = - m_backend.makeFrag(slot.blockCount, slot.blockNames, - tag.getDepthWrite() && m_backend.m_blendDst == hecl::Backend::BlendFactor::InvSrcAlpha, - slot.noReflection ? Backend::ReflectionType::None : tag.getReflectionType(), - slot.lighting, slot.post, slot.texCount, slot.texs); - - boo::ZTest zTest; - switch (slot.depthTest) - { - case hecl::Backend::ZTest::Original: - default: - zTest = tag.getDepthTest() ? boo::ZTest::LEqual : boo::ZTest::None; - break; - case hecl::Backend::ZTest::None: - zTest = boo::ZTest::None; - break; - case hecl::Backend::ZTest::LEqual: - zTest = boo::ZTest::LEqual; - break; - case hecl::Backend::ZTest::Greater: - zTest = boo::ZTest::Greater; - break; - case hecl::Backend::ZTest::Equal: - zTest = boo::ZTest::Equal; - break; - case hecl::Backend::ZTest::GEqual: - zTest = boo::ZTest::GEqual; - break; - } - - blobs.emplace_back(); - auto ret = - static_cast(ctx). - newShaderPipeline(vertSource.c_str(), fragSource.c_str(), - &blobs.back().first, &blobs.back().second, - tag.newVertexFormat(ctx), - boo::BlendFactor((slot.srcFactor == hecl::Backend::BlendFactor::Original) ? m_backend.m_blendSrc : slot.srcFactor), - boo::BlendFactor((slot.dstFactor == hecl::Backend::BlendFactor::Original) ? m_backend.m_blendDst : slot.dstFactor), - tag.getPrimType(), zTest, slot.noDepthWrite ? false : tag.getDepthWrite(), - !slot.noColorWrite, !slot.noAlphaWrite, - (slot.cullMode == hecl::Backend::CullMode::Original) ? - (tag.getBackfaceCulling() ? boo::CullMode::Backface : boo::CullMode::None) : - boo::CullMode(slot.cullMode), !slot.noAlphaOverwrite); - if (!ret) - Log.report(logvisor::Fatal, "unable to build shader"); - - cachedSz += blobs.back().first.size() + 4; - cachedSz += blobs.back().second.size() + 4; - returnFunc(ret); - } - - ShaderCachedData dataOut(tag, cachedSz); - athena::io::MemoryWriter w(dataOut.m_data.get(), dataOut.m_sz); - w.writeUByte(atUint8(m_backend.m_blendSrc)); - w.writeUByte(atUint8(m_backend.m_blendDst)); - for (auto& blob : blobs) - { - w.writeUint32Big(blob.first.size()); - w.writeUBytes(blob.first.data(), blob.first.size()); - w.writeUint32Big(blob.second.size()); - w.writeUBytes(blob.second.data(), blob.second.size()); - } - - return dataOut; - } - - bool buildExtendedShaderFromCache(const ShaderCachedData& data, - const std::vector& extensionSlots, - boo::IGraphicsDataFactory::Context& ctx, - FReturnExtensionShader returnFunc) - { - const ShaderTag& tag = data.m_tag; - athena::io::MemoryReader r(data.m_data.get(), data.m_sz, false, false); - hecl::Backend::BlendFactor blendSrc = hecl::Backend::BlendFactor(r.readUByte()); - hecl::Backend::BlendFactor blendDst = hecl::Backend::BlendFactor(r.readUByte()); - - if (r.hasError()) - return false; - - for (const ShaderCacheExtensions::ExtensionSlot& slot : extensionSlots) - { - std::vector vertBlob; - std::vector fragBlob; - atUint32 vertLen = r.readUint32Big(); - if (vertLen) - { - vertBlob.resize(vertLen); - r.readUBytesToBuf(&vertBlob[0], vertLen); - } - atUint32 fragLen = r.readUint32Big(); - if (fragLen) - { - fragBlob.resize(fragLen); - r.readUBytesToBuf(&fragBlob[0], fragLen); - } - - if (r.hasError()) - return false; - - boo::ZTest zTest; - switch (slot.depthTest) - { - case hecl::Backend::ZTest::Original: - default: - zTest = tag.getDepthTest() ? boo::ZTest::LEqual : boo::ZTest::None; - break; - case hecl::Backend::ZTest::None: - zTest = boo::ZTest::None; - break; - case hecl::Backend::ZTest::LEqual: - zTest = boo::ZTest::LEqual; - break; - case hecl::Backend::ZTest::Greater: - zTest = boo::ZTest::Greater; - break; - case hecl::Backend::ZTest::Equal: - zTest = boo::ZTest::Equal; - break; - case hecl::Backend::ZTest::GEqual: - zTest = boo::ZTest::GEqual; - break; - } - - auto ret = - static_cast(ctx). - newShaderPipeline(nullptr, nullptr, - &vertBlob, &fragBlob, - tag.newVertexFormat(ctx), - boo::BlendFactor((slot.srcFactor == hecl::Backend::BlendFactor::Original) ? blendSrc : slot.srcFactor), - boo::BlendFactor((slot.dstFactor == hecl::Backend::BlendFactor::Original) ? blendDst : slot.dstFactor), - tag.getPrimType(), zTest, slot.noDepthWrite ? false : tag.getDepthWrite(), - !slot.noColorWrite, !slot.noAlphaWrite, - (slot.cullMode == hecl::Backend::CullMode::Original) ? - (tag.getBackfaceCulling() ? boo::CullMode::Backface : boo::CullMode::None) : - boo::CullMode(slot.cullMode), !slot.noAlphaOverwrite); - if (!ret) - Log.report(logvisor::Fatal, "unable to build shader"); - returnFunc(ret); - } - - return true; - } -}; - -IShaderBackendFactory* _NewMetalBackendFactory() -{ - return new struct MetalBackendFactory(); -} - -} - -#endif diff --git a/hecl/lib/CMakeLists.txt b/hecl/lib/CMakeLists.txt index 00e70ad35..6910ec620 100644 --- a/hecl/lib/CMakeLists.txt +++ b/hecl/lib/CMakeLists.txt @@ -6,6 +6,8 @@ macro(hecl_add_list rel_path a_list) set(${a_list} "${tmp_list}" PARENT_SCOPE) endmacro(hecl_add_list) +include_directories(../extern/boo/glslang ../extern/boo) + add_subdirectory(Blender) add_subdirectory(Backend) add_subdirectory(Frontend) @@ -17,7 +19,6 @@ endif() atdna(atdna_HMDLMeta.cpp ../include/hecl/HMDLMeta.hpp) atdna(atdna_Frontend.cpp ../include/hecl/Frontend.hpp) -atdna(atdna_Runtime.cpp ../include/hecl/Runtime.hpp) atdna(atdna_CVar.cpp ../include/hecl/CVar.hpp) if("${CMAKE_BUILD_TYPE}" STREQUAL "Release" OR "${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo") @@ -50,7 +51,10 @@ set(HECL_HEADERS ../include/hecl/BitVector.hpp ../include/hecl/MathExtras.hpp ../include/hecl/UniformBufferPool.hpp - ../include/hecl/VertexBufferPool.hpp) + ../include/hecl/VertexBufferPool.hpp + ../include/hecl/PipelineBase.hpp + ../include/hecl/Pipeline.hpp + ../include/hecl/Compilers.hpp) set(COMMON_SOURCES hecl.cpp MultiProgressPrinter.cpp @@ -63,9 +67,10 @@ set(COMMON_SOURCES ClientProcess.cpp SteamFinder.cpp WideStringConvert.cpp + Compilers.cpp + Pipeline.cpp atdna_HMDLMeta.cpp atdna_Frontend.cpp - atdna_Runtime.cpp atdna_CVar.cpp) add_library(hecl-full @@ -81,10 +86,15 @@ add_library(hecl-light ${COMMON_SOURCES} ${HECL_HEADERS} ${PLAT_SRCS}) +add_library(hecl-compilers Compilers.cpp) + +add_dependencies(hecl-full ${HECL_APPLICATION_REPS_TARGETS_LIST}) +add_dependencies(hecl-light ${HECL_APPLICATION_REPS_TARGETS_LIST}) if(COMMAND add_sanitizers) add_sanitizers(hecl-full) add_sanitizers(hecl-light) + add_sanitizers(hecl-compilers) endif() if(WINDOWS_STORE) diff --git a/hecl/lib/Compilers.cpp b/hecl/lib/Compilers.cpp new file mode 100644 index 000000000..791e45871 --- /dev/null +++ b/hecl/lib/Compilers.cpp @@ -0,0 +1,261 @@ +#include "hecl/Compilers.hpp" +#include "boo/graphicsdev/GLSLMacros.hpp" +#include "logvisor/logvisor.hpp" +#if BOO_HAS_VULKAN +#include +#include +#include +#include +#endif + +namespace hecl +{ +logvisor::Module Log("hecl::Compilers"); + +template struct ShaderCompiler {}; + +template<> struct ShaderCompiler +{ + template + static std::pair, size_t> Compile(std::string_view text) + { + std::string str = "#version 330\n"; + str += BOO_GLSL_BINDING_HEAD; + str += text; + std::pair, size_t> ret(new uint8_t[str.size() + 1], str.size() + 1); + memcpy(ret.first.get(), str.data(), str.size() + 1); + return ret; + } +}; + +template<> struct ShaderCompiler +{ + static constexpr EShLanguage ShaderTypes[] = + { + EShLangVertex, /* Invalid */ + EShLangVertex, + EShLangFragment, + EShLangGeometry, + EShLangTessControl, + EShLangTessEvaluation + }; + + template + static std::pair, size_t> Compile(std::string_view text) + { + EShLanguage lang = ShaderTypes[int(S::Enum)]; + const EShMessages messages = EShMessages(EShMsgSpvRules | EShMsgVulkanRules); + glslang::TShader shader(lang); + const char* strings[] = { "#version 330\n", BOO_GLSL_BINDING_HEAD, text.data() }; + shader.setStrings(strings, 3); + if (!shader.parse(&glslang::DefaultTBuiltInResource, 110, false, messages)) + { + printf("%s\n", text.data()); + Log.report(logvisor::Fatal, "unable to compile shader\n%s", shader.getInfoLog()); + return {}; + } + + glslang::TProgram prog; + prog.addShader(&shader); + if (!prog.link(messages)) + { + Log.report(logvisor::Fatal, "unable to link shader program\n%s", prog.getInfoLog()); + return {}; + } + + std::vector out; + glslang::GlslangToSpv(*prog.getIntermediate(lang), out); + std::pair, size_t> ret(new uint8_t[out.size() * 4], out.size() * 4); + memcpy(ret.first.get(), out.data(), ret.second); + return ret; + } +}; + +template<> struct ShaderCompiler +{ + template + static std::pair, size_t> Compile(std::string_view text) + { + std::string str = "#version 330\n"; + str += BOO_GLSL_BINDING_HEAD; + str += text; + std::pair, size_t> ret(new uint8_t[str.size() + 1], str.size() + 1); + memcpy(ret.first.get(), str.data(), str.size() + 1); + return ret; + } +}; + +template +std::pair, size_t> CompileShader(std::string_view text) +{ + return ShaderCompiler

::template Compile(text); +} +#define SPECIALIZE_COMPILE_SHADER(P) \ +template std::pair, size_t> CompileShader(std::string_view text); \ +template std::pair, size_t> CompileShader(std::string_view text); \ +template std::pair, size_t> CompileShader(std::string_view text); \ +template std::pair, size_t> CompileShader(std::string_view text); \ +template std::pair, size_t> CompileShader(std::string_view text); +SPECIALIZE_COMPILE_SHADER(PlatformType::OpenGL) +SPECIALIZE_COMPILE_SHADER(PlatformType::Vulkan) +#if _WIN32 +SPECIALIZE_COMPILE_SHADER(PlatformType::D3D11) +#endif +#if __APPLE__ +SPECIALIZE_COMPILE_SHADER(PlatformType::Metal) +#endif +#if HECL_NOUVEAU_NX +SPECIALIZE_COMPILE_SHADER(PlatformType::NX) +#endif + +#if _WIN32 +static const char* ShaderTypes[] = +{ + "vs_5_0", + "ps_5_0", + "gs_5_0", + "hs_5_0", + "ds_5_0" +}; +template<> +std::vector CompileShader(std::string_view text, PipelineStage stage) +{ + ComPtr errBlob; + ComPtr blobOut; + if (FAILED(D3DCompilePROC(text.data(), text.size(), "Boo HLSL Source", nullptr, nullptr, "main", + ShaderTypes[int(stage)], BOO_D3DCOMPILE_FLAG, 0, &blobOut, &errBlob))) + { + printf("%s\n", source); + Log.report(logvisor::Fatal, "error compiling shader: %s", errBlob->GetBufferPointer()); + return {}; + } + std::vector ret(blobOut.GetBufferSize()); + memcpy(ret.data(), blobOut.GetBufferPointer(), blobOut.GetBufferSize()); + return ret; +}; +#endif + +#if BOO_HAS_METAL +static int HasMetalCompiler = -1; + +static void CheckForMetalCompiler() +{ + pid_t pid = fork(); + if (!pid) + { + execlp("xcrun", "xcrun", "-sdk", "macosx", "metal", NULL); + /* xcrun returns 72 if metal command not found; + * emulate that if xcrun not found */ + exit(72); + } + + int status, ret; + while ((ret = waitpid(pid, &status, 0)) < 0 && errno == EINTR) {} + if (ret < 0) + HasMetalCompiler = 0; + else + HasMetalCompiler = WEXITSTATUS(status) == 1; +} + +template<> +std::vector CompileShader(std::string_view text, PipelineStage stage) +{ + if (HasMetalCompiler == -1) + CheckForMetalCompiler(); + + std::vector blobOut; + if (!HasMetalCompiler) + { + /* Cache the source if there's no compiler */ + size_t sourceLen = strlen(source); + + /* First byte unset to indicate source data */ + blobOut.resize(sourceLen + 2); + memcpy(&blobOut[1], source, sourceLen); + } + else + { + /* Cache the binary otherwise */ + int compilerOut[2]; + int compilerIn[2]; + pipe(compilerOut); + pipe(compilerIn); + + /* Pipe source write to compiler */ + pid_t compilerPid = fork(); + if (!compilerPid) + { + dup2(compilerIn[0], STDIN_FILENO); + dup2(compilerOut[1], STDOUT_FILENO); + + close(compilerOut[0]); + close(compilerOut[1]); + close(compilerIn[0]); + close(compilerIn[1]); + + execlp("xcrun", "xcrun", "-sdk", "macosx", "metal", "-o", "/dev/stdout", "-Wno-unused-variable", + "-Wno-unused-const-variable", "-Wno-unused-function", "-x", "metal", "-", NULL); + fprintf(stderr, "execlp fail %s\n", strerror(errno)); + exit(1); + } + close(compilerIn[0]); + close(compilerOut[1]); + + /* Pipe compiler to linker */ + pid_t linkerPid = fork(); + if (!linkerPid) + { + dup2(compilerOut[0], STDIN_FILENO); + + close(compilerOut[0]); + close(compilerIn[1]); + + /* metallib doesn't like outputting to a pipe, so temp file will have to do */ + execlp("xcrun", "xcrun", "-sdk", "macosx", "metallib", "-", "-o", m_libfile, NULL); + fprintf(stderr, "execlp fail %s\n", strerror(errno)); + exit(1); + } + close(compilerOut[0]); + + /* Stream in source */ + const char* inPtr = source; + size_t inRem = strlen(source); + while (inRem) + { + ssize_t writeRes = write(compilerIn[1], inPtr, inRem); + if (writeRes < 0) + { + fprintf(stderr, "write fail %s\n", strerror(errno)); + break; + } + inPtr += writeRes; + inRem -= writeRes; + } + close(compilerIn[1]); + + /* Wait for completion */ + int compilerStat, linkerStat; + if (waitpid(compilerPid, &compilerStat, 0) < 0 || waitpid(linkerPid, &linkerStat, 0) < 0) + { + fprintf(stderr, "waitpid fail %s\n", strerror(errno)); + return {}; + } + + if (WEXITSTATUS(compilerStat) || WEXITSTATUS(linkerStat)) + return {}; + + /* Copy temp file into buffer with first byte set to indicate binary data */ + FILE* fin = fopen(m_libfile, "rb"); + fseek(fin, 0, SEEK_END); + long libLen = ftell(fin); + fseek(fin, 0, SEEK_SET); + blobOut.resize(libLen + 1); + blobOut[0] = 1; + fread(&blobOut[1], 1, libLen, fin); + fclose(fin); + } + return blobOut; +} +#endif + +} diff --git a/hecl/lib/Console.cpp b/hecl/lib/Console.cpp index 1fc3f3927..5fffbfbfc 100644 --- a/hecl/lib/Console.cpp +++ b/hecl/lib/Console.cpp @@ -490,14 +490,6 @@ void Console::dumpLog() void Console::RegisterLogger(Console* con) { - /* Determine if console logger already added */ - for (auto& logger : logvisor::MainLoggers) - { - if (typeid(logger.get()) == typeid(LogVisorAdapter)) - return; - } - - /* Otherwise construct new console logger */ logvisor::MainLoggers.emplace_back(new LogVisorAdapter(con)); } diff --git a/hecl/lib/Pipeline.cpp b/hecl/lib/Pipeline.cpp new file mode 100644 index 000000000..0440f155e --- /dev/null +++ b/hecl/lib/Pipeline.cpp @@ -0,0 +1,181 @@ +#include "hecl/Pipeline.hpp" +#include "athena/FileReader.hpp" +#include + +namespace hecl +{ + +#if HECL_RUNTIME + +PipelineConverterBase* conv = nullptr; + +class ShaderCacheZipStream : public athena::io::IStreamReader +{ + std::unique_ptr m_compBuf; + athena::io::FileReader m_reader; + z_stream m_zstrm = {}; +public: + explicit ShaderCacheZipStream(const hecl::SystemChar* path) + : m_reader(path) + { + if (m_reader.hasError()) + return; + if (m_reader.readUint32Big() != SBIG('SHAD')) + return; + m_compBuf.reset(new uint8_t[4096]); + m_zstrm.next_in = m_compBuf.get(); + m_zstrm.avail_in = 0; + inflateInit(&m_zstrm); + } + ~ShaderCacheZipStream() + { + inflateEnd(&m_zstrm); + } + operator bool() const { return m_compBuf.operator bool(); } + atUint64 readUBytesToBuf(void *buf, atUint64 len) + { + m_zstrm.next_out = (Bytef*)buf; + m_zstrm.avail_out = len; + m_zstrm.total_out = 0; + while (m_zstrm.avail_out != 0) + { + if (m_zstrm.avail_in == 0) + { + atUint64 readSz = m_reader.readUBytesToBuf(m_compBuf.get(), 4096); + m_zstrm.avail_in = readSz; + m_zstrm.next_in = m_compBuf.get(); + } + int inflateRet = inflate(&m_zstrm, Z_NO_FLUSH); + if (inflateRet != Z_OK) + break; + } + return m_zstrm.total_out; + } + void seek(atInt64, athena::SeekOrigin) {} + atUint64 position() const {return 0;} + atUint64 length() const {return 0;} +}; + +template +void StageConverter::loadFromStream(FactoryCtx& ctx, ShaderCacheZipStream& r) +{ + uint32_t count = r.readUint32Big(); + for (uint32_t i = 0; i < count; ++i) + { + uint64_t hash = r.readUint64Big(); + uint32_t size = r.readUint32Big(); + std::shared_ptr data(new uint8_t[size]); + r.readUBytesToBuf(data.get(), size); + m_stageCache.insert(std::make_pair(hash, Do(ctx, StageBinary(data, size)))); + } +} + +static boo::AdditionalPipelineInfo ReadAdditionalInfo(ShaderCacheZipStream& r) +{ + boo::AdditionalPipelineInfo additionalInfo; + additionalInfo.srcFac = boo::BlendFactor(r.readUint32Big()); + additionalInfo.dstFac = boo::BlendFactor(r.readUint32Big()); + additionalInfo.prim = boo::Primitive(r.readUint32Big()); + additionalInfo.depthTest = boo::ZTest(r.readUint32Big()); + additionalInfo.depthWrite = r.readBool(); + additionalInfo.colorWrite = r.readBool(); + additionalInfo.alphaWrite = r.readBool(); + additionalInfo.culling = boo::CullMode(r.readUint32Big()); + additionalInfo.patchSize = r.readUint32Big(); + additionalInfo.overwriteAlpha = r.readBool(); + additionalInfo.depthAttachment = r.readBool(); + return additionalInfo; +} + +static std::vector ReadVertexFormat(ShaderCacheZipStream& r) +{ + std::vector ret; + uint32_t count = r.readUint32Big(); + ret.reserve(count); + + for (int i = 0; i < count; ++i) + { + ret.emplace_back(); + ret.back().semantic = boo::VertexSemantic(r.readUint32Big()); + ret.back().semanticIdx = int(r.readUint32Big()); + } + + return ret; +} + +template +bool PipelineConverter

::loadFromFile(FactoryCtx& ctx, const hecl::SystemChar* path) +{ + ShaderCacheZipStream r(path); + if (!r) + return false; + + m_vertexConverter.loadFromStream(ctx, r); + m_fragmentConverter.loadFromStream(ctx, r); + m_geometryConverter.loadFromStream(ctx, r); + m_controlConverter.loadFromStream(ctx, r); + m_evaluationConverter.loadFromStream(ctx, r); + + uint32_t count = r.readUint32Big(); + for (uint32_t i = 0; i < count; ++i) + { + uint64_t hash = r.readUint64Big(); + StageRuntimeObject vertex; + StageRuntimeObject fragment; + StageRuntimeObject geometry; + StageRuntimeObject control; + StageRuntimeObject evaluation; + if (uint64_t vhash = r.readUint64Big()) + vertex = m_vertexConverter.m_stageCache.find(vhash)->second; + if (uint64_t fhash = r.readUint64Big()) + fragment = m_fragmentConverter.m_stageCache.find(fhash)->second; + if (uint64_t ghash = r.readUint64Big()) + geometry = m_geometryConverter.m_stageCache.find(ghash)->second; + if (uint64_t chash = r.readUint64Big()) + control = m_controlConverter.m_stageCache.find(chash)->second; + if (uint64_t ehash = r.readUint64Big()) + evaluation = m_evaluationConverter.m_stageCache.find(ehash)->second; + + boo::AdditionalPipelineInfo additionalInfo = ReadAdditionalInfo(r); + std::vector vtxFmt = ReadVertexFormat(r); + + m_pipelineCache.insert(std::make_pair(hash, FinalPipeline

(*this, ctx, + StageCollection> + (vertex, fragment, geometry, control, evaluation, additionalInfo, + boo::VertexFormatInfo(vtxFmt.size(), vtxFmt.data()))))); + } + + return true; +} + +#define SPECIALIZE_STAGE_CONVERTER(P) \ +template class StageConverter; \ +template class StageConverter; \ +template class StageConverter; \ +template class StageConverter; \ +template class StageConverter; + +#if BOO_HAS_GL +template class PipelineConverter; +SPECIALIZE_STAGE_CONVERTER(PlatformType::OpenGL) +#endif +#if BOO_HAS_VULKAN +template class PipelineConverter; +SPECIALIZE_STAGE_CONVERTER(PlatformType::Vulkan) +#endif +#if _WIN32 +template class PipelineConverter; +SPECIALIZE_STAGE_CONVERTER(PlatformType::D3D11) +#endif +#if BOO_HAS_METAL +template class PipelineConverter; +SPECIALIZE_STAGE_CONVERTER(PlatformType::Metal) +#endif +#if BOO_HAS_NX +template class PipelineConverter; +SPECIALIZE_STAGE_CONVERTER(PlatformType::NX) +#endif + +#endif + +} \ No newline at end of file diff --git a/hecl/lib/Runtime/CMakeLists.txt b/hecl/lib/Runtime/CMakeLists.txt index 4e985de32..ebee9e91b 100644 --- a/hecl/lib/Runtime/CMakeLists.txt +++ b/hecl/lib/Runtime/CMakeLists.txt @@ -1,6 +1,5 @@ set(RUNTIME_SOURCES FileStoreManager.cpp - ShaderCacheManager.cpp HMDL_RT.cpp) hecl_add_list(Runtime RUNTIME_SOURCES) diff --git a/hecl/lib/Runtime/HMDL_RT.cpp b/hecl/lib/Runtime/HMDL_RT.cpp index 8f422e64b..dc9020cca 100644 --- a/hecl/lib/Runtime/HMDL_RT.cpp +++ b/hecl/lib/Runtime/HMDL_RT.cpp @@ -20,83 +20,32 @@ HMDLData::HMDLData(boo::IGraphicsDataFactory::Context& ctx, m_vbo = ctx.newStaticBuffer(boo::BufferUse::Vertex, vbo, meta.vertStride, meta.vertCount); m_ibo = ctx.newStaticBuffer(boo::BufferUse::Index, ibo, 4, meta.indexCount); - if (ctx.bindingNeedsVertexFormat()) - m_vtxFmt = NewVertexFormat(ctx, meta, m_vbo.get(), m_ibo.get()); -} - -/* For binding constructors that require vertex format up front (GLSL) */ -boo::ObjToken -HMDLData::NewVertexFormat(boo::IGraphicsDataFactory::Context& ctx, const HMDLMeta& meta, - const boo::ObjToken& vbo, - const boo::ObjToken& ibo) -{ size_t elemCount = 2 + meta.colorCount + meta.uvCount + meta.weightCount; - std::unique_ptr vdescs(new boo::VertexElementDescriptor[elemCount]); - for (size_t i=0 ; i ShaderTag::newVertexFormat(boo::IGraphicsDataFactory::Context& ctx) const -{ - size_t elemCount = 2 + m_colorCount + m_uvCount + m_weightCount; - std::unique_ptr vdescs(new boo::VertexElementDescriptor[elemCount]); - for (size_t i=0 ; i -#include -#include -#include -#include - -#include "hecl/Backend/GLSL.hpp" -#include "hecl/Backend/Metal.hpp" - -#undef min -#undef max - -namespace hecl::Runtime -{ -#if BOO_HAS_GL -IShaderBackendFactory* _NewGLSLBackendFactory(); -#endif -#if _WIN32 -IShaderBackendFactory* _NewHLSLBackendFactory(); -#endif -#if BOO_HAS_METAL -IShaderBackendFactory* _NewMetalBackendFactory(); -#endif -#if BOO_HAS_VULKAN -IShaderBackendFactory* _NewSPIRVBackendFactory(); -#endif - -static logvisor::Module SCM_Log("ShaderCacheManager"); -static uint64_t IDX_MAGIC = SBig(uint64_t(0xDEADFEEDC001D00D)); -static uint64_t DAT_MAGIC = SBig(uint64_t(0xC001D00DDEADBABE)); -static uint64_t ZERO64 = 0; - -static uint64_t timeHash() -{ - char buf[80]; - time_t now; - struct tm* timeinfo; - - time(&now); - timeinfo = localtime(&now); - strftime(buf, 80, "%Y-%m-%dT%H:%M:%S+%H:%M", timeinfo); - Hash tmp(buf, 80); - return tmp.val64(); -} - -static void UpdateFunctionHash(XXH64_state_t* st, const ShaderCacheExtensions::Function& func) -{ - if (func.m_source) - XXH64_update(st, func.m_source, strlen(func.m_source)); - if (func.m_entry) - XXH64_update(st, func.m_entry, strlen(func.m_entry)); -} -template -static void UpdateFieldHash(XXH64_state_t* st, T field) -{ - XXH64_update(st, &field, sizeof(field)); -} -uint64_t ShaderCacheExtensions::hashExtensions() const -{ - XXH64_state_t st; - XXH64_reset(&st, 0); - for (const ExtensionSlot& slot : m_extensionSlots) - { - UpdateFunctionHash(&st, slot.lighting); - UpdateFunctionHash(&st, slot.post); - UpdateFieldHash(&st, slot.srcFactor); - UpdateFieldHash(&st, slot.dstFactor); - UpdateFieldHash(&st, slot.depthTest); - UpdateFieldHash(&st, slot.cullMode); - UpdateFieldHash(&st, slot.noDepthWrite); - UpdateFieldHash(&st, slot.noColorWrite); - UpdateFieldHash(&st, slot.noAlphaWrite); - UpdateFieldHash(&st, slot.noAlphaOverwrite); - UpdateFieldHash(&st, slot.noReflection); - } - return XXH64_digest(&st); -} - -void ShaderCacheManager::bootstrapIndex() -{ - m_timeHash = timeHash(); - m_idxFr.close(); - m_datFr.close(); - -#if _WIN32 - SystemString idxFilename = m_idxFr.wfilename(); -#else - SystemString idxFilename = m_idxFr.filename(); -#endif - - FILE* idxFp = hecl::Fopen(idxFilename.c_str(), _S("wb")); - if (!idxFp) - SCM_Log.report(logvisor::Fatal, _S("unable to write shader cache index at %s"), - idxFilename.c_str()); - fwrite(&IDX_MAGIC, 1, 8, idxFp); - fwrite(&m_timeHash, 1, 8, idxFp); - fwrite(&m_extensionsHash, 1, 8, idxFp); - fwrite(&ZERO64, 1, 8, idxFp); - fclose(idxFp); -#if _WIN32 - SystemString datFilename = m_datFr.wfilename(); -#else - SystemString datFilename = m_datFr.filename(); -#endif - - FILE* datFp = hecl::Fopen(datFilename.c_str(), _S("wb")); - if (!datFp) - SCM_Log.report(logvisor::Fatal, _S("unable to write shader cache data at %s"), - datFilename.c_str()); - fwrite(&DAT_MAGIC, 1, 8, datFp); - fwrite(&m_timeHash, 1, 8, datFp); - fclose(datFp); - - m_idxFr.open(); - m_datFr.open(); -} - -ShaderCacheManager::ShaderCacheManager(const FileStoreManager& storeMgr, - boo::IGraphicsDataFactory* gfxFactory, - ShaderCacheExtensions&& extension) -: m_storeMgr(storeMgr), - m_extensions(std::move(extension)), - m_idxFr(SystemString(storeMgr.getStoreRoot()) + _S("/shadercache") + - gfxFactory->platformName() + _S(".idx"), 32*1024, false), - m_datFr(SystemString(storeMgr.getStoreRoot()) + _S("/shadercache") + - gfxFactory->platformName() + _S(".dat"), 32*1024, false) -{ - boo::IGraphicsDataFactory::Platform plat = gfxFactory->platform(); - if (m_extensions && m_extensions.m_plat != plat) - SCM_Log.report(logvisor::Fatal, "ShaderCacheExtension backend mismatch (should be %s)", - gfxFactory->platformName()); - m_extensionsHash = m_extensions.hashExtensions(); - - switch (plat) - { -#if BOO_HAS_GL - case boo::IGraphicsDataFactory::Platform::OpenGL: - m_factory.reset(_NewGLSLBackendFactory()); - break; -#endif -#if _WIN32 - case boo::IGraphicsDataFactory::Platform::D3D11: - m_factory.reset(_NewHLSLBackendFactory()); - break; -#endif -#if BOO_HAS_METAL - case boo::IGraphicsDataFactory::Platform::Metal: - m_factory.reset(_NewMetalBackendFactory()); - break; -#endif -#if BOO_HAS_VULKAN - case boo::IGraphicsDataFactory::Platform::Vulkan: - m_factory.reset(_NewSPIRVBackendFactory()); - break; -#endif - default: - SCM_Log.report(logvisor::Fatal, _S("unsupported backend %s"), gfxFactory->platformName()); - } - - reload(); -} - -void ShaderCacheManager::reload() -{ - m_entries.clear(); - m_entryLookup.clear(); - m_timeHash = 0; - - /* Attempt to open existing index */ - m_idxFr.seek(0, athena::Begin); - m_datFr.seek(0, athena::Begin); - if (m_idxFr.hasError() || m_datFr.hasError()) - { - bootstrapIndex(); - return; - } - else - { - uint64_t idxMagic; - size_t rb = m_idxFr.readUBytesToBuf(&idxMagic, 8); - if (rb != 8 || idxMagic != IDX_MAGIC) - { - bootstrapIndex(); - return; - } - - uint64_t datMagic; - rb = m_datFr.readUBytesToBuf(&datMagic, 8); - if (rb != 8 || datMagic != DAT_MAGIC) - { - bootstrapIndex(); - return; - } - - uint64_t idxRand, datRand; - rb = m_idxFr.readUBytesToBuf(&idxRand, 8); - size_t rb2 = m_datFr.readUBytesToBuf(&datRand, 8); - if (rb != 8 || rb2 != 8 || idxRand != datRand) - { - bootstrapIndex(); - return; - } - m_timeHash = idxRand; - } - - atUint64 extensionsHash; - size_t rb = m_idxFr.readUBytesToBuf(&extensionsHash, 8); - if (rb != 8 || extensionsHash != m_extensionsHash) - { - bootstrapIndex(); - return; - } - - atUint64 idxCount = m_idxFr.readUint64Big(); - if (m_idxFr.position() != 32) - { - bootstrapIndex(); - return; - } - - /* Read existing entries */ - if (idxCount) - { - m_entries.reserve(idxCount); - m_entryLookup.reserve(idxCount); - for (atUint64 i=0 ; isecond]; - if (ent.m_compOffset + ent.m_compSize > m_datFr.length()) - { - SCM_Log.report(logvisor::Warning, "shader cache not long enough to read entry, might be corrupt"); - return {}; - } - - /* File-streamed decompression */ - m_datFr.seek(ent.m_compOffset, athena::Begin); - ShaderCachedData ret(ShaderTag(ent.m_hash, ent.m_meta), ent.m_decompSize); - uint8_t compDat[2048]; - z_stream z = {}; - inflateInit(&z); - z.avail_out = ent.m_decompSize; - z.next_out = ret.m_data.get(); - while (z.avail_out) - { - z.avail_in = std::min(size_t(2048), size_t(ent.m_compSize - z.total_in)); - m_datFr.readUBytesToBuf(compDat, z.avail_in); - z.next_in = compDat; - int ret = inflate(&z, Z_NO_FLUSH); - if (ret == Z_STREAM_END) - break; - if (ret != Z_OK) - { - inflateEnd(&z); - return {}; - } - } - inflateEnd(&z); - return ret; -} - -bool ShaderCacheManager::addData(const ShaderCachedData& data) -{ - m_idxFr.close(); - m_datFr.close(); - - /* Perform one-shot buffer compression */ - uLong cBound = compressBound(data.m_sz); - void* compBuf = malloc(cBound); - if (compress((Bytef*)compBuf, &cBound, (Bytef*)data.m_data.get(), data.m_sz) != Z_OK) - SCM_Log.report(logvisor::Fatal, "unable to deflate data"); - - /* Open index for writing (non overwriting) */ - athena::io::FileWriter idxFw(m_idxFr.filename(), false); - if (idxFw.hasError()) - SCM_Log.report(logvisor::Fatal, _S("unable to append shader cache index at %s"), - m_idxFr.filename().c_str()); - - /* Open data for writing (non overwriting) */ - athena::io::FileWriter datFw(m_datFr.filename(), false); - if (datFw.hasError()) - SCM_Log.report(logvisor::Fatal, _S("unable to append shader cache data at %s"), - m_datFr.filename().c_str()); - - size_t targetOffset = 0; - auto search = m_entryLookup.find(data.m_tag); - if (search != m_entryLookup.cend()) - { - /* Hash already present, attempt to replace data */ - IndexEntry& ent = m_entries[search->second]; - if (search->second == m_entries.size() - 1) - { - /* Replacing final entry; simply write-over */ - ent.m_meta = data.m_tag.getMetaData(); - ent.m_compSize = cBound; - ent.m_decompSize = data.m_sz; - targetOffset = ent.m_compOffset; - idxFw.seek(search->second * 32 + 32); - ent.write(idxFw); - } - else - { - /* Replacing non-final entry; write into available space */ - IndexEntry& nent = m_entries[search->second+1]; - size_t space = nent.m_compOffset - ent.m_compOffset; - if (cBound <= space) - { - ent.m_meta = data.m_tag.getMetaData(); - ent.m_compSize = cBound; - ent.m_decompSize = data.m_sz; - targetOffset = ent.m_compOffset; - idxFw.seek(search->second * 32 + 32); - ent.write(idxFw); - } - else - { - /* Not enough space; null-entry and add to end */ - ent.m_hash = 0; - ent.m_meta = 0; - ent.m_compOffset = 0; - ent.m_compSize = 0; - ent.m_decompSize = 0; - idxFw.seek(search->second * 32 + 32); - ent.write(idxFw); - } - } - } - - if (!targetOffset) - { - /* New index entry at end */ - idxFw.seek(24, athena::Begin); - idxFw.writeUint64Big(m_entries.size() + 1); - idxFw.seek(m_entries.size() * 32 + 32, athena::Begin); - datFw.seek(0, athena::End); - m_entryLookup[data.m_tag] = m_entries.size(); - m_entries.emplace_back(); - - IndexEntry& ent = m_entries.back(); - ent.m_hash = data.m_tag.val64(); - ent.m_meta = data.m_tag.getMetaData(); - ent.m_compOffset = datFw.position(); - ent.m_compSize = cBound; - ent.m_decompSize = data.m_sz; - ent.write(idxFw); - - datFw.writeUBytes((atUint8*)compBuf, cBound); - } - else - { - /* Reusing index entry and data space */ - datFw.seek(targetOffset, athena::Begin); - datFw.writeUBytes((atUint8*)compBuf, cBound); - } - - free(compBuf); - - idxFw.close(); - datFw.close(); - - m_idxFr.open(); - m_datFr.open(); - - return true; -} - -boo::ObjToken -ShaderCacheManager::buildFromCache(const ShaderCachedData& foundData, - boo::IGraphicsDataFactory::Context& ctx) -{ - return m_factory->buildShaderFromCache(foundData, ctx); -} - -std::shared_ptr -ShaderCacheManager::buildShader(const ShaderTag& tag, std::string_view source, - std::string_view diagName, - boo::IGraphicsDataFactory& factory) -{ - auto search = m_pipelineLookup.find(tag); - if (search != m_pipelineLookup.cend()) - return search->second; - - std::shared_ptr ret = std::make_shared(); - ShaderCachedData foundData = lookupData(tag); - if (foundData) - { - factory.commitTransaction([&](boo::IGraphicsDataFactory::Context& ctx) - { - SCM_Log.report(logvisor::Info, "building cached shader '%s' %016llX", diagName.data(), tag.val64()); - boo::ObjToken build = buildFromCache(foundData, ctx); - if (build) - { - ret->m_pipelines.push_back(build); - return true; - } - return false; - } BooTrace); - - if (ret->m_pipelines.size()) - { - m_pipelineLookup[tag] = ret; - return ret; - } - SCM_Log.report(logvisor::Warning, "invalid cache read, rebuilding shader '%s'", diagName.data()); - } - - factory.commitTransaction([&](boo::IGraphicsDataFactory::Context& ctx) - { - hecl::Frontend::IR ir = FE.compileSource(source, diagName); - SCM_Log.report(logvisor::Info, "building shader '%s' %016llX", diagName.data(), tag.val64()); - FE.getDiagnostics().reset(diagName); - boo::ObjToken build; - addData(m_factory->buildShaderFromIR(tag, ir, FE.getDiagnostics(), ctx, build)); - ret->m_pipelines.push_back(build); - return true; - } BooTrace); - m_pipelineLookup[tag] = ret; - return ret; -} - -std::shared_ptr -ShaderCacheManager::buildShader(const ShaderTag& tag, const hecl::Frontend::IR& ir, - std::string_view diagName, - boo::IGraphicsDataFactory& factory) -{ - auto search = m_pipelineLookup.find(tag); - if (search != m_pipelineLookup.cend()) - return search->second; - - std::shared_ptr ret = std::make_shared(); - ShaderCachedData foundData = lookupData(tag); - if (foundData) - { - factory.commitTransaction([&](boo::IGraphicsDataFactory::Context& ctx) - { - SCM_Log.report(logvisor::Info, "building cached shader '%s' %016llX", diagName.data(), tag.val64()); - boo::ObjToken build = buildFromCache(foundData, ctx); - if (build) - { - ret->m_pipelines.push_back(build); - return true; - } - return false; - } BooTrace); - - if (ret->m_pipelines.size()) - { - m_pipelineLookup[tag] = ret; - return ret; - } - SCM_Log.report(logvisor::Warning, "invalid cache read, rebuilding shader '%s'", diagName.data()); - } - - factory.commitTransaction([&](boo::IGraphicsDataFactory::Context& ctx) - { - SCM_Log.report(logvisor::Info, "building shader '%s' %016llX", diagName.data(), tag.val64()); - FE.getDiagnostics().reset(diagName); - boo::ObjToken build; - addData(m_factory->buildShaderFromIR(tag, ir, FE.getDiagnostics(), ctx, build)); - ret->m_pipelines.push_back(build); - return true; - } BooTrace); - m_pipelineLookup[tag] = ret; - return ret; -} - -std::vector> -ShaderCacheManager::buildExtendedFromCache(const ShaderCachedData& foundData, - boo::IGraphicsDataFactory::Context& ctx) -{ - std::vector> shaders; - shaders.reserve(m_extensions.m_extensionSlots.size()); - if (!m_factory->buildExtendedShaderFromCache(foundData, m_extensions.m_extensionSlots, ctx, - [&](const boo::ObjToken& shader){shaders.push_back(shader);})) - return {}; - if (shaders.size() != m_extensions.m_extensionSlots.size()) - SCM_Log.report(logvisor::Fatal, "buildShaderFromCache returned %" PRISize " times, expected %" PRISize, - shaders.size(), m_extensions.m_extensionSlots.size()); - return shaders; -} - -std::shared_ptr -ShaderCacheManager::buildExtendedShader(const ShaderTag& tag, std::string_view source, - std::string_view diagName, - boo::IGraphicsDataFactory& factory) -{ - auto search = m_pipelineLookup.find(tag); - if (search != m_pipelineLookup.cend()) - return search->second; - - std::shared_ptr ret = std::make_shared(); - ShaderCachedData foundData = lookupData(tag); - if (foundData) - { - factory.commitTransaction([&](boo::IGraphicsDataFactory::Context& ctx) - { - SCM_Log.report(logvisor::Info, "building cached shader '%s' %016llX", diagName.data(), tag.val64()); - ret->m_pipelines = buildExtendedFromCache(foundData, ctx); - return ret->m_pipelines.size() != 0; - } BooTrace); - - if (ret->m_pipelines.size()) - { - m_pipelineLookup[tag] = ret; - return ret; - } - SCM_Log.report(logvisor::Warning, "invalid cache read, rebuilding shader '%s'", diagName.data()); - } - - hecl::Frontend::IR ir = FE.compileSource(source, diagName); - - factory.commitTransaction([&](boo::IGraphicsDataFactory::Context& ctx) - { - ret->m_pipelines.reserve(m_extensions.m_extensionSlots.size()); - FE.getDiagnostics().reset(diagName); - SCM_Log.report(logvisor::Info, "building shader '%s' %016llX", diagName.data(), tag.val64()); - ShaderCachedData data = - m_factory->buildExtendedShaderFromIR(tag, ir, FE.getDiagnostics(), m_extensions.m_extensionSlots, ctx, - [&](const boo::ObjToken& shader){ret->m_pipelines.push_back(shader);}); - if (ret->m_pipelines.size() != m_extensions.m_extensionSlots.size()) - SCM_Log.report(logvisor::Fatal, "buildShaderFromIR returned %" PRISize " times, expected %" PRISize, - ret->m_pipelines.size(), m_extensions.m_extensionSlots.size()); - addData(data); - return true; - } BooTrace); - m_pipelineLookup[tag] = ret; - return ret; -} - -std::shared_ptr -ShaderCacheManager::buildExtendedShader(const ShaderTag& tag, const hecl::Frontend::IR& ir, - std::string_view diagName, - boo::IGraphicsDataFactory& factory) -{ - auto search = m_pipelineLookup.find(tag); - if (search != m_pipelineLookup.cend()) - return search->second; - - std::shared_ptr ret = std::make_shared(); - ShaderCachedData foundData = lookupData(tag); - if (foundData) - { - factory.commitTransaction([&](boo::IGraphicsDataFactory::Context& ctx) - { - SCM_Log.report(logvisor::Info, "building cached shader '%s' %016llX", diagName.data(), tag.val64()); - ret->m_pipelines = buildExtendedFromCache(foundData, ctx); - return ret->m_pipelines.size() != 0; - } BooTrace); - - if (ret->m_pipelines.size() != 0) - { - m_pipelineLookup[tag] = ret; - return ret; - } - SCM_Log.report(logvisor::Warning, "invalid cache read, rebuilding shader '%s'", diagName.data()); - } - - factory.commitTransaction([&](boo::IGraphicsDataFactory::Context& ctx) - { - ret->m_pipelines.reserve(m_extensions.m_extensionSlots.size()); - FE.getDiagnostics().reset(diagName); - SCM_Log.report(logvisor::Info, "building shader '%s' %016llX", diagName.data(), tag.val64()); - ShaderCachedData data = - m_factory->buildExtendedShaderFromIR(tag, ir, FE.getDiagnostics(), m_extensions.m_extensionSlots, ctx, - [&](const boo::ObjToken& shader){ret->m_pipelines.push_back(shader);}); - if (ret->m_pipelines.size() != m_extensions.m_extensionSlots.size()) - SCM_Log.report(logvisor::Fatal, "buildShaderFromIR returned %" PRISize " times, expected %" PRISize, - ret->m_pipelines.size(), m_extensions.m_extensionSlots.size()); - addData(data); - return true; - } BooTrace); - m_pipelineLookup[tag] = ret; - return ret; -} - -} diff --git a/hecl/shaderc/CMakeLists.txt b/hecl/shaderc/CMakeLists.txt new file mode 100644 index 000000000..2e58ee123 --- /dev/null +++ b/hecl/shaderc/CMakeLists.txt @@ -0,0 +1,43 @@ +include_directories(../include + ${CMAKE_CURRENT_SOURCE_DIR}/../extern/athena/include + ${CMAKE_CURRENT_SOURCE_DIR}/../extern/boo + ${CMAKE_CURRENT_SOURCE_DIR}/../extern/boo/include + ${CMAKE_CURRENT_SOURCE_DIR}/../extern/boo/logvisor/include) +add_library(shaderc_lib shaderc.cpp shaderc.hpp) +add_executable(shaderc main.cpp) +target_link_libraries(shaderc shaderc_lib hecl-compilers glslang soxr xxhash OSDependent OGLCompiler + SPIRV glslang-default-resource-limits athena-core logvisor) +if (NOT WIN32) + target_link_libraries(shaderc pthread) + if(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") + target_link_libraries(shaderc execinfo) + else() + target_link_libraries(shaderc dl) + endif() +endif() +if(COMMAND add_sanitizers) + add_sanitizers(shaderc_lib) + add_sanitizers(shaderc) +endif() + +function(shaderc out) + if(IS_ABSOLUTE ${out}) + set(theOut ${out}) + else() + set(theOut ${CMAKE_CURRENT_BINARY_DIR}/${out}) + endif() + unset(theInsList) + foreach(in ${ARGN}) + if(IS_ABSOLUTE ${in}) + list(APPEND theInsList ${in}) + else() + list(APPEND theInsList ${CMAKE_CURRENT_SOURCE_DIR}/${in}) + endif() + endforeach() + get_filename_component(outDir ${theOut} DIRECTORY) + file(MAKE_DIRECTORY ${outDir}) + file(RELATIVE_PATH outRel ${CMAKE_BINARY_DIR} ${theOut}) + add_custom_command(OUTPUT ${theOut}.cpp ${theOut}.hpp + COMMAND $ ARGS -o ${theOut} ${theInsList} + DEPENDS ${theInsList} shaderc COMMENT "Compiling shader ${outRel}.shader") +endfunction() diff --git a/hecl/shaderc/main.cpp b/hecl/shaderc/main.cpp new file mode 100644 index 000000000..1d12eb291 --- /dev/null +++ b/hecl/shaderc/main.cpp @@ -0,0 +1,130 @@ +#include "shaderc.hpp" +#include "logvisor/logvisor.hpp" +#include "athena/FileWriter.hpp" +#include "glslang/Public/ShaderLang.h" +#include "hecl/hecl.hpp" + +static logvisor::Module Log("shaderc"); + +#if _WIN32 +int wmain(int argc, const hecl::SystemChar** argv) +#else +int main(int argc, const hecl::SystemChar** argv) +#endif +{ + logvisor::RegisterConsoleLogger(); + logvisor::RegisterStandardExceptions(); + + if (argc == 1) + { + Log.report(logvisor::Info, "Usage: shaderc -o [-D definevar=defineval]... ..."); + return 0; + } + + hecl::SystemString outPath; + hecl::shaderc::Compiler c; + for (int i = 1; i < argc; ++i) + { + if (argv[i][0] == '-') + { + if (argv[i][1] == 'o') + { + if (argv[i][2]) + { + outPath = &argv[i][2]; + } + else if (i + 1 < argc) + { + ++i; + outPath = argv[i]; + } + else + { + Log.report(logvisor::Error, "Invalid -o argument"); + return 1; + } + } + else if (argv[i][1] == 'D') + { + const hecl::SystemChar* define; + if (argv[i][2]) + { + define = &argv[i][2]; + } + else if (i + 1 < argc) + { + ++i; + define = argv[i]; + } + else + { + Log.report(logvisor::Error, "Invalid -D argument"); + return 1; + } + hecl::SystemUTF8Conv conv(define); + const char* defineU8 = conv.c_str(); + if (char* equals = strchr(defineU8, '=')) + c.addDefine(std::string(defineU8, equals - defineU8), equals + 1); + else + c.addDefine(defineU8, ""); + } + else + { + Log.report(logvisor::Error, "Unrecognized flag option '%c'", argv[i][1]); + return 1; + } + } + else + { + c.addInputFile(argv[i]); + } + } + + if (outPath.empty()) + { + Log.report(logvisor::Error, "-o option is required"); + return 1; + } + + hecl::SystemStringView baseName; + auto slashPos = outPath.find_last_of("/\\"); + if (slashPos != hecl::SystemString::npos) + baseName = outPath.data() + slashPos + 1; + else + baseName = outPath; + + if (!glslang::InitializeProcess()) + { + Log.report(logvisor::Error, "Unable to initialize glslang"); + return 1; + } + + hecl::SystemUTF8Conv conv(baseName); + std::pair ret; + if (!c.compile(conv.str(), ret)) + return 1; + + { + hecl::SystemString headerPath = outPath + _S(".hpp"); + athena::io::FileWriter w(headerPath); + if (w.hasError()) + { + Log.report(logvisor::Error, _S("Error opening '%s' for writing"), headerPath.c_str()); + return 1; + } + w.writeBytes(ret.first.data(), ret.first.size()); + } + + { + hecl::SystemString impPath = outPath + _S(".cpp"); + athena::io::FileWriter w(impPath); + if (w.hasError()) + { + Log.report(logvisor::Error, _S("Error opening '%s' for writing"), impPath.c_str()); + return 1; + } + w.writeBytes(ret.second.data(), ret.second.size()); + } + + return 0; +} diff --git a/hecl/shaderc/shaderc.cpp b/hecl/shaderc/shaderc.cpp new file mode 100644 index 000000000..900126907 --- /dev/null +++ b/hecl/shaderc/shaderc.cpp @@ -0,0 +1,948 @@ +#include "shaderc.hpp" +#include "athena/FileReader.hpp" +#include "logvisor/logvisor.hpp" +#include "hecl/hecl.hpp" +#include "hecl/PipelineBase.hpp" +#include +#include +#include +#include + +using namespace std::literals; + +namespace hecl::shaderc +{ +static logvisor::Module Log("shaderc"); + +static constexpr std::regex::flag_type RegexFlags = std::regex::ECMAScript|std::regex::optimize; + +#if __GNUC__ +__attribute__((__format__ (__printf__, 1, 2))) +#endif +static std::string Format(const char* format, ...) +{ + char resultBuf[FORMAT_BUF_SZ]; + va_list va; + va_start(va, format); + int printSz = vsnprintf(resultBuf, FORMAT_BUF_SZ, format, va); + va_end(va); + return std::string(resultBuf, printSz); +} + +const std::string* Compiler::getFileContents(SystemStringView path) +{ + auto search = m_fileContents.find(path.data()); + if (search == m_fileContents.end()) + { + athena::io::FileReader r(path); + if (r.hasError()) + return nullptr; + auto len = r.length(); + auto data = r.readBytes(len); + search = m_fileContents.insert(std::make_pair(path.data(), std::string((char*)data.get(), len))).first; + } + return &search->second; +} + +void Compiler::addInputFile(SystemStringView file) +{ + if (std::find(m_inputFiles.begin(), m_inputFiles.end(), file) == m_inputFiles.end()) + m_inputFiles.emplace_back(file); +} + +void Compiler::addDefine(std::string_view var, std::string_view val) +{ + m_defines[var.data()] = val; +} + +static const char* ShaderHeaderTemplate = +"class Shader_%s : public hecl::GeneralShader\n" +"{\n" +"public:\n" +" static const boo::VertexFormatInfo VtxFmt;\n" +" static const boo::AdditionalPipelineInfo PipelineInfo;\n" +" static constexpr bool HasHash = true;\n" +" static constexpr uint64_t Hash() { return 0x%016llX; }\n" +"};\n\n"; + +static const char* StageObjectHeaderTemplate = +"template\n" +"class StageObject_%s : public hecl::StageBinary\n" +"{\n" +" static const hecl::StageBinary Prototype;\n" +"public:\n" +" StageObject_%s(hecl::StageConverter& conv, hecl::FactoryCtx& ctx, const Shader_%s& in)\n" +" : hecl::StageBinary(Prototype) {}\n" +"};\n" +"STAGEOBJECT_PROTOTYPE_DECLARATIONS(StageObject_%s)\n\n"; + +static const char* StageObjectImplTemplate = +"template<>\n" +"const hecl::StageBinary\n" +"StageObject_%s::Prototype = \n" +"{%s_%s_%s_data, sizeof(%s_%s_%s_data)};\n\n"; + +struct CompileSubStageAction +{ + template + static bool Do(const std::string& name, const std::string& basename, const std::string& stage, std::string& implOut) + { + implOut += Format(StageObjectImplTemplate, P::Name, S::Name, name.c_str(), P::Name, S::Name, + basename.c_str(), P::Name, S::Name, basename.c_str(), P::Name, S::Name); + + return true; + } +}; + +struct CompileStageAction +{ + template + static bool Do(const std::string& name, const std::string& basename, const std::string& stage, std::string& implOut) + { + std::pair, size_t> data = CompileShader(stage); + if (data.second == 0) + return false; + + implOut += Format("static const uint8_t %s_%s_%s_data[] = {\n", name.c_str(), P::Name, S::Name); + for (size_t i = 0; i < data.second; ) + { + implOut += " "; + for (int j = 0; j < 10 && i < data.second ; ++i, ++j) + implOut += Format("0x%02X, ", data.first[i]); + implOut += "\n"; + } + implOut += "};\n\n"; + implOut += Format(StageObjectImplTemplate, P::Name, S::Name, name.c_str(), P::Name, S::Name, + name.c_str(), P::Name, S::Name, name.c_str(), P::Name, S::Name); + + return true; + } +}; + +template +bool Compiler::StageAction(StageType type, + const std::string& name, const std::string& basename, const std::string& stage, + std::string& implOut) +{ + switch (type) + { + case StageType::Vertex: + return Action::template Do(name, basename, stage, implOut); + case StageType::Fragment: + return Action::template Do(name, basename, stage, implOut); + case StageType::Geometry: + return Action::template Do(name, basename, stage, implOut); + case StageType::Control: + return Action::template Do(name, basename, stage, implOut); + case StageType::Evaluation: + return Action::template Do(name, basename, stage, implOut); + default: + break; + } + Log.report(logvisor::Error, "Unknown stage type"); + return false; +} + +static const std::regex regWord(R"((\w+))", RegexFlags); + +template +bool Compiler::StageAction(const std::string& platforms, StageType type, + const std::string& name, const std::string& basename, const std::string& stage, + std::string& implOut) +{ + std::smatch match; + auto begin = platforms.cbegin(); + auto end = platforms.cend(); + while (std::regex_search(begin, end, match, regWord)) + { + std::string plat = match[1].str(); + std::transform(plat.begin(), plat.end(), plat.begin(), ::tolower); + if (plat == "glsl") + { + if (!StageAction(type, name, basename, stage, implOut) || + !StageAction(type, name, basename, stage, implOut) +#if HECL_NOUVEAU_NX + || !StageAction(type, name, basename, stage, implOut) +#endif + ) + return false; + } + else if (plat == "opengl") + { + if (!StageAction(type, name, basename, stage, implOut)) + return false; + } + else if (plat == "vulkan") + { + if (!StageAction(type, name, basename, stage, implOut)) + return false; + } + else if (plat == "nx") + { +#if HECL_NOUVEAU_NX + if (!StageAction(type, name, basename, stage, implOut)) + return false; +#endif + } + else if (plat == "d3d11" || plat == "hlsl") + { +#if _WIN32 + if (!StageAction(type, name, basename, stage, implOut)) + return false; +#endif + } + else if (plat == "metal") + { +#if __APPLE__ + if (!StageAction(type, name, basename, stage, implOut)) + return false; +#endif + } + else + { + Log.report(logvisor::Error, "Unknown platform '%s'", plat.c_str()); + return false; + } + begin = match.suffix().first; + } + + return true; +} + +static const std::regex regInclude(R"(#\s*include\s+\"(.*)\")", RegexFlags); +static const std::regex regDefine(R"(#\s*define\s+(\w+)\s*(.*))", RegexFlags); +static const std::regex regShaderEx(R"(#\s*shader\s+(\w+)\s*:\s*(\w+))", RegexFlags); +static const std::regex regShader(R"(#\s*shader\s+(\w+))", RegexFlags); +static const std::regex regAttributeEx(R"(#\s*attribute\s+(\w+)\s+([0-9]+))", RegexFlags); +static const std::regex regAttribute(R"(#\s*attribute\s+(\w+))", RegexFlags); +static const std::regex regInstAttributeEx(R"(#\s*instattribute\s+(\w+)\s+([0-9]+))", RegexFlags); +static const std::regex regInstAttribute(R"(#\s*instattribute\s+(\w+))", RegexFlags); +static const std::regex regSrcFac(R"(#\s*srcfac\s+(\w+))", RegexFlags); +static const std::regex regDstFac(R"(#\s*dstfac\s+(\w+))", RegexFlags); +static const std::regex regPrim(R"(#\s*primitive\s+(\w+))", RegexFlags); +static const std::regex regZTest(R"(#\s*depthtest\s+(\w+))", RegexFlags); +static const std::regex regDepthWrite(R"(#\s*depthwrite\s+(\w+))", RegexFlags); +static const std::regex regColorWrite(R"(#\s*colorwrite\s+(\w+))", RegexFlags); +static const std::regex regAlphaWrite(R"(#\s*alphawrite\s+(\w+))", RegexFlags); +static const std::regex regCulling(R"(#\s*culling\s+(\w+))", RegexFlags); +static const std::regex regPatchSize(R"(#\s*patchsize\s+(\w+))", RegexFlags); +static const std::regex regOverwriteAlpha(R"(#\s*overwritealpha\s+(\w+))", RegexFlags); +static const std::regex regDepthAttachment(R"(#\s*depthattachment\s+(\w+))", RegexFlags); +static const std::regex regVertex(R"(#\s*vertex\s+(.*))", RegexFlags); +static const std::regex regFragment(R"(#\s*fragment\s+(.*))", RegexFlags); +static const std::regex regGeometry(R"(#\s*geometry\s+(.*))", RegexFlags); +static const std::regex regControl(R"(#\s*control\s+(.*))", RegexFlags); +static const std::regex regEvaluation(R"(#\s*evaluation\s+(.*))", RegexFlags); + +bool Compiler::includeFile(SystemStringView file, std::string& out, int depth) +{ + if (depth > 32) + { + Log.report(logvisor::Error, _S("Too many levels of includes (>32) at '%s'"), file.data()); + return false; + } + + const std::string* data = getFileContents(file); + if (!data) + { + Log.report(logvisor::Error, _S("Unable to access '%s'"), file.data()); + return false; + } + const std::string& sdata = *data; + + SystemString directory; + auto slashPos = file.find_last_of("/\\"); + if (slashPos != SystemString::npos) + directory = SystemString(file.begin(), file.begin() + slashPos); + else + directory = _S("."); + + auto begin = sdata.cbegin(); + auto end = sdata.cend(); + while (begin != end) + { + std::string::const_iterator nextBegin; + auto findPos = sdata.find('\n', begin - sdata.begin()); + if (findPos == std::string::npos) + nextBegin = end; + else + nextBegin = sdata.begin() + findPos + 1; + + std::smatch subMatch; + if (std::regex_search(begin, nextBegin, subMatch, regInclude)) + { + std::string path = subMatch[1].str(); + if (path.empty()) + { + Log.report(logvisor::Error, _S("Empty path provided to include in '%s'"), file.data()); + return false; + } + + hecl::SystemString pathStr(hecl::SystemStringConv(path).sys_str()); + if (!hecl::IsAbsolute(pathStr)) + pathStr = directory + _S('/') + pathStr; + if (!includeFile(pathStr, out, depth + 1)) + return false; + } + else + { + out.insert(out.end(), begin, nextBegin); + } + + begin = nextBegin; + } + return true; +} + +static std::string_view BlendFactorToStr(boo::BlendFactor fac) +{ + switch (fac) + { + case boo::BlendFactor::Zero: + default: + return "boo::BlendFactor::Zero"sv; + case boo::BlendFactor::One: + return "boo::BlendFactor::One"sv; + case boo::BlendFactor::SrcColor: + return "boo::BlendFactor::SrcColor"sv; + case boo::BlendFactor::InvSrcColor: + return "boo::BlendFactor::InvSrcColor"sv; + case boo::BlendFactor::DstColor: + return "boo::BlendFactor::DstColor"sv; + case boo::BlendFactor::InvDstColor: + return "boo::BlendFactor::InvDstColor"sv; + case boo::BlendFactor::SrcAlpha: + return "boo::BlendFactor::SrcAlpha"sv; + case boo::BlendFactor::InvSrcAlpha: + return "boo::BlendFactor::InvSrcAlpha"sv; + case boo::BlendFactor::DstAlpha: + return "boo::BlendFactor::DstAlpha"sv; + case boo::BlendFactor::InvDstAlpha: + return "boo::BlendFactor::InvDstAlpha"sv; + case boo::BlendFactor::SrcColor1: + return "boo::BlendFactor::SrcColor1"sv; + case boo::BlendFactor::InvSrcColor1: + return "boo::BlendFactor::InvSrcColor1"sv; + case boo::BlendFactor::Subtract: + return "boo::BlendFactor::Subtract"sv; + } +} + +static bool StrToBlendFactor(std::string str, boo::BlendFactor& fac) +{ + std::transform(str.begin(), str.end(), str.begin(), ::tolower); + if (str == "zero") + fac = boo::BlendFactor::Zero; + else if (str == "one") + fac = boo::BlendFactor::One; + else if (str == "srccolor") + fac = boo::BlendFactor::SrcColor; + else if (str == "invsrccolor") + fac = boo::BlendFactor::InvSrcColor; + else if (str == "dstcolor") + fac = boo::BlendFactor::DstColor; + else if (str == "invdstcolor") + fac = boo::BlendFactor::InvDstColor; + else if (str == "srcalpha") + fac = boo::BlendFactor::SrcAlpha; + else if (str == "invsrcalpha") + fac = boo::BlendFactor::InvSrcAlpha; + else if (str == "dstalpha") + fac = boo::BlendFactor::DstAlpha; + else if (str == "invdstalpha") + fac = boo::BlendFactor::InvDstAlpha; + else if (str == "srccolor1") + fac = boo::BlendFactor::SrcColor1; + else if (str == "invsrccolor1") + fac = boo::BlendFactor::InvSrcColor1; + else if (str == "subtract") + fac = boo::BlendFactor::Subtract; + else + { + Log.report(logvisor::Error, "Unrecognized blend mode '%s'", str.c_str()); + return false; + } + return true; +} + +static std::string_view PrimitiveToStr(boo::Primitive prim) +{ + switch (prim) + { + case boo::Primitive::Triangles: + default: + return "boo::Primitive::Triangles"sv; + case boo::Primitive::TriStrips: + return "boo::Primitive::TriStrips"sv; + case boo::Primitive::Patches: + return "boo::Primitive::Patches"sv; + } +} + +static bool StrToPrimitive(std::string str, boo::Primitive& prim) +{ + std::transform(str.begin(), str.end(), str.begin(), ::tolower); + if (str == "triangles") + prim = boo::Primitive::Triangles; + else if (str == "tristrips") + prim = boo::Primitive::TriStrips; + else if (str == "patches") + prim = boo::Primitive::Patches; + else + { + Log.report(logvisor::Error, "Unrecognized primitive '%s'", str.c_str()); + return false; + } + return true; +} + +static std::string_view ZTestToStr(boo::ZTest ztest) +{ + switch (ztest) + { + case boo::ZTest::None: + default: + return "boo::ZTest::None"sv; + case boo::ZTest::LEqual: + return "boo::ZTest::LEqual"sv; + case boo::ZTest::Greater: + return "boo::ZTest::Greater"sv; + case boo::ZTest::GEqual: + return "boo::ZTest::GEqual"sv; + case boo::ZTest::Equal: + return "boo::ZTest::Equal"sv; + } +} + +static bool StrToZTest(std::string str, boo::ZTest& ztest) +{ + std::transform(str.begin(), str.end(), str.begin(), ::tolower); + if (str == "none") + ztest = boo::ZTest::None; + else if (str == "lequal") + ztest = boo::ZTest::LEqual; + else if (str == "greater") + ztest = boo::ZTest::Greater; + else if (str == "gequal") + ztest = boo::ZTest::GEqual; + else if (str == "equal") + ztest = boo::ZTest::Equal; + else + { + Log.report(logvisor::Error, "Unrecognized ztest '%s'", str.c_str()); + return false; + } + return true; +} + +static std::string_view CullModeToStr(boo::CullMode cull) +{ + switch (cull) + { + case boo::CullMode::None: + default: + return "boo::CullMode::None"sv; + case boo::CullMode::Backface: + return "boo::CullMode::Backface"sv; + case boo::CullMode::Frontface: + return "boo::CullMode::Frontface"sv; + } +} + +static bool StrToCullMode(std::string str, boo::CullMode& cull) +{ + std::transform(str.begin(), str.end(), str.begin(), ::tolower); + if (str == "none") + cull = boo::CullMode::None; + else if (str == "backface") + cull = boo::CullMode::Backface; + else if (str == "frontface") + cull = boo::CullMode::Frontface; + else + { + Log.report(logvisor::Error, "Unrecognized cull mode '%s'", str.c_str()); + return false; + } + return true; +} + +static std::string_view BoolToStr(bool b) +{ + return b ? "true"sv : "false"sv; +} + +static bool StrToBool(std::string str, bool& b) +{ + std::transform(str.begin(), str.end(), str.begin(), ::tolower); + if (strtol(str.c_str(), nullptr, 0)) + b = true; + else if (str == "true") + b = true; + else if (str == "false") + b = false; + else + { + Log.report(logvisor::Error, "Unrecognized bool '%s'", str.c_str()); + return false; + } + return true; +} + +bool Compiler::compileFile(SystemStringView file, std::string_view baseName, std::pair& out) +{ + std::string includesPass; + if (!includeFile(file, includesPass)) + return false; + + std::string shaderName(baseName); + std::string shaderBase; + std::vector> shaderAttributes; + bool shaderAttributesReset = false; + boo::AdditionalPipelineInfo shaderInfo = {}; + std::string stagePlatforms; + StageType stageType; + auto stageBegin = includesPass.cend(); + auto stageEnd = includesPass.cend(); + std::unordered_map, std::set>> shaderStageUses; + std::unordered_map shaderBases; + + auto _DoCompile = [&]() + { + if (stageBegin == includesPass.end()) + return true; + + if (shaderName.empty()) + { + Log.report(logvisor::Error, "`#shader ` must be issued before stages"); + return false; + } + std::string stage(stageBegin, stageEnd); + for (const auto& define : m_defines) + { + std::string::size_type pos = 0; + while ((pos = stage.find(define.first, pos)) != std::string::npos) + { + stage = std::string(stage.begin(), stage.begin() + pos) + define.second + + std::string(stage.begin() + pos + define.first.size(), stage.end()); + } + } + stageBegin = includesPass.end(); + std::pair, std::set>& uses = shaderStageUses[shaderName]; + uses.first.set(size_t(stageType)); + uses.second.insert(stagePlatforms); + return StageAction(stagePlatforms, stageType, shaderName, + shaderBase, stage, out.second); + }; + auto DoCompile = [&](std::string platform, StageType type, + std::string::const_iterator end, std::string::const_iterator begin) + { + stageEnd = end; + bool ret = _DoCompile(); + stagePlatforms = std::move(platform); + stageType = type; + stageBegin = begin; + return ret; + }; + auto DoShader = [&]() + { + if (shaderBase.empty() && shaderStageUses.find(shaderName) == shaderStageUses.cend()) + return true; + std::pair, std::set>& uses = shaderStageUses[shaderName]; + if (uses.first.test(5)) + return true; + + out.first += Format(ShaderHeaderTemplate, shaderName.c_str(), + XXH64(shaderName.c_str(), shaderName.size(), 0)); + out.first += Format(StageObjectHeaderTemplate, shaderName.c_str(), shaderName.c_str(), + shaderName.c_str(), shaderName.c_str()); + + if (!shaderBase.empty()) + { + shaderBases[shaderName] = shaderBase; + + for (int i = 0; i < 5; ++i) + { + if (uses.first.test(size_t(i))) + continue; + + std::string useBase = shaderBase; + std::unordered_map, std::set>>::const_iterator + baseUses = shaderStageUses.find(useBase); + while (baseUses == shaderStageUses.cend() || !baseUses->second.first.test(size_t(i))) + { + auto search = shaderBases.find(useBase); + if (search == shaderBases.cend()) + break; + useBase = search->second; + baseUses = shaderStageUses.find(useBase); + } + if (baseUses == shaderStageUses.cend() || !baseUses->second.first.test(size_t(i))) + continue; + for (const std::string& basePlatforms : baseUses->second.second) + { + StageAction(basePlatforms, StageType(i), shaderName, + useBase, {}, out.second); + } + } + shaderBase.clear(); + } + + out.second += Format("static const boo::VertexElementDescriptor %s_vtxfmtelems[] = {\n", shaderName.c_str()); + for (const auto& attr : shaderAttributes) + { + const char* fmt; + switch (attr.first & boo::VertexSemantic::SemanticMask) + { + case boo::VertexSemantic::Position3: + fmt = "{boo::VertexSemantic::Position3%s, %d},\n"; + break; + case boo::VertexSemantic::Position4: + fmt = "{boo::VertexSemantic::Position4%s, %d},\n"; + break; + case boo::VertexSemantic::Normal3: + fmt = "{boo::VertexSemantic::Normal3%s, %d},\n"; + break; + case boo::VertexSemantic::Normal4: + fmt = "{boo::VertexSemantic::Normal4%s, %d},\n"; + break; + case boo::VertexSemantic::Color: + fmt = "{boo::VertexSemantic::Color%s, %d},\n"; + break; + case boo::VertexSemantic::ColorUNorm: + fmt = "{boo::VertexSemantic::ColorUNorm%s, %d},\n"; + break; + case boo::VertexSemantic::UV2: + fmt = "{boo::VertexSemantic::UV2%s, %d},\n"; + break; + case boo::VertexSemantic::UV4: + fmt = "{boo::VertexSemantic::UV4%s, %d},\n"; + break; + case boo::VertexSemantic::Weight: + fmt = "{boo::VertexSemantic::Weight%s, %d},\n"; + break; + case boo::VertexSemantic::ModelView: + fmt = "{boo::VertexSemantic::ModelView%s, %d},\n"; + break; + default: + fmt = "{boo::VertexSemantic::None%s, %d},\n"; + break; + } + out.second += Format(fmt, + (attr.first & boo::VertexSemantic::Instanced) != boo::VertexSemantic::None ? + " | boo::VertexSemantic::Instanced" : "", attr.second); + } + out.second += "};\n"; + out.second += Format("const boo::VertexFormatInfo Shader_%s::VtxFmt = { %s_vtxfmtelems };\n\n", + shaderName.c_str(), shaderName.c_str()); + out.second += Format("const boo::AdditionalPipelineInfo Shader_%s::PipelineInfo = {\n", shaderName.c_str()); + out.second += BlendFactorToStr(shaderInfo.srcFac); out.second += ", "; + out.second += BlendFactorToStr(shaderInfo.dstFac); out.second += ", "; + out.second += PrimitiveToStr(shaderInfo.prim); out.second += ", "; + out.second += ZTestToStr(shaderInfo.depthTest); out.second += ",\n"; + out.second += BoolToStr(shaderInfo.depthWrite); out.second += ", "; + out.second += BoolToStr(shaderInfo.colorWrite); out.second += ", "; + out.second += BoolToStr(shaderInfo.alphaWrite); out.second += ", "; + out.second += CullModeToStr(shaderInfo.culling); out.second += ", "; + out.second += Format("%d, ", shaderInfo.patchSize); + out.second += BoolToStr(shaderInfo.overwriteAlpha); out.second += ", "; + out.second += BoolToStr(shaderInfo.depthAttachment); out.second += ", "; + out.second += "};\n\n"; + + uses.first.set(5); + + return true; + }; + auto AddAttribute = [&](std::string semantic, std::string idx, bool inst) + { + if (shaderAttributesReset) + { + shaderAttributes.clear(); + shaderAttributesReset = false; + } + boo::VertexSemantic orsem = inst ? boo::VertexSemantic::Instanced : boo::VertexSemantic::None; + int idxNum = int(strtoul(idx.c_str(), nullptr, 10)); + std::transform(semantic.begin(), semantic.end(), semantic.begin(), ::tolower); + if (semantic == "position3") + shaderAttributes.push_back(std::make_pair(boo::VertexSemantic::Position3 | orsem, idxNum)); + else if (semantic == "position4") + shaderAttributes.push_back(std::make_pair(boo::VertexSemantic::Position4 | orsem, idxNum)); + else if (semantic == "normal3") + shaderAttributes.push_back(std::make_pair(boo::VertexSemantic::Normal3 | orsem, idxNum)); + else if (semantic == "normal4") + shaderAttributes.push_back(std::make_pair(boo::VertexSemantic::Normal4 | orsem, idxNum)); + else if (semantic == "color") + shaderAttributes.push_back(std::make_pair(boo::VertexSemantic::Color | orsem, idxNum)); + else if (semantic == "colorunorm") + shaderAttributes.push_back(std::make_pair(boo::VertexSemantic::ColorUNorm | orsem, idxNum)); + else if (semantic == "uv2") + shaderAttributes.push_back(std::make_pair(boo::VertexSemantic::UV2 | orsem, idxNum)); + else if (semantic == "uv4") + shaderAttributes.push_back(std::make_pair(boo::VertexSemantic::UV4 | orsem, idxNum)); + else if (semantic == "weight") + shaderAttributes.push_back(std::make_pair(boo::VertexSemantic::Weight | orsem, idxNum)); + else if (semantic == "modelview") + shaderAttributes.push_back(std::make_pair(boo::VertexSemantic::ModelView | orsem, idxNum)); + else + { + Log.report(logvisor::Error, "Unrecognized vertex semantic '%s'", semantic.c_str()); + return false; + } + return true; + }; + + auto begin = includesPass.cbegin(); + auto end = includesPass.cend(); + std::string* defineContinue = nullptr; + while (begin != end) + { + std::string::const_iterator nextBegin; + auto findPos = includesPass.find('\n', begin - includesPass.cbegin()); + if (findPos == std::string::npos) + nextBegin = end; + else + nextBegin = includesPass.cbegin() + findPos + 1; + + std::smatch subMatch; + if (defineContinue) + { + std::string extraLine; + if (findPos == std::string::npos) + extraLine = std::string(begin, end); + else + extraLine = std::string(begin, includesPass.cbegin() + findPos); + *defineContinue += extraLine; + if (!defineContinue->empty() && defineContinue->back() == '\\') + defineContinue->pop_back(); + else + defineContinue = nullptr; + } + else if (std::regex_search(begin, nextBegin, subMatch, regDefine)) + { + std::string& defOut = m_defines[subMatch[1].str()]; + defOut = subMatch[2].str(); + if (!defOut.empty() && defOut.back() == '\\') + { + defOut.pop_back(); + defineContinue = &defOut; + } + } + else if (std::regex_search(begin, nextBegin, subMatch, regShaderEx)) + { + stageEnd = begin; + if (!_DoCompile() || !DoShader()) + return false; + shaderName = subMatch[1].str(); + shaderBase = subMatch[2].str(); + shaderAttributesReset = true; + //shaderAttributes.clear(); + //shaderInfo = boo::AdditionalPipelineInfo(); + } + else if (std::regex_search(begin, nextBegin, subMatch, regShader)) + { + stageEnd = begin; + if (!_DoCompile() || !DoShader()) + return false; + shaderName = subMatch[1].str(); + shaderAttributesReset = true; + //shaderAttributes.clear(); + //shaderInfo = boo::AdditionalPipelineInfo(); + } + else if (std::regex_search(begin, nextBegin, subMatch, regAttributeEx)) + { + if (!AddAttribute(subMatch[1].str(), subMatch[2].str(), false)) + return false; + } + else if (std::regex_search(begin, nextBegin, subMatch, regAttribute)) + { + if (!AddAttribute(subMatch[1].str(), "0", false)) + return false; + } + else if (std::regex_search(begin, nextBegin, subMatch, regInstAttributeEx)) + { + if (!AddAttribute(subMatch[1].str(), subMatch[2].str(), true)) + return false; + } + else if (std::regex_search(begin, nextBegin, subMatch, regInstAttribute)) + { + if (!AddAttribute(subMatch[1].str(), "0", true)) + return false; + } + else if (std::regex_search(begin, nextBegin, subMatch, regSrcFac)) + { + if (!StrToBlendFactor(subMatch[1].str(), shaderInfo.srcFac)) + return false; + } + else if (std::regex_search(begin, nextBegin, subMatch, regDstFac)) + { + if (!StrToBlendFactor(subMatch[1].str(), shaderInfo.dstFac)) + return false; + } + else if (std::regex_search(begin, nextBegin, subMatch, regPrim)) + { + if (!StrToPrimitive(subMatch[1].str(), shaderInfo.prim)) + return false; + } + else if (std::regex_search(begin, nextBegin, subMatch, regZTest)) + { + if (!StrToZTest(subMatch[1].str(), shaderInfo.depthTest)) + return false; + } + else if (std::regex_search(begin, nextBegin, subMatch, regDepthWrite)) + { + if (!StrToBool(subMatch[1].str(), shaderInfo.depthWrite)) + return false; + } + else if (std::regex_search(begin, nextBegin, subMatch, regColorWrite)) + { + if (!StrToBool(subMatch[1].str(), shaderInfo.colorWrite)) + return false; + } + else if (std::regex_search(begin, nextBegin, subMatch, regAlphaWrite)) + { + if (!StrToBool(subMatch[1].str(), shaderInfo.alphaWrite)) + return false; + } + else if (std::regex_search(begin, nextBegin, subMatch, regCulling)) + { + if (!StrToCullMode(subMatch[1].str(), shaderInfo.culling)) + return false; + } + else if (std::regex_search(begin, nextBegin, subMatch, regPatchSize)) + { + auto str = subMatch[1].str(); + char* endptr; + shaderInfo.patchSize = uint32_t(strtoul(str.c_str(), &endptr, 0)); + if (endptr == str.c_str()) + { + Log.report(logvisor::Error, "Non-unsigned-integer value for #patchsize directive"); + return false; + } + } + else if (std::regex_search(begin, nextBegin, subMatch, regOverwriteAlpha)) + { + if (!StrToBool(subMatch[1].str(), shaderInfo.overwriteAlpha)) + return false; + } + else if (std::regex_search(begin, nextBegin, subMatch, regDepthAttachment)) + { + if (!StrToBool(subMatch[1].str(), shaderInfo.depthAttachment)) + return false; + } + else if (std::regex_search(begin, nextBegin, subMatch, regVertex)) + { + if (!DoCompile(subMatch[1].str(), StageType::Vertex, begin, nextBegin)) + return false; + } + else if (std::regex_search(begin, nextBegin, subMatch, regFragment)) + { + if (!DoCompile(subMatch[1].str(), StageType::Fragment, begin, nextBegin)) + return false; + } + else if (std::regex_search(begin, nextBegin, subMatch, regGeometry)) + { + if (!DoCompile(subMatch[1].str(), StageType::Geometry, begin, nextBegin)) + return false; + } + else if (std::regex_search(begin, nextBegin, subMatch, regControl)) + { + if (!DoCompile(subMatch[1].str(), StageType::Control, begin, nextBegin)) + return false; + } + else if (std::regex_search(begin, nextBegin, subMatch, regEvaluation)) + { + if (!DoCompile(subMatch[1].str(), StageType::Evaluation, begin, nextBegin)) + return false; + } + + begin = nextBegin; + } + + stageEnd = begin; + if (!_DoCompile() || !DoShader()) + return false; + + out.first += "#define UNIVERSAL_PIPELINES_"; + out.first += baseName; + for (const auto& shader : shaderStageUses) + { + out.first += " \\\n"; + out.first += "::Shader_"; + out.first += shader.first; + } + out.first += "\n"; + + out.first += "#define OPENGL_STAGES_"; + out.first += baseName; + for (const auto& shader : shaderStageUses) + { + out.first += " \\\n"; + out.first += "STAGE_SPECIALIZATIONS(::StageObject_"; + out.first += shader.first; + out.first += ", hecl::PlatformType::OpenGL)"; + } + out.first += "\n"; + + out.first += "#define VULKAN_STAGES_"; + out.first += baseName; + for (const auto& shader : shaderStageUses) + { + out.first += " \\\n"; + out.first += "STAGE_SPECIALIZATIONS(::StageObject_"; + out.first += shader.first; + out.first += ", hecl::PlatformType::Vulkan)"; + } + out.first += "\n"; + + out.first += "#define D3D11_STAGES_"; + out.first += baseName; + for (const auto& shader : shaderStageUses) + { + out.first += " \\\n"; + out.first += "STAGE_SPECIALIZATIONS(::StageObject_"; + out.first += shader.first; + out.first += ", hecl::PlatformType::D3D11)"; + } + out.first += "\n"; + + out.first += "#define METAL_STAGES_"; + out.first += baseName; + for (const auto& shader : shaderStageUses) + { + out.first += " \\\n"; + out.first += "STAGE_SPECIALIZATIONS(::StageObject_"; + out.first += shader.first; + out.first += ", hecl::PlatformType::Metal)"; + } + out.first += "\n"; + + out.first += "#define NX_STAGES_"; + out.first += baseName; + for (const auto& shader : shaderStageUses) + { + out.first += " \\\n"; + out.first += "STAGE_SPECIALIZATIONS(::StageObject_"; + out.first += shader.first; + out.first += ", hecl::PlatformType::NX)"; + } + out.first += "\n"; + + return true; +} + +bool Compiler::compile(std::string_view baseName, std::pair& out) +{ + out = + { + "#pragma once\n" + "#include \"hecl/PipelineBase.hpp\"\n\n", + Format("#include \"%s.hpp\"\n\n", baseName.data()) + }; + + for (const auto& file : m_inputFiles) + if (!compileFile(file, baseName, out)) + return false; + + return true; +} + +} diff --git a/hecl/shaderc/shaderc.hpp b/hecl/shaderc/shaderc.hpp new file mode 100644 index 000000000..7c3d66836 --- /dev/null +++ b/hecl/shaderc/shaderc.hpp @@ -0,0 +1,41 @@ +#pragma once +#include "hecl/SystemChar.hpp" +#include +#include +#include + +namespace hecl::shaderc +{ + +class Compiler +{ + enum class StageType + { + Vertex, + Fragment, + Geometry, + Control, + Evaluation + }; + + std::vector m_inputFiles; + std::unordered_map m_fileContents; + const std::string* getFileContents(SystemStringView path); + std::unordered_map m_defines; + template + static bool StageAction(StageType type, + const std::string& name, const std::string& basename, const std::string& stage, + std::string& implOut); + template + static bool StageAction(const std::string& platforms, StageType type, + const std::string& name, const std::string& basename, const std::string& stage, + std::string& implOut); + bool includeFile(SystemStringView file, std::string& out, int depth = 0); + bool compileFile(SystemStringView file, std::string_view baseName, std::pair& out); +public: + void addInputFile(SystemStringView file); + void addDefine(std::string_view var, std::string_view val); + bool compile(std::string_view baseName, std::pair& out); +}; + +} diff --git a/hecl/test/CMakeLists.txt b/hecl/test/CMakeLists.txt index 0aff6bfc0..012539730 100644 --- a/hecl/test/CMakeLists.txt +++ b/hecl/test/CMakeLists.txt @@ -1,8 +1,9 @@ add_executable(heclTest WIN32 main.cpp) target_link_libraries(heclTest + ${HECL_APPLICATION_REPS_TARGETS_LIST} hecl-full hecl-blender-addon athena-core athena-libyaml xxhash logvisor boo ${ZLIB_LIBRARIES} ${LZO_LIB} ${BOO_SYS_LIBS}) if(COMMAND add_sanitizers) add_sanitizers(heclTest) -endif() \ No newline at end of file +endif() diff --git a/hecl/test/main.cpp b/hecl/test/main.cpp index 613bfbe27..52736564d 100644 --- a/hecl/test/main.cpp +++ b/hecl/test/main.cpp @@ -2,9 +2,11 @@ #include "logvisor/logvisor.hpp" #include "hecl/Console.hpp" #include "hecl/CVarManager.hpp" -#include +#include "athena/MemoryWriter.hpp" #include "hecl/Runtime.hpp" +#include "hecl/Backend/Backend.hpp" #include "hecl/HMDLMeta.hpp" +#include "hecl/Pipeline.hpp" #include #include #include @@ -62,7 +64,8 @@ struct HECLApplicationCallback : boo::IApplicationCallback m_cvarManager(m_fileStoreMgr), m_console(&m_cvarManager) { - m_console.registerCommand("quit"sv, "Quits application"sv, "", std::bind(&HECLApplicationCallback::quit, this, std::placeholders::_1, std::placeholders::_2)); + m_console.registerCommand("quit"sv, "Quits application"sv, "", + std::bind(&HECLApplicationCallback::quit, this, std::placeholders::_1, std::placeholders::_2)); } virtual ~HECLApplicationCallback(); @@ -77,7 +80,8 @@ struct HECLApplicationCallback : boo::IApplicationCallback boo::ObjToken renderTex; boo::ObjToken vubo; - boo::ObjToken binding; + boo::ObjToken pipeline, pipeline2; + boo::ObjToken binding, binding2; struct VertexUBO { @@ -108,104 +112,88 @@ struct HECLApplicationCallback : boo::IApplicationCallback for (int j=0 ; j<256 ; ++j) { tex[i][j][0] = uint8_t(i); - tex[i][j][1] = uint8_t(i); + tex[i][j][1] = uint8_t(j); tex[i][j][2] = 0; tex[i][j][3] = 0xff; } - std::mutex initmt; - std::condition_variable initcv; - std::mutex loadmt; - std::condition_variable loadcv; - std::unique_lock outerLk(initmt); - std::thread loaderThr([&]() + boo::IGraphicsDataFactory* gfxF = m_mainWindow->getMainContextDataFactory(); + if (gfxF->platform() == boo::IGraphicsDataFactory::Platform::Vulkan) + vuboData.modelview[1][1] = -1.f; + + /* Pipeline converter */ + std::unique_ptr conv = hecl::NewPipelineConverter(gfxF); + + /* Compile HECL shader */ + static std::string testShader = "HECLOpaque(Texture(0, UV(0)))"; + //static std::string testShader = "HECLOpaque(vec3(1.0,1.0,1.0),1.0)"; + hecl::Backend::ShaderTag testShaderTag(testShader, 0, 1, 0, 0, boo::Primitive::TriStrips, + hecl::Backend::ReflectionType::None, false, false, false); + hecl::Frontend::Frontend FE; + hecl::HECLIR irObj(FE.compileSource(testShader, "booTest"), testShaderTag, 0); + + gfxF->commitTransaction([&](boo::IGraphicsDataFactory::Context& ctx) { - std::unique_lock innerLk(initmt); - boo::IGraphicsDataFactory* gfxF = m_mainWindow->getLoadContextDataFactory(); + pipeline = conv->convert(ctx, irObj); + pipeline2 = conv->convert(ctx, Shader_test{}); - /* HECL managers */ - hecl::Runtime::FileStoreManager fileMgr(app->getUniqueName()); - hecl::Runtime::ShaderCacheManager shaderMgr(fileMgr, gfxF); + boo::SWindowRect mainWindowRect = m_mainWindow->getWindowFrame(); + renderTex = ctx.newRenderTexture(size_t(mainWindowRect.size[0]), size_t(mainWindowRect.size[1]), + boo::TextureClampMode::Repeat, 1, 0); - /* Compile HECL shader */ - static std::string testShader = "HECLOpaque(Texture(0, UV(0)))"; - //static std::string testShader = "HECLOpaque(vec3(1.0,1.0,1.0),1.0)"; - hecl::Runtime::ShaderTag testShaderTag(testShader, 0, 1, 0, 0, boo::Primitive::TriStrips, - hecl::Backend::ReflectionType::None, false, false, false); - std::shared_ptr testShaderObj = - shaderMgr.buildShader(testShaderTag, testShader, "testShader", *gfxF); + /* Generate meta structure (usually statically serialized) */ + hecl::HMDLMeta testMeta; + testMeta.topology = hecl::HMDLTopology::TriStrips; + testMeta.vertStride = 32; + testMeta.vertCount = 4; + testMeta.indexCount = 4; + testMeta.colorCount = 0; + testMeta.uvCount = 1; + testMeta.weightCount = 0; + testMeta.bankCount = 0; - gfxF->commitTransaction([&](boo::IGraphicsDataFactory::Context& ctx) + /* Binary form of meta structure */ + atUint8 testMetaBuf[HECL_HMDL_META_SZ]; + athena::io::MemoryWriter testMetaWriter(testMetaBuf, HECL_HMDL_META_SZ); + testMeta.write(testMetaWriter); + + /* Make Tri-strip VBO */ + struct Vert { - boo::SWindowRect mainWindowRect = m_mainWindow->getWindowFrame(); - renderTex = ctx.newRenderTexture(size_t(mainWindowRect.size[0]), size_t(mainWindowRect.size[1]), - boo::TextureClampMode::Repeat, 0, 0); - - /* Generate meta structure (usually statically serialized) */ - hecl::HMDLMeta testMeta; - testMeta.topology = hecl::HMDLTopology::TriStrips; - testMeta.vertStride = 32; - testMeta.vertCount = 4; - testMeta.indexCount = 4; - testMeta.colorCount = 0; - testMeta.uvCount = 1; - testMeta.weightCount = 0; - testMeta.bankCount = 0; - - /* Binary form of meta structure */ - atUint8 testMetaBuf[HECL_HMDL_META_SZ]; - athena::io::MemoryWriter testMetaWriter(testMetaBuf, HECL_HMDL_META_SZ); - testMeta.write(testMetaWriter); - - /* Make Tri-strip VBO */ - struct Vert - { - float pos[3]; - float norm[3]; - float uv[2]; - }; - static const Vert quad[4] = - { - {{0.5,0.5},{},{1.0,1.0}}, - {{-0.5,0.5},{},{0.0,1.0}}, - {{0.5,-0.5},{},{1.0,0.0}}, - {{-0.5,-0.5},{},{0.0,0.0}} - }; - - /* Now simple IBO */ - static const uint32_t ibo[4] = {0,1,2,3}; - - /* Construct quad mesh against boo factory */ - hecl::Runtime::HMDLData testData(ctx, testMetaBuf, quad, ibo); - - boo::ObjToken texture = - ctx.newStaticTexture(256, 256, 1, boo::TextureFormat::RGBA8, boo::TextureClampMode::Repeat, tex, 256*256*4).get(); - - /* Make vertex uniform buffer */ - vubo = ctx.newDynamicBuffer(boo::BufferUse::Uniform, sizeof(VertexUBO), 1).get(); - - /* Assemble data binding */ - binding = testData.newShaderDataBindng(ctx, testShaderObj->m_pipelines[0], 1, &vubo, nullptr, 1, &texture); - return true; - } BooTrace); - - /* Return control to main thread */ - innerLk.unlock(); - initcv.notify_one(); - - /* Wait for exit */ - std::unique_lock lk(loadmt); - while (m_running) + float pos[3]; + float norm[3]; + float uv[2]; + }; + static const Vert quad[4] = { - loadcv.wait(lk); - } - }); - initcv.wait(outerLk); + {{0.5,0.5},{},{1.0,1.0}}, + {{-0.5,0.5},{},{0.0,1.0}}, + {{0.5,-0.5},{},{1.0,0.0}}, + {{-0.5,-0.5},{},{0.0,0.0}} + }; + + /* Now simple IBO */ + static const uint32_t ibo[4] = {0,1,2,3}; + + /* Construct quad mesh against boo factory */ + hecl::Runtime::HMDLData testData(ctx, testMetaBuf, quad, ibo); + + boo::ObjToken texture = + ctx.newStaticTexture(256, 256, 1, boo::TextureFormat::RGBA8, boo::TextureClampMode::Repeat, tex, 256*256*4).get(); + + /* Make vertex uniform buffer */ + vubo = ctx.newDynamicBuffer(boo::BufferUse::Uniform, sizeof(VertexUBO), 1).get(); + + /* Assemble data binding */ + binding = testData.newShaderDataBindng(ctx, pipeline, 1, &vubo, nullptr, 1, &texture); + binding2 = testData.newShaderDataBindng(ctx, pipeline2, 1, &vubo, nullptr, 1, &texture); + return true; + } BooTrace); + m_mainWindow->showWindow(); m_windowCb.m_latestSize = m_mainWindow->getWindowFrame(); boo::IGraphicsCommandQueue* gfxQ = m_mainWindow->getCommandQueue(); - m_mainWindow->getMainContextDataFactory(); size_t frameIdx = 0; while (m_running) @@ -242,7 +230,7 @@ struct HECLApplicationCallback : boo::IApplicationCallback vuboData.modelview[3][1] = cosf(frameIdx / 60.0f) * 0.5f; vubo.cast()->load(&vuboData, sizeof(vuboData)); - gfxQ->setShaderDataBinding(binding); + gfxQ->setShaderDataBinding(binding2); gfxQ->drawIndexed(0, 4); gfxQ->resolveDisplay(renderTex); m_console.draw(gfxQ); @@ -251,12 +239,8 @@ struct HECLApplicationCallback : boo::IApplicationCallback ++frameIdx; } - std::unique_lock finallk(loadmt); m_cvarManager.serialize(); - finallk.unlock(); gfxQ->stopRenderer(); - loadcv.notify_one(); - loaderThr.join(); return 0; } void appQuitting(boo::IApplication* /*app*/) diff --git a/hecl/test/test.shader b/hecl/test/test.shader new file mode 100644 index 000000000..cfdf3ab67 --- /dev/null +++ b/hecl/test/test.shader @@ -0,0 +1,25 @@ +#culling none +#attribute position3 +#attribute normal3 +#attribute uv2 + +#vertex glsl +layout(location=0) in vec3 in_pos; +layout(location=1) in vec3 in_norm; +layout(location=2) in vec2 in_uv; +SBINDING(0) out vec2 out_uv; +void main() +{ + gl_Position = vec4(in_pos, 1.0); + out_uv = in_uv; +} + +#fragment glsl +precision highp float; +TBINDING0 uniform sampler2D texs[1]; +layout(location=0) out vec4 out_frag; +SBINDING(0) in vec2 out_uv; +void main() +{ + out_frag = texture(texs[0], out_uv); +}