diff --git a/UnleashedRecomp/CMakeLists.txt b/UnleashedRecomp/CMakeLists.txt index d3fd5a9..7551299 100644 --- a/UnleashedRecomp/CMakeLists.txt +++ b/UnleashedRecomp/CMakeLists.txt @@ -79,6 +79,8 @@ set(SWA_UI_CXX_SOURCES "ui/window.cpp" ) +set(SMOLV_SOURCE_DIR "${SWA_THIRDPARTY_ROOT}/ShaderRecomp/thirdparty/smol-v/source") + set(SWA_CXX_SOURCES "app.cpp" "main.cpp" @@ -93,6 +95,8 @@ set(SWA_CXX_SOURCES ${SWA_HID_CXX_SOURCES} ${SWA_PATCHES_CXX_SOURCES} ${SWA_UI_CXX_SOURCES} + + "${SMOLV_SOURCE_DIR}/smolv.cpp" ) if (WIN32) @@ -122,6 +126,7 @@ find_package(zstd CONFIG REQUIRED) find_package(Stb REQUIRED) find_package(unofficial-concurrentqueue REQUIRED) find_package(imgui CONFIG REQUIRED) +find_package(magic_enum CONFIG REQUIRED) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/D3D12) add_custom_command(TARGET UnleashedRecomp POST_BUILD @@ -130,10 +135,13 @@ add_custom_command(TARGET UnleashedRecomp POST_BUILD COMMAND_EXPAND_LISTS ) +file(COPY ${PACKAGE_PREFIX_DIR}/bin/dxil.dll DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) + target_link_libraries(UnleashedRecomp PRIVATE Microsoft::DirectX-Headers Microsoft::DirectX-Guids Microsoft::DirectX12-Agility + Microsoft::DirectXShaderCompiler comctl32 dxgi Vulkan::Headers @@ -154,6 +162,7 @@ target_link_libraries(UnleashedRecomp PRIVATE unofficial::concurrentqueue::concurrentqueue Synchronization imgui::imgui + magic_enum::magic_enum ) target_include_directories(UnleashedRecomp PRIVATE @@ -161,6 +170,7 @@ target_include_directories(UnleashedRecomp PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/api ${SWA_THIRDPARTY_ROOT}/ddspp ${Stb_INCLUDE_DIR} + ${SMOLV_SOURCE_DIR} ) target_precompile_headers(UnleashedRecomp PUBLIC ${SWA_PRECOMPILED_HEADERS}) diff --git a/UnleashedRecomp/api/Hedgehog/Base/Container/hhMap.h b/UnleashedRecomp/api/Hedgehog/Base/Container/hhMap.h new file mode 100644 index 0000000..a38ba35 --- /dev/null +++ b/UnleashedRecomp/api/Hedgehog/Base/Container/hhMap.h @@ -0,0 +1,378 @@ +#pragma once + +#include + +namespace hh +{ + template< + class Key, + class T, + class Compare = std::less, + class Allocator = Hedgehog::Base::TAllocator>> + class map + { + protected: + enum EColor + { + eColor_Red, + eColor_Black + }; + + struct SNode + { + using allocator_type = typename std::allocator_traits::template rebind_alloc; + + xpointer pLeft; + xpointer pParent; + xpointer pRight; + std::pair Value; + uint8_t Color; + bool IsNil; + }; + + Compare m_Comp; + typename SNode::allocator_type m_Alloc; + xpointer m_pHead; + be m_Count; + + struct SFindResult + { + SNode* pParent; + bool IsRight; + SNode* pBound; + }; + + bool LowerBoundDuplicate(const SNode* pBound, const Key& in_rKey) const + { + return !pBound->IsNil && !m_Comp(in_rKey, pBound->Value.first); + } + + SFindResult FindLowerBound(const Key& in_rKey) const + { + SFindResult result{ m_pHead->pParent, true, m_pHead }; + SNode* pNode = result.pParent; + + while (!pNode->IsNil) + { + result.pParent = pNode; + if (m_Comp(pNode->Value.first, in_rKey)) + { + result.IsRight = true; + pNode = pNode->pRight; + } + else + { + result.IsRight = false; + result.pBound = pNode; + pNode = pNode->pLeft; + } + } + + return result; + } + + SNode* Find(const Key& in_rKey) const + { + const SFindResult result = FindLowerBound(in_rKey); + return LowerBoundDuplicate(result.pBound, in_rKey) ? result.pBound : m_pHead.get(); + } + + static SNode* Max(SNode* pNode) + { + while (!pNode->pRight->IsNil) + pNode = pNode->pRight; + + return pNode; + } + + static SNode* Min(SNode* pNode) + { + while (!pNode->pLeft->IsNil) + pNode = pNode->pLeft; + + return pNode; + } + + public: + using key_type = Key; + using mapped_type = T; + using value_type = std::pair; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using key_compare = Compare; + using allocator_type = Allocator; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = typename std::allocator_traits::pointer; + using const_pointer = typename std::allocator_traits::const_pointer; + + class iterator + { + public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = std::pair; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + + iterator(const std::nullptr_t&) = delete; + iterator(SNode* pNode) : m_pNode(pNode) {} + + reference operator*() const { return m_pNode->Value; } + pointer operator->() const { return &m_pNode->Value; } + + iterator& operator++() + { + if (m_pNode->pRight->IsNil) + { + SNode* pNode; + while (!(pNode = m_pNode->pParent)->IsNil && m_pNode == pNode->pRight) + m_pNode = pNode; + + m_pNode = pNode; + } + else + { + m_pNode = map::Min(m_pNode->pRight); + } + + return *this; + } + + iterator operator++(int) + { + iterator temp(*this); + ++(*this); + return temp; + } + + iterator& operator--() + { + if (m_pNode->IsNil) + { + m_pNode = m_pNode->pRight; + } + else if (m_pNode->pLeft->IsNil) + { + SNode* pNode; + while (!(pNode = m_pNode->pParent)->IsNil && m_pNode == pNode->pLeft) + m_pNode = pNode; + + if (!m_pNode->IsNil) + m_pNode = pNode; + } + else + { + m_pNode = map::Max(m_pNode->pLeft); + } + + return *this; + } + + iterator operator--(int) + { + iterator temp(*this); + --(*this); + return temp; + } + + bool operator==(const iterator& rhs) const { return m_pNode == rhs.m_pNode; } + bool operator!=(const iterator& rhs) const { return !(*this == rhs); } + + private: + SNode* m_pNode; + friend class iterator; + friend class map; + }; + + class const_iterator + { + public: + using iterator_category = std::bidirectional_iterator_tag; + using value_type = std::pair; + using difference_type = std::ptrdiff_t; + using pointer = const value_type*; + using reference = const value_type&; + + const_iterator(const std::nullptr_t&) = delete; + const_iterator(SNode* pNode) : m_pNode(pNode) {} + const_iterator(const iterator& iterator) : m_pNode(iterator.m_pNode) {} + + reference operator*() const { return m_pNode->Value; } + pointer operator->() const { return &m_pNode->Value; } + + const_iterator& operator++() + { + if (m_pNode->pRight->IsNil) + { + SNode* pNode; + while (!(pNode = m_pNode->pParent)->IsNil && m_pNode == pNode->pRight) + m_pNode = pNode; + + m_pNode = pNode; + } + else + { + m_pNode = map::Min(m_pNode->pRight); + } + + return *this; + } + + const_iterator operator++(int) + { + const_iterator temp(*this); + ++(*this); + return temp; + } + + const_iterator& operator--() + { + if (m_pNode->IsNil) + { + m_pNode = m_pNode->pRight; + } + else if (m_pNode->pLeft->IsNil) + { + SNode* pNode; + while (!(pNode = m_pNode->pParent)->IsNil && m_pNode == pNode->pLeft) + m_pNode = pNode; + + if (!m_pNode->IsNil) + m_pNode = pNode; + } + else + { + m_pNode = map::Max(m_pNode->pLeft); + } + + return *this; + } + + const_iterator operator--(int) + { + const_iterator temp(*this); + --(*this); + return temp; + } + + bool operator==(const const_iterator& rhs) const { return m_pNode == rhs.m_pNode; } + bool operator!=(const const_iterator& rhs) const { return !(*this == rhs); } + + private: + SNode* m_pNode; + friend class map; + }; + + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + public: + allocator_type get_allocator() const + { + return m_Alloc; + } + + T& at(const Key& key) + { + return Find(key)->Value.second; + } + + const T& at(const Key& key) const + { + return Find(key)->Value.second; + } + + iterator begin() + { + return iterator(m_pHead->pLeft); + } + + const_iterator begin() const + { + return const_iterator(m_pHead->pLeft); + } + + const_iterator cbegin() const + { + return const_iterator(m_pHead->pLeft); + } + + iterator end() + { + return iterator(m_pHead); + } + + const_iterator end() const + { + return const_iterator(m_pHead); + } + + const_iterator cend() const + { + return const_iterator(m_pHead); + } + + reverse_iterator rbegin() + { + return reverse_iterator(end()); + } + + const_reverse_iterator rbegin() const + { + return const_reverse_iterator(end()); + } + + const_reverse_iterator crbegin() const + { + return const_reverse_iterator(cend()); + } + + reverse_iterator rend() + { + return reverse_iterator(begin()); + } + + const_reverse_iterator rend() const + { + return const_reverse_iterator(begin()); + } + + const_reverse_iterator crend() const + { + return const_reverse_iterator(cbegin()); + } + + bool empty() const + { + return m_Count == 0; + } + + size_type size() const + { + return m_Count; + } + + size_type max_size() const + { + return ~0u; + } + + size_type count(const Key& key) const + { + return LowerBoundDuplicate(FindLowerBound(key).pBound, key) ? 1u : 0u; + } + + iterator find(const Key& key) + { + return iterator(Find(key)); + } + + const_iterator find(const Key& key) const + { + return const_iterator(Find(key)); + } + }; + + static_assert(sizeof(map) == 0xC); +} diff --git a/UnleashedRecomp/api/Hedgehog/Base/Container/hhVector.h b/UnleashedRecomp/api/Hedgehog/Base/Container/hhVector.h new file mode 100644 index 0000000..42f3b95 --- /dev/null +++ b/UnleashedRecomp/api/Hedgehog/Base/Container/hhVector.h @@ -0,0 +1,255 @@ +#pragma once + +#include + +namespace hh +{ + template> + class vector + { + protected: + Allocator m_Alloc; + xpointer m_pFirst; + xpointer m_pLast; + xpointer m_pEnd; + + public: + using value_type = T; + using allocator_type = Allocator; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = typename std::allocator_traits::pointer; + using const_pointer = typename std::allocator_traits::const_pointer; + + class iterator + { + public: + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = T*; + using reference = T&; + + iterator() : m_pPtr(nullptr) {} + iterator(T* p) : m_pPtr(p) {} + + reference operator*() const { return *m_pPtr; } + pointer operator->() const { return m_pPtr; } + + iterator& operator++() { ++m_pPtr; return *this; } + iterator operator++(int) { iterator tmp = *this; ++(*this); return tmp; } + + iterator& operator--() { --m_pPtr; return *this; } + iterator operator--(int) { iterator tmp = *this; --(*this); return tmp; } + + iterator& operator+=(difference_type n) { m_pPtr += n; return *this; } + iterator operator+(difference_type n) const { iterator tmp = *this; return tmp += n; } + friend iterator operator+(difference_type n, iterator it) { return it + n; } + + iterator& operator-=(difference_type n) { return *this += -n; } + iterator operator-(difference_type n) const { iterator tmp = *this; return tmp -= n; } + difference_type operator-(const iterator& other) const { return m_pPtr - other.m_pPtr; } + + reference operator[](difference_type n) const { return *(*this + n); } + + bool operator==(const iterator& other) const { return m_pPtr == other.m_pPtr; } + bool operator!=(const iterator& other) const { return !(*this == other); } + bool operator<(const iterator& other) const { return m_pPtr < other.m_pPtr; } + bool operator<=(const iterator& other) const { return !(other < *this); } + bool operator>(const iterator& other) const { return other < *this; } + bool operator>=(const iterator& other) const { return !(*this < other); } + + private: + T* m_pPtr; + + friend class vector; + friend class const_iterator; + }; + + class const_iterator + { + public: + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = const T*; + using reference = const T&; + + const_iterator() : m_pPtr(nullptr) {} + const_iterator(T* p) : m_pPtr(p) {} + const_iterator(const iterator& other) : m_pPtr(other.m_pPtr) {} + + reference operator*() const { return *m_pPtr; } + pointer operator->() const { return m_pPtr; } + + const_iterator& operator++() { ++m_pPtr; return *this; } + const_iterator operator++(int) { const_iterator tmp = *this; ++(*this); return tmp; } + + const_iterator& operator--() { --m_pPtr; return *this; } + const_iterator operator--(int) { const_iterator tmp = *this; --(*this); return tmp; } + + const_iterator& operator+=(difference_type n) { m_pPtr += n; return *this; } + const_iterator operator+(difference_type n) const { const_iterator tmp = *this; return tmp += n; } + friend const_iterator operator+(difference_type n, const_iterator it) { return it + n; } + + const_iterator& operator-=(difference_type n) { return *this += -n; } + const_iterator operator-(difference_type n) const { const_iterator tmp = *this; return tmp -= n; } + difference_type operator-(const const_iterator& other) const { return m_pPtr - other.m_pPtr; } + + reference operator[](difference_type n) const { return *(*this + n); } + + bool operator==(const const_iterator& other) const { return m_pPtr == other.m_pPtr; } + bool operator!=(const const_iterator& other) const { return !(*this == other); } + bool operator<(const const_iterator& other) const { return m_pPtr < other.m_pPtr; } + bool operator<=(const const_iterator& other) const { return !(other < *this); } + bool operator>(const const_iterator& other) const { return other < *this; } + bool operator>=(const const_iterator& other) const { return !(*this < other); } + + private: + T* m_pPtr; + friend class vector; + }; + + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + allocator_type get_allocator() const + { + return m_Alloc; + } + + reference at(size_type pos) + { + return m_pFirst[pos]; + } + + const_reference at(size_type pos) const + { + return m_pFirst[pos]; + } + + reference operator[](size_type pos) + { + return m_pFirst[pos]; + } + + const_reference operator[](size_type pos) const + { + return m_pFirst[pos]; + } + + reference front() + { + return m_pFirst[0]; + } + + const_reference front() const + { + return m_pFirst[0]; + } + + reference back() + { + return m_pLast[-1]; + } + + const_reference back() const + { + return m_pLast[-1]; + } + + T* data() + { + return m_pFirst; + } + + const T* data() const + { + return m_pFirst; + } + + iterator begin() + { + return iterator(m_pFirst); + } + + const_iterator begin() const + { + return const_iterator(m_pFirst); + } + + const_iterator cbegin() const + { + return const_iterator(m_pFirst); + } + + iterator end() + { + return iterator(m_pLast); + } + + const_iterator end() const + { + return const_iterator(m_pLast); + } + + const_iterator cend() const + { + return const_iterator(m_pLast); + } + + reverse_iterator rbegin() + { + return reverse_iterator(end()); + } + + const_reverse_iterator rbegin() const + { + return const_reverse_iterator(end()); + } + + const_reverse_iterator crbegin() const + { + return const_reverse_iterator(cend()); + } + + reverse_iterator rend() + { + return reverse_iterator(begin()); + } + + const_reverse_iterator rend() const + { + return const_reverse_iterator(begin()); + } + + const_reverse_iterator crend() const + { + return const_reverse_iterator(cbegin()); + } + + bool empty() const + { + return m_pFirst == m_pLast; + } + + size_type size() const + { + return m_pLast - m_pFirst; + } + + size_type max_size() const + { + return ~0u; + } + + size_type capacity() const + { + return m_pEnd - m_pFirst; + } + }; + + SWA_ASSERT_SIZEOF(vector, 0x10); +} diff --git a/UnleashedRecomp/api/Hedgehog/Base/System/hhSymbol.h b/UnleashedRecomp/api/Hedgehog/Base/System/hhSymbol.h new file mode 100644 index 0000000..2b874e9 --- /dev/null +++ b/UnleashedRecomp/api/Hedgehog/Base/System/hhSymbol.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +namespace Hedgehog::Base +{ + class CSharedString; + + class CStringSymbol + { + public: + be m_Index; + + CStringSymbol(); + CStringSymbol(const char* in_pName); + CStringSymbol(const CSharedString& in_rName); + + bool operator==(const CStringSymbol& in_rOther) const; + bool operator!=(const CStringSymbol& in_rOther) const; + bool operator<(const CStringSymbol& in_rOther) const; + }; + + SWA_ASSERT_OFFSETOF(CStringSymbol, m_Index, 0); + SWA_ASSERT_SIZEOF(CStringSymbol, 4); +} + +#include diff --git a/UnleashedRecomp/api/Hedgehog/Base/System/hhSymbol.inl b/UnleashedRecomp/api/Hedgehog/Base/System/hhSymbol.inl new file mode 100644 index 0000000..b0acc10 --- /dev/null +++ b/UnleashedRecomp/api/Hedgehog/Base/System/hhSymbol.inl @@ -0,0 +1,31 @@ +namespace Hedgehog::Base +{ + inline CStringSymbol::CStringSymbol() + { + } + + inline CStringSymbol::CStringSymbol(const char* in_pName) + { + GuestToHostFunction(sub_82E014D8, this, in_pName); + } + + inline CStringSymbol::CStringSymbol(const CSharedString& in_rName) + { + GuestToHostFunction(sub_82E013B0, this, &in_rName); + } + + inline bool CStringSymbol::operator==(const CStringSymbol& in_rOther) const + { + return m_Index == in_rOther.m_Index; + } + + inline bool CStringSymbol::operator!=(const CStringSymbol& in_rOther) const + { + return m_Index != in_rOther.m_Index; + } + + inline bool CStringSymbol::operator<(const CStringSymbol& in_rOther) const + { + return m_Index < in_rOther.m_Index; + } +} diff --git a/UnleashedRecomp/api/Hedgehog/Base/Type/detail/hhStringHolder.h b/UnleashedRecomp/api/Hedgehog/Base/Type/detail/hhStringHolder.h index 087de60..4fd1cd2 100644 --- a/UnleashedRecomp/api/Hedgehog/Base/Type/detail/hhStringHolder.h +++ b/UnleashedRecomp/api/Hedgehog/Base/Type/detail/hhStringHolder.h @@ -1,6 +1,6 @@ #pragma once -#include "SWA.inl" +#include namespace Hedgehog::Base { @@ -41,4 +41,4 @@ namespace Hedgehog::Base }; } -#include "Hedgehog/Base/Type/detail/hhStringHolder.inl" +#include diff --git a/UnleashedRecomp/api/Hedgehog/Database/System/hhDatabaseData.h b/UnleashedRecomp/api/Hedgehog/Database/System/hhDatabaseData.h index 8552a51..3a7ac2b 100644 --- a/UnleashedRecomp/api/Hedgehog/Database/System/hhDatabaseData.h +++ b/UnleashedRecomp/api/Hedgehog/Database/System/hhDatabaseData.h @@ -1,6 +1,7 @@ #pragma once -#include "Hedgehog/Base/hhObject.h" +#include +#include namespace Hedgehog::Database { diff --git a/UnleashedRecomp/api/Hedgehog/Database/System/hhDatabaseData.inl b/UnleashedRecomp/api/Hedgehog/Database/System/hhDatabaseData.inl index 7e3e383..6132b39 100644 --- a/UnleashedRecomp/api/Hedgehog/Database/System/hhDatabaseData.inl +++ b/UnleashedRecomp/api/Hedgehog/Database/System/hhDatabaseData.inl @@ -7,7 +7,7 @@ namespace Hedgehog::Database inline bool CDatabaseData::CheckMadeAll() { - return true; + return GuestToHostFunction(m_pVftable->m_fpCheckMadeAll, this); } inline bool CDatabaseData::IsMadeOne() const diff --git a/UnleashedRecomp/api/Hedgehog/MirageCore/Misc/hhVertexDeclarationPtr.h b/UnleashedRecomp/api/Hedgehog/MirageCore/Misc/hhVertexDeclarationPtr.h new file mode 100644 index 0000000..bf0c491 --- /dev/null +++ b/UnleashedRecomp/api/Hedgehog/MirageCore/Misc/hhVertexDeclarationPtr.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +namespace Hedgehog::Mirage +{ + class CRenderingInfrastructure; + + class CVertexDeclarationPtr + { + public: + xpointer m_pD3DVertexDeclaration; + xpointer m_pRenderingInfrastructure; + }; + + SWA_ASSERT_OFFSETOF(CVertexDeclarationPtr, m_pD3DVertexDeclaration, 0x0); + SWA_ASSERT_OFFSETOF(CVertexDeclarationPtr, m_pRenderingInfrastructure, 0x4); + SWA_ASSERT_SIZEOF(CVertexDeclarationPtr, 0x8); +} diff --git a/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhMaterialData.h b/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhMaterialData.h new file mode 100644 index 0000000..0be6a3e --- /dev/null +++ b/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhMaterialData.h @@ -0,0 +1,39 @@ +#pragma once + +#include + +#include +#include + +namespace Hedgehog::Mirage +{ + class CMaterialData; + class CTexsetData; + class CShaderListData; + class CParameterFloat4Element; + class CParameterInt4Element; + class CParameterBoolElement; + + class CMaterialData : public Database::CDatabaseData + { + public: + boost::shared_ptr m_spShaderListData; + boost::shared_ptr m_spTexsetData; + hh::vector> m_Float4Params; + hh::vector> m_Int4Params; + hh::vector> m_Bool4Params; + uint8_t m_AlphaThreshold; + bool m_DoubleSided; + bool m_Additive; + }; + + SWA_ASSERT_OFFSETOF(CMaterialData, m_spShaderListData, 0xC); + SWA_ASSERT_OFFSETOF(CMaterialData, m_spTexsetData, 0x14); + SWA_ASSERT_OFFSETOF(CMaterialData, m_Float4Params, 0x1C); + SWA_ASSERT_OFFSETOF(CMaterialData, m_Int4Params, 0x2C); + SWA_ASSERT_OFFSETOF(CMaterialData, m_Bool4Params, 0x3C); + SWA_ASSERT_OFFSETOF(CMaterialData, m_AlphaThreshold, 0x4C); + SWA_ASSERT_OFFSETOF(CMaterialData, m_DoubleSided, 0x4D); + SWA_ASSERT_OFFSETOF(CMaterialData, m_Additive, 0x4E); + SWA_ASSERT_SIZEOF(CMaterialData, 0x50); +} diff --git a/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhMeshData.h b/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhMeshData.h new file mode 100644 index 0000000..3fc6a9f --- /dev/null +++ b/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhMeshData.h @@ -0,0 +1,43 @@ +#pragma once + +#include + +#include +#include +#include + +namespace Hedgehog::Mirage +{ + class CMaterialData; + + class CMeshData : public Database::CDatabaseData + { + public: + be m_IndexNum; + be m_VertexNum; + be m_VertexSize; + be m_NodeNum; + xpointer m_pNodeIndices; + be m_VertexOffset; + be m_IndexOffset; + xpointer m_pD3DIndexBuffer; + xpointer m_pD3DVertexBuffer; + CVertexDeclarationPtr m_VertexDeclarationPtr; + SWA_INSERT_PADDING(0x4); + boost::shared_ptr m_spMaterial; + SWA_INSERT_PADDING(0xC); + }; + + SWA_ASSERT_OFFSETOF(CMeshData, m_IndexNum, 0xC); + SWA_ASSERT_OFFSETOF(CMeshData, m_VertexNum, 0x10); + SWA_ASSERT_OFFSETOF(CMeshData, m_VertexSize, 0x14); + SWA_ASSERT_OFFSETOF(CMeshData, m_NodeNum, 0x18); + SWA_ASSERT_OFFSETOF(CMeshData, m_pNodeIndices, 0x1C); + SWA_ASSERT_OFFSETOF(CMeshData, m_VertexOffset, 0x20); + SWA_ASSERT_OFFSETOF(CMeshData, m_IndexOffset, 0x24); + SWA_ASSERT_OFFSETOF(CMeshData, m_pD3DIndexBuffer, 0x28); + SWA_ASSERT_OFFSETOF(CMeshData, m_pD3DVertexBuffer, 0x2C); + SWA_ASSERT_OFFSETOF(CMeshData, m_VertexDeclarationPtr, 0x30); + SWA_ASSERT_OFFSETOF(CMeshData, m_spMaterial, 0x3C); + SWA_ASSERT_SIZEOF(CMeshData, 0x50); +} diff --git a/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhModelData.h b/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhModelData.h new file mode 100644 index 0000000..d29bc11 --- /dev/null +++ b/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhModelData.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +namespace Hedgehog::Mirage +{ + class CNodeGroupModelData; + class CMeshData; + class CModelNodeData; + class CMatrixData; + class CAabbData; + class CSphereData; + class CMorphModelData; + + class CModelData : public Database::CDatabaseData + { + public: + be m_NodeGroupModelNum; + hh::vector> m_NodeGroupModels; + hh::vector> m_OpaqueMeshes; + hh::vector> m_TransparentMeshes; + hh::vector> m_PunchThroughMeshes; + be m_NodeNum; + boost::shared_ptr m_spNodeParentIndices; + boost::shared_ptr m_spNodes; + boost::shared_ptr m_spNodeMatrices; + boost::shared_ptr m_spAabb; + boost::shared_ptr m_spSphere; + hh::vector> m_MorphModels; + }; + + SWA_ASSERT_OFFSETOF(CModelData, m_NodeGroupModelNum, 0xC); + SWA_ASSERT_OFFSETOF(CModelData, m_NodeGroupModels, 0x10); + SWA_ASSERT_OFFSETOF(CModelData, m_OpaqueMeshes, 0x20); + SWA_ASSERT_OFFSETOF(CModelData, m_TransparentMeshes, 0x30); + SWA_ASSERT_OFFSETOF(CModelData, m_PunchThroughMeshes, 0x40); + SWA_ASSERT_OFFSETOF(CModelData, m_NodeNum, 0x50); + SWA_ASSERT_OFFSETOF(CModelData, m_spNodeParentIndices, 0x54); + SWA_ASSERT_OFFSETOF(CModelData, m_spNodes, 0x5C); + SWA_ASSERT_OFFSETOF(CModelData, m_spNodeMatrices, 0x64); + SWA_ASSERT_OFFSETOF(CModelData, m_spAabb, 0x6C); + SWA_ASSERT_OFFSETOF(CModelData, m_spSphere, 0x74); + SWA_ASSERT_OFFSETOF(CModelData, m_MorphModels, 0x7C); + SWA_ASSERT_SIZEOF(CModelData, 0x8C); +} diff --git a/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhNodeGroupModelData.h b/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhNodeGroupModelData.h new file mode 100644 index 0000000..1c37548 --- /dev/null +++ b/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhNodeGroupModelData.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include + +namespace Hedgehog::Mirage +{ + class CMeshData; + + class CNodeGroupModelData : public Database::CDatabaseData + { + public: + hh::vector> m_OpaqueMeshes; + hh::vector> m_TransparentMeshes; + hh::vector> m_PunchThroughMeshes; + be m_SpecialMeshGroupNum; + boost::shared_ptr> m_SpecialMeshGroupModes; + hh::vector>> m_SpecialMeshGroups; + Base::CSharedString m_Name; + }; + + SWA_ASSERT_OFFSETOF(CNodeGroupModelData, m_OpaqueMeshes, 0xC); + SWA_ASSERT_OFFSETOF(CNodeGroupModelData, m_TransparentMeshes, 0x1C); + SWA_ASSERT_OFFSETOF(CNodeGroupModelData, m_PunchThroughMeshes, 0x2C); + SWA_ASSERT_OFFSETOF(CNodeGroupModelData, m_SpecialMeshGroupNum, 0x3C); + SWA_ASSERT_OFFSETOF(CNodeGroupModelData, m_SpecialMeshGroupModes, 0x40); + SWA_ASSERT_OFFSETOF(CNodeGroupModelData, m_SpecialMeshGroups, 0x48); + SWA_ASSERT_OFFSETOF(CNodeGroupModelData, m_Name, 0x58); + SWA_ASSERT_SIZEOF(CNodeGroupModelData, 0x5C); +} diff --git a/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhPixelShaderCodeData.h b/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhPixelShaderCodeData.h new file mode 100644 index 0000000..ab275e2 --- /dev/null +++ b/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhPixelShaderCodeData.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +#include + +namespace Hedgehog::Base +{ + class CCriticalSectionD3D9; +} + +namespace Hedgehog::Mirage +{ + class CRenderingInfrastructure; + + class CPixelShaderCodeData : public Database::CDatabaseData + { + public: + xpointer m_pD3DPixelShader; + xpointer m_spFunction; + boost::shared_ptr m_spCriticalSection; + xpointer m_pRenderingInfrastructure; + }; + + SWA_ASSERT_OFFSETOF(CPixelShaderCodeData, m_pD3DPixelShader, 0xC); + SWA_ASSERT_OFFSETOF(CPixelShaderCodeData, m_spFunction, 0x10); + SWA_ASSERT_OFFSETOF(CPixelShaderCodeData, m_spCriticalSection, 0x14); + SWA_ASSERT_OFFSETOF(CPixelShaderCodeData, m_pRenderingInfrastructure, 0x1C); + SWA_ASSERT_SIZEOF(CPixelShaderCodeData, 0x20); +} diff --git a/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhPixelShaderData.h b/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhPixelShaderData.h new file mode 100644 index 0000000..e274024 --- /dev/null +++ b/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhPixelShaderData.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +namespace Hedgehog::Mirage +{ + class CPixelShaderCodeData; + class CPixelShaderParameterData; + + class CPixelShaderData : public Hedgehog::Database::CDatabaseData + { + public: + boost::shared_ptr m_spCode; + SWA_INSERT_PADDING(0x4); + hh::vector> m_ParameterList; + }; + + SWA_ASSERT_OFFSETOF(CPixelShaderData, m_spCode, 0xC); + SWA_ASSERT_OFFSETOF(CPixelShaderData, m_ParameterList, 0x18); + SWA_ASSERT_SIZEOF(CPixelShaderData, 0x28); +} diff --git a/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhShaderListData.h b/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhShaderListData.h new file mode 100644 index 0000000..3731fff --- /dev/null +++ b/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhShaderListData.h @@ -0,0 +1,46 @@ +#pragma once + +#include + +#include +#include +#include + +namespace Hedgehog::Mirage +{ + class CVertexShaderData; + class CPixelShaderData; + + class CVertexShaderPermutationData + { + public: + hh::map, boost::shared_ptr> m_VertexShaders; + be m_SubPermutations; + }; + + SWA_ASSERT_OFFSETOF(CVertexShaderPermutationData, m_VertexShaders, 0x0); + SWA_ASSERT_OFFSETOF(CVertexShaderPermutationData, m_SubPermutations, 0xC); + SWA_ASSERT_SIZEOF(CVertexShaderPermutationData, 0x10); + + class CPixelShaderPermutationData + { + public: + hh::map> m_VertexShaderPermutations; + hh::map, boost::shared_ptr> m_PixelShaders; + be m_SubPermutations; + }; + + SWA_ASSERT_OFFSETOF(CPixelShaderPermutationData, m_VertexShaderPermutations, 0x0); + SWA_ASSERT_OFFSETOF(CPixelShaderPermutationData, m_PixelShaders, 0xC); + SWA_ASSERT_OFFSETOF(CPixelShaderPermutationData, m_SubPermutations, 0x18); + SWA_ASSERT_SIZEOF(CPixelShaderPermutationData, 0x1C); + + class CShaderListData : public Database::CDatabaseData + { + public: + hh::map m_PixelShaderPermutations; + }; + + SWA_ASSERT_OFFSETOF(CShaderListData, m_PixelShaderPermutations, 0xC); + SWA_ASSERT_SIZEOF(CShaderListData, 0x18); +} diff --git a/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhTerrainModelData.h b/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhTerrainModelData.h new file mode 100644 index 0000000..5f0d6b3 --- /dev/null +++ b/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhTerrainModelData.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + +namespace Hedgehog::Mirage +{ + class CSphereData; + class CNodeGroupModelData; + class CMeshData; + + class CTerrainModelData : public Database::CDatabaseData + { + public: + SWA_INSERT_PADDING(0x4); + hh::vector> m_NodeGroupModels; + hh::vector> m_OpaqueMeshes; + hh::vector> m_TransparentMeshes; + hh::vector> m_PunchThroughMeshes; + boost::shared_ptr m_spSphere; + }; + + SWA_ASSERT_OFFSETOF(CTerrainModelData, m_NodeGroupModels, 0x10); + SWA_ASSERT_OFFSETOF(CTerrainModelData, m_OpaqueMeshes, 0x20); + SWA_ASSERT_OFFSETOF(CTerrainModelData, m_TransparentMeshes, 0x30); + SWA_ASSERT_OFFSETOF(CTerrainModelData, m_PunchThroughMeshes, 0x40); + SWA_ASSERT_OFFSETOF(CTerrainModelData, m_spSphere, 0x50); + SWA_ASSERT_SIZEOF(CTerrainModelData, 0x58); +} diff --git a/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhTexsetData.h b/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhTexsetData.h new file mode 100644 index 0000000..627bf04 --- /dev/null +++ b/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhTexsetData.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +#include +#include + +namespace Hedgehog::Mirage +{ + class CTextureData; + + class CTexsetData : public Database::CDatabaseData + { + public: + hh::vector> m_TextureList; + hh::vector m_TextureNameList; + bool m_ConstTexCoord; + }; + + SWA_ASSERT_OFFSETOF(CTexsetData, m_TextureList, 0xC); + SWA_ASSERT_OFFSETOF(CTexsetData, m_TextureNameList, 0x1C); + SWA_ASSERT_OFFSETOF(CTexsetData, m_ConstTexCoord, 0x2C); + SWA_ASSERT_SIZEOF(CTexsetData, 0x30); +} diff --git a/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhTextureData.h b/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhTextureData.h new file mode 100644 index 0000000..cf8abbe --- /dev/null +++ b/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhTextureData.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +namespace Hedgehog::Mirage +{ + class CTextureData : public Database::CDatabaseData + { + public: + SWA_INSERT_PADDING(0x8); + uint8_t m_TexcoordIndex; + SWA_INSERT_PADDING(0x1B); + }; + + SWA_ASSERT_OFFSETOF(CTextureData, m_TexcoordIndex, 0x14); + SWA_ASSERT_SIZEOF(CTextureData, 0x30); +} diff --git a/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhVertexShaderCodeData.h b/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhVertexShaderCodeData.h new file mode 100644 index 0000000..9956d2e --- /dev/null +++ b/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhVertexShaderCodeData.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +#include + +namespace Hedgehog::Base +{ + class CCriticalSectionD3D9; +} + +namespace Hedgehog::Mirage +{ + class CRenderingInfrastructure; + + class CVertexShaderCodeData : public Database::CDatabaseData + { + public: + xpointer m_pD3DVertexShader; + xpointer m_spFunction; + boost::shared_ptr m_spCriticalSection; + xpointer m_pRenderingInfrastructure; + }; + + SWA_ASSERT_OFFSETOF(CVertexShaderCodeData, m_pD3DVertexShader, 0xC); + SWA_ASSERT_OFFSETOF(CVertexShaderCodeData, m_spFunction, 0x10); + SWA_ASSERT_OFFSETOF(CVertexShaderCodeData, m_spCriticalSection, 0x14); + SWA_ASSERT_OFFSETOF(CVertexShaderCodeData, m_pRenderingInfrastructure, 0x1C); + SWA_ASSERT_SIZEOF(CVertexShaderCodeData, 0x20); +} diff --git a/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhVertexShaderData.h b/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhVertexShaderData.h new file mode 100644 index 0000000..f950995 --- /dev/null +++ b/UnleashedRecomp/api/Hedgehog/MirageCore/RenderData/hhVertexShaderData.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +namespace Hedgehog::Mirage +{ + class CVertexShaderCodeData; + class CVertexShaderParameterData; + + class CVertexShaderData : public Hedgehog::Database::CDatabaseData + { + public: + boost::shared_ptr m_spCode; + SWA_INSERT_PADDING(0x4); + hh::vector> m_ParameterList; + }; + + SWA_ASSERT_OFFSETOF(CVertexShaderData, m_spCode, 0xC); + SWA_ASSERT_OFFSETOF(CVertexShaderData, m_ParameterList, 0x18); + SWA_ASSERT_SIZEOF(CVertexShaderData, 0x28); +} diff --git a/UnleashedRecomp/api/Hedgehog/Sparkle/hhParticleMaterial.h b/UnleashedRecomp/api/Hedgehog/Sparkle/hhParticleMaterial.h new file mode 100644 index 0000000..75bb1b3 --- /dev/null +++ b/UnleashedRecomp/api/Hedgehog/Sparkle/hhParticleMaterial.h @@ -0,0 +1,41 @@ +#pragma once + +#include + +#include +#include + +namespace Hedgehog::Mirage +{ + class CShaderListData; +} + +namespace Hedgehog::Sparkle +{ + class CParticleMaterial : public Hedgehog::Database::CDatabaseData + { + public: + enum EBlendMode + { + eBlendMode_Zero, + eBlendMode_Typical, + eBlendMode_Add, + eBlendMode_Subtract + }; + + hh::vector m_spFieldC; + boost::shared_ptr m_spDefaultShaderListData; // BillboardParticle_d[v] + boost::shared_ptr m_spShaderListData; + bool m_Field2C; + be m_BlendMode; + be m_AddressMode; + Hedgehog::Base::CSharedString m_MaterialName; + Hedgehog::Base::CSharedString m_TextureName; + Hedgehog::Base::CSharedString m_DeflectionTextureName; + Hedgehog::Base::CSharedString m_ShaderName; + be m_Field48; + be m_Field4C; + }; + + SWA_ASSERT_SIZEOF(CParticleMaterial, 0x50); +} diff --git a/UnleashedRecomp/api/SWA.h b/UnleashedRecomp/api/SWA.h index bf18f4b..f72f0e1 100644 --- a/UnleashedRecomp/api/SWA.h +++ b/UnleashedRecomp/api/SWA.h @@ -15,7 +15,10 @@ #include "CSD/Manager/csdmSceneObserver.h" #include "CSD/Manager/csdmSubjectBase.h" #include "CSD/Platform/csdTexList.h" +#include "Hedgehog/Base/Container/hhMap.h" +#include "Hedgehog/Base/Container/hhVector.h" #include "Hedgehog/Base/System/hhAllocator.h" +#include "Hedgehog/Base/System/hhSymbol.h" #include "Hedgehog/Base/Thread/hhHolder.h" #include "Hedgehog/Base/Thread/hhHolderBase.h" #include "Hedgehog/Base/Thread/hhSynchronizedObject.h" @@ -25,7 +28,21 @@ #include "Hedgehog/Base/hhObject.h" #include "Hedgehog/Database/System/hhDatabaseData.h" #include "Hedgehog/Math/Vector2.h" +#include "Hedgehog/MirageCore/Misc/hhVertexDeclarationPtr.h" +#include "Hedgehog/MirageCore/RenderData/hhMaterialData.h" +#include "Hedgehog/MirageCore/RenderData/hhMeshData.h" +#include "Hedgehog/MirageCore/RenderData/hhModelData.h" +#include "Hedgehog/MirageCore/RenderData/hhNodeGroupModelData.h" +#include "Hedgehog/MirageCore/RenderData/hhPixelShaderCodeData.h" +#include "Hedgehog/MirageCore/RenderData/hhPixelShaderData.h" +#include "Hedgehog/MirageCore/RenderData/hhShaderListData.h" +#include "Hedgehog/MirageCore/RenderData/hhTerrainModelData.h" +#include "Hedgehog/MirageCore/RenderData/hhTexsetData.h" +#include "Hedgehog/MirageCore/RenderData/hhTextureData.h" +#include "Hedgehog/MirageCore/RenderData/hhVertexShaderCodeData.h" +#include "Hedgehog/MirageCore/RenderData/hhVertexShaderData.h" #include "Hedgehog/MirageCore/Renderable/hhRenderable.h" +#include "Hedgehog/Sparkle/hhParticleMaterial.h" #include "Hedgehog/Universe/Engine/hhMessageActor.h" #include "Hedgehog/Universe/Engine/hhMessageProcess.h" #include "Hedgehog/Universe/Engine/hhUpdateInfo.h" diff --git a/UnleashedRecomp/api/boost/smart_ptr/shared_ptr.h b/UnleashedRecomp/api/boost/smart_ptr/shared_ptr.h index ecea043..5504359 100644 --- a/UnleashedRecomp/api/boost/smart_ptr/shared_ptr.h +++ b/UnleashedRecomp/api/boost/smart_ptr/shared_ptr.h @@ -69,7 +69,12 @@ namespace boost uint32_t use_count() const { - return use_count_; + return std::byteswap(static_cast(use_count_.value)); + } + + bool unique() const + { + return use_count() == 1; } }; @@ -150,6 +155,16 @@ namespace boost return *this; } + shared_ptr& operator=(std::nullptr_t) + { + release(); + + px = NULL; + pn = NULL; + + return *this; + } + T* get() const { return px; } detail::sp_dereference operator*() const { assert(px); return *px; } @@ -158,6 +173,7 @@ namespace boost explicit operator bool() const { return px != nullptr; } size_t use_count() const { return pn ? pn->use_count() : 0; } + bool unique() const { return !pn || pn->unique(); } }; using anonymous_shared_ptr = shared_ptr; diff --git a/UnleashedRecomp/gpu/cache/pipeline_state_cache.h b/UnleashedRecomp/gpu/cache/pipeline_state_cache.h new file mode 100644 index 0000000..e26e174 --- /dev/null +++ b/UnleashedRecomp/gpu/cache/pipeline_state_cache.h @@ -0,0 +1,65 @@ +{ reinterpret_cast(0x135289E4F64C6EEA),reinterpret_cast(0x6B812461EA74928D),reinterpret_cast(0xD452411D3FB80A0D),false,false,false,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::LESS,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0x8,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R8G8B8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x135289E4F64C6EEA),reinterpret_cast(0x7C6A695E4296FA3B),reinterpret_cast(0xD452411D3FB80A0D),false,false,false,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::LESS,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R8G8B8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x135289E4F64C6EEA),reinterpret_cast(0xC20BFEE4B086F5EF),reinterpret_cast(0xD452411D3FB80A0D),false,false,false,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::LESS,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0x8,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R8G8B8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x1D29E1386144A506),reinterpret_cast(0x22CDDA5C6346A97),reinterpret_cast(0x5A2395E29F93DA3C),false,true,false,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderCullMode::NONE,RenderComparisonFunction::GREATER_EQUAL,true,RenderBlendOperation::ADD,0,0,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_FAN,{ 32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R16G16B16A16_FLOAT,RenderFormat::D32_FLOAT,1,false,0x10 }, +{ reinterpret_cast(0x36DB3B40FA419EF6),reinterpret_cast(0x3BF885B6A8263F78),reinterpret_cast(0xFFFDDC62D86892F1),false,true,true,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::GREATER_EQUAL,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_LIST,{ 32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R16G16B16A16_FLOAT,RenderFormat::D32_FLOAT,1,false,0x10 }, +{ reinterpret_cast(0x36DB3B40FA419EF6),reinterpret_cast(0x3BF885B6A8263F78),reinterpret_cast(0xFFFDDC62D86892F1),false,true,true,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::GREATER_EQUAL,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R16G16B16A16_FLOAT,RenderFormat::D32_FLOAT,1,false,0x10 }, +{ reinterpret_cast(0x681C97662BFE9150),reinterpret_cast(0xDC635C4E7013864E),reinterpret_cast(0x6196BF64CB935CA5),false,true,false,RenderBlend::ONE,RenderBlend::ONE,RenderCullMode::NONE,RenderComparisonFunction::GREATER_EQUAL,true,RenderBlendOperation::ADD,0,0,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 24,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R16G16B16A16_FLOAT,RenderFormat::D32_FLOAT,1,false,0x10 }, +{ reinterpret_cast(0x681E682667303C76),reinterpret_cast(0x38771885AC644B5),reinterpret_cast(0xC64D046063DE2F63),false,true,false,RenderBlend::SRC_ALPHA,RenderBlend::ONE,RenderCullMode::BACK,RenderComparisonFunction::GREATER_EQUAL,true,RenderBlendOperation::ADD,0,0,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R16G16B16A16_FLOAT,RenderFormat::D32_FLOAT,1,false,0x11 }, +{ reinterpret_cast(0x681E682667303C76),reinterpret_cast(0x604349CBF72CCF8C),reinterpret_cast(0xC64D046063DE2F63),false,true,false,RenderBlend::SRC_ALPHA,RenderBlend::ONE,RenderCullMode::BACK,RenderComparisonFunction::GREATER_EQUAL,true,RenderBlendOperation::ADD,0,0,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R16G16B16A16_FLOAT,RenderFormat::D32_FLOAT,1,false,0x11 }, +{ reinterpret_cast(0x6DE86503F8AA38E2),reinterpret_cast(0x132D2F2079D74CB3),reinterpret_cast(0xD452411D3FB80A0D),false,false,false,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::LESS,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R8G8B8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x6DE86503F8AA38E2),reinterpret_cast(0x198E2B57B47DAF53),reinterpret_cast(0xD452411D3FB80A0D),false,false,false,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::LESS,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R16G16_FLOAT,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x6DE86503F8AA38E2),reinterpret_cast(0x3016DA5F348C87A7),reinterpret_cast(0xD452411D3FB80A0D),false,false,false,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::LESS,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R16G16_FLOAT,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x6DE86503F8AA38E2),reinterpret_cast(0x3016DA5F348C87A7),reinterpret_cast(0xD452411D3FB80A0D),false,false,false,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::LESS,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R16G16B16A16_FLOAT,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x6DE86503F8AA38E2),reinterpret_cast(0x3016DA5F348C87A7),reinterpret_cast(0xD452411D3FB80A0D),false,false,false,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::LESS,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R8G8B8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x6DE86503F8AA38E2),reinterpret_cast(0x4294510C775F4EE8),reinterpret_cast(0xD452411D3FB80A0D),false,false,false,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::LESS,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R8G8B8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x6DE86503F8AA38E2),reinterpret_cast(0x48BE63A8F5F1C78A),reinterpret_cast(0xD452411D3FB80A0D),false,false,false,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::LESS,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R16G16_FLOAT,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x6DE86503F8AA38E2),reinterpret_cast(0x50700665A5F55DFE),reinterpret_cast(0xD452411D3FB80A0D),false,false,false,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::LESS,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R8G8B8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x6DE86503F8AA38E2),reinterpret_cast(0x605C1E349CC4CAAB),reinterpret_cast(0xD452411D3FB80A0D),false,false,false,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::LESS,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R16G16B16A16_FLOAT,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x6DE86503F8AA38E2),reinterpret_cast(0x65325B9C7DA3DB04),reinterpret_cast(0xD452411D3FB80A0D),false,false,false,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::LESS,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R8G8B8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x6DE86503F8AA38E2),reinterpret_cast(0x67064E6EA39B439E),reinterpret_cast(0xD452411D3FB80A0D),false,false,false,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::LESS,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R8G8B8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x6DE86503F8AA38E2),reinterpret_cast(0x7D9F06B0E048B75D),reinterpret_cast(0xD452411D3FB80A0D),false,false,false,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::LESS,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R8G8B8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x6DE86503F8AA38E2),reinterpret_cast(0x891B8684FB17752B),reinterpret_cast(0xD452411D3FB80A0D),false,false,false,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::LESS,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R8G8B8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x6DE86503F8AA38E2),reinterpret_cast(0x9FA5AACB5B14A226),reinterpret_cast(0xD452411D3FB80A0D),false,false,false,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::LESS,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R8G8B8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x6DE86503F8AA38E2),reinterpret_cast(0xC47F2F91BA2A5D86),reinterpret_cast(0xD452411D3FB80A0D),false,false,false,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::LESS,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R8G8B8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x6DE86503F8AA38E2),reinterpret_cast(0xFB79F59782376846),reinterpret_cast(0xD452411D3FB80A0D),false,false,false,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::LESS,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R8G8B8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x720D6E5EDA78433B),reinterpret_cast(0x96EACBACDE1AAFAA),reinterpret_cast(0xA81F28FA43A9B511),false,true,false,RenderBlend::SRC_ALPHA,RenderBlend::ONE,RenderCullMode::NONE,RenderComparisonFunction::GREATER_EQUAL,true,RenderBlendOperation::ADD,0,0,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R16G16B16A16_FLOAT,RenderFormat::D32_FLOAT,1,false,0x10 }, +{ reinterpret_cast(0x7C34DB46DEEFB0C2),reinterpret_cast(0xDBD8B29544AC9277),reinterpret_cast(0xB22B7B7B968141C6),false,false,false,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderCullMode::NONE,RenderComparisonFunction::LESS,true,RenderBlendOperation::ADD,0,0,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::B8G8R8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x7CC3A0B01523741B),reinterpret_cast(0x315BB80DAE685834),reinterpret_cast(0xB22B7B7B968141C6),false,false,false,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderCullMode::NONE,RenderComparisonFunction::LESS,true,RenderBlendOperation::ADD,0,0,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::B8G8R8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x86FE3502D5EC24AA),reinterpret_cast(0x68FCC0B90EBC457B),reinterpret_cast(0xD452411D3FB80A0D),false,false,false,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderCullMode::NONE,RenderComparisonFunction::LESS,true,RenderBlendOperation::ADD,0,0,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::B8G8R8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x86FE3502D5EC24AA),reinterpret_cast(0x94A71CC9B94E3101),reinterpret_cast(0xD452411D3FB80A0D),false,false,false,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::LESS,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R16G16_FLOAT,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x88F67387B88F932F),reinterpret_cast(0x49101E452DF2FE98),reinterpret_cast(0x84BACD816D86543C),false,true,false,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderCullMode::BACK,RenderComparisonFunction::GREATER_EQUAL,true,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 104,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R16G16B16A16_FLOAT,RenderFormat::D32_FLOAT,1,false,0x10 }, +{ reinterpret_cast(0x8A459F1CE0E957D3),reinterpret_cast(0x5D7ACAE543747185),reinterpret_cast(0x84BACD816D86543C),false,true,false,RenderBlend::SRC_ALPHA,RenderBlend::ONE,RenderCullMode::BACK,RenderComparisonFunction::GREATER_EQUAL,true,RenderBlendOperation::ADD,0,0,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 104,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R8G8B8A8_UNORM,RenderFormat::D32_FLOAT,1,false,0x10 }, +{ reinterpret_cast(0x8E4BB23465BD909E),reinterpret_cast(0x0),reinterpret_cast(0xFFFDDC62D86892F1),false,true,true,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::LESS_EQUAL,false,RenderBlendOperation::ADD,1,33554,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0x0,RenderPrimitiveTopology::TRIANGLE_LIST,{ 32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::UNKNOWN,RenderFormat::D32_FLOAT,1,false,0x0 }, +{ reinterpret_cast(0x8E4BB23465BD909E),reinterpret_cast(0x0),reinterpret_cast(0xFFFDDC62D86892F1),false,true,true,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::LESS_EQUAL,false,RenderBlendOperation::ADD,1,33554,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0x0,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::UNKNOWN,RenderFormat::D32_FLOAT,1,false,0x0 }, +{ reinterpret_cast(0x965929BAA69F0AE),reinterpret_cast(0x66578F29004F8FD0),reinterpret_cast(0xD452411D3FB80A0D),false,false,false,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::LESS,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R8G8B8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x965929BAA69F0AE),reinterpret_cast(0x6B9732B4CD7E7740),reinterpret_cast(0xD452411D3FB80A0D),false,false,false,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::LESS,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R8G8B8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x965929BAA69F0AE),reinterpret_cast(0x6C50210CBDA8A23A),reinterpret_cast(0xD452411D3FB80A0D),false,false,false,RenderBlend::ONE,RenderBlend::ONE,RenderCullMode::NONE,RenderComparisonFunction::LESS,true,RenderBlendOperation::ADD,0,0,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R8G8B8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x965929BAA69F0AE),reinterpret_cast(0x6C50210CBDA8A23A),reinterpret_cast(0xD452411D3FB80A0D),false,false,false,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::LESS,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::B8G8R8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x965929BAA69F0AE),reinterpret_cast(0x6C50210CBDA8A23A),reinterpret_cast(0xD452411D3FB80A0D),false,false,false,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::LESS,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R16G16_FLOAT,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x965929BAA69F0AE),reinterpret_cast(0x6C50210CBDA8A23A),reinterpret_cast(0xD452411D3FB80A0D),false,false,false,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::LESS,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R16G16B16A16_FLOAT,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x965929BAA69F0AE),reinterpret_cast(0x6C50210CBDA8A23A),reinterpret_cast(0xD452411D3FB80A0D),false,false,false,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::LESS,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R8G8B8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x965929BAA69F0AE),reinterpret_cast(0x6C50210CBDA8A23A),reinterpret_cast(0xD452411D3FB80A0D),false,false,false,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderCullMode::NONE,RenderComparisonFunction::LESS,true,RenderBlendOperation::ADD,0,0,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R8G8B8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x965929BAA69F0AE),reinterpret_cast(0x92940FDD115733E1),reinterpret_cast(0xD452411D3FB80A0D),false,false,false,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::LESS,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R8G8B8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x965929BAA69F0AE),reinterpret_cast(0xA305C47ED9FB58CF),reinterpret_cast(0xD452411D3FB80A0D),false,true,true,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::ALWAYS,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0x0,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::UNKNOWN,RenderFormat::D32_FLOAT,1,false,0x0 }, +{ reinterpret_cast(0x965929BAA69F0AE),reinterpret_cast(0xD44C189D5067922E),reinterpret_cast(0xD452411D3FB80A0D),false,false,false,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::LESS,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R8G8B8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0x965929BAA69F0AE),reinterpret_cast(0xD736237D80908063),reinterpret_cast(0xD452411D3FB80A0D),false,false,false,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::LESS,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R8G8B8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0xA3CD49F172D99214),reinterpret_cast(0xD5EA32DB758EF0B8),reinterpret_cast(0x7F12180DC3A24B53),true,true,true,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::GREATER_EQUAL,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 32,24,4,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R16G16B16A16_FLOAT,RenderFormat::D32_FLOAT,1,false,0x14 }, +{ reinterpret_cast(0xA3FD368C62079765),reinterpret_cast(0xE8E3FF65F2356201),reinterpret_cast(0xB22B7B7B968141C6),false,false,false,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderCullMode::NONE,RenderComparisonFunction::LESS,true,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::B8G8R8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0xA3FD368C62079765),reinterpret_cast(0xE8E3FF65F2356201),reinterpret_cast(0xB22B7B7B968141C6),false,false,false,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderCullMode::NONE,RenderComparisonFunction::LESS,true,RenderBlendOperation::ADD,0,0,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::B8G8R8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x0 }, +{ reinterpret_cast(0xA4CF0215A03D9571),reinterpret_cast(0xA3A659F1590CC180),reinterpret_cast(0x5A2395E29F93DA3C),false,true,false,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderCullMode::NONE,RenderComparisonFunction::GREATER_EQUAL,true,RenderBlendOperation::ADD,0,0,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_FAN,{ 32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R16G16B16A16_FLOAT,RenderFormat::D32_FLOAT,1,false,0x10 }, +{ reinterpret_cast(0xB1086A4947A797DE),reinterpret_cast(0x996F5A774A646D92),reinterpret_cast(0x6FAE71C7134074A4),false,true,false,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderCullMode::NONE,RenderComparisonFunction::GREATER_EQUAL,true,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::B8G8R8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x4 }, +{ reinterpret_cast(0xB1086A4947A797DE),reinterpret_cast(0x996F5A774A646D92),reinterpret_cast(0x6FAE71C7134074A4),false,true,false,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderCullMode::NONE,RenderComparisonFunction::GREATER_EQUAL,true,RenderBlendOperation::ADD,0,0,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::B8G8R8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x4 }, +{ reinterpret_cast(0xB1086A4947A797DE),reinterpret_cast(0x996F5A774A646D92),reinterpret_cast(0x6FAE71C7134074A4),false,true,false,RenderBlend::SRC_ALPHA,RenderBlend::ONE,RenderCullMode::NONE,RenderComparisonFunction::GREATER_EQUAL,true,RenderBlendOperation::ADD,0,0,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::B8G8R8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x4 }, +{ reinterpret_cast(0xB1EA909C660122A7),reinterpret_cast(0xC96708D6F26CC6D9),reinterpret_cast(0x84BACD816D86543C),false,true,false,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderCullMode::BACK,RenderComparisonFunction::GREATER_EQUAL,true,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 104,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R16G16B16A16_FLOAT,RenderFormat::D32_FLOAT,1,false,0x10 }, +{ reinterpret_cast(0xB4CAFC034A37C8A8),reinterpret_cast(0x31173204A896098A),reinterpret_cast(0x5A22D93C543DF925),false,true,false,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderCullMode::NONE,RenderComparisonFunction::GREATER_EQUAL,true,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::B8G8R8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x4 }, +{ reinterpret_cast(0xB4CAFC034A37C8A8),reinterpret_cast(0x31173204A896098A),reinterpret_cast(0x5A22D93C543DF925),false,true,false,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderCullMode::NONE,RenderComparisonFunction::GREATER_EQUAL,true,RenderBlendOperation::ADD,0,0,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::B8G8R8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x4 }, +{ reinterpret_cast(0xB4CAFC034A37C8A8),reinterpret_cast(0x31173204A896098A),reinterpret_cast(0x5A22D93C543DF925),false,true,false,RenderBlend::SRC_ALPHA,RenderBlend::ONE,RenderCullMode::NONE,RenderComparisonFunction::GREATER_EQUAL,true,RenderBlendOperation::ADD,0,0,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::B8G8R8A8_UNORM,RenderFormat::UNKNOWN,1,false,0x4 }, +{ reinterpret_cast(0xBBD01C46D72EDC37),reinterpret_cast(0x4E1399E5E0E40511),reinterpret_cast(0xE6B3B3D286909AB9),true,true,false,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderCullMode::NONE,RenderComparisonFunction::GREATER_EQUAL,true,RenderBlendOperation::ADD,0,0,RenderBlend::SRC_ALPHA,RenderBlend::INV_SRC_ALPHA,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 32,24,4,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R16G16B16A16_FLOAT,RenderFormat::D32_FLOAT,1,false,0x10 }, +{ reinterpret_cast(0xBDA992649419F2DD),reinterpret_cast(0x5A4CFA9D29441E0),reinterpret_cast(0x25B8F9D92644DAE0),true,true,true,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::GREATER_EQUAL,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 44,24,4,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R16G16B16A16_FLOAT,RenderFormat::D32_FLOAT,1,false,0x11 }, +{ reinterpret_cast(0xBDA992649419F2DD),reinterpret_cast(0x5A4CFA9D29441E0),reinterpret_cast(0x25B8F9D92644DAE0),true,true,true,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::GREATER_EQUAL,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 44,24,4,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R16G16B16A16_FLOAT,RenderFormat::D32_FLOAT,1,false,0x15 }, +{ reinterpret_cast(0xCF28F33974EC44F6),reinterpret_cast(0xD5EA32DB758EF0B8),reinterpret_cast(0x7F12180DC3A24B53),true,true,true,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::GREATER_EQUAL,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 32,24,4,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R16G16B16A16_FLOAT,RenderFormat::D32_FLOAT,1,false,0x14 }, +{ reinterpret_cast(0xD6EA5D49CB1F965C),reinterpret_cast(0x2FE1A3913EF3EE8A),reinterpret_cast(0x25B8F9D92644DAE0),true,true,true,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::GREATER_EQUAL,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 44,24,4,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R16G16B16A16_FLOAT,RenderFormat::D32_FLOAT,1,false,0x15 }, +{ reinterpret_cast(0xD6EA5D49CB1F965C),reinterpret_cast(0x53EF4B071D1B59D3),reinterpret_cast(0x25B8F9D92644DAE0),true,true,true,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::GREATER_EQUAL,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 44,24,4,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R16G16B16A16_FLOAT,RenderFormat::D32_FLOAT,1,false,0x15 }, +{ reinterpret_cast(0xD738D79626374EBE),reinterpret_cast(0xAAE40F54746EB116),reinterpret_cast(0xB7BBCC93738C9DE4),false,true,true,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::BACK,RenderComparisonFunction::GREATER_EQUAL,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0x0,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 40,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::UNKNOWN,RenderFormat::D32_FLOAT,1,false,0x11 }, +{ reinterpret_cast(0xFF5A2C4C81AA6FDF),reinterpret_cast(0xE105CB09EA634E60),reinterpret_cast(0x7F12180DC3A24B53),true,true,true,RenderBlend::ONE,RenderBlend::ZERO,RenderCullMode::NONE,RenderComparisonFunction::GREATER_EQUAL,false,RenderBlendOperation::ADD,0,0,RenderBlend::ONE,RenderBlend::ZERO,RenderBlendOperation::ADD,0xF,RenderPrimitiveTopology::TRIANGLE_STRIP,{ 32,24,4,0,0,0,0,0,0,0,0,0,0,0,0,0 },RenderFormat::R16G16B16A16_FLOAT,RenderFormat::D32_FLOAT,1,false,0x14 }, diff --git a/UnleashedRecomp/gpu/cache/vertex_declaration_cache.h b/UnleashedRecomp/gpu/cache/vertex_declaration_cache.h new file mode 100644 index 0000000..dbf9eab --- /dev/null +++ b/UnleashedRecomp/gpu/cache/vertex_declaration_cache.h @@ -0,0 +1,14 @@ +g_vertexElements_25B8F9D92644DAE0, +g_vertexElements_5A22D93C543DF925, +g_vertexElements_5A2395E29F93DA3C, +g_vertexElements_6196BF64CB935CA5, +g_vertexElements_6FAE71C7134074A4, +g_vertexElements_7F12180DC3A24B53, +g_vertexElements_84BACD816D86543C, +g_vertexElements_A81F28FA43A9B511, +g_vertexElements_B22B7B7B968141C6, +g_vertexElements_B7BBCC93738C9DE4, +g_vertexElements_C64D046063DE2F63, +g_vertexElements_D452411D3FB80A0D, +g_vertexElements_E6B3B3D286909AB9, +g_vertexElements_FFFDDC62D86892F1, \ No newline at end of file diff --git a/UnleashedRecomp/gpu/cache/vertex_element_cache.h b/UnleashedRecomp/gpu/cache/vertex_element_cache.h new file mode 100644 index 0000000..0d152f0 --- /dev/null +++ b/UnleashedRecomp/gpu/cache/vertex_element_cache.h @@ -0,0 +1,14 @@ +static uint8_t g_vertexElements_25B8F9D92644DAE0[] = {0x0,0x0,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x2A,0x21,0x90,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x2A,0x21,0x90,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x14,0x0,0x2A,0x21,0x90,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x2C,0x23,0x5F,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x1C,0x0,0x2C,0x23,0x5F,0x0,0x5,0x1,0x0,0x0,0x0,0x0,0x20,0x0,0x1A,0x20,0x86,0x0,0xA,0x0,0x0,0x0,0x0,0x0,0x24,0x0,0x1A,0x22,0x86,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x28,0x0,0x1A,0x20,0x86,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x5,0x4,0x0,0x0,0x1,0x0,0xC,0x0,0x2C,0x21,0x59,0x0,0x5,0x5,0x0,0x0,0x1,0x0,0x10,0x0,0x2C,0x21,0x59,0x0,0x5,0x6,0x0,0x0,0x1,0x0,0x14,0x0,0x18,0x28,0x86,0x0,0xA,0x1,0x0,0x0,0x2,0x0,0x0,0x0,0x2C,0x82,0xA1,0x0,0x0,0x1,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; +static uint8_t g_vertexElements_5A22D93C543DF925[] = {0x0,0x0,0x0,0x0,0x0,0x2C,0x23,0xA5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x18,0x28,0x86,0x0,0xA,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x2C,0x23,0xA5,0x0,0x5,0x0,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; +static uint8_t g_vertexElements_5A2395E29F93DA3C[] = {0x0,0x0,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x2A,0x23,0xB9,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x2C,0x23,0xA5,0x0,0x5,0x0,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; +static uint8_t g_vertexElements_6196BF64CB935CA5[] = {0x0,0x0,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x2C,0x23,0xA5,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x14,0x0,0x18,0x28,0x86,0x0,0xA,0x0,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; +static uint8_t g_vertexElements_6FAE71C7134074A4[] = {0x0,0x0,0x0,0x0,0x0,0x2C,0x23,0xA5,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x18,0x28,0x86,0x0,0xA,0x0,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; +static uint8_t g_vertexElements_7F12180DC3A24B53[] = {0x0,0x0,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x2A,0x23,0xB9,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x2C,0x23,0xA5,0x0,0x5,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x5,0x1,0x0,0x0,0x1,0x0,0xC,0x0,0x1A,0x22,0x86,0x0,0x5,0x2,0x0,0x0,0x1,0x0,0x10,0x0,0x2C,0x21,0x59,0x0,0x5,0x3,0x0,0x0,0x1,0x0,0x14,0x0,0x18,0x28,0x86,0x0,0xA,0x1,0x0,0x0,0x2,0x0,0x0,0x0,0x2C,0x82,0xA1,0x0,0x0,0x1,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; +static uint8_t g_vertexElements_84BACD816D86543C[] = {0x0,0x0,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x2A,0x23,0xB9,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x2A,0x23,0xB9,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x24,0x0,0x2A,0x23,0xB9,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x30,0x0,0x2C,0x23,0xA5,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x38,0x0,0x2C,0x23,0xA5,0x0,0x5,0x1,0x0,0x0,0x0,0x0,0x40,0x0,0x2C,0x23,0xA5,0x0,0x5,0x2,0x0,0x0,0x0,0x0,0x48,0x0,0x2C,0x23,0xA5,0x0,0x5,0x3,0x0,0x0,0x0,0x0,0x50,0x0,0x1A,0x23,0xA6,0x0,0xA,0x0,0x0,0x0,0x0,0x0,0x60,0x0,0x1A,0x23,0x86,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x64,0x0,0x1A,0x20,0x86,0x0,0x1,0x0,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; +static uint8_t g_vertexElements_A81F28FA43A9B511[] = {0x0,0x0,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x2A,0x23,0xB9,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x2C,0x23,0xA5,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x20,0x0,0x18,0x28,0x86,0x0,0xA,0x0,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; +static uint8_t g_vertexElements_B22B7B7B968141C6[] = {0x0,0x0,0x0,0x0,0x0,0x1A,0x23,0xA6,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x18,0x28,0x86,0x0,0xA,0x0,0x0,0x0,0x0,0x0,0x14,0x0,0x2C,0x23,0xA5,0x0,0x5,0x0,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; +static uint8_t g_vertexElements_B7BBCC93738C9DE4[] = {0x0,0x0,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x1A,0x22,0x86,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x1A,0x20,0x86,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x14,0x0,0x2A,0x21,0x90,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x2A,0x21,0x90,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x1C,0x0,0x2A,0x21,0x90,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x20,0x0,0x2C,0x23,0x5F,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x24,0x0,0x1A,0x20,0x86,0x0,0xA,0x0,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; +static uint8_t g_vertexElements_C64D046063DE2F63[] = {0x0,0x0,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x1A,0x22,0x86,0x0,0x2,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x1A,0x20,0x86,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x14,0x0,0x2A,0x21,0x90,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x2A,0x21,0x90,0x0,0x6,0x0,0x0,0x0,0x0,0x0,0x1C,0x0,0x2A,0x21,0x90,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x20,0x0,0x2C,0x23,0x5F,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x24,0x0,0x2C,0x23,0x5F,0x0,0x5,0x1,0x0,0x0,0x0,0x0,0x28,0x0,0x1A,0x20,0x86,0x0,0xA,0x0,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; +static uint8_t g_vertexElements_D452411D3FB80A0D[] = {0x0,0x0,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x2C,0x23,0xA5,0x0,0x5,0x0,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; +static uint8_t g_vertexElements_E6B3B3D286909AB9[] = {0x0,0x0,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x2A,0x23,0xB9,0x0,0x3,0x0,0x0,0x0,0x0,0x0,0x18,0x0,0x2C,0x23,0xA5,0x0,0x5,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x5,0x1,0x0,0x0,0x1,0x0,0xC,0x0,0x1A,0x22,0x86,0x0,0x5,0x2,0x0,0x0,0x1,0x0,0x10,0x0,0x2C,0x83,0xA4,0x0,0x5,0x3,0x0,0x0,0x1,0x0,0x14,0x0,0x18,0x28,0x86,0x0,0xA,0x1,0x0,0x0,0x2,0x0,0x0,0x0,0x2C,0x82,0xA1,0x0,0x0,0x1,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0x0,}; +static uint8_t g_vertexElements_FFFDDC62D86892F1[] = {0x0,0x0,0x0,0x0,0x0,0x2A,0x23,0xB9,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xC,0x0,0x2C,0x23,0xA5,0x0,0x5,0x0,0x0,0x0,0x0,0x0,0x14,0x0,0x2A,0x23,0xB9,0x0,0x3,0x0,0x0,0x0,0xFF,0x0,0x0,0xFF,0xFF,0xFF,0xFF,0x0,0x0,0x0,0xC9,}; \ No newline at end of file diff --git a/UnleashedRecomp/gpu/rhi/rt64_d3d12.cpp b/UnleashedRecomp/gpu/rhi/rt64_d3d12.cpp index 310e535..eccf3cc 100644 --- a/UnleashedRecomp/gpu/rhi/rt64_d3d12.cpp +++ b/UnleashedRecomp/gpu/rhi/rt64_d3d12.cpp @@ -2672,6 +2672,10 @@ namespace RT64 { } } + void D3D12ComputePipeline::setName(const std::string& name) const { + setObjectName(d3d, name); + } + RenderPipelineProgram D3D12ComputePipeline::getProgram(const std::string &name) const { assert(false && "Compute pipelines can't retrieve shader programs."); return RenderPipelineProgram(); @@ -2791,6 +2795,10 @@ namespace RT64 { } } + void D3D12GraphicsPipeline::setName(const std::string& name) const { + setObjectName(d3d, name); + } + RenderPipelineProgram D3D12GraphicsPipeline::getProgram(const std::string &name) const { assert(false && "Graphics pipelines can't retrieve shader programs."); return RenderPipelineProgram(); @@ -3011,6 +3019,10 @@ namespace RT64 { } } + void D3D12RaytracingPipeline::setName(const std::string& name) const { + setObjectName(stateObject, name); + } + RenderPipelineProgram D3D12RaytracingPipeline::getProgram(const std::string &name) const { auto it = nameProgramMap.find(name); assert((it != nameProgramMap.end()) && "Program must exist in the PSO."); diff --git a/UnleashedRecomp/gpu/rhi/rt64_d3d12.h b/UnleashedRecomp/gpu/rhi/rt64_d3d12.h index ff62d1b..96f98bd 100644 --- a/UnleashedRecomp/gpu/rhi/rt64_d3d12.h +++ b/UnleashedRecomp/gpu/rhi/rt64_d3d12.h @@ -355,6 +355,7 @@ namespace RT64 { D3D12ComputePipeline(D3D12Device *device, const RenderComputePipelineDesc &desc); ~D3D12ComputePipeline() override; + virtual void setName(const std::string& name) const override; virtual RenderPipelineProgram getProgram(const std::string &name) const override; }; @@ -365,6 +366,7 @@ namespace RT64 { D3D12GraphicsPipeline(D3D12Device *device, const RenderGraphicsPipelineDesc &desc); ~D3D12GraphicsPipeline() override; + virtual void setName(const std::string& name) const override; virtual RenderPipelineProgram getProgram(const std::string &name) const override; }; @@ -377,6 +379,7 @@ namespace RT64 { D3D12RaytracingPipeline(D3D12Device *device, const RenderRaytracingPipelineDesc &desc, const RenderPipeline *previousPipeline); ~D3D12RaytracingPipeline() override; + virtual void setName(const std::string& name) const override; virtual RenderPipelineProgram getProgram(const std::string &name) const override; }; diff --git a/UnleashedRecomp/gpu/rhi/rt64_render_interface.h b/UnleashedRecomp/gpu/rhi/rt64_render_interface.h index 438590b..a01b70e 100644 --- a/UnleashedRecomp/gpu/rhi/rt64_render_interface.h +++ b/UnleashedRecomp/gpu/rhi/rt64_render_interface.h @@ -53,6 +53,7 @@ namespace RT64 { struct RenderPipeline { virtual ~RenderPipeline() { } + virtual void setName(const std::string& name) const = 0; virtual RenderPipelineProgram getProgram(const std::string &name) const = 0; }; diff --git a/UnleashedRecomp/gpu/rhi/rt64_vulkan.cpp b/UnleashedRecomp/gpu/rhi/rt64_vulkan.cpp index 19da76d..d5e9db1 100644 --- a/UnleashedRecomp/gpu/rhi/rt64_vulkan.cpp +++ b/UnleashedRecomp/gpu/rhi/rt64_vulkan.cpp @@ -1316,6 +1316,10 @@ namespace RT64 { } } + void VulkanComputePipeline::setName(const std::string& name) const { + setObjectName(device->vk, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT, uint64_t(vk), name); + } + RenderPipelineProgram VulkanComputePipeline::getProgram(const std::string &name) const { assert(false && "Compute pipelines can't retrieve shader programs."); return RenderPipelineProgram(); @@ -1552,6 +1556,10 @@ namespace RT64 { } } + void VulkanGraphicsPipeline::setName(const std::string& name) const { + setObjectName(device->vk, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT, uint64_t(vk), name); + } + RenderPipelineProgram VulkanGraphicsPipeline::getProgram(const std::string &name) const { assert(false && "Graphics pipelines can't retrieve shader programs."); return RenderPipelineProgram(); @@ -1752,6 +1760,10 @@ namespace RT64 { } } + void VulkanRaytracingPipeline::setName(const std::string& name) const { + setObjectName(device->vk, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT, uint64_t(vk), name); + } + RenderPipelineProgram VulkanRaytracingPipeline::getProgram(const std::string &name) const { auto it = nameProgramMap.find(name); assert((it != nameProgramMap.end()) && "Program must exist in the PSO."); diff --git a/UnleashedRecomp/gpu/rhi/rt64_vulkan.h b/UnleashedRecomp/gpu/rhi/rt64_vulkan.h index ef2f1ff..20f6fe0 100644 --- a/UnleashedRecomp/gpu/rhi/rt64_vulkan.h +++ b/UnleashedRecomp/gpu/rhi/rt64_vulkan.h @@ -158,6 +158,7 @@ namespace RT64 { VulkanComputePipeline(VulkanDevice *device, const RenderComputePipelineDesc &desc); ~VulkanComputePipeline() override; + void setName(const std::string& name) const override; RenderPipelineProgram getProgram(const std::string &name) const override; }; @@ -167,6 +168,7 @@ namespace RT64 { VulkanGraphicsPipeline(VulkanDevice *device, const RenderGraphicsPipelineDesc &desc); ~VulkanGraphicsPipeline() override; + void setName(const std::string& name) const override; RenderPipelineProgram getProgram(const std::string &name) const override; static VkRenderPass createRenderPass(VulkanDevice *device, const VkFormat *renderTargetFormat, uint32_t renderTargetCount, VkFormat depthTargetFormat, VkSampleCountFlagBits sampleCount); }; @@ -179,6 +181,7 @@ namespace RT64 { VulkanRaytracingPipeline(VulkanDevice *device, const RenderRaytracingPipelineDesc &desc, const RenderPipeline *previousPipeline); ~VulkanRaytracingPipeline() override; + void setName(const std::string& name) const override; RenderPipelineProgram getProgram(const std::string &name) const override; }; diff --git a/UnleashedRecomp/gpu/shader/gamma_correction_ps.hlsl b/UnleashedRecomp/gpu/shader/gamma_correction_ps.hlsl index dfed91b..b1e2f99 100644 --- a/UnleashedRecomp/gpu/shader/gamma_correction_ps.hlsl +++ b/UnleashedRecomp/gpu/shader/gamma_correction_ps.hlsl @@ -1,4 +1,4 @@ -#include "../../../thirdparty/ShaderRecomp/ShaderRecomp/shader_common.hlsli" +#include "../../../thirdparty/ShaderRecomp/ShaderRecomp/shader_common.h" #ifdef __spirv__ diff --git a/UnleashedRecomp/gpu/shader/movie_common.hlsli b/UnleashedRecomp/gpu/shader/movie_common.hlsli index d890e48..02e92e9 100644 --- a/UnleashedRecomp/gpu/shader/movie_common.hlsli +++ b/UnleashedRecomp/gpu/shader/movie_common.hlsli @@ -1,6 +1,6 @@ #pragma once -#include "../../../thirdparty/ShaderRecomp/ShaderRecomp/shader_common.hlsli" +#include "../../../thirdparty/ShaderRecomp/ShaderRecomp/shader_common.h" #ifdef __spirv__ diff --git a/UnleashedRecomp/gpu/shader/resolve_msaa_depth.hlsli b/UnleashedRecomp/gpu/shader/resolve_msaa_depth.hlsli index bc04695..d413717 100644 --- a/UnleashedRecomp/gpu/shader/resolve_msaa_depth.hlsli +++ b/UnleashedRecomp/gpu/shader/resolve_msaa_depth.hlsli @@ -14,7 +14,7 @@ float main(in float4 position : SV_Position) : SV_Depth float result = g_Texture2DMSDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].Load(int2(position.xy), 0); [unroll] for (int i = 1; i < SAMPLE_COUNT; i++) - result = max(result, g_Texture2DMSDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].Load(int2(position.xy), i)); + result = min(result, g_Texture2DMSDescriptorHeap[g_PushConstants.ResourceDescriptorIndex].Load(int2(position.xy), i)); return result; } diff --git a/UnleashedRecomp/gpu/video.cpp b/UnleashedRecomp/gpu/video.cpp index 109468e..36c5dde 100644 --- a/UnleashedRecomp/gpu/video.cpp +++ b/UnleashedRecomp/gpu/video.cpp @@ -4,15 +4,19 @@ #include #include #include +#include #include #include #include #include "imgui_snapshot.h" #include "video.h" -#include +#include #include +#include + +#include "../../thirdparty/ShaderRecomp/ShaderRecomp/shader_common.h" #include "shader/copy_vs.hlsl.dxil.h" #include "shader/copy_vs.hlsl.spirv.h" #include "shader/gamma_correction_ps.hlsl.dxil.h" @@ -32,6 +36,10 @@ #include "shader/resolve_msaa_depth_8x.hlsl.dxil.h" #include "shader/resolve_msaa_depth_8x.hlsl.spirv.h" +#if defined(ASYNC_PSO_DEBUG) || defined(PSO_CACHING) +#include +#endif + extern "C" { __declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001; @@ -44,6 +52,7 @@ namespace RT64 extern std::unique_ptr CreateVulkanInterface(); } +#pragma pack(push, 1) struct PipelineState { GuestShader* vertexShader = nullptr; @@ -70,14 +79,9 @@ struct PipelineState RenderFormat depthStencilFormat{}; RenderSampleCounts sampleCount = RenderSampleCount::COUNT_1; bool enableAlphaToCoverage = false; + uint32_t specConstants = 0; }; - -enum class AlphaTestMode : uint32_t -{ - Disabled, - AlphaThreshold, - AlphaToCoverage -}; +#pragma pack(pop) struct SharedConstants { @@ -85,12 +89,9 @@ struct SharedConstants uint32_t texture3DIndices[16]{}; uint32_t textureCubeIndices[16]{}; uint32_t samplerIndices[16]{}; - AlphaTestMode alphaTestMode{}; - float alphaThreshold{}; uint32_t booleans{}; uint32_t swappedTexcoords{}; - uint32_t inputLayoutFlags{}; - uint32_t enableGIBicubicFiltering{}; + float alphaThreshold{}; }; static GuestSurface* g_renderTarget; @@ -98,7 +99,6 @@ static GuestSurface* g_depthStencil; static RenderFramebuffer* g_framebuffer; static RenderViewport g_viewport(0.0f, 0.0f, 1280.0f, 720.0f); static bool g_halfPixel = true; -static uint32_t g_zFunc; static PipelineState g_pipelineState; static SharedConstants g_sharedConstants; static RenderSamplerDesc g_samplerDescs[16]; @@ -239,6 +239,37 @@ static TextureDescriptorAllocator g_textureDescriptorAllocator; static std::unique_ptr g_pipelineLayout; static xxHashMap> g_pipelines; +#ifdef ASYNC_PSO_DEBUG +static std::atomic g_pipelinesCreatedInRenderThread; +static std::atomic g_pipelinesCreatedAsynchronously; +static std::atomic g_pipelinesDropped; +static std::atomic g_pipelinesCurrentlyCompiling; +static std::string g_pipelineDebugText; +static Mutex g_debugMutex; +#endif + +#ifdef PSO_CACHING +static std::vector g_pipelineStatesToCache; +static Mutex g_pipelineCacheMutex; +#endif + +static std::atomic g_compilingDataCount; +static std::atomic g_pendingDataCount; + +static const PipelineState g_pipelineStateCache[] = +{ +#include "cache/pipeline_state_cache.h" +}; + +static bool g_pendingPipelineStateCache = true; + +#include "cache/vertex_element_cache.h" + +static uint8_t* const g_vertexDeclarationCache[] = +{ +#include "cache/vertex_declaration_cache.h" +}; + static xxHashMap>> g_samplerStates; static Mutex g_vertexDeclarationMutex; @@ -472,9 +503,11 @@ static void DestructTempResources() case ResourceType::VertexShader: case ResourceType::PixelShader: + { reinterpret_cast(resource)->~GuestShader(); break; } + } g_userHeap.Free(resource); } @@ -545,6 +578,7 @@ enum class RenderCommandType SetBooleans, SetVertexShaderConstants, SetPixelShaderConstants, + AddPipeline, DrawPrimitive, DrawIndexedPrimitive, DrawPrimitiveUP, @@ -652,6 +686,12 @@ struct RenderCommand UploadAllocation allocation; } setPixelShaderConstants; + struct + { + XXH64_hash_t hash; + RenderPipeline* pipeline; + } addPipeline; + struct { uint32_t primitiveType; @@ -723,18 +763,23 @@ static void SetRenderStateUnimplemented(GuestDevice* device, uint32_t value) static void SetAlphaTestMode(bool enable) { - AlphaTestMode alphaTestMode = AlphaTestMode::Disabled; + uint32_t specConstants = 0; + bool enableAlphaToCoverage = false; if (enable) { - if (Config::AlphaToCoverage && g_renderTarget != nullptr && g_renderTarget->sampleCount != RenderSampleCount::COUNT_1) - alphaTestMode = AlphaTestMode::AlphaToCoverage; + enableAlphaToCoverage = Config::AlphaToCoverage && g_renderTarget != nullptr && g_renderTarget->sampleCount != RenderSampleCount::COUNT_1; + + if (enableAlphaToCoverage) + specConstants = SPEC_CONSTANT_ALPHA_TO_COVERAGE; else - alphaTestMode = AlphaTestMode::AlphaThreshold; + specConstants = SPEC_CONSTANT_ALPHA_TEST; } - SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.alphaTestMode, alphaTestMode); - SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.enableAlphaToCoverage, alphaTestMode == AlphaTestMode::AlphaToCoverage); + specConstants |= (g_pipelineState.specConstants & ~(SPEC_CONSTANT_ALPHA_TEST | SPEC_CONSTANT_ALPHA_TO_COVERAGE)); + + SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.enableAlphaToCoverage, enableAlphaToCoverage); + SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.specConstants, specConstants); } static RenderBlend ConvertBlendMode(uint32_t blendMode) @@ -767,36 +812,6 @@ static RenderBlend ConvertBlendMode(uint32_t blendMode) } } -// The game renders the main scene with reverse Z where the viewport's minDepth and maxDepth -// values are swapped. We negate this to improve compatibility with old hardware. -static RenderComparisonFunction ConvertComparisonFunc(uint32_t cmpFunc, bool reverseZ) -{ - switch (cmpFunc) - { - case D3DCMP_LESS: - return reverseZ ? RenderComparisonFunction::GREATER : RenderComparisonFunction::LESS; - case D3DCMP_LESSEQUAL: - return reverseZ ? RenderComparisonFunction::GREATER_EQUAL : RenderComparisonFunction::LESS_EQUAL; - case D3DCMP_GREATER: - return reverseZ ? RenderComparisonFunction::LESS : RenderComparisonFunction::GREATER; - case D3DCMP_GREATEREQUAL: - return reverseZ ? RenderComparisonFunction::LESS_EQUAL : RenderComparisonFunction::GREATER_EQUAL; - - case D3DCMP_NEVER: - return RenderComparisonFunction::NEVER; - case D3DCMP_EQUAL: - return RenderComparisonFunction::EQUAL; - case D3DCMP_NOTEQUAL: - return RenderComparisonFunction::NOT_EQUAL; - case D3DCMP_ALWAYS: - return RenderComparisonFunction::ALWAYS; - - default: - assert(false && "Unknown comparison function"); - return RenderComparisonFunction::NEVER; - } -} - static RenderBlendOperation ConvertBlendOp(uint32_t blendOp) { switch (blendOp) @@ -875,8 +890,41 @@ static void ProcSetRenderState(const RenderCommand& cmd) } case D3DRS_ZFUNC: { - g_zFunc = value; - SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.zFunc, ConvertComparisonFunc(value, g_viewport.minDepth >= g_viewport.maxDepth)); + RenderComparisonFunction comparisonFunc; + + switch (value) + { + case D3DCMP_NEVER: + comparisonFunc = RenderComparisonFunction::NEVER; + break; + case D3DCMP_LESS: + comparisonFunc = RenderComparisonFunction::LESS; + break; + case D3DCMP_EQUAL: + comparisonFunc = RenderComparisonFunction::EQUAL; + break; + case D3DCMP_LESSEQUAL: + comparisonFunc = RenderComparisonFunction::LESS_EQUAL; + break; + case D3DCMP_GREATER: + comparisonFunc = RenderComparisonFunction::GREATER; + break; + case D3DCMP_NOTEQUAL: + comparisonFunc = RenderComparisonFunction::NOT_EQUAL; + break; + case D3DCMP_GREATEREQUAL: + comparisonFunc = RenderComparisonFunction::GREATER_EQUAL; + break; + case D3DCMP_ALWAYS: + comparisonFunc = RenderComparisonFunction::ALWAYS; + break; + default: + assert(false && "Unknown comparison function"); + comparisonFunc = RenderComparisonFunction::NEVER; + break; + } + + SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.zFunc, comparisonFunc); break; } case D3DRS_ALPHAREF: @@ -1373,7 +1421,10 @@ static void BeginCommandList() g_sharedConstants.textureCubeIndices[i] = TEXTURE_DESCRIPTOR_NULL_TEXTURE_CUBE; } - g_sharedConstants.enableGIBicubicFiltering = (Config::GITextureFiltering == EGITextureFiltering::Bicubic); + if (Config::GITextureFiltering == EGITextureFiltering::Bicubic) + g_pipelineState.specConstants |= SPEC_CONSTANT_BICUBIC_GI_FILTER; + else + g_pipelineState.specConstants &= ~SPEC_CONSTANT_BICUBIC_GI_FILTER; auto& commandList = g_commandLists[g_frame]; @@ -1616,7 +1667,23 @@ static void DrawImGui() { ImGui_ImplSDL2_NewFrame(); ImGui::NewFrame(); - // ImGui logic here + +#ifdef ASYNC_PSO_DEBUG + if (ImGui::Begin("Async PSO Stats")) + { + ImGui::Text("Pipelines Created In Render Thread: %d", g_pipelinesCreatedInRenderThread.load()); + ImGui::Text("Pipelines Created Asynchronously: %d", g_pipelinesCreatedAsynchronously.load()); + ImGui::Text("Pipelines Dropped: %d", g_pipelinesDropped.load()); + ImGui::Text("Pipelines Currently Compiling: %d", g_pipelinesCurrentlyCompiling.load()); + ImGui::Text("Compiling Data Count: %d", g_compilingDataCount.load()); + ImGui::Text("Pending Data Count: %d", g_pendingDataCount.load()); + + std::lock_guard lock(g_debugMutex); + ImGui::TextUnformatted(g_pipelineDebugText.c_str()); + } + ImGui::End(); +#endif + ImGui::Render(); auto drawData = ImGui::GetDrawData(); @@ -1680,6 +1747,8 @@ static void ProcDrawImGui(const RenderCommand& cmd) } } +static bool g_precompiledPipelineStateCache = false; + static void Present() { DrawImGui(); @@ -1690,6 +1759,18 @@ static void Present() RenderCommand cmd; cmd.type = RenderCommandType::Present; g_renderQueue.enqueue(cmd); + + // All the shaders are available at this point. We can precompile embedded PSOs then. + if (!g_precompiledPipelineStateCache) + { + // This is all the model consumer thread needs to see. + ++g_compilingDataCount; + + if ((++g_pendingDataCount) == 1) + g_pendingDataCount.notify_all(); + + g_precompiledPipelineStateCache = true; + } } static void SetRootDescriptor(const UploadAllocation& allocation, size_t index) @@ -1998,11 +2079,8 @@ static void FlushViewport() viewport.height *= height / 720.0f; } - if (viewport.minDepth >= viewport.maxDepth) - { - viewport.minDepth = 1.0f - viewport.minDepth; - viewport.maxDepth = 1.0f - viewport.maxDepth; - } + if (viewport.minDepth > viewport.maxDepth) + std::swap(viewport.minDepth, viewport.maxDepth); commandList->setViewports(viewport); @@ -2190,7 +2268,7 @@ static void ProcSetRenderTarget(const RenderCommand& cmd) SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.sampleCount, args.renderTarget != nullptr ? args.renderTarget->sampleCount : RenderSampleCount::COUNT_1); // When alpha to coverage is enabled, update the alpha test mode as it's dependent on sample count. - SetAlphaTestMode(g_sharedConstants.alphaTestMode != AlphaTestMode::Disabled); + SetAlphaTestMode((g_pipelineState.specConstants & (SPEC_CONSTANT_ALPHA_TEST | SPEC_CONSTANT_ALPHA_TO_COVERAGE)) != 0); } static void SetDepthStencilSurface(GuestDevice* device, GuestSurface* depthStencil) @@ -2314,8 +2392,7 @@ static void ProcClear(const RenderCommand& cmd) if (!canClearInOnePass) SetFramebuffer(nullptr, g_depthStencil, true); - // The condition here is done by the game to determine reverse Z. - commandList->clearDepth(true, g_depthStencil->guestFormat == D3DFMT_D24FS8 ? (1.0f - args.z) : args.z); + commandList->clearDepth(true, args.z); } } @@ -2349,11 +2426,16 @@ static void ProcSetViewport(const RenderCommand& cmd) SetDirtyValue(g_dirtyStates.viewport, g_viewport.height, args.height); SetDirtyValue(g_dirtyStates.viewport, g_viewport.minDepth, args.minDepth); SetDirtyValue(g_dirtyStates.viewport, g_viewport.maxDepth, args.maxDepth); + + uint32_t specConstants = g_pipelineState.specConstants; + if (args.minDepth > args.maxDepth) + specConstants |= SPEC_CONSTANT_REVERSE_Z; + else + specConstants &= ~SPEC_CONSTANT_REVERSE_Z; + + SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.specConstants, specConstants); g_dirtyStates.scissorRect |= g_dirtyStates.viewport; - - // Update Z function as it's dependent on reverse Z. - SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.zFunc, ConvertComparisonFunc(g_zFunc, args.minDepth >= args.maxDepth)); } static void SetTexture(GuestDevice* device, uint32_t index, GuestTexture* texture) @@ -2404,9 +2486,163 @@ static void ProcSetScissorRect(const RenderCommand& cmd) SetDirtyValue(g_dirtyStates.scissorRect, g_scissorRect.right, args.right); } -static RenderPipeline* CreateGraphicsPipeline(PipelineState pipelineState) +static Mutex g_compiledSpecConstantLibraryBlobMutex; +static ankerl::unordered_dense::map> g_compiledSpecConstantLibraryBlobs; + +static RenderShader* GetOrLinkShader(GuestShader* guestShader, uint32_t specConstants) +{ + if (g_vulkan || + guestShader->shaderCacheEntry == nullptr || + guestShader->shaderCacheEntry->specConstantsMask == 0) + { + std::lock_guard lock(guestShader->mutex); + + if (guestShader->shader == nullptr) + { + assert(guestShader->shaderCacheEntry != nullptr); + + if (g_vulkan) + { + auto compressedSpirvData = g_shaderCache.get() + guestShader->shaderCacheEntry->spirvOffset; + + std::vector decoded(smolv::GetDecodedBufferSize(compressedSpirvData, guestShader->shaderCacheEntry->spirvSize)); + bool result = smolv::Decode(compressedSpirvData, guestShader->shaderCacheEntry->spirvSize, decoded.data(), decoded.size()); + assert(result); + + guestShader->shader = g_device->createShader(decoded.data(), decoded.size(), "main", RenderShaderFormat::SPIRV); + } + else + { + guestShader->shader = g_device->createShader(g_shaderCache.get() + guestShader->shaderCacheEntry->dxilOffset, + guestShader->shaderCacheEntry->dxilSize, "main", RenderShaderFormat::DXIL); + } + } + + return guestShader->shader.get(); + } + + specConstants &= guestShader->shaderCacheEntry->specConstantsMask; + + RenderShader* shader; + { + std::lock_guard lock(guestShader->mutex); + shader = guestShader->linkedShaders[specConstants].get(); + } + + if (shader == nullptr) + { + thread_local ComPtr s_dxcCompiler; + thread_local ComPtr s_dxcLinker; + thread_local ComPtr s_dxcUtils; + + wchar_t specConstantsLibName[0x100]; + swprintf_s(specConstantsLibName, L"SpecConstants_%d", specConstants); + + ComPtr specConstantLibraryBlob; + { + std::lock_guard lock(g_compiledSpecConstantLibraryBlobMutex); + specConstantLibraryBlob = g_compiledSpecConstantLibraryBlobs[specConstants]; + } + + if (specConstantLibraryBlob == nullptr) + { + if (s_dxcCompiler == nullptr) + { + HRESULT hr = DxcCreateInstance(CLSID_DxcCompiler, IID_PPV_ARGS(s_dxcCompiler.GetAddressOf())); + assert(SUCCEEDED(hr) && s_dxcCompiler != nullptr); + } + + char libraryHlsl[0x100]; + sprintf_s(libraryHlsl, "export uint g_SpecConstants() { return %d; }", specConstants); + + DxcBuffer buffer{}; + buffer.Ptr = libraryHlsl; + buffer.Size = strlen(libraryHlsl); + + const wchar_t* args[1]; + args[0] = L"-T lib_6_3"; + + ComPtr result; + HRESULT hr = s_dxcCompiler->Compile(&buffer, args, std::size(args), nullptr, IID_PPV_ARGS(result.GetAddressOf())); + assert(SUCCEEDED(hr) && result != nullptr); + + hr = result->GetResult(specConstantLibraryBlob.GetAddressOf()); + assert(SUCCEEDED(hr) && specConstantLibraryBlob != nullptr); + + std::lock_guard lock(g_compiledSpecConstantLibraryBlobMutex); + g_compiledSpecConstantLibraryBlobs.emplace(specConstants, specConstantLibraryBlob); + } + + if (s_dxcLinker == nullptr) + { + HRESULT hr = DxcCreateInstance(CLSID_DxcLinker, IID_PPV_ARGS(s_dxcLinker.GetAddressOf())); + assert(SUCCEEDED(hr) && s_dxcLinker != nullptr); + } + + s_dxcLinker->RegisterLibrary(specConstantsLibName, specConstantLibraryBlob.Get()); + + wchar_t shaderLibName[0x100]; + swprintf_s(shaderLibName, L"Shader_%d", guestShader->shaderCacheEntry->dxilOffset); + + ComPtr shaderLibraryBlob; + { + std::lock_guard lock(guestShader->mutex); + shaderLibraryBlob = guestShader->libraryBlob; + } + + if (shaderLibraryBlob == nullptr) + { + if (s_dxcUtils == nullptr) + { + HRESULT hr = DxcCreateInstance(CLSID_DxcUtils, IID_PPV_ARGS(s_dxcUtils.GetAddressOf())); + assert(SUCCEEDED(hr) && s_dxcUtils != nullptr); + } + + HRESULT hr = s_dxcUtils->CreateBlobFromPinned( + g_shaderCache.get() + guestShader->shaderCacheEntry->dxilOffset, + guestShader->shaderCacheEntry->dxilSize, + DXC_CP_ACP, + shaderLibraryBlob.GetAddressOf()); + + assert(SUCCEEDED(hr) && shaderLibraryBlob != nullptr); + + std::lock_guard lock(guestShader->mutex); + guestShader->libraryBlob = shaderLibraryBlob; + } + + s_dxcLinker->RegisterLibrary(shaderLibName, shaderLibraryBlob.Get()); + + const wchar_t* libraryNames[] = { specConstantsLibName, shaderLibName }; + + ComPtr result; + HRESULT hr = s_dxcLinker->Link(L"main", guestShader->type == ResourceType::VertexShader ? L"vs_6_0" : L"ps_6_0", + libraryNames, std::size(libraryNames), nullptr, 0, result.GetAddressOf()); + + assert(SUCCEEDED(hr) && result != nullptr); + + ComPtr blob; + hr = result->GetResult(blob.GetAddressOf()); + assert(SUCCEEDED(hr) && blob != nullptr); + + { + std::lock_guard lock(guestShader->mutex); + + auto& linkedShader = guestShader->linkedShaders[specConstants]; + if (linkedShader == nullptr) + { + linkedShader = g_device->createShader(blob->GetBufferPointer(), blob->GetBufferSize(), "main", RenderShaderFormat::DXIL); + guestShader->shaderBlobs.push_back(std::move(blob)); + } + + shader = linkedShader.get(); + } + } + + return shader; +} + +static void SanitizePipelineState(PipelineState& pipelineState) { - // Sanitize to prevent state leaking. if (!pipelineState.zEnable) { pipelineState.zWriteEnable = false; @@ -2416,6 +2652,9 @@ static RenderPipeline* CreateGraphicsPipeline(PipelineState pipelineState) pipelineState.depthStencilFormat = RenderFormat::UNKNOWN; } + if (pipelineState.slopeScaledDepthBias == 0.0f) + pipelineState.slopeScaledDepthBias = 0.0f; // Remove sign. + if (!pipelineState.colorWriteEnable) { pipelineState.alphaBlendEnable = false; @@ -2432,64 +2671,186 @@ static RenderPipeline* CreateGraphicsPipeline(PipelineState pipelineState) pipelineState.blendOpAlpha = RenderBlendOperation::ADD; } - auto& pipeline = g_pipelines[XXH3_64bits(&pipelineState, sizeof(PipelineState))]; + uint32_t specConstantsMask = 0; + if (pipelineState.vertexShader->shaderCacheEntry != nullptr) + specConstantsMask |= pipelineState.vertexShader->shaderCacheEntry->specConstantsMask; + + if (pipelineState.pixelShader != nullptr && pipelineState.pixelShader->shaderCacheEntry != nullptr) + specConstantsMask |= pipelineState.pixelShader->shaderCacheEntry->specConstantsMask; + + pipelineState.specConstants &= specConstantsMask; +} + +static std::unique_ptr CreateGraphicsPipeline(const PipelineState& pipelineState) +{ +#ifdef ASYNC_PSO_DEBUG + ++g_pipelinesCurrentlyCompiling; +#endif + + RenderGraphicsPipelineDesc desc; + desc.pipelineLayout = g_pipelineLayout.get(); + desc.vertexShader = GetOrLinkShader(pipelineState.vertexShader, pipelineState.specConstants); + desc.pixelShader = pipelineState.pixelShader != nullptr ? GetOrLinkShader(pipelineState.pixelShader, pipelineState.specConstants) : nullptr; + desc.depthFunction = pipelineState.zFunc; + desc.depthEnabled = pipelineState.zEnable; + desc.depthWriteEnabled = pipelineState.zWriteEnable; + desc.depthBias = pipelineState.depthBias; + desc.slopeScaledDepthBias = pipelineState.slopeScaledDepthBias; + desc.depthClipEnabled = true; + desc.primitiveTopology = pipelineState.primitiveTopology; + desc.cullMode = pipelineState.cullMode; + desc.renderTargetFormat[0] = pipelineState.renderTargetFormat; + desc.renderTargetBlend[0].blendEnabled = pipelineState.alphaBlendEnable; + desc.renderTargetBlend[0].srcBlend = pipelineState.srcBlend; + desc.renderTargetBlend[0].dstBlend = pipelineState.destBlend; + desc.renderTargetBlend[0].blendOp = pipelineState.blendOp; + desc.renderTargetBlend[0].srcBlendAlpha = pipelineState.srcBlendAlpha; + desc.renderTargetBlend[0].dstBlendAlpha = pipelineState.destBlendAlpha; + desc.renderTargetBlend[0].blendOpAlpha = pipelineState.blendOpAlpha; + desc.renderTargetBlend[0].renderTargetWriteMask = pipelineState.colorWriteEnable; + desc.renderTargetCount = pipelineState.renderTargetFormat != RenderFormat::UNKNOWN ? 1 : 0; + desc.depthTargetFormat = pipelineState.depthStencilFormat; + desc.multisampling.sampleCount = pipelineState.sampleCount; + desc.alphaToCoverageEnabled = pipelineState.enableAlphaToCoverage; + desc.inputElements = pipelineState.vertexDeclaration->inputElements.get(); + desc.inputElementsCount = pipelineState.vertexDeclaration->inputElementCount; + + RenderSpecConstant specConstant{}; + specConstant.value = pipelineState.specConstants; + + if (pipelineState.specConstants != 0) + { + desc.specConstants = &specConstant; + desc.specConstantsCount = 1; + } + + RenderInputSlot inputSlots[16]{}; + uint32_t inputSlotIndices[16]{}; + uint32_t inputSlotCount = 0; + + for (size_t i = 0; i < pipelineState.vertexDeclaration->inputElementCount; i++) + { + auto& inputElement = pipelineState.vertexDeclaration->inputElements[i]; + auto& inputSlotIndex = inputSlotIndices[inputElement.slotIndex]; + + if (inputSlotIndex == NULL) + inputSlotIndex = ++inputSlotCount; + + auto& inputSlot = inputSlots[inputSlotIndex - 1]; + inputSlot.index = inputElement.slotIndex; + inputSlot.stride = pipelineState.vertexStrides[inputElement.slotIndex]; + + if (pipelineState.instancing && inputElement.slotIndex != 0 && inputElement.slotIndex != 15) + inputSlot.classification = RenderInputSlotClassification::PER_INSTANCE_DATA; + else + inputSlot.classification = RenderInputSlotClassification::PER_VERTEX_DATA; + } + + desc.inputSlots = inputSlots; + desc.inputSlotsCount = inputSlotCount; + + auto pipeline = g_device->createGraphicsPipeline(desc); + +#ifdef ASYNC_PSO_DEBUG + --g_pipelinesCurrentlyCompiling; +#endif + + return pipeline; +} + +static RenderPipeline* CreateGraphicsPipelineInRenderThread(PipelineState pipelineState) +{ + SanitizePipelineState(pipelineState); + + XXH64_hash_t hash = XXH3_64bits(&pipelineState, sizeof(pipelineState)); + auto& pipeline = g_pipelines[hash]; if (pipeline == nullptr) { - RenderGraphicsPipelineDesc desc; - desc.pipelineLayout = g_pipelineLayout.get(); - desc.vertexShader = pipelineState.vertexShader->shader.get(); - desc.pixelShader = pipelineState.pixelShader != nullptr ? pipelineState.pixelShader->shader.get() : nullptr; - desc.depthFunction = pipelineState.zFunc; - desc.depthEnabled = pipelineState.zEnable; - desc.depthWriteEnabled = pipelineState.zWriteEnable; - desc.depthBias = pipelineState.depthBias; - desc.slopeScaledDepthBias = pipelineState.slopeScaledDepthBias; - desc.depthClipEnabled = true; - desc.primitiveTopology = pipelineState.primitiveTopology; - desc.cullMode = pipelineState.cullMode; - desc.renderTargetFormat[0] = pipelineState.renderTargetFormat; - desc.renderTargetBlend[0].blendEnabled = pipelineState.alphaBlendEnable; - desc.renderTargetBlend[0].srcBlend = pipelineState.srcBlend; - desc.renderTargetBlend[0].dstBlend = pipelineState.destBlend; - desc.renderTargetBlend[0].blendOp = pipelineState.blendOp; - desc.renderTargetBlend[0].srcBlendAlpha = pipelineState.srcBlendAlpha; - desc.renderTargetBlend[0].dstBlendAlpha = pipelineState.destBlendAlpha; - desc.renderTargetBlend[0].blendOpAlpha = pipelineState.blendOpAlpha; - desc.renderTargetBlend[0].renderTargetWriteMask = pipelineState.colorWriteEnable; - desc.renderTargetCount = pipelineState.renderTargetFormat != RenderFormat::UNKNOWN ? 1 : 0; - desc.depthTargetFormat = pipelineState.depthStencilFormat; - desc.multisampling.sampleCount = pipelineState.sampleCount; - desc.alphaToCoverageEnabled = pipelineState.enableAlphaToCoverage; - desc.inputElements = pipelineState.vertexDeclaration->inputElements.get(); - desc.inputElementsCount = pipelineState.vertexDeclaration->inputElementCount; + pipeline = CreateGraphicsPipeline(pipelineState); - RenderInputSlot inputSlots[16]{}; - uint32_t inputSlotIndices[16]{}; - uint32_t inputSlotCount = 0; +#ifdef ASYNC_PSO_DEBUG + bool loading = *reinterpret_cast(g_memory.Translate(0x83367A4C)); - for (size_t i = 0; i < pipelineState.vertexDeclaration->inputElementCount; i++) + if (loading) + ++g_pipelinesCreatedAsynchronously; + else + ++g_pipelinesCreatedInRenderThread; + + pipeline->setName(std::format("{} {} {} {:X}", loading ? "ASYNC" : "", + pipelineState.vertexShader->name, pipelineState.pixelShader != nullptr ? pipelineState.pixelShader->name : "", hash)); + + if (!loading) { - auto& inputElement = pipelineState.vertexDeclaration->inputElements[i]; - auto& inputSlotIndex = inputSlotIndices[inputElement.slotIndex]; - - if (inputSlotIndex == NULL) - inputSlotIndex = ++inputSlotCount; - - auto& inputSlot = inputSlots[inputSlotIndex - 1]; - inputSlot.index = inputElement.slotIndex; - inputSlot.stride = pipelineState.vertexStrides[inputElement.slotIndex]; - - if (pipelineState.instancing && inputElement.slotIndex != 0 && inputElement.slotIndex != 15) - inputSlot.classification = RenderInputSlotClassification::PER_INSTANCE_DATA; - else - inputSlot.classification = RenderInputSlotClassification::PER_VERTEX_DATA; + std::lock_guard lock(g_debugMutex); + g_pipelineDebugText = std::format( + "PipelineState {:X}:\n" + " vertexShader: {}\n" + " pixelShader: {}\n" + " vertexDeclaration: {:X}\n" + " instancing: {}\n" + " zEnable: {}\n" + " zWriteEnable: {}\n" + " srcBlend: {}\n" + " destBlend: {}\n" + " cullMode: {}\n" + " zFunc: {}\n" + " alphaBlendEnable: {}\n" + " blendOp: {}\n" + " slopeScaledDepthBias: {}\n" + " depthBias: {}\n" + " srcBlendAlpha: {}\n" + " destBlendAlpha: {}\n" + " blendOpAlpha: {}\n" + " colorWriteEnable: {:X}\n" + " primitiveTopology: {}\n" + " vertexStrides[0]: {}\n" + " vertexStrides[1]: {}\n" + " vertexStrides[2]: {}\n" + " vertexStrides[3]: {}\n" + " renderTargetFormat: {}\n" + " depthStencilFormat: {}\n" + " sampleCount: {}\n" + " enableAlphaToCoverage: {}\n" + " specConstants: {:X}\n", + hash, + pipelineState.vertexShader->name, + pipelineState.pixelShader != nullptr ? pipelineState.pixelShader->name : "", + reinterpret_cast(pipelineState.vertexDeclaration), + pipelineState.instancing, + pipelineState.zEnable, + pipelineState.zWriteEnable, + magic_enum::enum_name(pipelineState.srcBlend), + magic_enum::enum_name(pipelineState.destBlend), + magic_enum::enum_name(pipelineState.cullMode), + magic_enum::enum_name(pipelineState.zFunc), + pipelineState.alphaBlendEnable, + magic_enum::enum_name(pipelineState.blendOp), + pipelineState.slopeScaledDepthBias, + pipelineState.depthBias, + magic_enum::enum_name(pipelineState.srcBlendAlpha), + magic_enum::enum_name(pipelineState.destBlendAlpha), + magic_enum::enum_name(pipelineState.blendOpAlpha), + pipelineState.colorWriteEnable, + magic_enum::enum_name(pipelineState.primitiveTopology), + pipelineState.vertexStrides[0], + pipelineState.vertexStrides[1], + pipelineState.vertexStrides[2], + pipelineState.vertexStrides[3], + magic_enum::enum_name(pipelineState.renderTargetFormat), + magic_enum::enum_name(pipelineState.depthStencilFormat), + pipelineState.sampleCount, + pipelineState.enableAlphaToCoverage, + pipelineState.specConstants) + + g_pipelineDebugText; } +#endif - desc.inputSlots = inputSlots; - desc.inputSlotsCount = inputSlotCount; - - pipeline = g_device->createGraphicsPipeline(desc); +#ifdef PSO_CACHING + std::lock_guard lock(g_pipelineCacheMutex); + g_pipelineStatesToCache.push_back(pipelineState); +#endif } + return pipeline.get(); } @@ -2672,6 +3033,27 @@ static void ProcSetPixelShaderConstants(const RenderCommand& cmd) SetRootDescriptor(cmd.setPixelShaderConstants.allocation, 1); } +static void ProcAddPipeline(const RenderCommand& cmd) +{ + auto& args = cmd.addPipeline; + auto& pipeline = g_pipelines[args.hash]; + + if (pipeline == nullptr) + { + pipeline = std::unique_ptr(args.pipeline); +#ifdef ASYNC_PSO_DEBUG + ++g_pipelinesCreatedAsynchronously; +#endif + } + else + { +#ifdef ASYNC_PSO_DEBUG + ++g_pipelinesDropped; +#endif + delete args.pipeline; + } +} + static void FlushRenderStateForRenderThread() { auto renderTarget = g_pipelineState.colorWriteEnable ? g_renderTarget : nullptr; @@ -2686,7 +3068,7 @@ static void FlushRenderStateForRenderThread() auto& commandList = g_commandLists[g_frame]; if (g_dirtyStates.pipelineState) - commandList->setPipeline(CreateGraphicsPipeline(g_pipelineState)); + commandList->setPipeline(CreateGraphicsPipelineInRenderThread(g_pipelineState)); if (g_dirtyStates.sharedConstants) { @@ -2947,7 +3329,7 @@ static RenderFormat ConvertDeclType(uint32_t type) } } -static GuestVertexDeclaration* CreateVertexDeclaration(GuestVertexElement* vertexElements) +static GuestVertexDeclaration* CreateVertexDeclarationWithoutAddRef(GuestVertexElement* vertexElements) { size_t vertexElementCount = 0; auto vertexElement = vertexElements; @@ -2961,12 +3343,13 @@ static GuestVertexDeclaration* CreateVertexDeclaration(GuestVertexElement* verte std::lock_guard lock(g_vertexDeclarationMutex); - auto& vertexDeclaration = g_vertexDeclarations[ - XXH3_64bits(vertexElements, vertexElementCount * sizeof(GuestVertexElement))]; + XXH64_hash_t hash = XXH3_64bits(vertexElements, vertexElementCount * sizeof(GuestVertexElement)); + auto& vertexDeclaration = g_vertexDeclarations[hash]; if (vertexDeclaration == nullptr) { vertexDeclaration = g_userHeap.AllocPhysical(ResourceType::VertexDeclaration); + vertexDeclaration->hash = hash; static std::vector inputElements; inputElements.clear(); @@ -3036,18 +3419,13 @@ static GuestVertexDeclaration* CreateVertexDeclaration(GuestVertexElement* verte vertexDeclaration->indexVertexStream = vertexElement->stream; break; - case D3DDECLUSAGE_BLENDWEIGHT: - case D3DDECLUSAGE_BLENDINDICES: - vertexDeclaration->inputLayoutFlags |= INPUT_LAYOUT_FLAG_HAS_BONE_WEIGHTS; - break; - case D3DDECLUSAGE_NORMAL: case D3DDECLUSAGE_TANGENT: case D3DDECLUSAGE_BINORMAL: if (vertexElement->type == D3DDECLTYPE_FLOAT3) inputElement.format = RenderFormat::R32G32B32_UINT; else - vertexDeclaration->inputLayoutFlags |= INPUT_LAYOUT_FLAG_HAS_R11G11B10_NORMAL; + vertexDeclaration->hasR11G11B10Normal = true; break; case D3DDECLUSAGE_TEXCOORD: @@ -3132,6 +3510,13 @@ static GuestVertexDeclaration* CreateVertexDeclaration(GuestVertexElement* verte return vertexDeclaration; } +static GuestVertexDeclaration* CreateVertexDeclaration(GuestVertexElement* vertexElements) +{ + auto vertexDeclaration = CreateVertexDeclarationWithoutAddRef(vertexElements); + vertexDeclaration->AddRef(); + return vertexDeclaration; +} + static void SetVertexDeclaration(GuestDevice* device, GuestVertexDeclaration* vertexDeclaration) { RenderCommand cmd; @@ -3149,39 +3534,48 @@ static void ProcSetVertexDeclaration(const RenderCommand& cmd) if (args.vertexDeclaration != nullptr) { SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.swappedTexcoords, args.vertexDeclaration->swappedTexcoords); - SetDirtyValue(g_dirtyStates.sharedConstants, g_sharedConstants.inputLayoutFlags, args.vertexDeclaration->inputLayoutFlags); + + uint32_t specConstants = g_pipelineState.specConstants; + if (args.vertexDeclaration->hasR11G11B10Normal) + specConstants |= SPEC_CONSTANT_R11G11B10_NORMAL; + else + specConstants &= ~SPEC_CONSTANT_R11G11B10_NORMAL; + + SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.specConstants, specConstants); } SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.vertexDeclaration, args.vertexDeclaration); } -static GuestShader* CreateShader(const be* function, ResourceType resourceType) +static ShaderCacheEntry* FindShaderCacheEntry(XXH64_hash_t hash) { - XXH64_hash_t hash = XXH3_64bits(function, function[1] + function[2]); - auto end = g_shaderCacheEntries + g_shaderCacheEntryCount; auto findResult = std::lower_bound(g_shaderCacheEntries, end, hash, [](ShaderCacheEntry& lhs, XXH64_hash_t rhs) { return lhs.hash < rhs; }); + return findResult != end && findResult->hash == hash ? findResult : nullptr; +} + +static GuestShader* CreateShader(const be* function, ResourceType resourceType) +{ + XXH64_hash_t hash = XXH3_64bits(function, function[1] + function[2]); + + auto findResult = FindShaderCacheEntry(hash); GuestShader* shader = nullptr; - if (findResult != end && findResult->hash == hash) + if (findResult != nullptr) { - if (findResult->userData == nullptr) + if (findResult->guestShader == nullptr) { shader = g_userHeap.AllocPhysical(resourceType); + shader->shaderCacheEntry = findResult; - if (g_vulkan) - shader->shader = g_device->createShader(g_shaderCache.get() + findResult->spirvOffset, findResult->spirvSize, "main", RenderShaderFormat::SPIRV); - else - shader->shader = g_device->createShader(g_shaderCache.get() + findResult->dxilOffset, findResult->dxilSize, "main", RenderShaderFormat::DXIL); - - findResult->userData = shader; + findResult->guestShader = shader; } else { - shader = reinterpret_cast(findResult->userData); + shader = findResult->guestShader; } } @@ -3278,6 +3672,8 @@ static void ProcSetPixelShader(const RenderCommand& cmd) static std::thread g_renderThread([] { + GuestThread::SetThreadName(GetCurrentThreadId(), "Render Thread"); + RenderCommand commands[32]; while (true) @@ -3306,6 +3702,7 @@ static std::thread g_renderThread([] case RenderCommandType::SetBooleans: ProcSetBooleans(cmd); break; case RenderCommandType::SetVertexShaderConstants: ProcSetVertexShaderConstants(cmd); break; case RenderCommandType::SetPixelShaderConstants: ProcSetPixelShaderConstants(cmd); break; + case RenderCommandType::AddPipeline: ProcAddPipeline(cmd); break; case RenderCommandType::DrawPrimitive: ProcDrawPrimitive(cmd); break; case RenderCommandType::DrawIndexedPrimitive: ProcDrawIndexedPrimitive(cmd); break; case RenderCommandType::DrawPrimitiveUP: ProcDrawPrimitiveUP(cmd); break; @@ -3644,7 +4041,7 @@ static RenderFormat ConvertDXGIFormat(ddspp::DXGIFormat format) static void MakePictureData(GuestPictureData* pictureData, uint8_t* data, uint32_t dataSize) { - if ((pictureData->flags & 0x1) == 0) + if ((pictureData->flags & 0x1) == 0 && data != nullptr) { ddspp::Descriptor ddsDesc; if (ddspp::decode_header(data, ddsDesc) != ddspp::Error) @@ -3865,7 +4262,7 @@ static void ScreenShaderInit(be* a1, uint32_t a2, uint32_t a3, GuestVe } if (g_movieVertexDeclaration == nullptr) - g_movieVertexDeclaration = CreateVertexDeclaration(vertexElements); + g_movieVertexDeclaration = CreateVertexDeclarationWithoutAddRef(vertexElements); g_moviePixelShader->AddRef(); g_movieVertexShader->AddRef(); @@ -3976,6 +4373,1034 @@ void ParticleTestDrawIndexedPrimitiveMidAsmHook(PPCRegister& r7) r7.u64 = std::size(g_particleTestIndexBuffer); } +void MotionBlurPrevInvViewProjectionMidAsmHook(PPCRegister& r10) +{ + auto mtxProjection = reinterpret_cast*>(g_memory.Translate(r10.u32)); + + // Reverse Z. Have to be done on CPU side because the matrix multiplications + // add up and it loses precision by the time it's sent to GPU. + mtxProjection[10] = -(mtxProjection[10] + 1.0f); + mtxProjection[14] = -mtxProjection[14]; +} + +// Normally, we could delay setting IsMadeOne, but the game relies on that flag +// being present to handle load priority. To work around that, we can prevent +// IsMadeAll from being set until the compilation is finished. Time for a custom flag! +enum +{ + eDatabaseDataFlags_CompilingPipelines = 0x80 +}; + +// This is passed to pipeline compilation threads to keep the loading screen busy until +// all of them are finished. A shared pointer makes sure the destructor is called only once. +struct DatabaseDataHolder +{ + boost::shared_ptr databaseData; + + DatabaseDataHolder() : databaseData() + { + } + + DatabaseDataHolder(const DatabaseDataHolder&) = delete; + DatabaseDataHolder(DatabaseDataHolder&& other) + : databaseData(std::exchange(other.databaseData, nullptr)) + { + } + + ~DatabaseDataHolder() + { + if (databaseData.get() != nullptr) + { + databaseData->m_Flags &= ~eDatabaseDataFlags_CompilingPipelines; + + if ((--g_compilingDataCount) == 0) + g_compilingDataCount.notify_all(); + } + } +}; + +struct PipelineStateQueueItem +{ + XXH64_hash_t pipelineHash; + PipelineState pipelineState; + std::shared_ptr databaseDataHolder; +#ifdef ASYNC_PSO_DEBUG + std::string pipelineName; +#endif +}; + +static moodycamel::BlockingConcurrentQueue g_pipelineStateQueue; + +struct MinimalGuestThreadContext +{ + uint8_t* stack = nullptr; + PPCContext ppcContext{}; + + ~MinimalGuestThreadContext() + { + if (stack != nullptr) + g_userHeap.Free(stack); + } + + void ensureValid() + { + if (stack == nullptr) + { + stack = reinterpret_cast(g_userHeap.Alloc(0x4000)); + ppcContext.fn = (uint8_t*)g_codeCache.bucket; + ppcContext.r1.u64 = g_memory.MapVirtual(stack + 0x4000); + SetPPCContext(ppcContext); + } + } +}; + +static void PipelineCompilerThread() +{ + GuestThread::SetThreadName(GetCurrentThreadId(), "Pipeline Compiler Thread"); + MinimalGuestThreadContext ctx; + + while (true) + { + PipelineStateQueueItem queueItem; + g_pipelineStateQueue.wait_dequeue(queueItem); + + ctx.ensureValid(); + + auto pipeline = CreateGraphicsPipeline(queueItem.pipelineState); +#ifdef ASYNC_PSO_DEBUG + pipeline->setName(queueItem.pipelineName); +#endif + + // Will get dropped in render thread if a different thread already managed to compile this. + RenderCommand cmd; + cmd.type = RenderCommandType::AddPipeline; + cmd.addPipeline.hash = queueItem.pipelineHash; + cmd.addPipeline.pipeline = pipeline.release(); + g_renderQueue.enqueue(cmd); + } +} + +static std::vector> g_pipelineCompilerThreads = []() + { + size_t threadCount = std::max(2u, (std::thread::hardware_concurrency() * 2) / 3); + + std::vector> threads(threadCount); + for (auto& thread : threads) + thread = std::make_unique(PipelineCompilerThread); + + return threads; + }(); + +static constexpr uint32_t MODEL_DATA_VFTABLE = 0x82073A44; +static constexpr uint32_t TERRAIN_MODEL_DATA_VFTABLE = 0x8211D25C; +static constexpr uint32_t PARTICLE_MATERIAL_VFTABLE = 0x8211F198; + +// Allocate the shared pointer only when new compilations are happening. +// If nothing was compiled, the local "holder" variable will get destructed with RAII instead. +struct DatabaseDataHolderPair +{ + DatabaseDataHolder holder; + std::shared_ptr counter; +}; + +// Having this separate, because I don't want to lock a mutex in the render thread before +// every single draw. Might be worth profiling to see if it actually has an impact and merge them. +static ankerl::unordered_dense::set g_asyncPipelines; + +static void EnqueueGraphicsPipelineCompilation(const PipelineState& pipelineState, DatabaseDataHolderPair& databaseDataHolderPair, const char* name) +{ + XXH64_hash_t hash = XXH3_64bits(&pipelineState, sizeof(pipelineState)); + bool shouldCompile = g_asyncPipelines.emplace(hash).second; + + if (shouldCompile) + { + if (databaseDataHolderPair.counter == nullptr && databaseDataHolderPair.holder.databaseData.get() != nullptr) + databaseDataHolderPair.counter = std::make_unique(std::move(databaseDataHolderPair.holder)); + + PipelineStateQueueItem queueItem; + queueItem.pipelineHash = hash; + queueItem.pipelineState = pipelineState; + queueItem.databaseDataHolder = databaseDataHolderPair.counter; +#ifdef ASYNC_PSO_DEBUG + queueItem.pipelineName = std::format("ASYNC {} {:X}", name, hash); +#endif + g_pipelineStateQueue.enqueue(queueItem); + } +} + +struct CompilationArgs +{ + DatabaseDataHolderPair holderPair; + bool noGI{}; + bool hasMoreThanOneBone{}; + bool velocityMapQuickStep{}; + bool objectIcon{}; +}; + +enum class MeshLayer +{ + Opaque, + Transparent, + PunchThrough, + Special +}; + +static void CompileMeshPipeline(Hedgehog::Mirage::CMeshData* mesh, MeshLayer layer, CompilationArgs& args) +{ + if (mesh->m_spMaterial.get() == nullptr || mesh->m_spMaterial->m_spShaderListData.get() == nullptr) + return; + + auto& material = mesh->m_spMaterial; + auto& shaderList = material->m_spShaderListData; + + bool isFur = strstr(shaderList->m_TypeAndName.c_str(), "Fur") != nullptr; + bool isSky = strstr(shaderList->m_TypeAndName.c_str(), "Sky") != nullptr; + bool isSonicMouth = strcmp(material->m_TypeAndName.c_str() + 2, "sonic_gm_mouth_duble") == 0 && + strcmp(shaderList->m_TypeAndName.c_str() + 3, "SonicSkin_dspf[b]") == 0; + + bool compiledOutsideMainFramebuffer = !isFur && !isSky; + + bool constTexCoord = true; + if (material->m_spTexsetData.get() != nullptr) + { + for (size_t i = 1; i < material->m_spTexsetData->m_TextureList.size(); i++) + { + if (material->m_spTexsetData->m_TextureList[i]->m_TexcoordIndex != + material->m_spTexsetData->m_TextureList[0]->m_TexcoordIndex) + { + constTexCoord = false; + break; + } + } + } + + auto vertexDeclaration = reinterpret_cast(mesh->m_VertexDeclarationPtr.m_pD3DVertexDeclaration.get()); + + // Shadow pipeline. + if (compiledOutsideMainFramebuffer && (layer == MeshLayer::Opaque || layer == MeshLayer::PunchThrough)) + { + PipelineState pipelineState{}; + + if (layer == MeshLayer::PunchThrough) + { + pipelineState.vertexShader = FindShaderCacheEntry(0xDD4FA7BB53876300)->guestShader; + pipelineState.pixelShader = FindShaderCacheEntry(0xE2ECA594590DDE8B)->guestShader; + } + else + { + pipelineState.vertexShader = FindShaderCacheEntry(0x8E4BB23465BD909E)->guestShader; + } + + pipelineState.vertexDeclaration = vertexDeclaration; + pipelineState.cullMode = material->m_DoubleSided ? RenderCullMode::NONE : RenderCullMode::BACK; + pipelineState.zFunc = RenderComparisonFunction::LESS_EQUAL; + pipelineState.depthBias = (1 << 24) * (*reinterpret_cast*>(g_memory.Translate(0x83302760))); + pipelineState.slopeScaledDepthBias = *reinterpret_cast*>(g_memory.Translate(0x83302764)); + pipelineState.colorWriteEnable = 0; + pipelineState.primitiveTopology = RenderPrimitiveTopology::TRIANGLE_STRIP; + pipelineState.vertexStrides[0] = mesh->m_VertexSize; + pipelineState.depthStencilFormat = RenderFormat::D32_FLOAT; + + if (layer == MeshLayer::PunchThrough) + pipelineState.specConstants |= SPEC_CONSTANT_ALPHA_TEST; + + SanitizePipelineState(pipelineState); + EnqueueGraphicsPipelineCompilation(pipelineState, args.holderPair, layer == MeshLayer::PunchThrough ? "MakeShadowMapTransparent" : "MakeShadowMap"); + } + + // Motion blur pipeline. We could normally do the player here only, but apparently Werehog enemies also have object blur. + // TODO: Do punch through meshes get rendered? + if (compiledOutsideMainFramebuffer && args.hasMoreThanOneBone && layer == MeshLayer::Opaque) + { + PipelineState pipelineState{}; + pipelineState.vertexShader = FindShaderCacheEntry(0x4620B236DC38100C)->guestShader; + pipelineState.pixelShader = FindShaderCacheEntry(0xBBDB735BEACC8F41)->guestShader; + pipelineState.vertexDeclaration = vertexDeclaration; + pipelineState.cullMode = RenderCullMode::NONE; + pipelineState.zFunc = RenderComparisonFunction::GREATER_EQUAL; + pipelineState.primitiveTopology = RenderPrimitiveTopology::TRIANGLE_STRIP; + pipelineState.vertexStrides[0] = mesh->m_VertexSize; + pipelineState.renderTargetFormat = RenderFormat::R8G8B8A8_UNORM; + pipelineState.depthStencilFormat = RenderFormat::D32_FLOAT; + pipelineState.specConstants = SPEC_CONSTANT_REVERSE_Z; + + SanitizePipelineState(pipelineState); + EnqueueGraphicsPipelineCompilation(pipelineState, args.holderPair, "FxVelocityMap"); + + if (args.velocityMapQuickStep) + { + pipelineState.vertexShader = FindShaderCacheEntry(0x99DC3F27E402700D)->guestShader; + SanitizePipelineState(pipelineState); + EnqueueGraphicsPipelineCompilation(pipelineState, args.holderPair, "FxVelocityMapQuickStep"); + } + } + + guest_stack_var defaultSymbol(reinterpret_cast(g_memory.Translate(0x8202DDBC))); + auto defaultFindResult = shaderList->m_PixelShaderPermutations.find(*defaultSymbol); + if (defaultFindResult == shaderList->m_PixelShaderPermutations.end()) + return; + + uint32_t pixelShaderSubPermutationsToCompile = 0; + if (constTexCoord) pixelShaderSubPermutationsToCompile |= 0x1; + if (args.noGI) pixelShaderSubPermutationsToCompile |= 0x2; + + if ((defaultFindResult->second.m_SubPermutations.get() & (1 << pixelShaderSubPermutationsToCompile)) == 0) pixelShaderSubPermutationsToCompile &= ~0x1; + if ((defaultFindResult->second.m_SubPermutations.get() & (1 << pixelShaderSubPermutationsToCompile)) == 0) pixelShaderSubPermutationsToCompile &= ~0x2; + + guest_stack_var noneSymbol(reinterpret_cast(g_memory.Translate(0x8200D938))); + auto noneFindResult = defaultFindResult->second.m_VertexShaderPermutations.find(*noneSymbol); + if (noneFindResult == defaultFindResult->second.m_VertexShaderPermutations.end()) + return; + + uint32_t vertexShaderSubPermutationsToCompile = 0; + if (constTexCoord) vertexShaderSubPermutationsToCompile |= 0x1; + + if ((noneFindResult->second->m_SubPermutations.get() & (1 << vertexShaderSubPermutationsToCompile)) == 0) + vertexShaderSubPermutationsToCompile &= ~0x1; + + // Fur requires an instanced variant of the vertex declaration. + if (isFur) + { + GuestVertexElement vertexElements[64]; + memcpy(vertexElements, vertexDeclaration->vertexElements.get(), (vertexDeclaration->vertexElementCount - 1) * sizeof(GuestVertexElement)); + + vertexElements[vertexDeclaration->vertexElementCount - 1] = { 1, 0, 0x2C82A1, 0, 0, 1 }; + vertexElements[vertexDeclaration->vertexElementCount] = { 2, 0, 0x2C83A4, 0, 0, 2 }; + vertexElements[vertexDeclaration->vertexElementCount + 1] = D3DDECL_END(); + + vertexDeclaration = CreateVertexDeclarationWithoutAddRef(vertexElements); + } + + for (auto& [pixelShaderSubPermutations, pixelShader] : defaultFindResult->second.m_PixelShaders) + { + if (pixelShader.get() == nullptr || (pixelShaderSubPermutations & 0x3) != pixelShaderSubPermutationsToCompile) + continue; + + for (auto& [vertexShaderSubPermutations, vertexShader] : noneFindResult->second->m_VertexShaders) + { + if (vertexShader.get() == nullptr || (vertexShaderSubPermutations & 0x1) != vertexShaderSubPermutationsToCompile) + continue; + + PipelineState pipelineState{}; + pipelineState.vertexShader = reinterpret_cast(vertexShader->m_spCode->m_pD3DVertexShader.get()); + pipelineState.pixelShader = reinterpret_cast(pixelShader->m_spCode->m_pD3DPixelShader.get()); + pipelineState.vertexDeclaration = vertexDeclaration; + pipelineState.instancing = isFur; + pipelineState.zWriteEnable = !isSky && layer != MeshLayer::Transparent; + pipelineState.srcBlend = RenderBlend::SRC_ALPHA; + pipelineState.destBlend = material->m_Additive ? RenderBlend::ONE : RenderBlend::INV_SRC_ALPHA; + pipelineState.cullMode = material->m_DoubleSided ? RenderCullMode::NONE : RenderCullMode::BACK; + pipelineState.zFunc = RenderComparisonFunction::GREATER_EQUAL; // Reverse Z + pipelineState.alphaBlendEnable = layer == MeshLayer::Transparent || layer == MeshLayer::Special; + pipelineState.srcBlendAlpha = RenderBlend::SRC_ALPHA; + pipelineState.destBlendAlpha = RenderBlend::INV_SRC_ALPHA; + pipelineState.primitiveTopology = RenderPrimitiveTopology::TRIANGLE_STRIP; + pipelineState.vertexStrides[0] = mesh->m_VertexSize; + pipelineState.vertexStrides[1] = isFur ? 4 : 0; + pipelineState.vertexStrides[2] = isFur ? 4 : 0; + pipelineState.renderTargetFormat = RenderFormat::R16G16B16A16_FLOAT; + pipelineState.depthStencilFormat = RenderFormat::D32_FLOAT; + pipelineState.sampleCount = Config::MSAA > 1 ? Config::MSAA : 1; + + if (pipelineState.vertexDeclaration->hasR11G11B10Normal) + pipelineState.specConstants |= SPEC_CONSTANT_R11G11B10_NORMAL; + + if (Config::GITextureFiltering == EGITextureFiltering::Bicubic) + pipelineState.specConstants |= SPEC_CONSTANT_BICUBIC_GI_FILTER; + + if (layer == MeshLayer::PunchThrough) + { + if (Config::MSAA > 1 && Config::AlphaToCoverage) + { + pipelineState.enableAlphaToCoverage = true; + pipelineState.specConstants |= SPEC_CONSTANT_ALPHA_TO_COVERAGE; + } + else + { + pipelineState.specConstants |= SPEC_CONSTANT_ALPHA_TEST; + } + } + + if (!isSky) + pipelineState.specConstants |= SPEC_CONSTANT_REVERSE_Z; + + auto createGraphicsPipeline = [&](PipelineState& pipelineStateToCreate) + { + SanitizePipelineState(pipelineStateToCreate); + EnqueueGraphicsPipelineCompilation(pipelineStateToCreate, args.holderPair, shaderList->m_TypeAndName.c_str() + 3); + }; + + createGraphicsPipeline(pipelineState); + + bool planarReflectionEnabled = reinterpret_cast(g_memory.Translate(0x832FA0D8)); + + auto noMsaaPipeline = pipelineState; + noMsaaPipeline.sampleCount = 1; + noMsaaPipeline.enableAlphaToCoverage = false; + + if ((noMsaaPipeline.specConstants & SPEC_CONSTANT_ALPHA_TO_COVERAGE) != 0) + { + noMsaaPipeline.specConstants &= ~SPEC_CONSTANT_ALPHA_TO_COVERAGE; + noMsaaPipeline.specConstants |= SPEC_CONSTANT_ALPHA_TEST; + } + + if (planarReflectionEnabled) + { + // Planar reflections don't use MSAA. + createGraphicsPipeline(noMsaaPipeline); + } + + if (args.objectIcon) + { + // Object icons get rendered to a SDR buffer without MSAA. + auto iconPipelineState = noMsaaPipeline; + iconPipelineState.renderTargetFormat = RenderFormat::R8G8B8A8_UNORM; + createGraphicsPipeline(iconPipelineState); + } + + if (isSonicMouth) + { + // Sonic's mouth switches between "SonicSkin_dspf[b]" or "SonicSkinNodeInvX_dspf[b]" depending on the view angle. + auto mouthPipelineState = pipelineState; + mouthPipelineState.vertexShader = FindShaderCacheEntry(0x689AA3140AB9EBAA)->guestShader; + createGraphicsPipeline(mouthPipelineState); + + if (planarReflectionEnabled) + { + auto noMsaaMouthPipelineState = noMsaaPipeline; + noMsaaMouthPipelineState.vertexShader = mouthPipelineState.vertexShader; + createGraphicsPipeline(noMsaaMouthPipelineState); + } + } + } + } +} + +template +static void CompileMeshPipelines(const T& modelData, CompilationArgs& args) +{ + for (auto& meshGroup : modelData.m_NodeGroupModels) + { + for (auto& mesh : meshGroup->m_OpaqueMeshes) + { + CompileMeshPipeline(mesh.get(), MeshLayer::Opaque, args); + + if (args.noGI) // For models that can be shown transparent (eg. medals) + CompileMeshPipeline(mesh.get(), MeshLayer::Transparent, args); + } + + for (auto& mesh : meshGroup->m_TransparentMeshes) + CompileMeshPipeline(mesh.get(), MeshLayer::Transparent, args); + + for (auto& mesh : meshGroup->m_PunchThroughMeshes) + CompileMeshPipeline(mesh.get(), MeshLayer::PunchThrough, args); + + for (auto& specialMeshGroup : meshGroup->m_SpecialMeshGroups) + { + for (auto& mesh : specialMeshGroup) + CompileMeshPipeline(mesh.get(), MeshLayer::Special, args); // TODO: Are there layer types other than water in this game?? + } + } + + for (auto& mesh : modelData.m_OpaqueMeshes) + { + CompileMeshPipeline(mesh.get(), MeshLayer::Opaque, args); + + if (args.noGI) + CompileMeshPipeline(mesh.get(), MeshLayer::Transparent, args); + } + + for (auto& mesh : modelData.m_TransparentMeshes) + CompileMeshPipeline(mesh.get(), MeshLayer::Transparent, args); + + for (auto& mesh : modelData.m_PunchThroughMeshes) + CompileMeshPipeline(mesh.get(), MeshLayer::PunchThrough, args); +} + +static void CompileParticleMaterialPipeline(const Hedgehog::Sparkle::CParticleMaterial& material, DatabaseDataHolderPair& holderPair) +{ + auto& shaderList = material.m_spShaderListData; + if (shaderList.get() == nullptr) + return; + + guest_stack_var defaultSymbol(reinterpret_cast(g_memory.Translate(0x8202DDBC))); + auto defaultFindResult = shaderList->m_PixelShaderPermutations.find(*defaultSymbol); + if (defaultFindResult == shaderList->m_PixelShaderPermutations.end()) + return; + + guest_stack_var noneSymbol(reinterpret_cast(g_memory.Translate(0x8200D938))); + auto noneFindResult = defaultFindResult->second.m_VertexShaderPermutations.find(*noneSymbol); + if (noneFindResult == defaultFindResult->second.m_VertexShaderPermutations.end()) + return; + + // All the particle models in the game come with the unoptimized format, so we can assume it. + uint8_t unoptimizedVertexElements[144] = + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0x23, 0xB9, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0C, 0x00, 0x2A, 0x23, 0xB9, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x2A, 0x23, 0xB9, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x24, 0x00, 0x2A, 0x23, 0xB9, 0x00, 0x07, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x2C, 0x23, 0xA5, 0x00, 0x05, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x38, 0x00, 0x2C, 0x23, 0xA5, 0x00, 0x05, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x00, 0x2C, 0x23, 0xA5, 0x00, 0x05, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x48, 0x00, 0x2C, 0x23, 0xA5, 0x00, 0x05, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x00, 0x1A, 0x23, 0xA6, 0x00, 0x0A, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x00, 0x1A, 0x23, 0x86, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x64, 0x00, 0x1A, 0x20, 0x86, 0x00, 0x01, 0x00, 0x00, + 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00 + }; + + auto unoptimizedVertexDeclaration = CreateVertexDeclarationWithoutAddRef(reinterpret_cast(unoptimizedVertexElements)); + auto sparkleVertexDeclaration = CreateVertexDeclarationWithoutAddRef(reinterpret_cast(g_memory.Translate(0x8211F540))); + + bool isMeshShader = strstr(shaderList->m_TypeAndName.c_str(), "Mesh") != nullptr; + + PipelineState pipelineState{}; + pipelineState.vertexShader = reinterpret_cast(noneFindResult->second->m_VertexShaders.begin()->second->m_spCode->m_pD3DVertexShader.get()); + pipelineState.pixelShader = reinterpret_cast(defaultFindResult->second.m_PixelShaders.begin()->second->m_spCode->m_pD3DPixelShader.get()); + pipelineState.vertexDeclaration = isMeshShader ? unoptimizedVertexDeclaration : sparkleVertexDeclaration; + pipelineState.zWriteEnable = false; + pipelineState.srcBlend = RenderBlend::SRC_ALPHA; + pipelineState.destBlend = RenderBlend::INV_SRC_ALPHA; + pipelineState.zFunc = RenderComparisonFunction::GREATER_EQUAL; + pipelineState.alphaBlendEnable = true; + pipelineState.srcBlendAlpha = RenderBlend::SRC_ALPHA; + pipelineState.destBlendAlpha = RenderBlend::INV_SRC_ALPHA; + pipelineState.primitiveTopology = RenderPrimitiveTopology::TRIANGLE_STRIP; + pipelineState.vertexStrides[0] = isMeshShader ? 104 : 28; + pipelineState.renderTargetFormat = RenderFormat::R16G16B16A16_FLOAT; + pipelineState.depthStencilFormat = RenderFormat::D32_FLOAT; + pipelineState.sampleCount = Config::MSAA > 1 ? Config::MSAA : 1; + pipelineState.specConstants = SPEC_CONSTANT_REVERSE_Z; + + if (pipelineState.vertexDeclaration->hasR11G11B10Normal) + pipelineState.specConstants |= SPEC_CONSTANT_R11G11B10_NORMAL; + + switch (material.m_BlendMode.get()) + { + case Hedgehog::Sparkle::CParticleMaterial::eBlendMode_Zero: + // TODO: What are the render states for this?? + break; + case Hedgehog::Sparkle::CParticleMaterial::eBlendMode_Typical: + // Leave default. + break; + case Hedgehog::Sparkle::CParticleMaterial::eBlendMode_Add: + pipelineState.destBlend = RenderBlend::ONE; + break; + case Hedgehog::Sparkle::CParticleMaterial::eBlendMode_Subtract: + // TODO: Is this correct? + pipelineState.destBlend = RenderBlend::ONE; + pipelineState.blendOp = RenderBlendOperation::SUBTRACT; + break; + } + + auto createGraphicsPipeline = [&](PipelineState& pipelineStateToCreate) + { + SanitizePipelineState(pipelineStateToCreate); + EnqueueGraphicsPipelineCompilation(pipelineStateToCreate, holderPair, shaderList->m_TypeAndName.c_str() + 3); + }; + + // TODO: See if this is necessary for everything. + RenderCullMode cullModes[] = { RenderCullMode::NONE, RenderCullMode::BACK }; + + for (auto cullMode : cullModes) + { + pipelineState.cullMode = cullMode; + createGraphicsPipeline(pipelineState); + + bool planarReflectionEnabled = reinterpret_cast(g_memory.Translate(0x832FA0D8)); + + auto noMsaaPipelineState = pipelineState; + noMsaaPipelineState.sampleCount = 1; + + if (planarReflectionEnabled) + createGraphicsPipeline(noMsaaPipelineState); + + if (!isMeshShader) + { + // Previous compilation was for locus particles. This one will be for quads. + auto quadPipelineState = pipelineState; + quadPipelineState.primitiveTopology = RenderPrimitiveTopology::TRIANGLE_LIST; + createGraphicsPipeline(quadPipelineState); + + if (planarReflectionEnabled) + { + auto noMsaaQuadPipelineState = noMsaaPipelineState; + noMsaaQuadPipelineState.primitiveTopology = RenderPrimitiveTopology::TRIANGLE_LIST; + createGraphicsPipeline(noMsaaQuadPipelineState); + } + } + } +} + +// SWA::CGameModeStage::ExitLoading +PPC_FUNC_IMPL(__imp__sub_825369A0); +PPC_FUNC(sub_825369A0) +{ + // Wait for pipeline compilations to finish. + uint32_t value; + while ((value = g_compilingDataCount.load()) != 0) + g_compilingDataCount.wait(value); + + __imp__sub_825369A0(ctx, base); +} + +// CModelData::CheckMadeAll +PPC_FUNC_IMPL(__imp__sub_82E2EFB0); +PPC_FUNC(sub_82E2EFB0) +{ + if (reinterpret_cast(base + ctx.r3.u32)->m_Flags & eDatabaseDataFlags_CompilingPipelines) + { + ctx.r3.u64 = 0; + } + else + { + __imp__sub_82E2EFB0(ctx, base); + } +} + +// CTerrainModelData::CheckMadeAll +PPC_FUNC_IMPL(__imp__sub_82E243D8); +PPC_FUNC(sub_82E243D8) +{ + if (reinterpret_cast(base + ctx.r3.u32)->m_Flags & eDatabaseDataFlags_CompilingPipelines) + { + ctx.r3.u64 = 0; + } + else + { + __imp__sub_82E243D8(ctx, base); + } +} + +// CParticleMaterial::CheckMadeAll +PPC_FUNC_IMPL(__imp__sub_82E87598); +PPC_FUNC(sub_82E87598) +{ + if (reinterpret_cast(base + ctx.r3.u32)->m_Flags & eDatabaseDataFlags_CompilingPipelines) + { + ctx.r3.u64 = 0; + } + else + { + __imp__sub_82E87598(ctx, base); + } +} + +static Mutex g_pendingModelMutex; +static std::vector> g_pendingDataQueue; + +void GetDatabaseDataMidAsmHook(PPCRegister& r1, PPCRegister& r4) +{ + auto& databaseData = *reinterpret_cast*>( + g_memory.Translate(r1.u32 + 0x58)); + + if (!databaseData->IsMadeOne() && r4.u32 != NULL) + { + if (databaseData->m_pVftable.ptr == MODEL_DATA_VFTABLE) + { + // Ignore particle models, the materials they point at don't actually + // get used and give the threads unnecessary work. + bool isParticleModel = *reinterpret_cast*>(g_memory.Translate(r4.u32 + 4)) != 5 && + strncmp(databaseData->m_TypeAndName.c_str() + 2, "eff_", 4) == 0; + + if (isParticleModel) + return; + } + + ++g_compilingDataCount; + databaseData->m_Flags |= eDatabaseDataFlags_CompilingPipelines; + + { + std::lock_guard lock(g_pendingModelMutex); + g_pendingDataQueue.push_back(databaseData); + } + + if ((++g_pendingDataCount) == 1) + g_pendingDataCount.notify_all(); + } +} + +static bool CheckMadeAll(Hedgehog::Mirage::CMeshData* meshData) +{ + if (!meshData->IsMadeOne()) + return false; + + if (meshData->m_spMaterial.get() != nullptr) + { + if (!meshData->m_spMaterial->IsMadeOne()) + return false; + + if (meshData->m_spMaterial->m_spTexsetData.get() != nullptr) + { + if (!meshData->m_spMaterial->m_spTexsetData->IsMadeOne()) + return false; + + for (auto& texture : meshData->m_spMaterial->m_spTexsetData->m_TextureList) + { + if (!texture->IsMadeOne()) + return false; + } + } + } + + return true; +} + +template +static bool CheckMadeAll(const T& modelData) +{ + if (!modelData.IsMadeOne()) + return false; + + for (auto& meshGroup : modelData.m_NodeGroupModels) + { + for (auto& mesh : meshGroup->m_OpaqueMeshes) + { + if (!CheckMadeAll(mesh.get())) + return false; + } + + for (auto& mesh : meshGroup->m_TransparentMeshes) + { + if (!CheckMadeAll(mesh.get())) + return false; + } + + for (auto& mesh : meshGroup->m_PunchThroughMeshes) + { + if (!CheckMadeAll(mesh.get())) + return false; + } + + for (auto& specialMeshGroup : meshGroup->m_SpecialMeshGroups) + { + for (auto& mesh : specialMeshGroup) + { + if (!CheckMadeAll(mesh.get())) + return false; + } + } + } + + for (auto& mesh : modelData.m_OpaqueMeshes) + { + if (!CheckMadeAll(mesh.get())) + return false; + } + + for (auto& mesh : modelData.m_TransparentMeshes) + { + if (!CheckMadeAll(mesh.get())) + return false; + } + + for (auto& mesh : modelData.m_PunchThroughMeshes) + { + if (!CheckMadeAll(mesh.get())) + return false; + } + + return true; +} + +static void ModelConsumerThread() +{ + GuestThread::SetThreadName(GetCurrentThreadId(), "Model Consumer Thread"); + + std::vector> localPendingDataQueue; + MinimalGuestThreadContext ctx; + + while (true) + { + // Wait for models to arrive. + uint32_t pendingDataCount; + while ((pendingDataCount = g_pendingDataCount.load()) == 0) + g_pendingDataCount.wait(pendingDataCount); + + ctx.ensureValid(); + + if (g_pendingPipelineStateCache) + { + DatabaseDataHolderPair emptyHolderPair; + + for (auto vertexElements : g_vertexDeclarationCache) + CreateVertexDeclarationWithoutAddRef(reinterpret_cast(vertexElements)); + + for (auto pipelineState : g_pipelineStateCache) + { + // The hashes were reinterpret casted to pointers in the cache. + pipelineState.vertexShader = FindShaderCacheEntry(reinterpret_cast(pipelineState.vertexShader))->guestShader; + + if (pipelineState.pixelShader != nullptr) + pipelineState.pixelShader = FindShaderCacheEntry(reinterpret_cast(pipelineState.pixelShader))->guestShader; + + { + std::lock_guard lock(g_vertexDeclarationMutex); + pipelineState.vertexDeclaration = g_vertexDeclarations[reinterpret_cast(pipelineState.vertexDeclaration)]; + } + + if (Config::GITextureFiltering == EGITextureFiltering::Bicubic) + pipelineState.specConstants |= SPEC_CONSTANT_BICUBIC_GI_FILTER; + + // Compile both MSAA and non MSAA variants to work with reflection maps. The render formats are an assumption but it should hold true. + if (Config::MSAA > 1 && + pipelineState.renderTargetFormat == RenderFormat::R16G16B16A16_FLOAT && + pipelineState.depthStencilFormat == RenderFormat::D32_FLOAT) + { + auto msaaPipelineState = pipelineState; + msaaPipelineState.sampleCount = Config::MSAA; + + if (Config::AlphaToCoverage && (msaaPipelineState.specConstants & SPEC_CONSTANT_ALPHA_TEST) != 0) + { + msaaPipelineState.enableAlphaToCoverage = true; + msaaPipelineState.specConstants &= ~SPEC_CONSTANT_ALPHA_TEST; + msaaPipelineState.specConstants |= SPEC_CONSTANT_ALPHA_TO_COVERAGE; + } + + SanitizePipelineState(msaaPipelineState); + EnqueueGraphicsPipelineCompilation(msaaPipelineState, emptyHolderPair, "Precompiled Pipeline MSAA"); + } + + SanitizePipelineState(pipelineState); + EnqueueGraphicsPipelineCompilation(pipelineState, emptyHolderPair, "Precompiled Pipeline"); + } + + g_pendingPipelineStateCache = false; + --g_pendingDataCount; + + if ((--g_compilingDataCount) == 0) + g_compilingDataCount.notify_all(); + } + + { + std::lock_guard lock(g_pendingModelMutex); + localPendingDataQueue.insert(localPendingDataQueue.end(), g_pendingDataQueue.begin(), g_pendingDataQueue.end()); + g_pendingDataQueue.clear(); + } + + bool allHandled = true; + + for (auto& pendingData : localPendingDataQueue) + { + if (pendingData.get() != nullptr) + { + bool ready = false; + + if (pendingData->m_pVftable.ptr == MODEL_DATA_VFTABLE) + ready = CheckMadeAll(*reinterpret_cast(pendingData.get())); + else + ready = pendingData->IsMadeOne(); + + if (ready || pendingData.unique()) + { + if (pendingData->m_pVftable.ptr == TERRAIN_MODEL_DATA_VFTABLE) + { + CompilationArgs args{}; + args.holderPair.holder.databaseData = pendingData; + CompileMeshPipelines(*reinterpret_cast(pendingData.get()), args); + } + else if (pendingData->m_pVftable.ptr == PARTICLE_MATERIAL_VFTABLE) + { + DatabaseDataHolderPair holderPair; + holderPair.holder.databaseData = pendingData; + CompileParticleMaterialPipeline(*reinterpret_cast(pendingData.get()), holderPair); + } + else + { + assert(pendingData->m_pVftable.ptr == MODEL_DATA_VFTABLE); + + auto modelData = reinterpret_cast(pendingData.get()); + + CompilationArgs args{}; + args.holderPair.holder.databaseData = pendingData; + args.noGI = true; + args.hasMoreThanOneBone = modelData->m_NodeNum > 1; + args.velocityMapQuickStep = strcmp(pendingData->m_TypeAndName.c_str() + 2, "SonicRoot") == 0; + + // Check for the on screen items, eg. rings going to HUD. + auto items = reinterpret_cast*>(g_memory.Translate(0x832A8DD0)); + for (size_t i = 0; i < 50; i++) + { + if (strcmp(pendingData->m_TypeAndName.c_str() + 2, (*items).get()) == 0) + { + args.objectIcon = true; + break; + } + items += 7; + } + + CompileMeshPipelines(*modelData, args); + } + + pendingData = nullptr; + --g_pendingDataCount; + } + else + { + allHandled = false; + } + } + } + + if (allHandled) + localPendingDataQueue.clear(); + } +} + +static std::thread g_modelConsumerThread(ModelConsumerThread); + +#ifdef ASYNC_PSO_DEBUG + +PPC_FUNC_IMPL(__imp__sub_82E33330); +PPC_FUNC(sub_82E33330) +{ + auto vertexShaderCode = reinterpret_cast(g_memory.Translate(ctx.r4.u32)); + __imp__sub_82E33330(ctx, base); + reinterpret_cast(vertexShaderCode->m_pD3DVertexShader.get())->name = vertexShaderCode->m_TypeAndName.c_str() + 3; +} + +PPC_FUNC_IMPL(__imp__sub_82E328D8); +PPC_FUNC(sub_82E328D8) +{ + auto pixelShaderCode = reinterpret_cast(g_memory.Translate(ctx.r4.u32)); + __imp__sub_82E328D8(ctx, base); + reinterpret_cast(pixelShaderCode->m_pD3DPixelShader.get())->name = pixelShaderCode->m_TypeAndName.c_str() + 2; +} + +#endif + +#ifdef PSO_CACHING +class SDLEventListenerForPSOCaching : public SDLEventListener +{ +public: + void OnSDLEvent(SDL_Event* event) override + { + if (event->type != SDL_QUIT) + return; + + std::lock_guard lock(g_pipelineCacheMutex); + if (g_pipelineStatesToCache.empty()) + return; + + FILE* f = fopen("send_this_file_to_skyth.txt", "ab"); + if (f != nullptr) + { + ankerl::unordered_dense::set vertexDeclarations; + xxHashMap pipelineStatesToCache; + + for (auto& pipelineState : g_pipelineStatesToCache) + { + if (pipelineState.vertexShader->shaderCacheEntry == nullptr || + (pipelineState.pixelShader != nullptr && pipelineState.pixelShader->shaderCacheEntry == nullptr)) + { + continue; + } + + vertexDeclarations.emplace(pipelineState.vertexDeclaration); + + // Mask out the config options. + pipelineState.sampleCount = 1; + pipelineState.enableAlphaToCoverage = false; + + pipelineState.specConstants &= ~SPEC_CONSTANT_BICUBIC_GI_FILTER; + if ((pipelineState.specConstants & SPEC_CONSTANT_ALPHA_TO_COVERAGE) != 0) + { + pipelineState.specConstants &= ~SPEC_CONSTANT_ALPHA_TO_COVERAGE; + pipelineState.specConstants |= SPEC_CONSTANT_ALPHA_TEST; + } + + pipelineStatesToCache.emplace(XXH3_64bits(&pipelineState, sizeof(pipelineState)), pipelineState); + } + + for (auto vertexDeclaration : vertexDeclarations) + { + std::print(f, "static uint8_t g_vertexElements_{:016X}[] = {{", vertexDeclaration->hash); + + auto bytes = reinterpret_cast(vertexDeclaration->vertexElements.get()); + for (size_t i = 0; i < vertexDeclaration->vertexElementCount * sizeof(GuestVertexElement); i++) + std::print(f, "0x{:X},", bytes[i]); + + std::println(f, "}};"); + } + + for (auto& [pipelineHash, pipelineState] : pipelineStatesToCache) + { + std::println(f, "{{ " + "reinterpret_cast(0x{:X})," + "reinterpret_cast(0x{:X})," + "reinterpret_cast(0x{:X})," + "{}," + "{}," + "{}," + "RenderBlend::{}," + "RenderBlend::{}," + "RenderCullMode::{}," + "RenderComparisonFunction::{}," + "{}," + "RenderBlendOperation::{}," + "{}," + "{}," + "RenderBlend::{}," + "RenderBlend::{}," + "RenderBlendOperation::{}," + "0x{:X}," + "RenderPrimitiveTopology::{}," + "{{ {},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{} }}," + "RenderFormat::{}," + "RenderFormat::{}," + "{}," + "{}," + "0x{:X} }},", + pipelineState.vertexShader->shaderCacheEntry->hash, + pipelineState.pixelShader != nullptr ? pipelineState.pixelShader->shaderCacheEntry->hash : 0, + pipelineState.vertexDeclaration->hash, + pipelineState.instancing, + pipelineState.zEnable, + pipelineState.zWriteEnable, + magic_enum::enum_name(pipelineState.srcBlend), + magic_enum::enum_name(pipelineState.destBlend), + magic_enum::enum_name(pipelineState.cullMode), + magic_enum::enum_name(pipelineState.zFunc), + pipelineState.alphaBlendEnable, + magic_enum::enum_name(pipelineState.blendOp), + pipelineState.slopeScaledDepthBias, + pipelineState.depthBias, + magic_enum::enum_name(pipelineState.srcBlendAlpha), + magic_enum::enum_name(pipelineState.destBlendAlpha), + magic_enum::enum_name(pipelineState.blendOpAlpha), + pipelineState.colorWriteEnable, + magic_enum::enum_name(pipelineState.primitiveTopology), + pipelineState.vertexStrides[0], + pipelineState.vertexStrides[1], + pipelineState.vertexStrides[2], + pipelineState.vertexStrides[3], + pipelineState.vertexStrides[4], + pipelineState.vertexStrides[5], + pipelineState.vertexStrides[6], + pipelineState.vertexStrides[7], + pipelineState.vertexStrides[8], + pipelineState.vertexStrides[9], + pipelineState.vertexStrides[10], + pipelineState.vertexStrides[11], + pipelineState.vertexStrides[12], + pipelineState.vertexStrides[13], + pipelineState.vertexStrides[14], + pipelineState.vertexStrides[15], + magic_enum::enum_name(pipelineState.renderTargetFormat), + magic_enum::enum_name(pipelineState.depthStencilFormat), + pipelineState.sampleCount, + pipelineState.enableAlphaToCoverage, + pipelineState.specConstants); + } + + fclose(f); + } + } +}; +SDLEventListenerForPSOCaching g_sdlEventListenerForPSOCaching; +#endif + GUEST_FUNCTION_HOOK(sub_82BD99B0, CreateDevice); GUEST_FUNCTION_HOOK(sub_82BE6230, DestructResource); diff --git a/UnleashedRecomp/gpu/video.h b/UnleashedRecomp/gpu/video.h index 4b1b512..90b1441 100644 --- a/UnleashedRecomp/gpu/video.h +++ b/UnleashedRecomp/gpu/video.h @@ -1,5 +1,8 @@ #pragma once +//#define ASYNC_PSO_DEBUG +#define PSO_CACHING + #include "rhi/rt64_render_interface.h" #define D3DCLEAR_TARGET 0x1 @@ -78,6 +81,19 @@ struct GuestResource incrementedValue = std::byteswap(std::byteswap(originalValue) + 1); } while (InterlockedCompareExchange(reinterpret_cast(&refCount), incrementedValue, originalValue) != originalValue); } + + void Release() + { + uint32_t originalValue, decrementedValue; + do + { + originalValue = refCount.value; + decrementedValue = std::byteswap(std::byteswap(originalValue) - 1); + } while (InterlockedCompareExchange(reinterpret_cast(&refCount), decrementedValue, originalValue) != originalValue); + + // Normally we are supposed to release here, so only use this + // function when you know you won't be the one destructing it. + } }; enum GuestFormat @@ -226,27 +242,32 @@ struct GuestVertexElement uint8_t padding; }; -enum InputLayoutFlags -{ - INPUT_LAYOUT_FLAG_HAS_R11G11B10_NORMAL = 1 << 0, - INPUT_LAYOUT_FLAG_HAS_BONE_WEIGHTS = 1 << 1 -}; +#define D3DDECL_END() { 255, 0, 0xFFFFFFFF, 0, 0, 0 } struct GuestVertexDeclaration : GuestResource { + XXH64_hash_t hash = 0; std::unique_ptr inputElements; std::unique_ptr vertexElements; uint32_t inputElementCount = 0; uint32_t vertexElementCount = 0; uint32_t swappedTexcoords = 0; - uint32_t inputLayoutFlags = 0; + bool hasR11G11B10Normal = false; uint32_t indexVertexStream = 0; }; // VertexShader/PixelShader struct GuestShader : GuestResource { + Mutex mutex; std::unique_ptr shader; + struct ShaderCacheEntry* shaderCacheEntry = nullptr; + ankerl::unordered_dense::map> linkedShaders; + std::vector> shaderBlobs; + ComPtr libraryBlob; +#ifdef ASYNC_PSO_DEBUG + const char* name = ""; +#endif }; struct GuestViewport diff --git a/UnleashedRecomp/natvis.natvis b/UnleashedRecomp/natvis.natvis new file mode 100644 index 0000000..f55dca1 --- /dev/null +++ b/UnleashedRecomp/natvis.natvis @@ -0,0 +1,21 @@ + + + + {get()} + + get() + + + + {get()} + + get() + + + + {get()} + + get() + + + diff --git a/UnleashedRecomp/stdafx.h b/UnleashedRecomp/stdafx.h index f23df68..8fd5bb2 100644 --- a/UnleashedRecomp/stdafx.h +++ b/UnleashedRecomp/stdafx.h @@ -3,6 +3,7 @@ #define NOMINMAX #include +#include #include #include #include @@ -28,6 +29,11 @@ #include #include #include +#include +#include +#include + +using Microsoft::WRL::ComPtr; #include "framework.h" #include "mutex.h" diff --git a/UnleashedRecomp/ui/window.cpp b/UnleashedRecomp/ui/window.cpp index 2df7597..304b93e 100644 --- a/UnleashedRecomp/ui/window.cpp +++ b/UnleashedRecomp/ui/window.cpp @@ -10,6 +10,9 @@ int Window_OnSDLEvent(void*, SDL_Event* event) if (ImGui::GetIO().BackendPlatformUserData != nullptr) ImGui_ImplSDL2_ProcessEvent(event); + for (auto listener : Window::s_eventListeners) + listener->OnSDLEvent(event); + switch (event->type) { case SDL_QUIT: @@ -114,9 +117,6 @@ int Window_OnSDLEvent(void*, SDL_Event* event) } } - for (auto listener : Window::s_eventListeners) - listener->OnSDLEvent(event); - return 0; } diff --git a/UnleashedRecompLib/CMakeLists.txt b/UnleashedRecompLib/CMakeLists.txt index 6db0447..21ea860 100644 --- a/UnleashedRecompLib/CMakeLists.txt +++ b/UnleashedRecompLib/CMakeLists.txt @@ -26,7 +26,7 @@ add_custom_command( ) set(SHADER_RECOMP_ROOT "${SWA_THIRDPARTY_ROOT}/ShaderRecomp/ShaderRecomp") -set(SHADER_RECOMP_INCLUDE "${SHADER_RECOMP_ROOT}/shader_common.hlsli") +set(SHADER_RECOMP_INCLUDE "${SHADER_RECOMP_ROOT}/shader_common.h") target_compile_definitions(ShaderRecomp PRIVATE SHADER_RECOMP_INPUT=\"${CMAKE_CURRENT_SOURCE_DIR}/private\" diff --git a/UnleashedRecompLib/config/SWA.toml b/UnleashedRecompLib/config/SWA.toml index e154e37..0e2e975 100644 --- a/UnleashedRecompLib/config/SWA.toml +++ b/UnleashedRecompLib/config/SWA.toml @@ -422,3 +422,28 @@ registers = ["r7"] name = "LoadingScreenSpeedFixMidAsmHook" address = 0x824DAB60 registers = ["r4"] + +[[midasm_hook]] +name = "MotionBlurPrevInvViewProjectionMidAsmHook" +address = 0x82BA9E7C +registers = ["r10"] + +[[midasm_hook]] +name = "GetDatabaseDataMidAsmHook" +address = 0x82E38688 # Model +registers = ["r1", "r31"] + +[[midasm_hook]] +name = "GetDatabaseDataMidAsmHook" +address = 0x82E39650 # Terrain Model +registers = ["r1", "r31"] + +[[midasm_hook]] +name = "GetDatabaseDataMidAsmHook" +address = 0x827D614C # Particle Material Binary +registers = ["r1", "r29"] + +[[midasm_hook]] +name = "GetDatabaseDataMidAsmHook" +address = 0x827D6018 # Particle Material XML +registers = ["r1", "r30"] diff --git a/UnleashedRecompLib/shader/shader_cache.h b/UnleashedRecompLib/shader/shader_cache.h index dd3f671..4343940 100644 --- a/UnleashedRecompLib/shader/shader_cache.h +++ b/UnleashedRecompLib/shader/shader_cache.h @@ -7,7 +7,8 @@ struct ShaderCacheEntry const uint32_t dxilSize; const uint32_t spirvOffset; const uint32_t spirvSize; - void* userData; + const uint32_t specConstantsMask; + struct GuestShader* guestShader; }; extern ShaderCacheEntry g_shaderCacheEntries[]; diff --git a/thirdparty/ShaderRecomp b/thirdparty/ShaderRecomp index 30f5986..f936ed2 160000 --- a/thirdparty/ShaderRecomp +++ b/thirdparty/ShaderRecomp @@ -1 +1 @@ -Subproject commit 30f598604767602e3afce56b947e99dba2b51211 +Subproject commit f936ed2212d8291439003eb0c0d8edc0ecafd24d diff --git a/vcpkg.json b/vcpkg.json index 46ee79a..85e32c2 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -19,6 +19,7 @@ { "name": "imgui", "features": [ "sdl2-binding" ] - } + }, + "magic-enum" ] }