mirror of
https://github.com/izzy2lost/dolphin.git
synced 2026-06-19 01:16:48 -07:00
VideoCommon: Add class for quickly transforming and culling vertices on the CPU
This commit is contained in:
@@ -634,6 +634,8 @@
|
||||
<ClInclude Include="VideoCommon\CommandProcessor.h" />
|
||||
<ClInclude Include="VideoCommon\ConstantManager.h" />
|
||||
<ClInclude Include="VideoCommon\CPMemory.h" />
|
||||
<ClInclude Include="VideoCommon\CPUCull.h" />
|
||||
<ClInclude Include="VideoCommon\CPUCullImpl.h" />
|
||||
<ClInclude Include="VideoCommon\DataReader.h" />
|
||||
<ClInclude Include="VideoCommon\DriverDetails.h" />
|
||||
<ClInclude Include="VideoCommon\Fifo.h" />
|
||||
@@ -1238,6 +1240,7 @@
|
||||
<ClCompile Include="VideoCommon\BPStructs.cpp" />
|
||||
<ClCompile Include="VideoCommon\CommandProcessor.cpp" />
|
||||
<ClCompile Include="VideoCommon\CPMemory.cpp" />
|
||||
<ClCompile Include="VideoCommon\CPUCull.cpp" />
|
||||
<ClCompile Include="VideoCommon\DriverDetails.cpp" />
|
||||
<ClCompile Include="VideoCommon\Fifo.cpp" />
|
||||
<ClCompile Include="VideoCommon\FramebufferManager.cpp" />
|
||||
|
||||
@@ -23,6 +23,9 @@ add_library(videocommon
|
||||
ConstantManager.h
|
||||
CPMemory.cpp
|
||||
CPMemory.h
|
||||
CPUCull.cpp
|
||||
CPUCull.h
|
||||
CPUCullImpl.h
|
||||
DriverDetails.cpp
|
||||
DriverDetails.h
|
||||
Fifo.cpp
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VideoCommon/CPUCull.h"
|
||||
|
||||
#include "Common/Assert.h"
|
||||
#include "Common/CPUDetect.h"
|
||||
#include "Common/MathUtil.h"
|
||||
#include "Common/MemoryUtil.h"
|
||||
#include "Core/System.h"
|
||||
|
||||
#include "VideoCommon/CPMemory.h"
|
||||
#include "VideoCommon/VertexManagerBase.h"
|
||||
#include "VideoCommon/VertexShaderManager.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
#include "VideoCommon/XFMemory.h"
|
||||
|
||||
#if defined(_M_X86) || defined(_M_X86_64)
|
||||
#define USE_SSE
|
||||
#elif defined(_M_ARM_64)
|
||||
#define USE_NEON
|
||||
#else
|
||||
#define NO_SIMD
|
||||
#endif
|
||||
|
||||
#if defined(USE_SSE)
|
||||
#include <immintrin.h>
|
||||
#elif defined(USE_NEON)
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
#include "VideoCommon/CPUCullImpl.h"
|
||||
#ifdef USE_SSE
|
||||
#define USE_SSE3
|
||||
#include "VideoCommon/CPUCullImpl.h"
|
||||
#define USE_SSE41
|
||||
#include "VideoCommon/CPUCullImpl.h"
|
||||
#define USE_AVX
|
||||
#include "VideoCommon/CPUCullImpl.h"
|
||||
#define USE_FMA
|
||||
#include "VideoCommon/CPUCullImpl.h"
|
||||
#endif
|
||||
|
||||
#if defined(USE_SSE)
|
||||
#if defined(__AVX__) && defined(__FMA__)
|
||||
static constexpr int MIN_SSE = 51;
|
||||
#elif defined(__AVX__)
|
||||
static constexpr int MIN_SSE = 50;
|
||||
#elif defined(__SSE4_1__)
|
||||
static constexpr int MIN_SSE = 41;
|
||||
#elif defined(__SSE3__)
|
||||
static constexpr int MIN_SSE = 30;
|
||||
#else
|
||||
static constexpr int MIN_SSE = 0;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
template <bool PositionHas3Elems, bool PerVertexPosMtx>
|
||||
static CPUCull::TransformFunction GetTransformFunction()
|
||||
{
|
||||
#if defined(USE_SSE)
|
||||
if (MIN_SSE >= 51 || (cpu_info.bAVX && cpu_info.bFMA))
|
||||
return CPUCull_FMA::TransformVertices<PositionHas3Elems, PerVertexPosMtx>;
|
||||
else if (MIN_SSE >= 50 || cpu_info.bAVX)
|
||||
return CPUCull_AVX::TransformVertices<PositionHas3Elems, PerVertexPosMtx>;
|
||||
else if (PositionHas3Elems && PerVertexPosMtx && (MIN_SSE >= 41 || cpu_info.bSSE4_1))
|
||||
return CPUCull_SSE41::TransformVertices<PositionHas3Elems, PerVertexPosMtx>;
|
||||
else if (PositionHas3Elems && (MIN_SSE >= 30 || cpu_info.bSSE3))
|
||||
return CPUCull_SSE3::TransformVertices<PositionHas3Elems, PerVertexPosMtx>;
|
||||
else
|
||||
return CPUCull_SSE::TransformVertices<PositionHas3Elems, PerVertexPosMtx>;
|
||||
#elif defined(USE_NEON)
|
||||
return CPUCull_NEON::TransformVertices<PositionHas3Elems, PerVertexPosMtx>;
|
||||
#else
|
||||
return CPUCull_Scalar::TransformVertices<PositionHas3Elems, PerVertexPosMtx>;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <OpcodeDecoder::Primitive Primitive, CullMode Mode>
|
||||
static CPUCull::CullFunction GetCullFunction0()
|
||||
{
|
||||
#if defined(USE_SSE)
|
||||
// Note: AVX version only actually AVX on compilers that support __attribute__((target))
|
||||
// Sorry, MSVC + Sandy Bridge. (Ivy+ and AMD see very little benefit thanks to mov elimination)
|
||||
if (MIN_SSE >= 50 || cpu_info.bAVX)
|
||||
return CPUCull_AVX::AreAllVerticesCulled<Primitive, Mode>;
|
||||
else if (MIN_SSE >= 30 || cpu_info.bSSE3)
|
||||
return CPUCull_SSE3::AreAllVerticesCulled<Primitive, Mode>;
|
||||
else
|
||||
return CPUCull_SSE::AreAllVerticesCulled<Primitive, Mode>;
|
||||
#elif defined(USE_NEON)
|
||||
return CPUCull_NEON::AreAllVerticesCulled<Primitive, Mode>;
|
||||
#else
|
||||
return CPUCull_Scalar::AreAllVerticesCulled<Primitive, Mode>;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <OpcodeDecoder::Primitive Primitive>
|
||||
static Common::EnumMap<CPUCull::CullFunction, CullMode::All> GetCullFunction1()
|
||||
{
|
||||
return {
|
||||
GetCullFunction0<Primitive, CullMode::None>(),
|
||||
GetCullFunction0<Primitive, CullMode::Back>(),
|
||||
GetCullFunction0<Primitive, CullMode::Front>(),
|
||||
GetCullFunction0<Primitive, CullMode::All>(),
|
||||
};
|
||||
}
|
||||
|
||||
CPUCull::~CPUCull() = default;
|
||||
|
||||
void CPUCull::Init()
|
||||
{
|
||||
m_transform_table[false][false] = GetTransformFunction<false, false>();
|
||||
m_transform_table[false][true] = GetTransformFunction<false, true>();
|
||||
m_transform_table[true][false] = GetTransformFunction<true, false>();
|
||||
m_transform_table[true][true] = GetTransformFunction<true, true>();
|
||||
using Prim = OpcodeDecoder::Primitive;
|
||||
m_cull_table[Prim::GX_DRAW_QUADS] = GetCullFunction1<Prim::GX_DRAW_QUADS>();
|
||||
m_cull_table[Prim::GX_DRAW_QUADS_2] = GetCullFunction1<Prim::GX_DRAW_QUADS>();
|
||||
m_cull_table[Prim::GX_DRAW_TRIANGLES] = GetCullFunction1<Prim::GX_DRAW_TRIANGLES>();
|
||||
m_cull_table[Prim::GX_DRAW_TRIANGLE_STRIP] = GetCullFunction1<Prim::GX_DRAW_TRIANGLE_STRIP>();
|
||||
m_cull_table[Prim::GX_DRAW_TRIANGLE_FAN] = GetCullFunction1<Prim::GX_DRAW_TRIANGLE_FAN>();
|
||||
}
|
||||
|
||||
bool CPUCull::AreAllVerticesCulled(VertexLoaderBase* loader, OpcodeDecoder::Primitive primitive,
|
||||
const u8* src, u32 count)
|
||||
{
|
||||
ASSERT_MSG(VIDEO, primitive < OpcodeDecoder::Primitive::GX_DRAW_LINES,
|
||||
"CPUCull should not be called on lines or points");
|
||||
const u32 stride = loader->m_native_vtx_decl.stride;
|
||||
const bool posHas3Elems = loader->m_native_vtx_decl.position.components >= 3;
|
||||
const bool perVertexPosMtx = loader->m_native_vtx_decl.posmtx.enable;
|
||||
if (m_transform_buffer_size < count) [[unlikely]]
|
||||
{
|
||||
u32 new_size = MathUtil::NextPowerOf2(count);
|
||||
m_transform_buffer_size = new_size;
|
||||
m_transform_buffer.reset(static_cast<TransformedVertex*>(
|
||||
Common::AllocateAlignedMemory(new_size * sizeof(TransformedVertex), 32)));
|
||||
}
|
||||
|
||||
// transform functions need the projection matrix to tranform to clip space
|
||||
Core::System::GetInstance().GetVertexShaderManager().SetProjectionMatrix();
|
||||
|
||||
static constexpr Common::EnumMap<CullMode, CullMode::All> cullmode_invert = {
|
||||
CullMode::None, CullMode::Front, CullMode::Back, CullMode::All};
|
||||
|
||||
CullMode cullmode = bpmem.genMode.cullmode;
|
||||
if (xfmem.viewport.ht > 0) // See videosoftware Clipper.cpp:IsBackface
|
||||
cullmode = cullmode_invert[cullmode];
|
||||
const TransformFunction transform = m_transform_table[posHas3Elems][perVertexPosMtx];
|
||||
transform(m_transform_buffer.get(), src, stride, count);
|
||||
const CullFunction cull = m_cull_table[primitive][cullmode];
|
||||
return cull(m_transform_buffer.get(), count);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void CPUCull::BufferDeleter<T>::operator()(T* ptr)
|
||||
{
|
||||
Common::FreeAlignedMemory(ptr);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2022 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "VideoCommon/BPMemory.h"
|
||||
#include "VideoCommon/DataReader.h"
|
||||
#include "VideoCommon/OpcodeDecoding.h"
|
||||
|
||||
class CPUCull
|
||||
{
|
||||
public:
|
||||
~CPUCull();
|
||||
void Init();
|
||||
bool AreAllVerticesCulled(VertexLoaderBase* loader, OpcodeDecoder::Primitive primitive,
|
||||
const u8* src, u32 count);
|
||||
|
||||
struct alignas(16) TransformedVertex
|
||||
{
|
||||
float x, y, z, w;
|
||||
};
|
||||
|
||||
using TransformFunction = void (*)(void*, const void*, u32, int);
|
||||
using CullFunction = bool (*)(const CPUCull::TransformedVertex*, int);
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
struct BufferDeleter
|
||||
{
|
||||
void operator()(T* ptr);
|
||||
};
|
||||
std::unique_ptr<TransformedVertex[], BufferDeleter<TransformedVertex>> m_transform_buffer;
|
||||
u32 m_transform_buffer_size = 0;
|
||||
std::array<std::array<TransformFunction, 2>, 2> m_transform_table;
|
||||
Common::EnumMap<Common::EnumMap<CullFunction, CullMode::All>,
|
||||
OpcodeDecoder::Primitive::GX_DRAW_TRIANGLE_FAN>
|
||||
m_cull_table;
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,7 @@ enum class SrcBlendFactor : u32;
|
||||
enum class ZTexOp : u32;
|
||||
enum class LogicOp : u32;
|
||||
|
||||
struct PixelShaderConstants
|
||||
struct alignas(16) PixelShaderConstants
|
||||
{
|
||||
std::array<int4, 4> colors;
|
||||
std::array<int4, 4> kcolors;
|
||||
@@ -60,7 +60,7 @@ struct PixelShaderConstants
|
||||
LogicOp logic_op_mode;
|
||||
};
|
||||
|
||||
struct VertexShaderConstants
|
||||
struct alignas(16) VertexShaderConstants
|
||||
{
|
||||
u32 components; // .x
|
||||
u32 xfmem_dualTexInfo; // .y
|
||||
@@ -109,7 +109,7 @@ enum class VSExpand : u32
|
||||
Line,
|
||||
};
|
||||
|
||||
struct GeometryShaderConstants
|
||||
struct alignas(16) GeometryShaderConstants
|
||||
{
|
||||
float4 stereoparams;
|
||||
float4 lineptparams;
|
||||
|
||||
@@ -65,6 +65,97 @@ void VertexShaderManager::Dirty()
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
Common::Matrix44 VertexShaderManager::LoadProjectionMatrix()
|
||||
{
|
||||
const auto& rawProjection = xfmem.projection.rawProjection;
|
||||
|
||||
switch (xfmem.projection.type)
|
||||
{
|
||||
case ProjectionType::Perspective:
|
||||
{
|
||||
const Common::Vec2 fov_multiplier = g_freelook_camera.IsActive() ?
|
||||
g_freelook_camera.GetFieldOfViewMultiplier() :
|
||||
Common::Vec2{1, 1};
|
||||
m_projection_matrix[0] = rawProjection[0] * g_ActiveConfig.fAspectRatioHackW * fov_multiplier.x;
|
||||
m_projection_matrix[1] = 0.0f;
|
||||
m_projection_matrix[2] = rawProjection[1] * g_ActiveConfig.fAspectRatioHackW * fov_multiplier.x;
|
||||
m_projection_matrix[3] = 0.0f;
|
||||
|
||||
m_projection_matrix[4] = 0.0f;
|
||||
m_projection_matrix[5] = rawProjection[2] * g_ActiveConfig.fAspectRatioHackH * fov_multiplier.y;
|
||||
m_projection_matrix[6] = rawProjection[3] * g_ActiveConfig.fAspectRatioHackH * fov_multiplier.y;
|
||||
m_projection_matrix[7] = 0.0f;
|
||||
|
||||
m_projection_matrix[8] = 0.0f;
|
||||
m_projection_matrix[9] = 0.0f;
|
||||
m_projection_matrix[10] = rawProjection[4];
|
||||
m_projection_matrix[11] = rawProjection[5];
|
||||
|
||||
m_projection_matrix[12] = 0.0f;
|
||||
m_projection_matrix[13] = 0.0f;
|
||||
|
||||
m_projection_matrix[14] = -1.0f;
|
||||
m_projection_matrix[15] = 0.0f;
|
||||
|
||||
g_stats.gproj = m_projection_matrix;
|
||||
}
|
||||
break;
|
||||
|
||||
case ProjectionType::Orthographic:
|
||||
{
|
||||
m_projection_matrix[0] = rawProjection[0];
|
||||
m_projection_matrix[1] = 0.0f;
|
||||
m_projection_matrix[2] = 0.0f;
|
||||
m_projection_matrix[3] = rawProjection[1];
|
||||
|
||||
m_projection_matrix[4] = 0.0f;
|
||||
m_projection_matrix[5] = rawProjection[2];
|
||||
m_projection_matrix[6] = 0.0f;
|
||||
m_projection_matrix[7] = rawProjection[3];
|
||||
|
||||
m_projection_matrix[8] = 0.0f;
|
||||
m_projection_matrix[9] = 0.0f;
|
||||
m_projection_matrix[10] = rawProjection[4];
|
||||
m_projection_matrix[11] = rawProjection[5];
|
||||
|
||||
m_projection_matrix[12] = 0.0f;
|
||||
m_projection_matrix[13] = 0.0f;
|
||||
|
||||
m_projection_matrix[14] = 0.0f;
|
||||
m_projection_matrix[15] = 1.0f;
|
||||
|
||||
g_stats.g2proj = m_projection_matrix;
|
||||
g_stats.proj = rawProjection;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR_LOG_FMT(VIDEO, "Unknown projection type: {}", xfmem.projection.type);
|
||||
}
|
||||
|
||||
PRIM_LOG("Projection: {} {} {} {} {} {}", rawProjection[0], rawProjection[1], rawProjection[2],
|
||||
rawProjection[3], rawProjection[4], rawProjection[5]);
|
||||
|
||||
auto corrected_matrix = m_viewport_correction * Common::Matrix44::FromArray(m_projection_matrix);
|
||||
|
||||
if (g_freelook_camera.IsActive() && xfmem.projection.type == ProjectionType::Perspective)
|
||||
corrected_matrix *= g_freelook_camera.GetView();
|
||||
|
||||
g_freelook_camera.GetController()->SetClean();
|
||||
|
||||
return corrected_matrix;
|
||||
}
|
||||
|
||||
void VertexShaderManager::SetProjectionMatrix()
|
||||
{
|
||||
if (m_projection_changed || g_freelook_camera.GetController()->IsDirty())
|
||||
{
|
||||
m_projection_changed = false;
|
||||
auto corrected_matrix = LoadProjectionMatrix();
|
||||
memcpy(constants.projection.data(), corrected_matrix.data.data(), 4 * sizeof(float4));
|
||||
}
|
||||
}
|
||||
|
||||
// Syncs the shader constant buffers with xfmem
|
||||
// TODO: A cleaner way to control the matrices without making a mess in the parameters field
|
||||
void VertexShaderManager::SetConstants(const std::vector<std::string>& textures)
|
||||
@@ -317,84 +408,7 @@ void VertexShaderManager::SetConstants(const std::vector<std::string>& textures)
|
||||
m_projection_changed = false;
|
||||
m_projection_graphics_mod_change = !projection_actions.empty();
|
||||
|
||||
const auto& rawProjection = xfmem.projection.rawProjection;
|
||||
|
||||
switch (xfmem.projection.type)
|
||||
{
|
||||
case ProjectionType::Perspective:
|
||||
{
|
||||
const Common::Vec2 fov_multiplier = g_freelook_camera.IsActive() ?
|
||||
g_freelook_camera.GetFieldOfViewMultiplier() :
|
||||
Common::Vec2{1, 1};
|
||||
m_projection_matrix[0] =
|
||||
rawProjection[0] * g_ActiveConfig.fAspectRatioHackW * fov_multiplier.x;
|
||||
m_projection_matrix[1] = 0.0f;
|
||||
m_projection_matrix[2] =
|
||||
rawProjection[1] * g_ActiveConfig.fAspectRatioHackW * fov_multiplier.x;
|
||||
m_projection_matrix[3] = 0.0f;
|
||||
|
||||
m_projection_matrix[4] = 0.0f;
|
||||
m_projection_matrix[5] =
|
||||
rawProjection[2] * g_ActiveConfig.fAspectRatioHackH * fov_multiplier.y;
|
||||
m_projection_matrix[6] =
|
||||
rawProjection[3] * g_ActiveConfig.fAspectRatioHackH * fov_multiplier.y;
|
||||
m_projection_matrix[7] = 0.0f;
|
||||
|
||||
m_projection_matrix[8] = 0.0f;
|
||||
m_projection_matrix[9] = 0.0f;
|
||||
m_projection_matrix[10] = rawProjection[4];
|
||||
m_projection_matrix[11] = rawProjection[5];
|
||||
|
||||
m_projection_matrix[12] = 0.0f;
|
||||
m_projection_matrix[13] = 0.0f;
|
||||
|
||||
m_projection_matrix[14] = -1.0f;
|
||||
m_projection_matrix[15] = 0.0f;
|
||||
|
||||
g_stats.gproj = m_projection_matrix;
|
||||
}
|
||||
break;
|
||||
|
||||
case ProjectionType::Orthographic:
|
||||
{
|
||||
m_projection_matrix[0] = rawProjection[0];
|
||||
m_projection_matrix[1] = 0.0f;
|
||||
m_projection_matrix[2] = 0.0f;
|
||||
m_projection_matrix[3] = rawProjection[1];
|
||||
|
||||
m_projection_matrix[4] = 0.0f;
|
||||
m_projection_matrix[5] = rawProjection[2];
|
||||
m_projection_matrix[6] = 0.0f;
|
||||
m_projection_matrix[7] = rawProjection[3];
|
||||
|
||||
m_projection_matrix[8] = 0.0f;
|
||||
m_projection_matrix[9] = 0.0f;
|
||||
m_projection_matrix[10] = rawProjection[4];
|
||||
m_projection_matrix[11] = rawProjection[5];
|
||||
|
||||
m_projection_matrix[12] = 0.0f;
|
||||
m_projection_matrix[13] = 0.0f;
|
||||
|
||||
m_projection_matrix[14] = 0.0f;
|
||||
m_projection_matrix[15] = 1.0f;
|
||||
|
||||
g_stats.g2proj = m_projection_matrix;
|
||||
g_stats.proj = rawProjection;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR_LOG_FMT(VIDEO, "Unknown projection type: {}", xfmem.projection.type);
|
||||
}
|
||||
|
||||
PRIM_LOG("Projection: {} {} {} {} {} {}", rawProjection[0], rawProjection[1], rawProjection[2],
|
||||
rawProjection[3], rawProjection[4], rawProjection[5]);
|
||||
|
||||
auto corrected_matrix =
|
||||
m_viewport_correction * Common::Matrix44::FromArray(m_projection_matrix);
|
||||
|
||||
if (g_freelook_camera.IsActive() && xfmem.projection.type == ProjectionType::Perspective)
|
||||
corrected_matrix *= g_freelook_camera.GetView();
|
||||
auto corrected_matrix = LoadProjectionMatrix();
|
||||
|
||||
GraphicsModActionData::Projection projection{&corrected_matrix};
|
||||
for (auto action : projection_actions)
|
||||
@@ -404,8 +418,6 @@ void VertexShaderManager::SetConstants(const std::vector<std::string>& textures)
|
||||
|
||||
memcpy(constants.projection.data(), corrected_matrix.data.data(), 4 * sizeof(float4));
|
||||
|
||||
g_freelook_camera.GetController()->SetClean();
|
||||
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ public:
|
||||
void DoState(PointerWrap& p);
|
||||
|
||||
// constant management
|
||||
void SetProjectionMatrix();
|
||||
void SetConstants(const std::vector<std::string>& textures);
|
||||
|
||||
void InvalidateXFRange(int start, int end);
|
||||
@@ -64,4 +65,6 @@ private:
|
||||
std::array<int, 2> m_minmax_lights_changed{};
|
||||
|
||||
Common::Matrix44 m_viewport_correction{};
|
||||
|
||||
Common::Matrix44 LoadProjectionMatrix();
|
||||
};
|
||||
|
||||
@@ -423,7 +423,7 @@ struct Projection
|
||||
ProjectionType type;
|
||||
};
|
||||
|
||||
struct XFMemory
|
||||
struct alignas(16) XFMemory
|
||||
{
|
||||
float posMatrices[256]; // 0x0000 - 0x00ff
|
||||
u32 unk0[768]; // 0x0100 - 0x03ff
|
||||
|
||||
Reference in New Issue
Block a user