From b170ef9651cda959a5bfa277bb533815f10de5d8 Mon Sep 17 00:00:00 2001 From: TellowKrinkle Date: Thu, 10 Nov 2022 19:30:49 -0600 Subject: VideoCommon: Add class for quickly transforming and culling vertices on the CPU --- Source/Core/VideoCommon/CPUCull.cpp | 160 ++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 Source/Core/VideoCommon/CPUCull.cpp (limited to 'Source/Core/VideoCommon/CPUCull.cpp') diff --git a/Source/Core/VideoCommon/CPUCull.cpp b/Source/Core/VideoCommon/CPUCull.cpp new file mode 100644 index 0000000000..bb68376b09 --- /dev/null +++ b/Source/Core/VideoCommon/CPUCull.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 +#elif defined(USE_NEON) +#include +#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 +static CPUCull::TransformFunction GetTransformFunction() +{ +#if defined(USE_SSE) + if (MIN_SSE >= 51 || (cpu_info.bAVX && cpu_info.bFMA)) + return CPUCull_FMA::TransformVertices; + else if (MIN_SSE >= 50 || cpu_info.bAVX) + return CPUCull_AVX::TransformVertices; + else if (PositionHas3Elems && PerVertexPosMtx && (MIN_SSE >= 41 || cpu_info.bSSE4_1)) + return CPUCull_SSE41::TransformVertices; + else if (PositionHas3Elems && (MIN_SSE >= 30 || cpu_info.bSSE3)) + return CPUCull_SSE3::TransformVertices; + else + return CPUCull_SSE::TransformVertices; +#elif defined(USE_NEON) + return CPUCull_NEON::TransformVertices; +#else + return CPUCull_Scalar::TransformVertices; +#endif +} + +template +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; + else if (MIN_SSE >= 30 || cpu_info.bSSE3) + return CPUCull_SSE3::AreAllVerticesCulled; + else + return CPUCull_SSE::AreAllVerticesCulled; +#elif defined(USE_NEON) + return CPUCull_NEON::AreAllVerticesCulled; +#else + return CPUCull_Scalar::AreAllVerticesCulled; +#endif +} + +template +static Common::EnumMap GetCullFunction1() +{ + return { + GetCullFunction0(), + GetCullFunction0(), + GetCullFunction0(), + GetCullFunction0(), + }; +} + +CPUCull::~CPUCull() = default; + +void CPUCull::Init() +{ + m_transform_table[false][false] = GetTransformFunction(); + m_transform_table[false][true] = GetTransformFunction(); + m_transform_table[true][false] = GetTransformFunction(); + m_transform_table[true][true] = GetTransformFunction(); + using Prim = OpcodeDecoder::Primitive; + m_cull_table[Prim::GX_DRAW_QUADS] = GetCullFunction1(); + m_cull_table[Prim::GX_DRAW_QUADS_2] = GetCullFunction1(); + m_cull_table[Prim::GX_DRAW_TRIANGLES] = GetCullFunction1(); + m_cull_table[Prim::GX_DRAW_TRIANGLE_STRIP] = GetCullFunction1(); + m_cull_table[Prim::GX_DRAW_TRIANGLE_FAN] = GetCullFunction1(); +} + +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( + 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_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 +void CPUCull::BufferDeleter::operator()(T* ptr) +{ + Common::FreeAlignedMemory(ptr); +} -- cgit v1.2.3 From a0e6d7c173f79b553c21d87abcccf40549113600 Mon Sep 17 00:00:00 2001 From: TellowKrinkle Date: Sun, 13 Nov 2022 23:26:09 -0600 Subject: VideoCommon: Explicitly disable fp_contract for CPUCull --- Source/Core/VideoCommon/CPUCull.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'Source/Core/VideoCommon/CPUCull.cpp') diff --git a/Source/Core/VideoCommon/CPUCull.cpp b/Source/Core/VideoCommon/CPUCull.cpp index bb68376b09..92fdc81cc8 100644 --- a/Source/Core/VideoCommon/CPUCull.cpp +++ b/Source/Core/VideoCommon/CPUCull.cpp @@ -15,6 +15,20 @@ #include "VideoCommon/VideoConfig.h" #include "VideoCommon/XFMemory.h" +// We really want things like c.w * a.x - a.w * c.x to stay symmetric, so they cancel to zero on +// degenerate triangles. Make sure the compiler doesn't optimize in fmas where not requested. +#ifdef _MSC_VER +#pragma fp_contract(off) +#else +// GCC doesn't support any in-file way to turn off fp contract yet +// Not ideal, but worst case scenario its cpu cull is worse at detecting degenerate triangles +// (Most likely to happen on arm, as we don't compile the cull code for x86 fma) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunknown-pragmas" +#pragma STDC FP_CONTRACT OFF +#pragma GCC diagnostic pop +#endif + #if defined(_M_X86) || defined(_M_X86_64) #define USE_SSE #elif defined(_M_ARM_64) -- cgit v1.2.3