diff --git a/include/Nazara/Core/Stream.hpp b/include/Nazara/Core/Stream.hpp index ce7992b9a..0ebbd09bc 100644 --- a/include/Nazara/Core/Stream.hpp +++ b/include/Nazara/Core/Stream.hpp @@ -44,7 +44,8 @@ namespace Nz inline StreamOptionFlags GetStreamOptions() const; std::size_t Read(void* buffer, std::size_t size); - virtual std::string ReadLine(unsigned int lineSize = 0); + virtual void ReadLine(std::string& line, unsigned int lineSize = 0); + inline std::string ReadLine(unsigned int lineSize = 0); inline bool IsBufferingEnabled() const; inline bool IsMemoryMapped() const; diff --git a/include/Nazara/Core/Stream.inl b/include/Nazara/Core/Stream.inl index 5164448ed..27a923579 100644 --- a/include/Nazara/Core/Stream.inl +++ b/include/Nazara/Core/Stream.inl @@ -105,6 +105,27 @@ namespace Nz return m_streamOptions; } + /*! + * \brief Reads a line from the stream + * + * Reads the stream until a line separator or the end of the stream is found. + * + * If lineSize does not equal zero, it represents the maximum character count to be read from the stream. + * + * \param lineSize Maximum number of characters to read, or zero for no limit + * + * \return Line read from file + * + * \remark With the text stream option, "\r\n" is treated as "\n" + * \remark The line separator character is not returned as part of the string + */ + inline std::string Stream::ReadLine(unsigned int lineSize) + { + std::string line; + ReadLine(line, lineSize); + return line; + } + /*! * \brief Checks whether the stream is readable * \return true if it is the case diff --git a/include/Nazara/Graphics/FramePipelinePassRegistry.hpp b/include/Nazara/Graphics/FramePipelinePassRegistry.hpp index 066407347..4ab00ce25 100644 --- a/include/Nazara/Graphics/FramePipelinePassRegistry.hpp +++ b/include/Nazara/Graphics/FramePipelinePassRegistry.hpp @@ -33,17 +33,28 @@ namespace Nz inline std::unique_ptr BuildPass(std::size_t passIndex, FramePipelinePass::PassData& passData, std::string passName, const ParameterList& parameters) const; inline std::size_t GetPassIndex(std::string_view passName) const; + inline std::size_t GetPassInputIndex(std::size_t passIndex, std::string_view inputName) const; + inline std::size_t GetPassOutputIndex(std::size_t passIndex, std::string_view inputName) const; - template std::size_t RegisterPass(std::string passName); - inline std::size_t RegisterPass(std::string passName, Factory factory); + template std::size_t RegisterPass(std::string passName, std::vector inputs, std::vector outputs); + inline std::size_t RegisterPass(std::string passName, std::vector inputs, std::vector outputs, Factory factory); FramePipelinePassRegistry& operator=(const FramePipelinePassRegistry&) = default; FramePipelinePassRegistry& operator=(FramePipelinePassRegistry&&) = default; + static constexpr std::size_t InvalidIndex = std::numeric_limits::max(); + private: + struct Pass + { + Factory factory; + std::vector inputs; + std::vector outputs; + }; + std::list m_passNames; //< in order to allow std::string_view as a key in C++17 (keep std::string stable as well because of SSO) std::unordered_map m_passIndex; - std::vector m_passFactories; + std::vector m_passes; }; } diff --git a/include/Nazara/Graphics/FramePipelinePassRegistry.inl b/include/Nazara/Graphics/FramePipelinePassRegistry.inl index cce610dfe..d5184e0d7 100644 --- a/include/Nazara/Graphics/FramePipelinePassRegistry.inl +++ b/include/Nazara/Graphics/FramePipelinePassRegistry.inl @@ -9,29 +9,51 @@ namespace Nz { inline std::unique_ptr FramePipelinePassRegistry::BuildPass(std::size_t passIndex, FramePipelinePass::PassData& passData, std::string passName, const ParameterList& parameters) const { - assert(passIndex < m_passFactories.size()); - return m_passFactories[passIndex](passData, passName, parameters); + assert(passIndex < m_passes.size()); + return m_passes[passIndex].factory(passData, passName, parameters); } inline std::size_t FramePipelinePassRegistry::GetPassIndex(std::string_view passName) const { auto it = m_passIndex.find(passName); if (it == m_passIndex.end()) - throw std::runtime_error("pass " + std::string(passName) + " must be registered before being used"); + return InvalidIndex; return it->second; } - template - std::size_t FramePipelinePassRegistry::RegisterPass(std::string passName) + inline std::size_t FramePipelinePassRegistry::GetPassInputIndex(std::size_t passIndex, std::string_view inputName) const { - return RegisterPass(std::move(passName), [](FramePipelinePass::PassData& passData, std::string passName, const ParameterList& parameters) -> std::unique_ptr + assert(passIndex < m_passes.size()); + auto& passData = m_passes[passIndex]; + auto it = std::find(passData.inputs.begin(), passData.inputs.end(), inputName); + if (it == passData.inputs.end()) + return InvalidIndex; + + return std::distance(passData.inputs.begin(), it); + } + + inline std::size_t FramePipelinePassRegistry::GetPassOutputIndex(std::size_t passIndex, std::string_view outputName) const + { + assert(passIndex < m_passes.size()); + auto& passData = m_passes[passIndex]; + auto it = std::find(passData.outputs.begin(), passData.outputs.end(), outputName); + if (it == passData.outputs.end()) + return InvalidIndex; + + return std::distance(passData.outputs.begin(), it); + } + + template + std::size_t FramePipelinePassRegistry::RegisterPass(std::string passName, std::vector inputs, std::vector outputs) + { + return RegisterPass(std::move(passName), std::move(inputs), std::move(outputs), [](FramePipelinePass::PassData& passData, std::string passName, const ParameterList& parameters) -> std::unique_ptr { return std::make_unique(passData, std::move(passName), parameters); }); } - inline std::size_t FramePipelinePassRegistry::RegisterPass(std::string passName, Factory factory) + inline std::size_t FramePipelinePassRegistry::RegisterPass(std::string passName, std::vector inputs, std::vector outputs, Factory factory) { if (m_passIndex.find(passName) != m_passIndex.end()) throw std::runtime_error("pass " + passName + " is already registered"); @@ -40,7 +62,10 @@ namespace Nz std::size_t passIndex = m_passIndex.size(); m_passIndex.emplace(m_passNames.back(), passIndex); - m_passFactories.emplace_back(std::move(factory)); + auto& passData = m_passes.emplace_back(); + passData.factory = std::move(factory); + passData.inputs = std::move(inputs); + passData.outputs = std::move(outputs); return passIndex; } diff --git a/include/Nazara/Graphics/Graphics.hpp b/include/Nazara/Graphics/Graphics.hpp index 9ad9c39e4..86b7c2cd2 100644 --- a/include/Nazara/Graphics/Graphics.hpp +++ b/include/Nazara/Graphics/Graphics.hpp @@ -55,6 +55,8 @@ namespace Nz inline const MaterialInstanceLoader& GetMaterialInstanceLoader() const; inline MaterialLoader& GetMaterialLoader(); inline const MaterialLoader& GetMaterialLoader() const; + inline PipelinePassListLoader& GetPipelinePassListLoader(); + inline const PipelinePassListLoader& GetPipelinePassListLoader() const; inline PixelFormat GetPreferredDepthFormat() const; inline PixelFormat GetPreferredDepthStencilFormat() const; inline const std::shared_ptr& GetRenderDevice() const; @@ -115,6 +117,7 @@ namespace Nz MaterialInstanceLoader m_materialInstanceLoader; MaterialLoader m_materialLoader; MaterialPassRegistry m_materialPassRegistry; + PipelinePassListLoader m_pipelinePassListLoader; PixelFormat m_preferredDepthFormat; PixelFormat m_preferredDepthStencilFormat; diff --git a/include/Nazara/Graphics/Graphics.inl b/include/Nazara/Graphics/Graphics.inl index be53f3601..9c2da126f 100644 --- a/include/Nazara/Graphics/Graphics.inl +++ b/include/Nazara/Graphics/Graphics.inl @@ -71,6 +71,16 @@ namespace Nz return m_materialLoader; } + inline PipelinePassListLoader& Graphics::GetPipelinePassListLoader() + { + return m_pipelinePassListLoader; + } + + inline const PipelinePassListLoader& Graphics::GetPipelinePassListLoader() const + { + return m_pipelinePassListLoader; + } + inline PixelFormat Graphics::GetPreferredDepthFormat() const { return m_preferredDepthFormat; diff --git a/include/Nazara/Graphics/PipelinePassList.hpp b/include/Nazara/Graphics/PipelinePassList.hpp index 19c89b44b..40d1b206d 100644 --- a/include/Nazara/Graphics/PipelinePassList.hpp +++ b/include/Nazara/Graphics/PipelinePassList.hpp @@ -8,7 +8,10 @@ #define NAZARA_GRAPHICS_PIPELINEPASSLIST_HPP #include +#include #include +#include +#include #include #include #include @@ -21,11 +24,22 @@ namespace Nz { - class FrameGraph; + struct NAZARA_GRAPHICS_API PipelinePassListParams : ResourceParameters + { + bool IsValid() const; + }; - class NAZARA_GRAPHICS_API PipelinePassList + class FrameGraph; + class PipelinePassList; + + using PipelinePassListLibrary = ObjectLibrary; + using PipelinePassListLoader = ResourceLoader; + + class NAZARA_GRAPHICS_API PipelinePassList : public Resource { public: + using Params = PipelinePassListParams; + PipelinePassList() = default; PipelinePassList(const PipelinePassList&) = delete; PipelinePassList(PipelinePassList&&) = delete; @@ -50,6 +64,10 @@ namespace Nz PipelinePassList& operator=(const PipelinePassList&) = delete; PipelinePassList& operator=(PipelinePassList&&) = delete; + static std::shared_ptr LoadFromFile(const std::filesystem::path& filePath, const PipelinePassListParams& params = PipelinePassListParams()); + static std::shared_ptr LoadFromMemory(const void* data, std::size_t size, const PipelinePassListParams& params = PipelinePassListParams()); + static std::shared_ptr LoadFromStream(Stream& stream, const PipelinePassListParams& params = PipelinePassListParams()); + static constexpr std::size_t MaxPassAttachment = 8; private: diff --git a/include/Nazara/Utility/PixelFormat.hpp b/include/Nazara/Utility/PixelFormat.hpp index 4f56257ec..010663f2b 100644 --- a/include/Nazara/Utility/PixelFormat.hpp +++ b/include/Nazara/Utility/PixelFormat.hpp @@ -76,7 +76,8 @@ namespace Nz static inline bool HasAlpha(PixelFormat format); - static PixelFormat IdentifyFormat(const PixelFormatDescription& info); + static inline PixelFormat IdentifyFormat(const PixelFormatDescription& info); + static inline PixelFormat IdentifyFormat(std::string_view formatName); static inline bool IsCompressed(PixelFormat format); static inline bool IsConversionSupported(PixelFormat srcFormat, PixelFormat dstFormat); diff --git a/include/Nazara/Utility/PixelFormat.inl b/include/Nazara/Utility/PixelFormat.inl index a52c04097..3e6562580 100644 --- a/include/Nazara/Utility/PixelFormat.inl +++ b/include/Nazara/Utility/PixelFormat.inl @@ -243,6 +243,30 @@ namespace Nz return s_pixelFormatInfos[format].alphaMask.TestAny(); } + inline PixelFormat PixelFormatInfo::IdentifyFormat(const PixelFormatDescription& info) + { + for (auto&& [format, formatDesc] : s_pixelFormatInfos.iter_kv()) + { + if (info.bitsPerPixel == formatDesc.bitsPerPixel && info.content == formatDesc.content && + info.redMask == formatDesc.redMask && info.greenMask == formatDesc.greenMask && info.blueMask == formatDesc.blueMask && info.alphaMask == formatDesc.alphaMask && + info.redType == formatDesc.redType && info.greenType == formatDesc.greenType && info.blueType == formatDesc.blueType && info.alphaType == formatDesc.alphaType) + return format; + } + + return PixelFormat::Undefined; + } + + inline PixelFormat PixelFormatInfo::IdentifyFormat(std::string_view formatName) + { + for (auto&& [format, formatDesc] : s_pixelFormatInfos.iter_kv()) + { + if (formatDesc.name == formatName) + return format; + } + + return PixelFormat::Undefined; + } + inline bool PixelFormatInfo::IsCompressed(PixelFormat format) { return s_pixelFormatInfos[format].IsCompressed(); diff --git a/src/Nazara/Core/Stream.cpp b/src/Nazara/Core/Stream.cpp index 3520a0e18..03adb5836 100644 --- a/src/Nazara/Core/Stream.cpp +++ b/src/Nazara/Core/Stream.cpp @@ -135,6 +135,7 @@ namespace Nz * * If lineSize does not equal zero, it represents the maximum character count to be read from the stream. * + * \param line Line object to use to store characters * \param lineSize Maximum number of characters to read, or zero for no limit * * \return Line read from file @@ -142,9 +143,8 @@ namespace Nz * \remark With the text stream option, "\r\n" is treated as "\n" * \remark The line separator character is not returned as part of the string */ - std::string Stream::ReadLine(unsigned int lineSize) + void Stream::ReadLine(std::string& line, unsigned int lineSize) { - std::string line; if (lineSize == 0) // Maximal size undefined { const unsigned int bufferSize = 64; @@ -192,8 +192,9 @@ namespace Nz } else { - line.resize(lineSize, '\0'); - std::size_t readSize = Read(&line[0], lineSize); + std::size_t offset = line.size(); + line.resize(offset + lineSize, '\0'); + std::size_t readSize = Read(&line[offset], lineSize); std::size_t pos = line.find('\n'); if (pos <= readSize) // False only if the character is not available (npos being the biggest integer) { @@ -206,10 +207,8 @@ namespace Nz NazaraWarning("Failed to reset cursos pos"); } else - line.resize(readSize); + line.resize(offset + readSize); } - - return line; } bool Stream::SetCursorPos(UInt64 offset) diff --git a/src/Nazara/Graphics/DepthPipelinePass.cpp b/src/Nazara/Graphics/DepthPipelinePass.cpp index 84a457b6e..07dcf0d5a 100644 --- a/src/Nazara/Graphics/DepthPipelinePass.cpp +++ b/src/Nazara/Graphics/DepthPipelinePass.cpp @@ -179,7 +179,7 @@ namespace Nz // TODO: Log error if key is present but not of the right Result passResult = parameters.GetStringViewParameter("MatPass"); - if (passIndexResult.IsOk()) + if (passResult.IsOk()) { auto& materialPassRegistry = Graphics::Instance()->GetMaterialPassRegistry(); diff --git a/src/Nazara/Graphics/Formats/PipelinePassListLoader.cpp b/src/Nazara/Graphics/Formats/PipelinePassListLoader.cpp new file mode 100644 index 000000000..8ab334d42 --- /dev/null +++ b/src/Nazara/Graphics/Formats/PipelinePassListLoader.cpp @@ -0,0 +1,548 @@ +// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include + +namespace Nz::Loaders +{ + namespace NAZARA_ANONYMOUS_NAMESPACE + { + class PassListLoader + { + public: + PassListLoader(Stream& stream, const PipelinePassListParams& /*parameters*/) : + m_stream(stream) + { + } + + Result, ResourceLoadingError> Parse() + { + auto result = ExpectKeyword("passlist"); + if (!result) + return Err(std::move(result).GetError()); //< FIXME: why rvalue is needed + + Result passListName = GetString(); + if (!passListName) + return Err(std::move(passListName).GetError()); //< FIXME: why rvalue is needed + + m_current.emplace(); + m_current->passList = std::make_shared(); + result = Block([this]() -> Result + { + Result kw = GetKeyword(); + if (!kw) + return Err(std::move(kw).GetError()); //< FIXME: why rvalue is needed + + if (kw.GetValue() == "attachment") + { + auto result = HandleAttachment(); + if (!result) + return Err(std::move(result).GetError()); //< FIXME: why rvalue is needed + } + else if (kw.GetValue() == "pass") + { + auto result = HandlePass(); + if (!result) + return Err(std::move(result).GetError()); //< FIXME: why rvalue is needed + } + else if (kw.GetValue() == "output") + { + Result attachmentName = GetString(); + if (!attachmentName) + return Err(std::move(attachmentName).GetError()); //< FIXME: why rvalue is needed + + auto it = m_current->attachmentsByName.find(attachmentName.GetValue()); + if (it == m_current->attachmentsByName.end()) + { + NazaraErrorFmt("unknown attachment {}", attachmentName.GetValue()); + return Err(ResourceLoadingError::DecodingError); + } + + m_current->passList->SetFinalOutput(it->second); + } + else + { + NazaraErrorFmt("unexpected keyword {}", kw.GetValue()); + return Err(ResourceLoadingError::DecodingError); + } + + return Ok(); + }); + if (!result) + return Err(std::move(result).GetError()); //< FIXME: why rvalue is needed + + return Ok(std::move(m_current->passList)); + } + + private: + Result Block(const FunctionRef()>& callback) + { + auto beginBlock = GetKeyword(); + if (!beginBlock) + return Err(std::move(beginBlock).GetError()); //< FIXME: why rvalue is needed + + if (beginBlock.GetValue() != "{") + { + NazaraErrorFmt("expected \"{{\" token, got {}", beginBlock.GetValue()); + return Err(ResourceLoadingError::DecodingError); + } + + for (;;) + { + auto nextKw = GetKeyword(true); + if (!nextKw) + return Err(std::move(nextKw).GetError()); //< FIXME: why rvalue is needed + + if (nextKw.GetValue() == "}") + break; + + auto result = callback(); + if (!result) + return Err(std::move(result).GetError()); //< FIXME: why rvalue is needed + } + + auto endBlock = GetKeyword(); + if (!endBlock) + return Err(std::move(endBlock).GetError()); //< FIXME: why rvalue is needed + + if (endBlock.GetValue() != "}") + { + NazaraErrorFmt("expected \"}}\" token, got {}", endBlock.GetValue()); + return Err(ResourceLoadingError::DecodingError); + } + + return Ok(); + } + + Result ExpectKeyword(std::string_view expectedKeyword) + { + Result passListKw = GetKeyword(); + if (!passListKw) + return Err(std::move(passListKw).GetError()); //< FIXME: why rvalue is needed + + if (passListKw.GetValue() != expectedKeyword) + { + NazaraErrorFmt("expected \"{}\" keyword, got {}", expectedKeyword, passListKw.GetValue()); + return Err(ResourceLoadingError::DecodingError); + } + + return Ok(); + } + + Result HandleAttachment() + { + Result attachmentName = GetString(); + if (!attachmentName) + return Err(std::move(attachmentName).GetError()); //< FIXME: why rvalue is needed + + std::string format; + auto result = Block([&]() -> Result + { + Result kw = GetKeyword(); + if (!kw) + return Err(std::move(kw).GetError()); //< FIXME: why rvalue is needed + + if (kw.GetValue() == "format") + { + Result formatStr = GetString(); + if (!formatStr) + return Err(std::move(formatStr).GetError()); //< FIXME: why rvalue is needed + + format = std::move(formatStr).GetValue(); + } + else + { + NazaraErrorFmt("unexpected keyword {}", kw.GetValue()); + return Err(ResourceLoadingError::DecodingError); + } + + return Ok(); + }); + if (!result) + return Err(std::move(result).GetError()); //< FIXME: why rvalue is needed + + if (format.empty()) + { + NazaraErrorFmt("missing mandatory format in attachment {}", attachmentName.GetValue()); + return Err(ResourceLoadingError::DecodingError); + } + + PixelFormat attachmentFormat = PixelFormat::Undefined; + if (format == "PreferredDepth") + attachmentFormat = Graphics::Instance()->GetPreferredDepthFormat(); + else if (format == "PreferredDepthStencil") + attachmentFormat = Graphics::Instance()->GetPreferredDepthStencilFormat(); + else + attachmentFormat = PixelFormatInfo::IdentifyFormat(format); + + if (attachmentFormat == PixelFormat::Undefined) + { + NazaraErrorFmt("unknown format {}", format); + return Err(ResourceLoadingError::DecodingError); + } + + if (m_current->attachmentsByName.find(attachmentName.GetValue()) != m_current->attachmentsByName.end()) + { + NazaraErrorFmt("attachment {} already exists", attachmentName.GetValue()); + return Err(ResourceLoadingError::DecodingError); + } + + std::size_t attachmentId = m_current->passList->AddAttachment({ + attachmentName.GetValue(), + attachmentFormat + }); + + m_current->attachmentsByName.emplace(attachmentName.GetValue(), attachmentId); + + return Ok(); + } + + Result HandlePass() + { + Result passName = GetString(); + if (!passName) + return Err(std::move(passName).GetError()); //< FIXME: why rvalue is needed + + struct InputOutput + { + std::string name; + std::string attachmentName; + }; + + ParameterList implConfig; + std::string impl; + std::string depthstencilInput; + std::string depthstencilOutput; + std::vector inputs; + std::vector outputs; + std::vector flags; + + auto result = Block([&]() -> Result + { + Result kw = GetKeyword(); + if (!kw) + return Err(std::move(kw).GetError()); //< FIXME: why rvalue is needed + + if (kw.GetValue() == "impl") + { + Result implStr = GetString(); + if (!implStr) + return Err(std::move(implStr).GetError()); //< FIXME: why rvalue is needed + + impl = std::move(implStr).GetValue(); + + auto nextKw = GetKeyword(true); + if (!nextKw) + return Err(std::move(nextKw).GetError()); //< FIXME: why rvalue is needed + + if (nextKw.GetValue() == "{") + { + Block([&]() -> Result + { + Result key = GetKeyword(); + if (!key) + return Err(std::move(key).GetError()); //< FIXME: why rvalue is needed + + Result value = GetString(); + if (!value) + return Err(std::move(value).GetError()); //< FIXME: why rvalue is needed + + implConfig.SetParameter(key.GetValue(), value.GetValue()); + return Ok(); + }); + } + } + else if (kw.GetValue() == "depthstencilinput") + { + Result attachmentStr = GetString(); + if (!attachmentStr) + return Err(std::move(attachmentStr).GetError()); //< FIXME: why rvalue is needed + + depthstencilInput = std::move(attachmentStr).GetValue(); + } + else if (kw.GetValue() == "depthstenciloutput") + { + Result attachmentStr = GetString(); + if (!attachmentStr) + return Err(std::move(attachmentStr).GetError()); //< FIXME: why rvalue is needed + + depthstencilOutput = std::move(attachmentStr).GetValue(); + } + else if (kw.GetValue() == "flag") + { + Result str = GetString(); + if (!str) + return Err(std::move(str).GetError()); //< FIXME: why rvalue is needed + + flags.push_back(std::move(str).GetValue()); + } + else if (kw.GetValue() == "input") + { + Result name = GetString(); + if (!name) + return Err(std::move(name).GetError()); //< FIXME: why rvalue is needed + + Result attachment = GetString(); + if (!attachment) + return Err(std::move(attachment).GetError()); //< FIXME: why rvalue is needed + + inputs.push_back({ + std::move(name).GetValue(), + std::move(attachment).GetValue(), + }); + } + else if (kw.GetValue() == "output") + { + Result name = GetString(); + if (!name) + return Err(std::move(name).GetError()); //< FIXME: why rvalue is needed + + Result attachment = GetString(); + if (!attachment) + return Err(std::move(attachment).GetError()); //< FIXME: why rvalue is needed + + outputs.push_back({ + std::move(name).GetValue(), + std::move(attachment).GetValue(), + }); + } + else + { + NazaraErrorFmt("unexpected keyword {}", kw.GetValue()); + return Err(ResourceLoadingError::DecodingError); + } + + return Ok(); + }); + if (!result) + return Err(std::move(result).GetError()); //< FIXME: why rvalue is needed + + FramePipelinePassRegistry& passRegistry = Graphics::Instance()->GetFramePipelinePassRegistry(); + + std::size_t implIndex = passRegistry.GetPassIndex(impl); + if (implIndex == FramePipelinePassRegistry::InvalidIndex) + { + NazaraErrorFmt("unknown pass {}", impl); + return Err(ResourceLoadingError::DecodingError); + } + + std::size_t passId = m_current->passList->AddPass(passName.GetValue(), implIndex, std::move(implConfig)); + + for (auto&& [inputName, attachmentName] : inputs) + { + std::size_t inputIndex = passRegistry.GetPassInputIndex(implIndex, inputName); + if (inputIndex == FramePipelinePassRegistry::InvalidIndex) + { + NazaraErrorFmt("pass {} has no input {}", impl, inputName); + return Err(ResourceLoadingError::DecodingError); + } + + auto it = m_current->attachmentsByName.find(attachmentName); + if (it == m_current->attachmentsByName.end()) + { + NazaraErrorFmt("unknown attachment {}", attachmentName); + return Err(ResourceLoadingError::DecodingError); + } + + m_current->passList->SetPassInput(passId, inputIndex, it->second); + } + + for (auto&& [outputName, attachmentName] : outputs) + { + std::size_t inputIndex = passRegistry.GetPassOutputIndex(implIndex, outputName); + if (inputIndex == FramePipelinePassRegistry::InvalidIndex) + { + NazaraErrorFmt("pass {} has no output {}", impl, outputName); + return Err(ResourceLoadingError::DecodingError); + } + + auto it = m_current->attachmentsByName.find(attachmentName); + if (it == m_current->attachmentsByName.end()) + { + NazaraErrorFmt("unknown attachment {}", attachmentName); + return Err(ResourceLoadingError::DecodingError); + } + + m_current->passList->SetPassOutput(passId, inputIndex, it->second); + } + + if (!depthstencilInput.empty()) + { + auto it = m_current->attachmentsByName.find(depthstencilInput); + if (it == m_current->attachmentsByName.end()) + { + NazaraErrorFmt("unknown attachment {}", depthstencilInput); + return Err(ResourceLoadingError::DecodingError); + } + + m_current->passList->SetPassDepthStencilInput(passId, it->second); + } + + if (!depthstencilOutput.empty()) + { + auto it = m_current->attachmentsByName.find(depthstencilOutput); + if (it == m_current->attachmentsByName.end()) + { + NazaraErrorFmt("unknown attachment {}", depthstencilOutput); + return Err(ResourceLoadingError::DecodingError); + } + + m_current->passList->SetPassDepthStencilOutput(passId, it->second); + } + + for (const auto& flagStr : flags) + { + if (flagStr == "LightShadowing") + m_current->passList->EnablePassFlags(passId, FramePipelinePassFlag::LightShadowing); + else + { + NazaraErrorFmt("unknown pass flag {}", flagStr); + return Err(ResourceLoadingError::DecodingError); + } + } + + return Ok(); + } + + Result GetKeyword(bool peek = false) + { + std::size_t beginOffset; + do + { + auto result = EnsureLine(); + if (result.IsErr()) + return Err(std::move(result).GetError()); //< FIXME: why rvalue is needed + + beginOffset = m_currentLine.find_first_not_of(" \r\t\n"); + } + while (beginOffset == m_currentLine.npos); + + if (m_currentLine[beginOffset] == '"') + { + NazaraError("expected a keyword, got a string"); + return Err(ResourceLoadingError::DecodingError); + } + + std::size_t endOffset = m_currentLine.find_first_of(" \r\t\n", beginOffset + 1); + if (endOffset == m_currentLine.npos) + endOffset = m_currentLine.size(); + + std::string currentToken = std::string(m_currentLine.substr(beginOffset, endOffset - beginOffset)); + if (!peek) + m_currentLine.erase(m_currentLine.begin(), m_currentLine.begin() + endOffset); + + return currentToken; + } + + + Result GetString() + { + std::size_t beginOffset; + do + { + auto result = EnsureLine(); + if (result.IsErr()) + return Err(std::move(result).GetError()); //< FIXME: why rvalue is needed + + beginOffset = m_currentLine.find_first_not_of(" \r\t\n"); + } + while (beginOffset == m_currentLine.npos); + + if (m_currentLine[beginOffset] != '"') + { + NazaraError("expected a string, got a keyword"); + return Err(ResourceLoadingError::DecodingError); + } + + std::string str; + for (std::size_t i = beginOffset + 1; i < m_currentLine.size(); ++i) + { + switch (m_currentLine[i]) + { + case '\0': + case '\n': + case '\r': + NazaraError("unfinished string"); + return Err(ResourceLoadingError::DecodingError); + + case '"': + { + m_currentLine.erase(m_currentLine.begin(), m_currentLine.begin() + beginOffset + i); + + return str; + } + + case '\\': + { + i++; + char character; + switch (m_currentLine[i]) + { + case 'n': character = '\n'; break; + case 'r': character = '\r'; break; + case 't': character = '\t'; break; + case '"': character = '"'; break; + case '\\': character = '\\'; break; + default: + NazaraErrorFmt("unrecognized character {}", character); + return Err(ResourceLoadingError::DecodingError); + } + + str.push_back(character); + break; + } + + default: + str.push_back(m_currentLine[i]); + } + } + + NazaraError("unfinished string"); + return Err(ResourceLoadingError::DecodingError); + } + + Result EnsureLine() + { + while (m_currentLine.find_first_not_of(" \r\t\n") == m_currentLine.npos) + { + m_currentLine.clear(); + m_stream.ReadLine(m_currentLine); + if (m_currentLine.empty()) + return Err(ResourceLoadingError::DecodingError); + } + + return Ok(); + } + + struct CurrentPassList + { + std::shared_ptr passList; + std::unordered_map attachmentsByName; + }; + + std::optional m_current; + std::string m_currentLine; + + Stream& m_stream; + }; + } + + PipelinePassListLoader::Entry GetPipelinePassListLoader() + { + NAZARA_USE_ANONYMOUS_NAMESPACE + + PipelinePassListLoader::Entry entry; + entry.extensionSupport = [](std::string_view ext) { return ext == ".passlist"; }; + entry.streamLoader = [](Stream& stream, const PipelinePassListParams& parameters) + { + PassListLoader passListLoader(stream, parameters); + return passListLoader.Parse(); + }; + + return entry; + } +} diff --git a/src/Nazara/Graphics/Formats/PipelinePassListLoader.hpp b/src/Nazara/Graphics/Formats/PipelinePassListLoader.hpp new file mode 100644 index 000000000..889e15828 --- /dev/null +++ b/src/Nazara/Graphics/Formats/PipelinePassListLoader.hpp @@ -0,0 +1,18 @@ +// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Graphics module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_GRAPHICS_FORMATS_PIPELINEPASSLISTLOADER_HPP +#define NAZARA_GRAPHICS_FORMATS_PIPELINEPASSLISTLOADER_HPP + +#include +#include + +namespace Nz::Loaders +{ + PipelinePassListLoader::Entry GetPipelinePassListLoader(); +} + +#endif // NAZARA_GRAPHICS_FORMATS_PIPELINEPASSLISTLOADER_HPP diff --git a/src/Nazara/Graphics/Graphics.cpp b/src/Nazara/Graphics/Graphics.cpp index 8f0f8b231..980bba5af 100644 --- a/src/Nazara/Graphics/Graphics.cpp +++ b/src/Nazara/Graphics/Graphics.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -158,6 +159,7 @@ namespace Nz Font::SetDefaultAtlas(std::make_shared(*m_renderDevice)); m_materialInstanceLoader.RegisterLoader(Loaders::GetMaterialInstanceLoader_Texture()); // texture to material loader + m_pipelinePassListLoader.RegisterLoader(Loaders::GetPipelinePassListLoader()); // texture to material loader } Graphics::~Graphics() @@ -466,10 +468,11 @@ namespace Nz void Graphics::RegisterPipelinePasses() { - m_pipelinePassRegistry.RegisterPass("Depth"); - m_pipelinePassRegistry.RegisterPass("Forward"); - m_pipelinePassRegistry.RegisterPass("PostProcess"); + m_pipelinePassRegistry.RegisterPass("Depth", {}, {}); + m_pipelinePassRegistry.RegisterPass("Forward", {}, { "Output" }); + m_pipelinePassRegistry.RegisterPass("PostProcess", { "Input" }, { "Output" }); } + void Graphics::RegisterShaderModules() { m_shaderModuleResolver = std::make_shared(); diff --git a/src/Nazara/Graphics/PipelinePassList.cpp b/src/Nazara/Graphics/PipelinePassList.cpp index 53277d7b3..9f6208157 100644 --- a/src/Nazara/Graphics/PipelinePassList.cpp +++ b/src/Nazara/Graphics/PipelinePassList.cpp @@ -11,6 +11,11 @@ namespace Nz { + bool PipelinePassListParams::IsValid() const + { + return true; + } + std::vector> PipelinePassList::BuildPasses(FramePipelinePass::PassData& passData) const { auto& passRegistry = Graphics::Instance()->GetFramePipelinePassRegistry(); @@ -66,4 +71,28 @@ namespace Nz return GetAttachmentIndex(m_finalOutputAttachment); } + + std::shared_ptr PipelinePassList::LoadFromFile(const std::filesystem::path& filePath, const PipelinePassListParams& params) + { + Graphics* graphics = Graphics::Instance(); + NazaraAssert(graphics, "Graphics module has not been initialized"); + + return graphics->GetPipelinePassListLoader().LoadFromFile(filePath, params); + } + + std::shared_ptr PipelinePassList::LoadFromMemory(const void* data, std::size_t size, const PipelinePassListParams& params) + { + Graphics* graphics = Graphics::Instance(); + NazaraAssert(graphics, "Graphics module has not been initialized"); + + return graphics->GetPipelinePassListLoader().LoadFromMemory(data, size, params); + } + + std::shared_ptr PipelinePassList::LoadFromStream(Stream& stream, const PipelinePassListParams& params) + { + Graphics* graphics = Graphics::Instance(); + NazaraAssert(graphics, "Graphics module has not been initialized"); + + return graphics->GetPipelinePassListLoader().LoadFromStream(stream, params); + } } diff --git a/src/Nazara/Utility/PixelFormat.cpp b/src/Nazara/Utility/PixelFormat.cpp index 2458576b7..3fa8ab26e 100644 --- a/src/Nazara/Utility/PixelFormat.cpp +++ b/src/Nazara/Utility/PixelFormat.cpp @@ -1467,19 +1467,6 @@ namespace Nz return true; } - PixelFormat PixelFormatInfo::IdentifyFormat(const PixelFormatDescription& info) - { - for (auto&& [format, formatDesc] : s_pixelFormatInfos.iter_kv()) - { - if (info.bitsPerPixel == formatDesc.bitsPerPixel && info.content == formatDesc.content && - info.redMask == formatDesc.redMask && info.greenMask == formatDesc.greenMask && info.blueMask == formatDesc.blueMask && info.alphaMask == formatDesc.alphaMask && - info.redType == formatDesc.redType && info.greenType == formatDesc.greenType && info.blueType == formatDesc.blueType && info.alphaType == formatDesc.alphaType) - return format; - } - - return PixelFormat::Undefined; - } - bool PixelFormatInfo::Initialize() { NAZARA_USE_ANONYMOUS_NAMESPACE