2015-05-24 04:55:12 +00:00
|
|
|
// Copyright 2008 Dolphin Emulator Project
|
2021-07-05 01:22:19 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2009-03-07 08:35:01 +00:00
|
|
|
|
2019-06-01 11:55:09 +00:00
|
|
|
#include "VideoCommon/VertexLoaderManager.h"
|
|
|
|
|
2010-03-06 02:07:48 +00:00
|
|
|
#include <algorithm>
|
2019-06-01 11:55:09 +00:00
|
|
|
#include <iterator>
|
2014-06-05 15:55:21 +00:00
|
|
|
#include <memory>
|
2014-08-25 03:53:28 +00:00
|
|
|
#include <mutex>
|
2016-01-17 21:54:31 +00:00
|
|
|
#include <string>
|
2023-03-23 17:49:09 +00:00
|
|
|
#include <type_traits>
|
2012-12-10 06:40:28 +00:00
|
|
|
#include <unordered_map>
|
2014-08-15 14:17:06 +00:00
|
|
|
#include <utility>
|
2009-03-07 08:35:01 +00:00
|
|
|
#include <vector>
|
|
|
|
|
2016-01-17 21:54:31 +00:00
|
|
|
#include "Common/CommonTypes.h"
|
2021-06-20 20:47:57 +00:00
|
|
|
#include "Common/EnumMap.h"
|
2021-02-28 21:53:32 +00:00
|
|
|
#include "Common/Logging/Log.h"
|
2021-03-07 23:42:10 +00:00
|
|
|
|
2022-05-17 20:42:31 +00:00
|
|
|
#include "Core/DolphinAnalytics.h"
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "Core/HW/Memmap.h"
|
2022-12-02 19:07:30 +00:00
|
|
|
#include "Core/System.h"
|
2009-03-07 08:35:01 +00:00
|
|
|
|
2023-01-26 22:34:59 +00:00
|
|
|
#include "VideoCommon/AbstractGfx.h"
|
2014-08-05 02:51:42 +00:00
|
|
|
#include "VideoCommon/BPMemory.h"
|
2021-02-28 21:53:32 +00:00
|
|
|
#include "VideoCommon/CPMemory.h"
|
2016-01-31 19:51:55 +00:00
|
|
|
#include "VideoCommon/DataReader.h"
|
2014-08-02 06:42:15 +00:00
|
|
|
#include "VideoCommon/IndexGenerator.h"
|
2016-01-17 21:54:31 +00:00
|
|
|
#include "VideoCommon/NativeVertexFormat.h"
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "VideoCommon/Statistics.h"
|
2014-12-13 00:51:14 +00:00
|
|
|
#include "VideoCommon/VertexLoaderBase.h"
|
2014-07-25 23:10:44 +00:00
|
|
|
#include "VideoCommon/VertexManagerBase.h"
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "VideoCommon/VertexShaderManager.h"
|
2022-07-26 08:57:30 +00:00
|
|
|
#include "VideoCommon/VideoConfig.h"
|
2022-05-17 20:42:31 +00:00
|
|
|
#include "VideoCommon/XFMemory.h"
|
2010-03-06 02:07:48 +00:00
|
|
|
|
2009-03-07 08:35:01 +00:00
|
|
|
namespace VertexLoaderManager
|
|
|
|
{
|
2022-04-13 23:12:53 +00:00
|
|
|
// Used by zfreeze
|
2022-04-14 19:01:57 +00:00
|
|
|
std::array<u32, 3> position_matrix_index_cache;
|
|
|
|
// 3 vertices, 4 floats each to allow SIMD overwrite
|
|
|
|
alignas(sizeof(std::array<float, 4>)) std::array<std::array<float, 4>, 3> position_cache;
|
2022-04-14 05:03:34 +00:00
|
|
|
alignas(sizeof(std::array<float, 4>)) std::array<float, 4> tangent_cache;
|
|
|
|
alignas(sizeof(std::array<float, 4>)) std::array<float, 4> binormal_cache;
|
2015-06-01 17:58:27 +00:00
|
|
|
|
2014-12-12 07:53:48 +00:00
|
|
|
static NativeVertexFormatMap s_native_vertex_map;
|
|
|
|
static NativeVertexFormat* s_current_vtx_fmt;
|
2015-11-01 21:39:31 +00:00
|
|
|
u32 g_current_components;
|
2014-12-12 07:53:48 +00:00
|
|
|
|
2014-12-13 00:51:14 +00:00
|
|
|
typedef std::unordered_map<VertexLoaderUID, std::unique_ptr<VertexLoaderBase>> VertexLoaderMap;
|
2014-08-25 03:53:28 +00:00
|
|
|
static std::mutex s_vertex_loader_map_lock;
|
|
|
|
static VertexLoaderMap s_vertex_loader_map;
|
2009-03-07 08:35:01 +00:00
|
|
|
// TODO - change into array of pointers. Keep a map of all seen so far.
|
|
|
|
|
2021-06-20 20:47:57 +00:00
|
|
|
Common::EnumMap<u8*, CPArray::TexCoord7> cached_arraybases;
|
|
|
|
|
|
|
|
BitSet8 g_main_vat_dirty;
|
|
|
|
BitSet8 g_preprocess_vat_dirty;
|
|
|
|
bool g_bases_dirty; // Main only
|
|
|
|
std::array<VertexLoaderBase*, CP_NUM_VAT_REG> g_main_vertex_loaders;
|
|
|
|
std::array<VertexLoaderBase*, CP_NUM_VAT_REG> g_preprocess_vertex_loaders;
|
2022-11-16 13:39:29 +00:00
|
|
|
bool g_needs_cp_xf_consistency_check;
|
2015-05-29 12:42:45 +00:00
|
|
|
|
2009-03-07 08:35:01 +00:00
|
|
|
void Init()
|
|
|
|
{
|
|
|
|
MarkAllDirty();
|
2021-05-13 23:05:31 +00:00
|
|
|
for (auto& map_entry : g_main_vertex_loaders)
|
2014-08-27 17:38:00 +00:00
|
|
|
map_entry = nullptr;
|
2021-05-13 23:05:31 +00:00
|
|
|
for (auto& map_entry : g_preprocess_vertex_loaders)
|
2014-08-25 03:53:28 +00:00
|
|
|
map_entry = nullptr;
|
2019-07-11 03:34:50 +00:00
|
|
|
SETSTAT(g_stats.num_vertex_loaders, 0);
|
2009-03-07 08:35:01 +00:00
|
|
|
}
|
|
|
|
|
2016-01-13 20:14:20 +00:00
|
|
|
void Clear()
|
2009-03-07 08:35:01 +00:00
|
|
|
{
|
2014-08-25 03:53:28 +00:00
|
|
|
std::lock_guard<std::mutex> lk(s_vertex_loader_map_lock);
|
|
|
|
s_vertex_loader_map.clear();
|
2014-12-12 07:53:48 +00:00
|
|
|
s_native_vertex_map.clear();
|
2009-03-07 08:35:01 +00:00
|
|
|
}
|
|
|
|
|
2015-05-29 12:42:45 +00:00
|
|
|
void UpdateVertexArrayPointers()
|
|
|
|
{
|
2015-05-29 15:58:27 +00:00
|
|
|
// Anything to update?
|
2022-09-15 21:38:57 +00:00
|
|
|
if (!g_bases_dirty) [[likely]]
|
2015-05-29 15:58:27 +00:00
|
|
|
return;
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2022-12-02 19:07:30 +00:00
|
|
|
auto& system = Core::System::GetInstance();
|
|
|
|
auto& memory = system.GetMemory();
|
|
|
|
|
2015-05-29 12:42:45 +00:00
|
|
|
// Some games such as Burnout 2 can put invalid addresses into
|
|
|
|
// the array base registers. (see issue 8591)
|
|
|
|
// But the vertex arrays with invalid addresses aren't actually enabled.
|
|
|
|
// Note: Only array bases 0 through 11 are used by the Vertex loaders.
|
|
|
|
// 12 through 15 are used for loading data into xfmem.
|
2021-02-08 23:22:10 +00:00
|
|
|
// We also only update the array base if the vertex description states we are going to use it.
|
|
|
|
if (IsIndexed(g_main_cp_state.vtx_desc.low.Position))
|
2021-06-20 20:47:57 +00:00
|
|
|
cached_arraybases[CPArray::Position] =
|
2022-12-02 19:07:30 +00:00
|
|
|
memory.GetPointer(g_main_cp_state.array_bases[CPArray::Position]);
|
2021-02-08 23:22:10 +00:00
|
|
|
|
|
|
|
if (IsIndexed(g_main_cp_state.vtx_desc.low.Normal))
|
2021-06-20 20:47:57 +00:00
|
|
|
cached_arraybases[CPArray::Normal] =
|
2022-12-02 19:07:30 +00:00
|
|
|
memory.GetPointer(g_main_cp_state.array_bases[CPArray::Normal]);
|
2021-02-08 23:22:10 +00:00
|
|
|
|
2021-06-20 20:47:57 +00:00
|
|
|
for (u8 i = 0; i < g_main_cp_state.vtx_desc.low.Color.Size(); i++)
|
2021-02-08 23:22:10 +00:00
|
|
|
{
|
|
|
|
if (IsIndexed(g_main_cp_state.vtx_desc.low.Color[i]))
|
2021-06-20 20:47:57 +00:00
|
|
|
cached_arraybases[CPArray::Color0 + i] =
|
2022-12-02 19:07:30 +00:00
|
|
|
memory.GetPointer(g_main_cp_state.array_bases[CPArray::Color0 + i]);
|
2021-02-08 23:22:10 +00:00
|
|
|
}
|
|
|
|
|
2021-06-20 20:47:57 +00:00
|
|
|
for (u8 i = 0; i < g_main_cp_state.vtx_desc.high.TexCoord.Size(); i++)
|
2015-05-29 12:42:45 +00:00
|
|
|
{
|
2021-02-08 23:22:10 +00:00
|
|
|
if (IsIndexed(g_main_cp_state.vtx_desc.high.TexCoord[i]))
|
2021-06-20 20:47:57 +00:00
|
|
|
cached_arraybases[CPArray::TexCoord0 + i] =
|
2022-12-02 19:07:30 +00:00
|
|
|
memory.GetPointer(g_main_cp_state.array_bases[CPArray::TexCoord0 + i]);
|
2015-05-29 12:42:45 +00:00
|
|
|
}
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2021-05-13 23:05:31 +00:00
|
|
|
g_bases_dirty = false;
|
2015-05-29 12:42:45 +00:00
|
|
|
}
|
|
|
|
|
2009-03-07 08:35:01 +00:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
struct entry
|
|
|
|
{
|
|
|
|
std::string text;
|
|
|
|
u64 num_verts;
|
|
|
|
bool operator<(const entry& other) const { return num_verts > other.num_verts; }
|
|
|
|
};
|
2019-05-05 23:48:12 +00:00
|
|
|
} // namespace
|
2009-03-07 08:35:01 +00:00
|
|
|
|
|
|
|
void MarkAllDirty()
|
|
|
|
{
|
2022-10-07 19:25:04 +00:00
|
|
|
g_bases_dirty = true;
|
2021-05-13 23:05:31 +00:00
|
|
|
g_main_vat_dirty = BitSet8::AllTrue(8);
|
|
|
|
g_preprocess_vat_dirty = BitSet8::AllTrue(8);
|
2022-11-16 13:39:29 +00:00
|
|
|
g_needs_cp_xf_consistency_check = true;
|
2009-03-07 08:35:01 +00:00
|
|
|
}
|
|
|
|
|
2017-07-03 09:43:47 +00:00
|
|
|
NativeVertexFormat* GetOrCreateMatchingFormat(const PortableVertexDeclaration& decl)
|
|
|
|
{
|
|
|
|
auto iter = s_native_vertex_map.find(decl);
|
|
|
|
if (iter == s_native_vertex_map.end())
|
|
|
|
{
|
2023-01-26 22:34:59 +00:00
|
|
|
std::unique_ptr<NativeVertexFormat> fmt = g_gfx->CreateNativeVertexFormat(decl);
|
2017-07-03 09:43:47 +00:00
|
|
|
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;
|
2023-03-23 17:49:09 +00:00
|
|
|
static_assert(std::is_trivially_copyable_v<PortableVertexDeclaration>);
|
|
|
|
std::memset(static_cast<void*>(&new_decl), 0, sizeof(new_decl));
|
2017-07-03 09:43:47 +00:00
|
|
|
new_decl.stride = decl.stride;
|
|
|
|
|
2021-06-26 19:48:28 +00:00
|
|
|
auto MakeDummyAttribute = [](AttributeFormat& attr, ComponentFormat type, int components,
|
|
|
|
bool integer) {
|
2017-07-03 09:43:47 +00:00
|
|
|
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
|
2021-06-26 19:48:28 +00:00
|
|
|
MakeDummyAttribute(new_decl.position, ComponentFormat::Float, 1, false);
|
2019-06-01 11:55:09 +00:00
|
|
|
for (size_t i = 0; i < std::size(new_decl.normals); i++)
|
2017-07-03 09:43:47 +00:00
|
|
|
{
|
|
|
|
if (decl.normals[i].enable)
|
|
|
|
CopyAttribute(new_decl.normals[i], decl.normals[i]);
|
|
|
|
else
|
2021-06-26 19:48:28 +00:00
|
|
|
MakeDummyAttribute(new_decl.normals[i], ComponentFormat::Float, 1, false);
|
2017-07-03 09:43:47 +00:00
|
|
|
}
|
2019-06-01 11:55:09 +00:00
|
|
|
for (size_t i = 0; i < std::size(new_decl.colors); i++)
|
2017-07-03 09:43:47 +00:00
|
|
|
{
|
|
|
|
if (decl.colors[i].enable)
|
|
|
|
CopyAttribute(new_decl.colors[i], decl.colors[i]);
|
|
|
|
else
|
2021-06-26 19:48:28 +00:00
|
|
|
MakeDummyAttribute(new_decl.colors[i], ComponentFormat::UByte, 4, false);
|
2017-07-03 09:43:47 +00:00
|
|
|
}
|
2019-06-01 11:55:09 +00:00
|
|
|
for (size_t i = 0; i < std::size(new_decl.texcoords); i++)
|
2017-07-03 09:43:47 +00:00
|
|
|
{
|
|
|
|
if (decl.texcoords[i].enable)
|
|
|
|
CopyAttribute(new_decl.texcoords[i], decl.texcoords[i]);
|
|
|
|
else
|
2021-06-26 19:48:28 +00:00
|
|
|
MakeDummyAttribute(new_decl.texcoords[i], ComponentFormat::Float, 1, false);
|
2017-07-03 09:43:47 +00:00
|
|
|
}
|
|
|
|
if (decl.posmtx.enable)
|
|
|
|
CopyAttribute(new_decl.posmtx, decl.posmtx);
|
|
|
|
else
|
2021-06-26 19:48:28 +00:00
|
|
|
MakeDummyAttribute(new_decl.posmtx, ComponentFormat::UByte, 1, true);
|
2017-07-03 09:43:47 +00:00
|
|
|
|
|
|
|
return GetOrCreateMatchingFormat(new_decl);
|
|
|
|
}
|
|
|
|
|
2022-09-15 21:38:57 +00:00
|
|
|
namespace detail
|
2014-08-15 14:17:06 +00:00
|
|
|
{
|
2022-09-15 21:38:57 +00:00
|
|
|
template <bool IsPreprocess>
|
|
|
|
VertexLoaderBase* GetOrCreateLoader(int vtx_attr_group)
|
|
|
|
{
|
|
|
|
constexpr CPState* state = IsPreprocess ? &g_preprocess_cp_state : &g_main_cp_state;
|
|
|
|
constexpr BitSet8& attr_dirty = IsPreprocess ? g_preprocess_vat_dirty : g_main_vat_dirty;
|
|
|
|
constexpr auto& vertex_loaders =
|
|
|
|
IsPreprocess ? g_preprocess_vertex_loaders : g_main_vertex_loaders;
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2014-12-13 00:51:14 +00:00
|
|
|
VertexLoaderBase* loader;
|
2022-09-15 21:38:57 +00:00
|
|
|
|
|
|
|
// We are not allowed to create a native vertex format on preprocessing as this is on the wrong
|
|
|
|
// thread
|
|
|
|
bool check_for_native_format = !IsPreprocess;
|
|
|
|
|
|
|
|
VertexLoaderUID uid(state->vtx_desc, state->vtx_attr[vtx_attr_group]);
|
|
|
|
std::lock_guard<std::mutex> lk(s_vertex_loader_map_lock);
|
|
|
|
VertexLoaderMap::iterator iter = s_vertex_loader_map.find(uid);
|
|
|
|
if (iter != s_vertex_loader_map.end())
|
2014-08-15 14:17:06 +00:00
|
|
|
{
|
2022-09-15 21:38:57 +00:00
|
|
|
loader = iter->second.get();
|
|
|
|
check_for_native_format &= !loader->m_native_vertex_format;
|
2014-08-25 03:53:28 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-09-15 21:38:57 +00:00
|
|
|
auto [it, added] = s_vertex_loader_map.try_emplace(
|
|
|
|
uid,
|
|
|
|
VertexLoaderBase::CreateVertexLoader(state->vtx_desc, state->vtx_attr[vtx_attr_group]));
|
|
|
|
loader = it->second.get();
|
|
|
|
INCSTAT(g_stats.num_vertex_loaders);
|
2014-08-15 14:17:06 +00:00
|
|
|
}
|
2022-09-15 21:38:57 +00:00
|
|
|
if (check_for_native_format)
|
|
|
|
{
|
|
|
|
// search for a cached native vertex format
|
|
|
|
loader->m_native_vertex_format = GetOrCreateMatchingFormat(loader->m_native_vtx_decl);
|
|
|
|
}
|
|
|
|
vertex_loaders[vtx_attr_group] = loader;
|
|
|
|
attr_dirty[vtx_attr_group] = false;
|
2014-08-25 03:53:28 +00:00
|
|
|
return loader;
|
2014-08-15 14:17:06 +00:00
|
|
|
}
|
|
|
|
|
2022-09-15 21:38:57 +00:00
|
|
|
} // namespace detail
|
|
|
|
|
2022-05-17 20:42:31 +00:00
|
|
|
static void CheckCPConfiguration(int vtx_attr_group)
|
|
|
|
{
|
|
|
|
// Validate that the XF input configuration matches the CP configuration
|
|
|
|
u32 num_cp_colors = std::count_if(
|
|
|
|
g_main_cp_state.vtx_desc.low.Color.begin(), g_main_cp_state.vtx_desc.low.Color.end(),
|
|
|
|
[](auto format) { return format != VertexComponentFormat::NotPresent; });
|
|
|
|
u32 num_cp_tex_coords = std::count_if(
|
|
|
|
g_main_cp_state.vtx_desc.high.TexCoord.begin(), g_main_cp_state.vtx_desc.high.TexCoord.end(),
|
|
|
|
[](auto format) { return format != VertexComponentFormat::NotPresent; });
|
|
|
|
|
|
|
|
u32 num_cp_normals;
|
|
|
|
if (g_main_cp_state.vtx_desc.low.Normal == VertexComponentFormat::NotPresent)
|
|
|
|
num_cp_normals = 0;
|
|
|
|
else if (g_main_cp_state.vtx_attr[vtx_attr_group].g0.NormalElements == NormalComponentCount::NTB)
|
|
|
|
num_cp_normals = 3;
|
|
|
|
else
|
|
|
|
num_cp_normals = 1;
|
|
|
|
|
|
|
|
std::optional<u32> num_xf_normals;
|
|
|
|
switch (xfmem.invtxspec.numnormals)
|
|
|
|
{
|
|
|
|
case NormalCount::None:
|
|
|
|
num_xf_normals = 0;
|
|
|
|
break;
|
|
|
|
case NormalCount::Normal:
|
|
|
|
num_xf_normals = 1;
|
|
|
|
break;
|
|
|
|
case NormalCount::NormalTangentBinormal:
|
2022-10-25 05:36:43 +00:00
|
|
|
case NormalCount::Invalid: // see https://bugs.dolphin-emu.org/issues/13070
|
2022-05-17 20:42:31 +00:00
|
|
|
num_xf_normals = 3;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (num_cp_colors != xfmem.invtxspec.numcolors || num_cp_normals != num_xf_normals ||
|
2022-11-16 13:39:29 +00:00
|
|
|
num_cp_tex_coords != xfmem.invtxspec.numtextures) [[unlikely]]
|
2022-05-17 20:42:31 +00:00
|
|
|
{
|
|
|
|
PanicAlertFmt("Mismatched configuration between CP and XF stages - {}/{} colors, {}/{} "
|
|
|
|
"normals, {}/{} texture coordinates. Please report on the issue tracker.\n\n"
|
|
|
|
"VCD: {:08x} {:08x}\nVAT {}: {:08x} {:08x} {:08x}\nXF vertex spec: {:08x}",
|
|
|
|
num_cp_colors, xfmem.invtxspec.numcolors, num_cp_normals,
|
|
|
|
num_xf_normals.has_value() ? fmt::to_string(num_xf_normals.value()) : "invalid",
|
|
|
|
num_cp_tex_coords, xfmem.invtxspec.numtextures, g_main_cp_state.vtx_desc.low.Hex,
|
|
|
|
g_main_cp_state.vtx_desc.high.Hex, vtx_attr_group,
|
|
|
|
g_main_cp_state.vtx_attr[vtx_attr_group].g0.Hex,
|
|
|
|
g_main_cp_state.vtx_attr[vtx_attr_group].g1.Hex,
|
|
|
|
g_main_cp_state.vtx_attr[vtx_attr_group].g2.Hex, xfmem.invtxspec.hex);
|
|
|
|
|
|
|
|
// Analytics reporting so we can discover which games have this problem, that way when we
|
|
|
|
// eventually simulate the behavior we have test cases for it.
|
2022-11-16 13:39:29 +00:00
|
|
|
if (num_cp_colors != xfmem.invtxspec.numcolors) [[unlikely]]
|
2022-05-17 20:42:31 +00:00
|
|
|
{
|
|
|
|
DolphinAnalytics::Instance().ReportGameQuirk(
|
|
|
|
GameQuirk::MISMATCHED_GPU_COLORS_BETWEEN_CP_AND_XF);
|
|
|
|
}
|
2022-11-16 13:39:29 +00:00
|
|
|
if (num_cp_normals != num_xf_normals) [[unlikely]]
|
2022-05-17 20:42:31 +00:00
|
|
|
{
|
|
|
|
DolphinAnalytics::Instance().ReportGameQuirk(
|
|
|
|
GameQuirk::MISMATCHED_GPU_NORMALS_BETWEEN_CP_AND_XF);
|
|
|
|
}
|
2022-11-16 13:39:29 +00:00
|
|
|
if (num_cp_tex_coords != xfmem.invtxspec.numtextures) [[unlikely]]
|
2022-05-17 20:42:31 +00:00
|
|
|
{
|
|
|
|
DolphinAnalytics::Instance().ReportGameQuirk(
|
|
|
|
GameQuirk::MISMATCHED_GPU_TEX_COORDS_BETWEEN_CP_AND_XF);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Don't bail out, though; we can still render something successfully
|
|
|
|
// (real hardware seems to hang in this case, though)
|
|
|
|
}
|
2022-05-17 20:58:54 +00:00
|
|
|
|
|
|
|
if (g_main_cp_state.matrix_index_a.Hex != xfmem.MatrixIndexA.Hex ||
|
2022-11-16 13:39:29 +00:00
|
|
|
g_main_cp_state.matrix_index_b.Hex != xfmem.MatrixIndexB.Hex) [[unlikely]]
|
2022-05-17 20:58:54 +00:00
|
|
|
{
|
2022-07-18 01:12:08 +00:00
|
|
|
WARN_LOG_FMT(VIDEO,
|
|
|
|
"Mismatched matrix index configuration between CP and XF stages - "
|
|
|
|
"index A: {:08x}/{:08x}, index B {:08x}/{:08x}.",
|
|
|
|
g_main_cp_state.matrix_index_a.Hex, xfmem.MatrixIndexA.Hex,
|
|
|
|
g_main_cp_state.matrix_index_b.Hex, xfmem.MatrixIndexB.Hex);
|
2022-05-17 20:58:54 +00:00
|
|
|
DolphinAnalytics::Instance().ReportGameQuirk(
|
|
|
|
GameQuirk::MISMATCHED_GPU_MATRIX_INDICES_BETWEEN_CP_AND_XF);
|
|
|
|
}
|
2022-05-17 20:42:31 +00:00
|
|
|
}
|
|
|
|
|
2022-09-15 21:38:57 +00:00
|
|
|
template <bool IsPreprocess>
|
2022-11-23 00:54:05 +00:00
|
|
|
int RunVertices(int vtx_attr_group, OpcodeDecoder::Primitive primitive, int count, const u8* src)
|
2009-03-07 08:35:01 +00:00
|
|
|
{
|
2022-11-16 13:39:29 +00:00
|
|
|
if (count == 0) [[unlikely]]
|
2014-12-09 07:35:04 +00:00
|
|
|
return 0;
|
2022-04-14 19:01:57 +00:00
|
|
|
ASSERT(count > 0);
|
2014-08-27 17:38:00 +00:00
|
|
|
|
2022-09-15 21:38:57 +00:00
|
|
|
VertexLoaderBase* loader = RefreshLoader<IsPreprocess>(vtx_attr_group);
|
2014-07-25 23:10:44 +00:00
|
|
|
|
2021-03-11 20:55:25 +00:00
|
|
|
int size = count * loader->m_vertex_size;
|
Refactor opcode decoding a bit to kill FifoCommandRunnable.
Separated out from my gpu-determinism branch by request. It's not a big
commit; I just like to write long commit messages.
The main reason to kill it is hopefully a slight performance improvement
from avoiding the double switch (especially in single core mode);
however, this also improves cycle calculation, as described below.
- FifoCommandRunnable is removed; in its stead, Decode returns the
number of cycles (which only matters for "sync" GPU mode), or 0 if there
was not enough data, and is also responsible for unknown opcode alerts.
Decode and DecodeSemiNop are almost identical, so the latter is replaced
with a skipped_frame parameter to Decode. Doesn't mean we can't improve
skipped_frame mode to do less work; if, at such a point, branching on it
has too much overhead (it certainly won't now), it can always be changed
to a template parameter.
- FifoCommandRunnable used a fixed, large cycle count for display lists,
regardless of the contents. Presumably the actual hardware's processing
time is mostly the processing time of whatever commands are in the list,
and with this change InterpretDisplayList can just return the list's
cycle count to be added to the total. (Since the calculation for this
is part of Decode, it didn't seem easy to split this change up.)
To facilitate this, Decode also gains an explicit 'end' parameter in
lieu of FifoCommandRunnable's call to GetVideoBufferEndPtr, which can
point to there or to the end of a display list (or elsewhere in
gpu-determinism, but that's another story). Also, as a small
optimization, InterpretDisplayList now calls OpcodeDecoder_Run rather
than having its own Decode loop, to allow Decode to be inlined (haven't
checked whether this actually happens though).
skipped_frame mode still does not traverse display lists and uses the
old fake value of 45 cycles. degasus has suggested that this hack is
not essential for performance and can be removed, but I want to separate
any potential performance impact of that from this commit.
2014-09-01 05:11:32 +00:00
|
|
|
|
2022-09-15 21:38:57 +00:00
|
|
|
if constexpr (!IsPreprocess)
|
|
|
|
{
|
|
|
|
// Doing early return for the opposite case would be cleaner
|
|
|
|
// but triggers a false unreachable code warning in MSVC debug builds.
|
2014-08-05 02:51:42 +00:00
|
|
|
|
2023-03-19 22:25:00 +00:00
|
|
|
if (g_needs_cp_xf_consistency_check) [[unlikely]]
|
|
|
|
{
|
|
|
|
CheckCPConfiguration(vtx_attr_group);
|
|
|
|
g_needs_cp_xf_consistency_check = false;
|
|
|
|
}
|
2022-05-17 20:42:31 +00:00
|
|
|
|
2022-09-15 21:38:57 +00:00
|
|
|
// If the native vertex format changed, force a flush.
|
|
|
|
if (loader->m_native_vertex_format != s_current_vtx_fmt ||
|
2022-11-16 13:39:29 +00:00
|
|
|
loader->m_native_components != g_current_components) [[unlikely]]
|
2022-09-15 21:38:57 +00:00
|
|
|
{
|
|
|
|
g_vertex_manager->Flush();
|
2023-03-19 22:25:00 +00:00
|
|
|
|
|
|
|
s_current_vtx_fmt = loader->m_native_vertex_format;
|
|
|
|
g_current_components = loader->m_native_components;
|
|
|
|
auto& system = Core::System::GetInstance();
|
|
|
|
auto& vertex_shader_manager = system.GetVertexShaderManager();
|
|
|
|
vertex_shader_manager.SetVertexFormat(loader->m_native_components,
|
|
|
|
loader->m_native_vertex_format->GetVertexDeclaration());
|
2022-09-15 21:38:57 +00:00
|
|
|
}
|
2014-07-25 23:10:44 +00:00
|
|
|
|
2022-07-26 08:57:30 +00:00
|
|
|
// CPUCull's performance increase comes from encoding fewer GPU commands, not sending less data
|
|
|
|
// Therefore it's only useful to check if culling could remove a flush
|
|
|
|
const bool can_cpu_cull = g_ActiveConfig.bCPUCull &&
|
|
|
|
primitive < OpcodeDecoder::Primitive::GX_DRAW_LINES &&
|
|
|
|
!g_vertex_manager->HasSendableVertices();
|
|
|
|
|
2022-09-15 21:38:57 +00:00
|
|
|
// if cull mode is CULL_ALL, tell VertexManager to skip triangles and quads.
|
2022-07-26 08:57:30 +00:00
|
|
|
// They still need to go through vertex loading, because we need to calculate a zfreeze
|
|
|
|
// reference slope.
|
|
|
|
const bool cullall = (bpmem.genMode.cullmode == CullMode::All &&
|
|
|
|
primitive < OpcodeDecoder::Primitive::GX_DRAW_LINES);
|
2015-01-24 01:37:20 +00:00
|
|
|
|
2022-07-26 08:57:30 +00:00
|
|
|
const int stride = loader->m_native_vtx_decl.stride;
|
|
|
|
DataReader dst = g_vertex_manager->PrepareForAdditionalData(primitive, count, stride,
|
|
|
|
cullall || can_cpu_cull);
|
2014-08-02 06:42:15 +00:00
|
|
|
|
2022-11-23 00:54:05 +00:00
|
|
|
count = loader->RunVertices(src, dst.GetPointer(), count);
|
2014-08-02 06:42:15 +00:00
|
|
|
|
2022-07-26 08:57:30 +00:00
|
|
|
if (can_cpu_cull && !cullall)
|
|
|
|
{
|
|
|
|
if (!g_vertex_manager->AreAllVerticesCulled(loader, primitive, dst.GetPointer(), count))
|
|
|
|
{
|
|
|
|
DataReader new_dst = g_vertex_manager->DisableCullAll(stride);
|
|
|
|
memmove(new_dst.GetPointer(), dst.GetPointer(), count * stride);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-15 21:38:57 +00:00
|
|
|
g_vertex_manager->AddIndices(primitive, count);
|
|
|
|
g_vertex_manager->FlushData(count, loader->m_native_vtx_decl.stride);
|
2014-12-09 07:35:04 +00:00
|
|
|
|
2022-09-15 21:38:57 +00:00
|
|
|
ADDSTAT(g_stats.this_frame.num_prims, count);
|
|
|
|
INCSTAT(g_stats.this_frame.num_primitive_joins);
|
|
|
|
}
|
2014-12-09 07:35:04 +00:00
|
|
|
return size;
|
2014-01-30 13:48:23 +00:00
|
|
|
}
|
2009-08-08 01:39:56 +00:00
|
|
|
|
2022-09-15 21:38:57 +00:00
|
|
|
template int RunVertices<false>(int vtx_attr_group, OpcodeDecoder::Primitive primitive, int count,
|
2022-11-23 00:54:05 +00:00
|
|
|
const u8* src);
|
2022-09-15 21:38:57 +00:00
|
|
|
template int RunVertices<true>(int vtx_attr_group, OpcodeDecoder::Primitive primitive, int count,
|
2022-11-23 00:54:05 +00:00
|
|
|
const u8* src);
|
2022-09-15 21:38:57 +00:00
|
|
|
|
2014-07-25 23:10:44 +00:00
|
|
|
NativeVertexFormat* GetCurrentVertexFormat()
|
2014-06-05 15:55:21 +00:00
|
|
|
{
|
2014-07-25 23:10:44 +00:00
|
|
|
return s_current_vtx_fmt;
|
2014-06-05 15:55:21 +00:00
|
|
|
}
|
|
|
|
|
2019-05-05 23:48:12 +00:00
|
|
|
} // namespace VertexLoaderManager
|