diff --git a/Source/Core/VideoCommon/VertexLoaderManager.cpp b/Source/Core/VideoCommon/VertexLoaderManager.cpp index d3f51ddfd9..cf13b0bcde 100644 --- a/Source/Core/VideoCommon/VertexLoaderManager.cpp +++ b/Source/Core/VideoCommon/VertexLoaderManager.cpp @@ -133,6 +133,75 @@ void MarkAllDirty() g_preprocess_cp_state.attr_dirty = BitSet32::AllTrue(8); } +NativeVertexFormat* GetOrCreateMatchingFormat(const PortableVertexDeclaration& decl) +{ + auto iter = s_native_vertex_map.find(decl); + if (iter == s_native_vertex_map.end()) + { + std::unique_ptr fmt = g_vertex_manager->CreateNativeVertexFormat(decl); + auto ipair = s_native_vertex_map.emplace(decl, std::move(fmt)); + iter = ipair.first; + } + + return iter->second.get(); +} + +NativeVertexFormat* GetUberVertexFormat(const PortableVertexDeclaration& decl) +{ + // The padding in the structs can cause the memcmp() in the map to create duplicates. + // Avoid this by initializing the padding to zero. + PortableVertexDeclaration new_decl; + std::memset(&new_decl, 0, sizeof(new_decl)); + new_decl.stride = decl.stride; + + auto MakeDummyAttribute = [](AttributeFormat& attr, VarType type, int components, bool integer) { + attr.type = type; + attr.components = components; + attr.offset = 0; + attr.enable = true; + attr.integer = integer; + }; + auto CopyAttribute = [](AttributeFormat& attr, const AttributeFormat& src) { + attr.type = src.type; + attr.components = src.components; + attr.offset = src.offset; + attr.enable = src.enable; + attr.integer = src.integer; + }; + + if (decl.position.enable) + CopyAttribute(new_decl.position, decl.position); + else + MakeDummyAttribute(new_decl.position, VAR_FLOAT, 1, false); + for (size_t i = 0; i < ArraySize(new_decl.normals); i++) + { + if (decl.normals[i].enable) + CopyAttribute(new_decl.normals[i], decl.normals[i]); + else + MakeDummyAttribute(new_decl.normals[i], VAR_FLOAT, 1, false); + } + for (size_t i = 0; i < ArraySize(new_decl.colors); i++) + { + if (decl.colors[i].enable) + CopyAttribute(new_decl.colors[i], decl.colors[i]); + else + MakeDummyAttribute(new_decl.colors[i], VAR_UNSIGNED_BYTE, 4, false); + } + for (size_t i = 0; i < ArraySize(new_decl.texcoords); i++) + { + if (decl.texcoords[i].enable) + CopyAttribute(new_decl.texcoords[i], decl.texcoords[i]); + else + MakeDummyAttribute(new_decl.texcoords[i], VAR_FLOAT, 1, false); + } + if (decl.posmtx.enable) + CopyAttribute(new_decl.posmtx, decl.posmtx); + else + MakeDummyAttribute(new_decl.posmtx, VAR_UNSIGNED_BYTE, 1, true); + + return GetOrCreateMatchingFormat(new_decl); +} + static VertexLoaderBase* RefreshLoader(int vtx_attr_group, bool preprocess = false) { CPState* state = preprocess ? &g_preprocess_cp_state : &g_main_cp_state; diff --git a/Source/Core/VideoCommon/VertexLoaderManager.h b/Source/Core/VideoCommon/VertexLoaderManager.h index 30ceb41aec..bd280d88db 100644 --- a/Source/Core/VideoCommon/VertexLoaderManager.h +++ b/Source/Core/VideoCommon/VertexLoaderManager.h @@ -26,6 +26,16 @@ void MarkAllDirty(); NativeVertexFormatMap* GetNativeVertexFormatMap(); +// Creates or obtains a pointer to a VertexFormat representing decl. +// If this results in a VertexFormat being created, if the game later uses a matching vertex +// declaration, the one that was previously created will be used. +NativeVertexFormat* GetOrCreateMatchingFormat(const PortableVertexDeclaration& decl); + +// For vertex ubershaders, all attributes need to be present, even when the vertex +// format does not contain them. This function returns a vertex format with dummy +// offsets set to the unused attributes. +NativeVertexFormat* GetUberVertexFormat(const PortableVertexDeclaration& decl); + // Returns -1 if buf_size is insufficient, else the amount of bytes consumed int RunVertices(int vtx_attr_group, int primitive, int count, DataReader src, bool is_preprocess);