mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Merge pull request #15363 from unknownbrackets/softjit-unittest
Add unit test to verify compilation of sampler/pixel jits
This commit is contained in:
commit
77502db4c4
13 changed files with 304 additions and 61 deletions
|
@ -2318,6 +2318,7 @@ if(UNITTEST)
|
|||
unittest/TestArm64Emitter.cpp
|
||||
unittest/TestX64Emitter.cpp
|
||||
unittest/TestVertexJit.cpp
|
||||
unittest/TestSoftwareGPUJit.cpp
|
||||
unittest/TestThreadManager.cpp
|
||||
unittest/JitHarness.cpp
|
||||
Core/MIPS/ARM/ArmRegCache.cpp
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
|
||||
#define LOG_BUF_SIZE 2048
|
||||
|
||||
static bool hitAnyAsserts = false;
|
||||
|
||||
bool HandleAssert(const char *function, const char *file, int line, const char *expression, const char* format, ...) {
|
||||
// Read message and write it to the log
|
||||
char text[LOG_BUF_SIZE];
|
||||
|
@ -50,16 +52,22 @@ bool HandleAssert(const char *function, const char *file, int line, const char *
|
|||
// Also do a simple printf for good measure, in case logging of SYSTEM is disabled (should we disallow that?)
|
||||
fprintf(stderr, "%s\n", formatted);
|
||||
|
||||
hitAnyAsserts = true;
|
||||
|
||||
#if defined(USING_WIN_UI)
|
||||
int msgBoxStyle = MB_ICONINFORMATION | MB_YESNO;
|
||||
std::wstring wtext = ConvertUTF8ToWString(formatted) + L"\n\nTry to continue?";
|
||||
std::wstring wcaption = ConvertUTF8ToWString(caption);
|
||||
OutputDebugString(wtext.c_str());
|
||||
if (IDYES != MessageBox(0, wtext.c_str(), wcaption.c_str(), msgBoxStyle)) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
// Avoid hanging on CI.
|
||||
if (!getenv("CI")) {
|
||||
int msgBoxStyle = MB_ICONINFORMATION | MB_YESNO;
|
||||
std::wstring wtext = ConvertUTF8ToWString(formatted) + L"\n\nTry to continue?";
|
||||
std::wstring wcaption = ConvertUTF8ToWString(caption);
|
||||
OutputDebugString(wtext.c_str());
|
||||
if (IDYES != MessageBox(0, wtext.c_str(), wcaption.c_str(), msgBoxStyle)) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
#elif PPSSPP_PLATFORM(ANDROID)
|
||||
__android_log_assert(expression, "PPSSPP", "%s", formatted);
|
||||
// Doesn't matter what we return here.
|
||||
|
@ -69,3 +77,11 @@ bool HandleAssert(const char *function, const char *file, int line, const char *
|
|||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// These are mainly used for unit testing.
|
||||
bool HitAnyAsserts() {
|
||||
return hitAnyAsserts;
|
||||
}
|
||||
void ResetHitAnyAsserts() {
|
||||
hitAnyAsserts = false;
|
||||
}
|
||||
|
|
|
@ -116,6 +116,9 @@ __attribute__((format(printf, 5, 6)))
|
|||
#endif
|
||||
;
|
||||
|
||||
bool HitAnyAsserts();
|
||||
void ResetHitAnyAsserts();
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
// Tricky macro to get the basename, that also works if *built* on Win32.
|
||||
// Doesn't mean this macro can be used on Win32 though.
|
||||
|
|
|
@ -59,15 +59,21 @@ SingleFunc PixelJitCache::CompileSingle(const PixelFuncID &id) {
|
|||
Describe("Init");
|
||||
WriteConstantPool(id);
|
||||
|
||||
const u8 *start = AlignCode16();
|
||||
const u8 *resetPos = AlignCode16();
|
||||
bool success = true;
|
||||
|
||||
#if PPSSPP_PLATFORM(WINDOWS)
|
||||
// RET + Windows reserves space to save args, half of 1 xmm + 4 ints before the id.
|
||||
_assert_(!regCache_.Has(RegCache::GEN_ARG_ID));
|
||||
stackIDOffset_ = 8 + 8 + 4 * PTRBITS / 8;
|
||||
int stackSpace = 0;
|
||||
if (id.hasStencilTestMask)
|
||||
stackSpace = WriteProlog(0, { XMM6, XMM7, XMM8, XMM9, XMM10, XMM11, XMM12, XMM13, XMM14, XMM15 }, { R12, R13, R14, R15 });
|
||||
else
|
||||
stackSpace = WriteProlog(0, {}, {});
|
||||
stackIDOffset_ = stackSpace + 8 + 8 + 4 * PTRBITS / 8;
|
||||
#else
|
||||
_assert_(regCache_.Has(RegCache::GEN_ARG_ID));
|
||||
WriteProlog(0, {}, {});
|
||||
stackIDOffset_ = -1;
|
||||
#endif
|
||||
|
||||
|
@ -105,19 +111,18 @@ SingleFunc PixelJitCache::CompileSingle(const PixelFuncID &id) {
|
|||
|
||||
if (regCache_.Has(RegCache::GEN_ARG_ID))
|
||||
regCache_.ForceRelease(RegCache::GEN_ARG_ID);
|
||||
regCache_.Reset(success);
|
||||
|
||||
if (!success) {
|
||||
ERROR_LOG_REPORT(G3D, "Could not compile pixel func: %s", DescribePixelFuncID(id).c_str());
|
||||
|
||||
regCache_.Reset(false);
|
||||
EndWrite();
|
||||
ResetCodePtr(GetOffset(start));
|
||||
ResetCodePtr(GetOffset(resetPos));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RET();
|
||||
|
||||
EndWrite();
|
||||
const u8 *start = WriteFinalizedEpilog();
|
||||
regCache_.Reset(true);
|
||||
return (SingleFunc)start;
|
||||
}
|
||||
|
||||
|
@ -595,7 +600,7 @@ bool PixelJitCache::Jit_StencilAndDepthTest(const PixelFuncID &id) {
|
|||
X64Reg stencilReg = GetDestStencil(id);
|
||||
Describe("StencilAndDepth");
|
||||
X64Reg maskedReg = stencilReg;
|
||||
if (id.hasStencilTestMask) {
|
||||
if (id.hasStencilTestMask && stencilReg != INVALID_REG) {
|
||||
X64Reg idReg = GetPixelID();
|
||||
maskedReg = regCache_.Alloc(RegCache::GEN_TEMP0);
|
||||
MOV(32, R(maskedReg), R(stencilReg));
|
||||
|
|
|
@ -253,6 +253,8 @@ std::string DescribePixelFuncID(const PixelFuncID &id) {
|
|||
}
|
||||
|
||||
if (id.AlphaTestFunc() != GE_COMP_ALWAYS) {
|
||||
if (id.clearMode)
|
||||
desc = "INVALID:" + desc;
|
||||
switch (id.AlphaTestFunc()) {
|
||||
case GE_COMP_NEVER: desc += "ANever"; break;
|
||||
case GE_COMP_ALWAYS: break;
|
||||
|
@ -266,9 +268,13 @@ std::string DescribePixelFuncID(const PixelFuncID &id) {
|
|||
if (id.hasAlphaTestMask)
|
||||
desc += "Msk";
|
||||
desc += StringFromFormat("%02X:", id.alphaTestRef);
|
||||
} else if (id.hasAlphaTestMask || id.alphaTestRef != 0) {
|
||||
desc = "INVALID:" + desc;
|
||||
}
|
||||
|
||||
if (id.DepthTestFunc() != GE_COMP_ALWAYS) {
|
||||
if (id.clearMode)
|
||||
desc = "INVALID:" + desc;
|
||||
switch (id.DepthTestFunc()) {
|
||||
case GE_COMP_NEVER: desc += "ZNever:"; break;
|
||||
case GE_COMP_ALWAYS: break;
|
||||
|
@ -300,6 +306,8 @@ std::string DescribePixelFuncID(const PixelFuncID &id) {
|
|||
if (id.hasStencilTestMask)
|
||||
desc += "Msk";
|
||||
desc += StringFromFormat("%02X:", id.stencilTestRef);
|
||||
} else if (id.hasStencilTestMask || id.stencilTestRef != 0 || id.stencilTestFunc != 0) {
|
||||
desc = "INVALID:" + desc;
|
||||
}
|
||||
|
||||
switch (id.SFail()) {
|
||||
|
@ -326,8 +334,14 @@ std::string DescribePixelFuncID(const PixelFuncID &id) {
|
|||
case GE_STENCILOP_INCR: desc += "ZTstTInc:"; break;
|
||||
case GE_STENCILOP_DECR: desc += "ZTstTDec:"; break;
|
||||
}
|
||||
if (!id.stencilTest || id.clearMode) {
|
||||
if (id.sFail != 0 || id.zFail != 0 || id.zPass != 0)
|
||||
desc = "INVALID:" + desc;
|
||||
}
|
||||
|
||||
if (id.alphaBlend) {
|
||||
if (id.clearMode)
|
||||
desc = "INVALID:" + desc;
|
||||
switch (id.AlphaBlendEq()) {
|
||||
case GE_BLENDMODE_MUL_AND_ADD: desc += "BlendAdd<"; break;
|
||||
case GE_BLENDMODE_MUL_AND_SUBTRACT: desc += "BlendSub<"; break;
|
||||
|
@ -366,15 +380,21 @@ std::string DescribePixelFuncID(const PixelFuncID &id) {
|
|||
case PixelBlendFactor::ZERO: desc += "0>:"; break;
|
||||
case PixelBlendFactor::ONE: desc += "1>:"; break;
|
||||
}
|
||||
} else if (id.alphaBlendEq != 0 || id.alphaBlendSrc != 0 || id.alphaBlendDst != 0) {
|
||||
desc = "INVALID:" + desc;
|
||||
}
|
||||
|
||||
if (id.applyLogicOp)
|
||||
desc += "Logic:";
|
||||
else if (id.clearMode)
|
||||
desc = "INVALID:" + desc;
|
||||
if (id.applyFog)
|
||||
desc += "Fog:";
|
||||
else if (id.clearMode)
|
||||
desc = "INVALID:" + desc;
|
||||
|
||||
if (desc.empty())
|
||||
return desc;
|
||||
return "INVALID";
|
||||
desc.resize(desc.size() - 1);
|
||||
return desc;
|
||||
}
|
||||
|
@ -405,6 +425,7 @@ void ComputeSamplerID(SamplerID *id_out) {
|
|||
id.cached.sizes[i].w = w;
|
||||
id.cached.sizes[i].h = h;
|
||||
}
|
||||
// TODO: What specifically happens if these are above 11?
|
||||
id.width0Shift = gstate.texsize[0] & 0xF;
|
||||
id.height0Shift = (gstate.texsize[0] >> 8) & 0xF;
|
||||
id.hasAnyMips = maxLevel != 0;
|
||||
|
@ -450,6 +471,7 @@ std::string DescribeSamplerID(const SamplerID &id) {
|
|||
case GE_TFMT_DXT1: name = "DXT1"; break;
|
||||
case GE_TFMT_DXT3: name = "DXT3"; break;
|
||||
case GE_TFMT_DXT5: name = "DXT5"; break;
|
||||
default: name = "INVALID"; break;
|
||||
}
|
||||
switch (id.ClutFmt()) {
|
||||
case GE_CMODE_16BIT_BGR5650:
|
||||
|
@ -529,6 +551,8 @@ std::string DescribeSamplerID(const SamplerID &id) {
|
|||
break;
|
||||
}
|
||||
name += StringFromFormat(":W%dH%d", 1 << id.width0Shift, 1 << id.height0Shift);
|
||||
if (id.width0Shift > 10 || id.height0Shift > 10)
|
||||
name = "INVALID:" + name;
|
||||
|
||||
return name;
|
||||
}
|
||||
|
|
|
@ -141,23 +141,15 @@ NearestFunc SamplerJitCache::GetNearest(const SamplerID &id) {
|
|||
std::lock_guard<std::mutex> guard(jitCacheLock);
|
||||
|
||||
auto it = cache_.find(id);
|
||||
if (it != cache_.end()) {
|
||||
if (it != cache_.end())
|
||||
return (NearestFunc)it->second;
|
||||
}
|
||||
|
||||
// TODO: What should be the min size? Can we even hit this?
|
||||
if (GetSpaceLeft() < 16384) {
|
||||
Clear();
|
||||
}
|
||||
Compile(id);
|
||||
|
||||
#if PPSSPP_ARCH(AMD64) && !PPSSPP_PLATFORM(UWP)
|
||||
if (g_Config.bSoftwareRenderingJit) {
|
||||
addresses_[id] = GetCodePointer();
|
||||
NearestFunc func = CompileNearest(id);
|
||||
cache_[id] = (NearestFunc)func;
|
||||
return func;
|
||||
}
|
||||
#endif
|
||||
// Okay, should be there now.
|
||||
it = cache_.find(id);
|
||||
if (it != cache_.end())
|
||||
return (NearestFunc)it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -165,23 +157,15 @@ LinearFunc SamplerJitCache::GetLinear(const SamplerID &id) {
|
|||
std::lock_guard<std::mutex> guard(jitCacheLock);
|
||||
|
||||
auto it = cache_.find(id);
|
||||
if (it != cache_.end()) {
|
||||
if (it != cache_.end())
|
||||
return (LinearFunc)it->second;
|
||||
}
|
||||
|
||||
// TODO: What should be the min size? Can we even hit this?
|
||||
if (GetSpaceLeft() < 16384) {
|
||||
Clear();
|
||||
}
|
||||
Compile(id);
|
||||
|
||||
#if PPSSPP_ARCH(AMD64) && !PPSSPP_PLATFORM(UWP)
|
||||
if (g_Config.bSoftwareRenderingJit) {
|
||||
addresses_[id] = GetCodePointer();
|
||||
LinearFunc func = CompileLinear(id);
|
||||
cache_[id] = (NearestFunc)func;
|
||||
return func;
|
||||
}
|
||||
#endif
|
||||
// Okay, should be there now.
|
||||
it = cache_.find(id);
|
||||
if (it != cache_.end())
|
||||
return (LinearFunc)it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -189,24 +173,47 @@ FetchFunc SamplerJitCache::GetFetch(const SamplerID &id) {
|
|||
std::lock_guard<std::mutex> guard(jitCacheLock);
|
||||
|
||||
auto it = cache_.find(id);
|
||||
if (it != cache_.end()) {
|
||||
if (it != cache_.end())
|
||||
return (FetchFunc)it->second;
|
||||
}
|
||||
|
||||
// TODO: What should be the min size? Can we even hit this?
|
||||
Compile(id);
|
||||
|
||||
// Okay, should be there now.
|
||||
it = cache_.find(id);
|
||||
if (it != cache_.end())
|
||||
return (FetchFunc)it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SamplerJitCache::Compile(const SamplerID &id) {
|
||||
// This should be sufficient.
|
||||
if (GetSpaceLeft() < 16384) {
|
||||
Clear();
|
||||
}
|
||||
|
||||
// We compile them together so the cache can't possibly be cleared in between.
|
||||
// We might vary between nearest and linear, so we can't clear between.
|
||||
#if PPSSPP_ARCH(AMD64) && !PPSSPP_PLATFORM(UWP)
|
||||
if (g_Config.bSoftwareRenderingJit) {
|
||||
addresses_[id] = GetCodePointer();
|
||||
FetchFunc func = CompileFetch(id);
|
||||
cache_[id] = (NearestFunc)func;
|
||||
return func;
|
||||
SamplerID fetchID = id;
|
||||
fetchID.linear = false;
|
||||
fetchID.fetch = true;
|
||||
addresses_[fetchID] = GetCodePointer();
|
||||
cache_[fetchID] = (NearestFunc)CompileFetch(fetchID);
|
||||
|
||||
SamplerID nearestID = id;
|
||||
nearestID.linear = false;
|
||||
nearestID.fetch = false;
|
||||
addresses_[nearestID] = GetCodePointer();
|
||||
cache_[nearestID] = (NearestFunc)CompileNearest(nearestID);
|
||||
|
||||
SamplerID linearID = id;
|
||||
linearID.linear = true;
|
||||
linearID.fetch = false;
|
||||
addresses_[linearID] = GetCodePointer();
|
||||
cache_[linearID] = (NearestFunc)CompileLinear(linearID);
|
||||
}
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <uint32_t texel_size_bits>
|
||||
|
|
|
@ -60,6 +60,7 @@ public:
|
|||
std::string DescribeCodePtr(const u8 *ptr) override;
|
||||
|
||||
private:
|
||||
void Compile(const SamplerID &id);
|
||||
FetchFunc CompileFetch(const SamplerID &id);
|
||||
NearestFunc CompileNearest(const SamplerID &id);
|
||||
LinearFunc CompileLinear(const SamplerID &id);
|
||||
|
@ -101,6 +102,7 @@ private:
|
|||
#if PPSSPP_ARCH(AMD64) || PPSSPP_ARCH(X86)
|
||||
int stackArgPos_ = 0;
|
||||
int stackIDOffset_ = -1;
|
||||
int stackLevelOffset_ = -1;
|
||||
int stackUV1Offset_ = 0;
|
||||
#endif
|
||||
|
||||
|
|
|
@ -53,9 +53,11 @@ FetchFunc SamplerJitCache::CompileFetch(const SamplerID &id) {
|
|||
// RET and shadow space.
|
||||
stackArgPos_ = 8 + 32;
|
||||
stackIDOffset_ = 8;
|
||||
stackLevelOffset_ = 0;
|
||||
#else
|
||||
stackArgPos_ = 0;
|
||||
stackIDOffset_ = -1;
|
||||
stackLevelOffset_ = -1;
|
||||
#endif
|
||||
|
||||
// Early exit on !srcPtr.
|
||||
|
@ -147,10 +149,12 @@ NearestFunc SamplerJitCache::CompileNearest(const SamplerID &id) {
|
|||
|
||||
// Positions: stackArgPos_+0=src, stackArgPos_+8=bufw, stackArgPos_+16=level, stackArgPos_+24=levelFrac
|
||||
stackIDOffset_ = 32;
|
||||
stackLevelOffset_ = 16;
|
||||
#else
|
||||
stackArgPos_ = 0;
|
||||
// This is the only arg that went to the stack, it's after the RET.
|
||||
stackIDOffset_ = 8;
|
||||
stackLevelOffset_ = -1;
|
||||
#endif
|
||||
|
||||
// Start out by saving some registers, since we'll need more.
|
||||
|
@ -173,7 +177,7 @@ NearestFunc SamplerJitCache::CompileNearest(const SamplerID &id) {
|
|||
#endif
|
||||
|
||||
// We can throw these away right off if there are no mips.
|
||||
if (!id.hasAnyMips && regCache_.Has(RegCache::GEN_ARG_LEVEL))
|
||||
if (!id.hasAnyMips && regCache_.Has(RegCache::GEN_ARG_LEVEL) && id.useSharedClut)
|
||||
regCache_.ForceRelease(RegCache::GEN_ARG_LEVEL);
|
||||
if (!id.hasAnyMips && regCache_.Has(RegCache::GEN_ARG_LEVELFRAC))
|
||||
regCache_.ForceRelease(RegCache::GEN_ARG_LEVELFRAC);
|
||||
|
@ -528,6 +532,7 @@ LinearFunc SamplerJitCache::CompileLinear(const SamplerID &id) {
|
|||
|
||||
// Positions: stackArgPos_+0=src, stackArgPos_+8=bufw, stackArgPos_+16=level, stackArgPos_+24=levelFrac
|
||||
stackIDOffset_ = 32;
|
||||
stackLevelOffset_ = 16;
|
||||
|
||||
// If needed, we could store UV1 data in shadow space, but we no longer due.
|
||||
stackUV1Offset_ = -8;
|
||||
|
@ -536,6 +541,7 @@ LinearFunc SamplerJitCache::CompileLinear(const SamplerID &id) {
|
|||
stackArgPos_ += WriteProlog(0, {}, { R15, R14, R13, R12 });
|
||||
// Just after the RET.
|
||||
stackIDOffset_ = 8;
|
||||
stackLevelOffset_ = -1;
|
||||
|
||||
// Use the red zone.
|
||||
stackUV1Offset_ = -stackArgPos_ - 8;
|
||||
|
@ -709,6 +715,12 @@ LinearFunc SamplerJitCache::CompileLinear(const SamplerID &id) {
|
|||
doNearestCall(4, false);
|
||||
doNearestCall(8, false);
|
||||
doNearestCall(12, false);
|
||||
|
||||
// After doing the calls, certain cached things aren't safe.
|
||||
if (regCache_.Has(RegCache::GEN_ID))
|
||||
regCache_.ForceRelease(RegCache::GEN_ID);
|
||||
if (regCache_.Has(RegCache::VEC_ZERO))
|
||||
regCache_.ForceRelease(RegCache::VEC_ZERO);
|
||||
} else {
|
||||
success = success && Jit_FetchQuad(id, false);
|
||||
}
|
||||
|
@ -3328,14 +3340,12 @@ bool SamplerJitCache::Jit_ReadClutColor(const SamplerID &id) {
|
|||
// We need to multiply by 16 and add, LEA allows us to copy too.
|
||||
LEA(32, temp2Reg, MScaled(levelReg, SCALE_4, 0));
|
||||
regCache_.Unlock(levelReg, RegCache::GEN_ARG_LEVEL);
|
||||
regCache_.ForceRelease(RegCache::GEN_ARG_LEVEL);
|
||||
if (id.fetch)
|
||||
regCache_.ForceRelease(RegCache::GEN_ARG_LEVEL);
|
||||
} else {
|
||||
#if PPSSPP_PLATFORM(WINDOWS)
|
||||
_assert_(stackLevelOffset_ != -1);
|
||||
// The argument was saved on the stack.
|
||||
MOV(32, R(temp2Reg), MDisp(RSP, stackArgPos_));
|
||||
#else
|
||||
_assert_(false);
|
||||
#endif
|
||||
MOV(32, R(temp2Reg), MDisp(RSP, stackArgPos_ + stackLevelOffset_));
|
||||
LEA(32, temp2Reg, MScaled(temp2Reg, SCALE_4, 0));
|
||||
}
|
||||
|
||||
|
|
|
@ -738,8 +738,9 @@ ifeq ($(UNITTEST),1)
|
|||
LOCAL_SRC_FILES := \
|
||||
$(SRC)/unittest/JitHarness.cpp \
|
||||
$(SRC)/unittest/TestShaderGenerators.cpp \
|
||||
$(SRC)/unittest/TestVertexJit.cpp \
|
||||
$(SRC)/unittest/TestSoftwareGPUJit.cpp \
|
||||
$(SRC)/unittest/TestThreadManager.cpp \
|
||||
$(SRC)/unittest/TestVertexJit.cpp \
|
||||
$(TESTARMEMITTER_FILE) \
|
||||
$(SRC)/unittest/UnitTest.cpp
|
||||
|
||||
|
|
170
unittest/TestSoftwareGPUJit.cpp
Normal file
170
unittest/TestSoftwareGPUJit.cpp
Normal file
|
@ -0,0 +1,170 @@
|
|||
// Copyright (c) 2022- PPSSPP Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0 or later versions.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official git repository and contact information can be found at
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include "Common/Data/Random/Rng.h"
|
||||
#include "Common/StringUtils.h"
|
||||
#include "Core/Config.h"
|
||||
#include "GPU/Software/DrawPixel.h"
|
||||
#include "GPU/Software/Sampler.h"
|
||||
#include "GPU/Software/SoftGpu.h"
|
||||
|
||||
static bool TestSamplerJit() {
|
||||
using namespace Sampler;
|
||||
SamplerJitCache *cache = new SamplerJitCache();
|
||||
|
||||
auto GetLinear = [&](SamplerID &id) {
|
||||
id.linear = true;
|
||||
id.fetch = false;
|
||||
return cache->GetLinear(id);
|
||||
};
|
||||
auto GetNearest = [&](SamplerID &id) {
|
||||
id.linear = false;
|
||||
id.fetch = false;
|
||||
return cache->GetNearest(id);
|
||||
};
|
||||
auto GetFetch = [&](SamplerID &id) {
|
||||
id.linear = false;
|
||||
id.fetch = true;
|
||||
return cache->GetFetch(id);
|
||||
};
|
||||
|
||||
GMRng rng;
|
||||
int successes = 0;
|
||||
int count = 3000;
|
||||
bool header = false;
|
||||
|
||||
u8 **tptr = new u8 *[8];
|
||||
int *bufw = new int[8];
|
||||
u8 *clut = new u8[1024];
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
tptr[i] = new u8[1024 * 1024 * 4];
|
||||
memset(tptr[i], 0, 1024 * 1024 * 4);
|
||||
bufw[i] = 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; ) {
|
||||
SamplerID id;
|
||||
memset(&id, 0, sizeof(id));
|
||||
id.fullKey = rng.R32();
|
||||
id.cached.clut = clut;
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
id.cached.sizes[i].w = 1;
|
||||
id.cached.sizes[i].h = 1;
|
||||
}
|
||||
|
||||
std::string desc = DescribeSamplerID(id);
|
||||
if (startsWith(desc, "INVALID"))
|
||||
continue;
|
||||
i++;
|
||||
|
||||
LinearFunc linearFunc = GetLinear(id);
|
||||
NearestFunc nearestFunc = GetNearest(id);
|
||||
FetchFunc fetchFunc = GetFetch(id);
|
||||
if (linearFunc != nullptr && nearestFunc != nullptr && fetchFunc != nullptr) {
|
||||
successes++;
|
||||
} else {
|
||||
if (!header)
|
||||
printf("Failed sampler funcs:\n");
|
||||
header = true;
|
||||
printf(" * %s (L:%d, N:%d, F:%d)\n", desc.c_str(), linearFunc != nullptr, nearestFunc != nullptr, fetchFunc != nullptr);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try running each to make sure they don't trivially crash.
|
||||
const auto primArg = Rasterizer::ToVec4IntArg(Math3D::Vec4<int>(127, 127, 127, 127));
|
||||
linearFunc(0.0f, 0.0f, 0, 0, primArg, tptr, bufw, 1, 7, id);
|
||||
nearestFunc(0.0f, 0.0f, 0, 0, primArg, tptr, bufw, 1, 7, id);
|
||||
fetchFunc(0, 0, tptr[0], bufw[0], 1, id);
|
||||
}
|
||||
|
||||
if (successes < count)
|
||||
printf("SamplerFunc success: %d / %d\n", successes, count);
|
||||
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
delete [] tptr[i];
|
||||
}
|
||||
delete [] tptr;
|
||||
delete [] bufw;
|
||||
delete [] clut;
|
||||
|
||||
delete cache;
|
||||
return successes == count && !HitAnyAsserts();
|
||||
}
|
||||
|
||||
static bool TestPixelJit() {
|
||||
using namespace Rasterizer;
|
||||
PixelJitCache *cache = new PixelJitCache();
|
||||
|
||||
GMRng rng;
|
||||
int successes = 0;
|
||||
int count = 3000;
|
||||
bool header = false;
|
||||
|
||||
u32 *fb_data = new u32[512 * 2];
|
||||
u16 *zb_data = new u16[512 * 2];
|
||||
fb.as32 = fb_data;
|
||||
depthbuf.as16 = zb_data;
|
||||
|
||||
for (int i = 0; i < count; ) {
|
||||
PixelFuncID id;
|
||||
memset(&id, 0, sizeof(id));
|
||||
id.fullKey = (uint64_t)rng.R32() | ((uint64_t)rng.R32() << 32);
|
||||
|
||||
std::string desc = DescribePixelFuncID(id);
|
||||
if (startsWith(desc, "INVALID"))
|
||||
continue;
|
||||
i++;
|
||||
|
||||
SingleFunc func = cache->GetSingle(id);
|
||||
SingleFunc genericFunc = cache->GenericSingle(id);
|
||||
if (func != genericFunc) {
|
||||
successes++;
|
||||
} else {
|
||||
if (!header)
|
||||
printf("Failed pixel funcs:\n");
|
||||
header = true;
|
||||
printf(" * %s\n", desc.c_str());
|
||||
}
|
||||
|
||||
// Try running it to make sure it doesn't trivially crash.
|
||||
func(0, 0, 1000, 255, ToVec4IntArg(Math3D::Vec4<int>(127, 127, 127, 127)), id);
|
||||
}
|
||||
|
||||
if (successes < count)
|
||||
printf("PixelFunc success: %d / %d\n", successes, count);
|
||||
|
||||
delete [] fb_data;
|
||||
delete [] zb_data;
|
||||
delete cache;
|
||||
return successes == count && !HitAnyAsserts();
|
||||
}
|
||||
|
||||
bool TestSoftwareGPUJit() {
|
||||
g_Config.bSoftwareRenderingJit = true;
|
||||
ResetHitAnyAsserts();
|
||||
|
||||
if (!TestSamplerJit()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TestPixelJit()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
|
@ -753,6 +753,7 @@ bool TestArmEmitter();
|
|||
bool TestArm64Emitter();
|
||||
bool TestX64Emitter();
|
||||
bool TestShaderGenerators();
|
||||
bool TestSoftwareGPUJit();
|
||||
bool TestThreadManager();
|
||||
|
||||
TestItem availableTests[] = {
|
||||
|
@ -778,6 +779,7 @@ TestItem availableTests[] = {
|
|||
TEST_ITEM(CLZ),
|
||||
TEST_ITEM(MemMap),
|
||||
TEST_ITEM(ShaderGenerators),
|
||||
TEST_ITEM(SoftwareGPUJit),
|
||||
TEST_ITEM(Path),
|
||||
TEST_ITEM(AndroidContentURI),
|
||||
TEST_ITEM(ThreadManager),
|
||||
|
|
|
@ -383,6 +383,7 @@
|
|||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TestShaderGenerators.cpp" />
|
||||
<ClCompile Include="TestSoftwareGPUJit.cpp" />
|
||||
<ClCompile Include="TestThreadManager.cpp" />
|
||||
<ClCompile Include="TestVertexJit.cpp" />
|
||||
<ClCompile Include="UnitTest.cpp" />
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
</ClCompile>
|
||||
<ClCompile Include="TestShaderGenerators.cpp" />
|
||||
<ClCompile Include="TestThreadManager.cpp" />
|
||||
<ClCompile Include="TestSoftwareGPUJit.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="JitHarness.h" />
|
||||
|
|
Loading…
Add table
Reference in a new issue