diff --git a/CMakeLists.txt b/CMakeLists.txt index 7c9ff39321..d0d5f69da9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/Common/Log.cpp b/Common/Log.cpp index 422ca88f0a..1a1df0be45 100644 --- a/Common/Log.cpp +++ b/Common/Log.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; +} diff --git a/Common/Log.h b/Common/Log.h index f8a56730a6..9821642f9f 100644 --- a/Common/Log.h +++ b/Common/Log.h @@ -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. diff --git a/GPU/Software/DrawPixelX86.cpp b/GPU/Software/DrawPixelX86.cpp index 8399390a92..24286858ae 100644 --- a/GPU/Software/DrawPixelX86.cpp +++ b/GPU/Software/DrawPixelX86.cpp @@ -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)); diff --git a/GPU/Software/FuncId.cpp b/GPU/Software/FuncId.cpp index 3af873fbbf..3c522d7e20 100644 --- a/GPU/Software/FuncId.cpp +++ b/GPU/Software/FuncId.cpp @@ -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; } diff --git a/GPU/Software/Sampler.cpp b/GPU/Software/Sampler.cpp index f2dbddeaa2..88b77e642e 100644 --- a/GPU/Software/Sampler.cpp +++ b/GPU/Software/Sampler.cpp @@ -141,23 +141,15 @@ NearestFunc SamplerJitCache::GetNearest(const SamplerID &id) { std::lock_guard 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 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 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 diff --git a/GPU/Software/Sampler.h b/GPU/Software/Sampler.h index 3c410cbed1..b4559994ab 100644 --- a/GPU/Software/Sampler.h +++ b/GPU/Software/Sampler.h @@ -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 diff --git a/GPU/Software/SamplerX86.cpp b/GPU/Software/SamplerX86.cpp index e65f620da1..dd58a32cd0 100644 --- a/GPU/Software/SamplerX86.cpp +++ b/GPU/Software/SamplerX86.cpp @@ -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)); } diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 6e1d3eb471..0ace3176d5 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -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 diff --git a/unittest/TestSoftwareGPUJit.cpp b/unittest/TestSoftwareGPUJit.cpp new file mode 100644 index 0000000000..23292ed214 --- /dev/null +++ b/unittest/TestSoftwareGPUJit.cpp @@ -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(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(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; +} diff --git a/unittest/UnitTest.cpp b/unittest/UnitTest.cpp index cc85af7e7a..fba781084b 100644 --- a/unittest/UnitTest.cpp +++ b/unittest/UnitTest.cpp @@ -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), diff --git a/unittest/UnitTests.vcxproj b/unittest/UnitTests.vcxproj index 0a7e620e41..ca158dd1e5 100644 --- a/unittest/UnitTests.vcxproj +++ b/unittest/UnitTests.vcxproj @@ -383,6 +383,7 @@ true + diff --git a/unittest/UnitTests.vcxproj.filters b/unittest/UnitTests.vcxproj.filters index d11a9119ca..bfa4b8bef8 100644 --- a/unittest/UnitTests.vcxproj.filters +++ b/unittest/UnitTests.vcxproj.filters @@ -13,6 +13,7 @@ +