Merge pull request #16518 from unknownbrackets/softgpu-fastrect

softgpu: Expand fast path to all fb formats
This commit is contained in:
Henrik Rydgård
2022-12-07 10:00:49 +01:00
committed by GitHub
10 changed files with 290 additions and 97 deletions
+8
View File
@@ -118,6 +118,14 @@ inline u16 RGBA8888ToRGBA4444(u32 value) {
return r | g | b | a;
}
inline u16 RGBA8888ToRGBA444X(u32 value) {
const u32 c = value >> 4;
const u16 r = (c >> 0) & 0x000F;
const u16 g = (c >> 4) & 0x00F0;
const u16 b = (c >> 8) & 0x0F00;
return r | g | b;
}
// convert image to 8888, parallelizable
// TODO: Implement these in terms of the conversion functions below.
void convert4444_gl(u16* data, u32* out, int width, int l, int u);
+1 -3
View File
@@ -172,9 +172,7 @@ void BinManager::UpdateState() {
creatingState_ = true;
stateIndex_ = (uint16_t)states_.Push(RasterizerState());
// When new funcs are compiled, we need to flush if WX exclusive.
ComputeRasterizerState(&states_[stateIndex_], [&]() {
Flush("compile");
});
ComputeRasterizerState(&states_[stateIndex_], this);
states_[stateIndex_].samplerID.cached.clut = cluts_[clutIndex_].readable;
creatingState_ = false;
+17 -7
View File
@@ -20,6 +20,7 @@
#include "Common/Data/Convert/ColorConv.h"
#include "Core/Config.h"
#include "GPU/GPUState.h"
#include "GPU/Software/BinManager.h"
#include "GPU/Software/DrawPixel.h"
#include "GPU/Software/FuncId.h"
#include "GPU/Software/Rasterizer.h"
@@ -707,8 +708,8 @@ void SOFTRAST_CALL DrawSinglePixel(int x, int y, int z, int fog, Vec4IntArg colo
SetPixelColor(fbFormat, pixelID.cached.framebufStride, x, y, new_color, old_color, targetWriteMask);
}
SingleFunc GetSingleFunc(const PixelFuncID &id, std::function<void()> flushForCompile) {
SingleFunc jitted = jitCache->GetSingle(id, flushForCompile);
SingleFunc GetSingleFunc(const PixelFuncID &id, BinManager *binner) {
SingleFunc jitted = jitCache->GetSingle(id, binner);
if (jitted) {
return jitted;
}
@@ -743,11 +744,15 @@ SingleFunc PixelJitCache::GenericSingle(const PixelFuncID &id) {
return nullptr;
}
thread_local PixelJitCache::LastCache PixelJitCache::lastSingle_;
// 256k should be plenty of space for plenty of variations.
PixelJitCache::PixelJitCache() : CodeBlock(1024 * 64 * 4), cache_(64) {
lastSingle_.gen = -1;
}
void PixelJitCache::Clear() {
clearGen_++;
CodeBlock::Clear();
cache_.Clear();
addresses_.clear();
@@ -786,26 +791,29 @@ void PixelJitCache::Flush() {
compileQueue_.clear();
}
SingleFunc PixelJitCache::GetSingle(const PixelFuncID &id, std::function<void()> flushForCompile) {
SingleFunc PixelJitCache::GetSingle(const PixelFuncID &id, BinManager *binner) {
if (!g_Config.bSoftwareRenderingJit)
return nullptr;
std::unique_lock<std::mutex> guard(jitCacheLock);
const size_t key = std::hash<PixelFuncID>()(id);
if (lastSingle_.Match(key, clearGen_))
return lastSingle_.func;
std::unique_lock<std::mutex> guard(jitCacheLock);
auto it = cache_.Get(key);
if (it != nullptr) {
lastSingle_.Set(key, it, clearGen_);
return it;
}
if (!flushForCompile) {
if (!binner) {
// Can't compile, let's try to do it later when there's an opportunity.
compileQueue_.insert(id);
return nullptr;
}
guard.unlock();
flushForCompile();
binner->Flush("compile");
guard.lock();
for (const auto &queued : compileQueue_) {
@@ -820,7 +828,9 @@ SingleFunc PixelJitCache::GetSingle(const PixelFuncID &id, std::function<void()>
if (!cache_.Get(key))
Compile(id);
return cache_.Get(key);
it = cache_.Get(key);
lastSingle_.Set(key, it, clearGen_);
return it;
}
void PixelJitCache::Compile(const PixelFuncID &id) {
+22 -3
View File
@@ -19,7 +19,6 @@
#include "ppsspp_config.h"
#include <functional>
#include <string>
#include <vector>
#include <unordered_map>
@@ -29,6 +28,8 @@
#include "GPU/Software/FuncId.h"
#include "GPU/Software/RasterizerRegCache.h"
class BinManager;
namespace Rasterizer {
// Our std::unordered_map argument will ignore the alignment attribute, but that doesn't matter.
@@ -39,7 +40,7 @@ namespace Rasterizer {
#endif
typedef void (SOFTRAST_CALL *SingleFunc)(int x, int y, int z, int fog, Vec4IntArg color_in, const PixelFuncID &pixelID);
SingleFunc GetSingleFunc(const PixelFuncID &id, std::function<void()> flushForCompile);
SingleFunc GetSingleFunc(const PixelFuncID &id, BinManager *binner);
void Init();
void FlushJit();
@@ -64,7 +65,7 @@ public:
PixelJitCache();
// Returns a pointer to the code to run.
SingleFunc GetSingle(const PixelFuncID &id, std::function<void()> flushForCompile);
SingleFunc GetSingle(const PixelFuncID &id, BinManager *binner);
SingleFunc GenericSingle(const PixelFuncID &id);
void Clear() override;
void Flush();
@@ -108,9 +109,27 @@ private:
bool Jit_ConvertFrom5551(const PixelFuncID &id, RegCache::Reg colorReg, RegCache::Reg temp1Reg, RegCache::Reg temp2Reg, bool keepAlpha);
bool Jit_ConvertFrom4444(const PixelFuncID &id, RegCache::Reg colorReg, RegCache::Reg temp1Reg, RegCache::Reg temp2Reg, bool keepAlpha);
struct LastCache {
size_t key;
SingleFunc func;
int gen = -1;
bool Match(size_t k, int g) const {
return key == k && gen == g;
}
void Set(size_t k, SingleFunc f, int g) {
key = k;
func = f;
gen = g;
}
};
DenseHashMap<size_t, SingleFunc, nullptr> cache_;
std::unordered_map<PixelFuncID, const u8 *> addresses_;
std::unordered_set<PixelFuncID> compileQueue_;
int clearGen_ = 0;
static thread_local LastCache lastSingle_;
const u8 *constBlendHalf_11_4s_ = nullptr;
const u8 *constBlendInvert_11_4s_ = nullptr;
+12 -8
View File
@@ -93,15 +93,15 @@ static inline Vec4<float> Interpolate(const float &c0, const float &c1, const fl
return Interpolate(c0, c1, c2, w0.Cast<float>(), w1.Cast<float>(), w2.Cast<float>(), wsum_recip);
}
void ComputeRasterizerState(RasterizerState *state, std::function<void()> flushForCompile) {
void ComputeRasterizerState(RasterizerState *state, BinManager *binner) {
ComputePixelFuncID(&state->pixelID);
state->drawPixel = Rasterizer::GetSingleFunc(state->pixelID, flushForCompile);
state->drawPixel = Rasterizer::GetSingleFunc(state->pixelID, binner);
state->enableTextures = gstate.isTextureMapEnabled() && !state->pixelID.clearMode;
if (state->enableTextures) {
ComputeSamplerID(&state->samplerID);
state->linear = Sampler::GetLinearFunc(state->samplerID, flushForCompile);
state->nearest = Sampler::GetNearestFunc(state->samplerID, flushForCompile);
state->linear = Sampler::GetLinearFunc(state->samplerID, binner);
state->nearest = Sampler::GetNearestFunc(state->samplerID, binner);
// Since the definitions are the same, just force this setting using the func pointer.
if (g_Config.iTexFiltering == TEX_FILTER_FORCE_LINEAR) {
@@ -1697,10 +1697,14 @@ bool GetCurrentTexture(GPUDebugBuffer &buffer, int level)
ComputeSamplerID(&id);
id.cached.clut = clut;
Sampler::FetchFunc sampler = Sampler::GetFetchFunc(id, [] {
if (gpuDebug)
gpuDebug->DispatchFlush();
});
// Slight annoyance, we may have to force a compile.
Sampler::FetchFunc sampler = Sampler::GetFetchFunc(id, nullptr);
if (!sampler) {
Sampler::FlushJit();
sampler = Sampler::GetFetchFunc(id, nullptr);
if (!sampler)
return false;
}
u8 *texptr = Memory::GetPointerWrite(texaddr);
u32 *row = (u32 *)buffer.GetData();
+2 -1
View File
@@ -30,6 +30,7 @@
struct GPUDebugBuffer;
struct BinCoords;
class BinManager;
namespace Rasterizer {
@@ -100,7 +101,7 @@ struct RasterizerState {
}
};
void ComputeRasterizerState(RasterizerState *state, std::function<void()> flushForCompile);
void ComputeRasterizerState(RasterizerState *state, BinManager *binner);
void CalculateRasterStateFlags(RasterizerState *state, const VertexData &v0);
void CalculateRasterStateFlags(RasterizerState *state, const VertexData &v0, const VertexData &v1, bool forceFlat);
void CalculateRasterStateFlags(RasterizerState *state, const VertexData &v0, const VertexData &v1, const VertexData &v2);
+141 -43
View File
@@ -72,19 +72,59 @@ static uint32_t StandardAlphaBlend(uint32_t source, uint32_t dst) {
}
// Through mode, with the specific Darkstalker settings.
template <bool alphaBlend>
inline void DrawSinglePixel5551(u16 *pixel, const u32 color_in) {
template <GEBufferFormat fmt, bool alphaBlend>
static inline void DrawSinglePixel(u16 *pixel, const u32 color_in) {
u32 new_color;
// Because of this check, we only support src.a / 1-src.a blending.
// We take advantage of short circuiting by checking the constant (template) value first.
if (!alphaBlend || (color_in >> 24) == 255) {
new_color = color_in & 0xFFFFFF;
} else {
const u32 old_color = RGBA5551ToRGBA8888(*pixel);
u32 old_color;
switch (fmt) {
case GE_FORMAT_565:
old_color = RGB565ToRGBA8888(*pixel);
break;
case GE_FORMAT_5551:
old_color = RGBA5551ToRGBA8888(*pixel);
break;
case GE_FORMAT_4444:
old_color = RGBA4444ToRGBA8888(*pixel);
break;
default:
break;
}
new_color = StandardAlphaBlend(color_in, old_color);
}
u16 value = RGBA8888ToRGBA555X(new_color) | (*pixel & 0x8000);
*pixel = value;
switch (fmt) {
case GE_FORMAT_565:
*pixel = RGBA8888ToRGB565(new_color);
break;
case GE_FORMAT_5551:
*pixel = RGBA8888ToRGBA555X(new_color) | (*pixel & 0x8000);
break;
case GE_FORMAT_4444:
*pixel = RGBA8888ToRGBA444X(new_color) | (*pixel & 0xF000);
break;
default:
break;
}
}
template <bool alphaBlend>
static inline void DrawSinglePixel32(u32 *pixel, const u32 color_in) {
u32 new_color;
// Because of this check, we only support src.a / 1-src.a blending.
if ((color_in >> 24) == 255 || !alphaBlend) {
new_color = color_in & 0xFFFFFF;
} else {
const u32 old_color = *pixel;
new_color = StandardAlphaBlend(color_in, old_color);
}
new_color |= *pixel & 0xFF000000;
*pixel = new_color;
}
// Check if we can safely ignore the alpha test, assuming standard alpha blending.
@@ -110,13 +150,11 @@ static inline bool AlphaTestIsNeedless(const PixelFuncID &pixelID) {
return false;
}
bool UseDrawSinglePixel5551(const PixelFuncID &pixelID) {
static bool UseDrawSinglePixel(const PixelFuncID &pixelID) {
if (pixelID.clearMode || pixelID.colorTest || pixelID.stencilTest)
return false;
if (!AlphaTestIsNeedless(pixelID) || pixelID.DepthTestFunc() != GE_COMP_ALWAYS)
return false;
if (pixelID.FBFormat() != GE_FORMAT_5551)
return false;
// We skip blending when alpha = FF, so we can't allow other blend modes.
if (pixelID.alphaBlend) {
if (pixelID.AlphaBlendEq() != GE_BLENDMODE_MUL_AND_ADD || pixelID.AlphaBlendSrc() != PixelBlendFactor::SRCALPHA)
@@ -160,8 +198,8 @@ static inline Vec4IntResult SOFTRAST_CALL ModulateRGBA(Vec4IntArg prim_in, Vec4I
return ToVec4IntResult(out);
}
template <bool isWhite, bool alphaBlend>
static void DrawSpriteTex5551(const DrawingCoords &pos0, const DrawingCoords &pos1, int s_start, int t_start, int ds, int dt, u32 color0, const RasterizerState &state, Sampler::FetchFunc fetchFunc) {
template <GEBufferFormat fmt, bool isWhite, bool alphaBlend>
static void DrawSpriteTex(const DrawingCoords &pos0, const DrawingCoords &pos1, int s_start, int t_start, int ds, int dt, u32 color0, const RasterizerState &state, Sampler::FetchFunc fetchFunc) {
const u8 *texptr = state.texptr[0];
uint16_t texbufw = state.texbufw[0];
@@ -169,42 +207,102 @@ static void DrawSpriteTex5551(const DrawingCoords &pos0, const DrawingCoords &po
const Vec4<int> c0 = Vec4<int>::FromRGBA(color0);
for (int y = pos0.y; y < pos1.y; y++) {
int s = s_start;
u16 *pixel = fb.Get16Ptr(pos0.x, y, state.pixelID.cached.framebufStride);
u16 *pixel16 = fb.Get16Ptr(pos0.x, y, state.pixelID.cached.framebufStride);
u32 *pixel32 = fb.Get32Ptr(pos0.x, y, state.pixelID.cached.framebufStride);
for (int x = pos0.x; x < pos1.x; x++) {
Vec4<int> tex_color = fetchFunc(s, t, texptr, texbufw, 0, state.samplerID);
if (isWhite) {
if (!alphaBlend || tex_color.a() != 0) {
u32 tex_color32 = tex_color.ToRGBA();
DrawSinglePixel5551<alphaBlend>(pixel, tex_color32);
if (fmt == GE_FORMAT_8888)
DrawSinglePixel32<alphaBlend>(pixel32, tex_color32);
else
DrawSinglePixel<fmt, alphaBlend>(pixel16, tex_color32);
}
} else {
Vec4<int> prim_color = c0;
prim_color = Vec4<int>(ModulateRGBA(ToVec4IntArg(prim_color), ToVec4IntArg(tex_color), state.samplerID));
if (!alphaBlend || prim_color.a() > 0) {
DrawSinglePixel5551<alphaBlend>(pixel, prim_color.ToRGBA());
if (fmt == GE_FORMAT_8888)
DrawSinglePixel32<alphaBlend>(pixel32, prim_color.ToRGBA());
else
DrawSinglePixel<fmt, alphaBlend>(pixel16, prim_color.ToRGBA());
}
}
s += ds;
pixel++;
if (fmt == GE_FORMAT_8888)
pixel32++;
else
pixel16++;
}
t += dt;
}
}
template <bool alphaBlend>
static void DrawSpriteNoTex5551(const DrawingCoords &pos0, const DrawingCoords &pos1, u32 color0, const RasterizerState &state) {
template <bool isWhite, bool alphaBlend>
static void DrawSpriteTex(const DrawingCoords &pos0, const DrawingCoords &pos1, int s_start, int t_start, int ds, int dt, u32 color0, const RasterizerState &state, Sampler::FetchFunc fetchFunc) {
switch (state.pixelID.FBFormat()) {
case GE_FORMAT_565:
DrawSpriteTex<GE_FORMAT_565, isWhite, alphaBlend>(pos0, pos1, s_start, t_start, ds, dt, color0, state, fetchFunc);
break;
case GE_FORMAT_5551:
DrawSpriteTex<GE_FORMAT_5551, isWhite, alphaBlend>(pos0, pos1, s_start, t_start, ds, dt, color0, state, fetchFunc);
break;
case GE_FORMAT_4444:
DrawSpriteTex<GE_FORMAT_4444, isWhite, alphaBlend>(pos0, pos1, s_start, t_start, ds, dt, color0, state, fetchFunc);
break;
case GE_FORMAT_8888:
DrawSpriteTex<GE_FORMAT_8888, isWhite, alphaBlend>(pos0, pos1, s_start, t_start, ds, dt, color0, state, fetchFunc);
break;
default:
// Invalid, don't draw anything...
break;
}
}
template <GEBufferFormat fmt, bool alphaBlend>
static void DrawSpriteNoTex(const DrawingCoords &pos0, const DrawingCoords &pos1, u32 color0, const RasterizerState &state) {
if (alphaBlend && Vec4<int>::FromRGBA(color0).a() == 0)
return;
for (int y = pos0.y; y < pos1.y; y++) {
u16 *pixel = fb.Get16Ptr(pos0.x, y, state.pixelID.cached.framebufStride);
for (int x = pos0.x; x < pos1.x; x++) {
DrawSinglePixel5551<alphaBlend>(pixel, color0);
pixel++;
if (fmt == GE_FORMAT_8888) {
u32 *pixel = fb.Get32Ptr(pos0.x, y, state.pixelID.cached.framebufStride);
for (int x = pos0.x; x < pos1.x; x++) {
DrawSinglePixel32<alphaBlend>(pixel, color0);
pixel++;
}
} else {
u16 *pixel = fb.Get16Ptr(pos0.x, y, state.pixelID.cached.framebufStride);
for (int x = pos0.x; x < pos1.x; x++) {
DrawSinglePixel<fmt, alphaBlend>(pixel, color0);
pixel++;
}
}
}
}
template <bool alphaBlend>
static void DrawSpriteNoTex(const DrawingCoords &pos0, const DrawingCoords &pos1, u32 color0, const RasterizerState &state) {
switch (state.pixelID.FBFormat()) {
case GE_FORMAT_565:
DrawSpriteNoTex<GE_FORMAT_565, alphaBlend>(pos0, pos1, color0, state);
break;
case GE_FORMAT_5551:
DrawSpriteNoTex<GE_FORMAT_5551, alphaBlend>(pos0, pos1, color0, state);
break;
case GE_FORMAT_4444:
DrawSpriteNoTex<GE_FORMAT_4444, alphaBlend>(pos0, pos1, color0, state);
break;
case GE_FORMAT_8888:
DrawSpriteNoTex<GE_FORMAT_8888, alphaBlend>(pos0, pos1, color0, state);
break;
default:
// Invalid, don't draw anything...
break;
}
}
void DrawSprite(const VertexData &v0, const VertexData &v1, const BinCoords &range, const RasterizerState &state) {
const u8 *texptr = state.texptr[0];
@@ -260,17 +358,17 @@ void DrawSprite(const VertexData &v0, const VertexData &v1, const BinCoords &ran
pos0.y = scissorTL.y;
}
if (UseDrawSinglePixel5551(pixelID) && (samplerID.TexFunc() == GE_TEXFUNC_MODULATE || samplerID.TexFunc() == GE_TEXFUNC_REPLACE) && samplerID.useTextureAlpha) {
if (UseDrawSinglePixel(pixelID) && (samplerID.TexFunc() == GE_TEXFUNC_MODULATE || samplerID.TexFunc() == GE_TEXFUNC_REPLACE) && samplerID.useTextureAlpha) {
if (isWhite || samplerID.TexFunc() == GE_TEXFUNC_REPLACE) {
if (pixelID.alphaBlend)
DrawSpriteTex5551<true, true>(pos0, pos1, s_start, t_start, ds, dt, v1.color0, state, fetchFunc);
DrawSpriteTex<true, true>(pos0, pos1, s_start, t_start, ds, dt, v1.color0, state, fetchFunc);
else
DrawSpriteTex5551<true, false>(pos0, pos1, s_start, t_start, ds, dt, v1.color0, state, fetchFunc);
DrawSpriteTex<true, false>(pos0, pos1, s_start, t_start, ds, dt, v1.color0, state, fetchFunc);
} else {
if (pixelID.alphaBlend)
DrawSpriteTex5551<false, true>(pos0, pos1, s_start, t_start, ds, dt, v1.color0, state, fetchFunc);
DrawSpriteTex<false, true>(pos0, pos1, s_start, t_start, ds, dt, v1.color0, state, fetchFunc);
else
DrawSpriteTex5551<false, false>(pos0, pos1, s_start, t_start, ds, dt, v1.color0, state, fetchFunc);
DrawSpriteTex<false, false>(pos0, pos1, s_start, t_start, ds, dt, v1.color0, state, fetchFunc);
}
} else {
float dsf = ds * (1.0f / (float)(1 << state.samplerID.width0Shift));
@@ -312,11 +410,11 @@ void DrawSprite(const VertexData &v0, const VertexData &v1, const BinCoords &ran
if (pos1.y > scissorBR.y) pos1.y = scissorBR.y + 1;
if (pos0.x < scissorTL.x) pos0.x = scissorTL.x;
if (pos0.y < scissorTL.y) pos0.y = scissorTL.y;
if (UseDrawSinglePixel5551(pixelID)) {
if (UseDrawSinglePixel(pixelID)) {
if (pixelID.alphaBlend)
DrawSpriteNoTex5551<true>(pos0, pos1, v1.color0, state);
DrawSpriteNoTex<true>(pos0, pos1, v1.color0, state);
else
DrawSpriteNoTex5551<false>(pos0, pos1, v1.color0, state);
DrawSpriteNoTex<false>(pos0, pos1, v1.color0, state);
} else if (pixelID.earlyZChecks) {
const Vec4<int> prim_color = Vec4<int>::FromRGBA(v1.color0);
for (int y = pos0.y; y < pos1.y; y++) {
@@ -366,20 +464,6 @@ bool RectangleFastPath(const VertexData &v0, const VertexData &v1, BinManager &b
const RasterizerState &state = binner.State();
g_DarkStalkerStretch = DSStretch::Off;
// Check for 1:1 texture mapping. In that case we can call DrawSprite.
int xdiff = v1.screenpos.x - v0.screenpos.x;
int ydiff = v1.screenpos.y - v0.screenpos.y;
int udiff = (v1.texturecoords.x - v0.texturecoords.x) * (float)SCREEN_SCALE_FACTOR;
int vdiff = (v1.texturecoords.y - v0.texturecoords.y) * (float)SCREEN_SCALE_FACTOR;
// Currently only works for TL/BR, which is the most common but not required.
bool orient_check = xdiff >= 0 && ydiff >= 0;
// We already have a fast path for clear in ClearRectangle.
bool state_check = state.throughMode && !state.pixelID.clearMode && !state.samplerID.hasAnyMips && !state.textureProj;
bool coord_check = true;
if (state.enableTextures) {
state_check = state_check && NoClampOrWrap(state, v0.texturecoords.uv()) && NoClampOrWrap(state, v1.texturecoords.uv());
coord_check = (xdiff == udiff || xdiff == -udiff) && (ydiff == vdiff || ydiff == -vdiff);
}
// Eliminate the stretch blit in DarkStalkers.
// We compensate for that when blitting the framebuffer in SoftGpu.cpp.
@@ -408,6 +492,21 @@ bool RectangleFastPath(const VertexData &v0, const VertexData &v1, BinManager &b
}
}
// Check for 1:1 texture mapping. In that case we can call DrawSprite.
int xdiff = v1.screenpos.x - v0.screenpos.x;
int ydiff = v1.screenpos.y - v0.screenpos.y;
int udiff = (v1.texturecoords.x - v0.texturecoords.x) * (float)SCREEN_SCALE_FACTOR;
int vdiff = (v1.texturecoords.y - v0.texturecoords.y) * (float)SCREEN_SCALE_FACTOR;
// Currently only works for TL/BR, which is the most common but not required.
bool orient_check = xdiff >= 0 && ydiff >= 0;
// We already have a fast path for clear in ClearRectangle.
bool state_check = state.throughMode && !state.pixelID.clearMode && !state.samplerID.hasAnyMips && !state.textureProj;
bool coord_check = true;
if (state.enableTextures) {
state_check = state_check && NoClampOrWrap(state, v0.texturecoords.uv()) && NoClampOrWrap(state, v1.texturecoords.uv());
coord_check = (xdiff == udiff || xdiff == -udiff) && (ydiff == vdiff || ydiff == -vdiff);
}
// This doesn't work well with offset drawing, see #15876. Through never has a subpixel offset.
bool subpixel_check = ((v0.screenpos.x | v0.screenpos.y | v1.screenpos.x | v1.screenpos.y) & 0xF) == 0;
if (coord_check && orient_check && state_check && subpixel_check) {
@@ -511,7 +610,6 @@ bool DetectRectangleFromStrip(const RasterizerState &state, const ClipVertexData
// There's the other vertex order too...
if (data[0].v.screenpos.x == data[2].v.screenpos.x &&
data[0].v.screenpos.y == data[1].v.screenpos.y &&
data[1].v.screenpos.x == data[3].v.screenpos.x &&
data[2].v.screenpos.y == data[3].v.screenpos.y) {
// Okay, this is in the shape of a rectangle, but what about texture?
+51 -20
View File
@@ -25,6 +25,7 @@
#include "Core/Reporting.h"
#include "GPU/Common/TextureDecoder.h"
#include "GPU/GPUState.h"
#include "GPU/Software/BinManager.h"
#include "GPU/Software/Rasterizer.h"
#include "GPU/Software/RasterizerRegCache.h"
#include "GPU/Software/Sampler.h"
@@ -67,9 +68,9 @@ bool DescribeCodePtr(const u8 *ptr, std::string &name) {
return true;
}
NearestFunc GetNearestFunc(SamplerID id, std::function<void()> flushForCompile) {
NearestFunc GetNearestFunc(SamplerID id, BinManager *binner) {
id.linear = false;
NearestFunc jitted = jitCache->GetNearest(id, flushForCompile);
NearestFunc jitted = jitCache->GetNearest(id, binner);
if (jitted) {
return jitted;
}
@@ -77,9 +78,9 @@ NearestFunc GetNearestFunc(SamplerID id, std::function<void()> flushForCompile)
return &SampleNearest;
}
LinearFunc GetLinearFunc(SamplerID id, std::function<void()> flushForCompile) {
LinearFunc GetLinearFunc(SamplerID id, BinManager *binner) {
id.linear = true;
LinearFunc jitted = jitCache->GetLinear(id, flushForCompile);
LinearFunc jitted = jitCache->GetLinear(id, binner);
if (jitted) {
return jitted;
}
@@ -87,9 +88,9 @@ LinearFunc GetLinearFunc(SamplerID id, std::function<void()> flushForCompile) {
return &SampleLinear;
}
FetchFunc GetFetchFunc(SamplerID id, std::function<void()> flushForCompile) {
FetchFunc GetFetchFunc(SamplerID id, BinManager *binner) {
id.fetch = true;
FetchFunc jitted = jitCache->GetFetch(id, flushForCompile);
FetchFunc jitted = jitCache->GetFetch(id, binner);
if (jitted) {
return jitted;
}
@@ -97,11 +98,19 @@ FetchFunc GetFetchFunc(SamplerID id, std::function<void()> flushForCompile) {
return &SampleFetch;
}
thread_local SamplerJitCache::LastCache SamplerJitCache::lastFetch_;
thread_local SamplerJitCache::LastCache SamplerJitCache::lastNearest_;
thread_local SamplerJitCache::LastCache SamplerJitCache::lastLinear_;
// 256k should be enough.
SamplerJitCache::SamplerJitCache() : Rasterizer::CodeBlock(1024 * 64 * 4), cache_(64) {
lastFetch_.gen = -1;
lastNearest_.gen = -1;
lastLinear_.gen = -1;
}
void SamplerJitCache::Clear() {
clearGen_++;
CodeBlock::Clear();
cache_.Clear();
addresses_.clear();
@@ -153,25 +162,20 @@ void SamplerJitCache::Flush() {
compileQueue_.clear();
}
NearestFunc SamplerJitCache::GetByID(const SamplerID &id, std::function<void()> flushForCompile) {
if (!g_Config.bSoftwareRenderingJit)
return nullptr;
NearestFunc SamplerJitCache::GetByID(const SamplerID &id, size_t key, BinManager *binner) {
std::unique_lock<std::mutex> guard(jitCacheLock);
const size_t key = std::hash<SamplerID>()(id);
auto it = cache_.Get(key);
if (it != nullptr)
return it;
if (!flushForCompile) {
if (!binner) {
// Can't compile, let's try to do it later when there's an opportunity.
compileQueue_.insert(id);
return nullptr;
}
guard.unlock();
flushForCompile();
binner->Flush("compile");
guard.lock();
for (const auto &queued : compileQueue_) {
@@ -189,16 +193,43 @@ NearestFunc SamplerJitCache::GetByID(const SamplerID &id, std::function<void()>
return cache_.Get(key);
}
NearestFunc SamplerJitCache::GetNearest(const SamplerID &id, std::function<void()> flushForCompile) {
return (NearestFunc)GetByID(id, flushForCompile);
NearestFunc SamplerJitCache::GetNearest(const SamplerID &id, BinManager *binner) {
if (!g_Config.bSoftwareRenderingJit)
return nullptr;
const size_t key = std::hash<SamplerID>()(id);
if (lastNearest_.Match(key, clearGen_))
return (NearestFunc)lastNearest_.func;
auto func = GetByID(id, key, binner);
lastNearest_.Set(key, func, clearGen_);
return (NearestFunc)func;
}
LinearFunc SamplerJitCache::GetLinear(const SamplerID &id, std::function<void()> flushForCompile) {
return (LinearFunc)GetByID(id, flushForCompile);
LinearFunc SamplerJitCache::GetLinear(const SamplerID &id, BinManager *binner) {
if (!g_Config.bSoftwareRenderingJit)
return nullptr;
const size_t key = std::hash<SamplerID>()(id);
if (lastLinear_.Match(key, clearGen_))
return (LinearFunc)lastLinear_.func;
auto func = GetByID(id, key, binner);
lastLinear_.Set(key, func, clearGen_);
return (LinearFunc)func;
}
FetchFunc SamplerJitCache::GetFetch(const SamplerID &id, std::function<void()> flushForCompile) {
return (FetchFunc)GetByID(id, flushForCompile);
FetchFunc SamplerJitCache::GetFetch(const SamplerID &id, BinManager *binner) {
if (!g_Config.bSoftwareRenderingJit)
return nullptr;
const size_t key = std::hash<SamplerID>()(id);
if (lastFetch_.Match(key, clearGen_))
return (FetchFunc)lastFetch_.func;
auto func = GetByID(id, key, binner);
lastFetch_.Set(key, func, clearGen_);
return (FetchFunc)func;
}
void SamplerJitCache::Compile(const SamplerID &id) {
+29 -8
View File
@@ -19,7 +19,6 @@
#include "ppsspp_config.h"
#include <functional>
#include <unordered_map>
#include <unordered_set>
#include "Common/Data/Collections/Hashmaps.h"
@@ -27,6 +26,8 @@
#include "GPU/Software/FuncId.h"
#include "GPU/Software/RasterizerRegCache.h"
class BinManager;
namespace Sampler {
// Our std::unordered_map argument will ignore the alignment attribute, but that doesn't matter.
@@ -37,13 +38,13 @@ namespace Sampler {
#endif
typedef Rasterizer::Vec4IntResult(SOFTRAST_CALL *FetchFunc)(int u, int v, const u8 *tptr, int bufw, int level, const SamplerID &samplerID);
FetchFunc GetFetchFunc(SamplerID id, std::function<void()> flushForCompile);
FetchFunc GetFetchFunc(SamplerID id, BinManager *binner);
typedef Rasterizer::Vec4IntResult (SOFTRAST_CALL *NearestFunc)(float s, float t, Rasterizer::Vec4IntArg prim_color, const u8 *const *tptr, const uint16_t *bufw, int level, int levelFrac, const SamplerID &samplerID);
NearestFunc GetNearestFunc(SamplerID id, std::function<void()> flushForCompile);
NearestFunc GetNearestFunc(SamplerID id, BinManager *binner);
typedef Rasterizer::Vec4IntResult (SOFTRAST_CALL *LinearFunc)(float s, float t, Rasterizer::Vec4IntArg prim_color, const u8 *const *tptr, const uint16_t *bufw, int level, int levelFrac, const SamplerID &samplerID);
LinearFunc GetLinearFunc(SamplerID id, std::function<void()> flushForCompile);
LinearFunc GetLinearFunc(SamplerID id, BinManager *binner);
void Init();
void FlushJit();
@@ -56,9 +57,9 @@ public:
SamplerJitCache();
// Returns a pointer to the code to run.
NearestFunc GetNearest(const SamplerID &id, std::function<void()> flushForCompile);
LinearFunc GetLinear(const SamplerID &id, std::function<void()> flushForCompile);
FetchFunc GetFetch(const SamplerID &id, std::function<void()> flushForCompile);
NearestFunc GetNearest(const SamplerID &id, BinManager *binner);
LinearFunc GetLinear(const SamplerID &id, BinManager *binner);
FetchFunc GetFetch(const SamplerID &id, BinManager *binner);
void Clear() override;
void Flush();
@@ -66,7 +67,7 @@ public:
private:
void Compile(const SamplerID &id);
NearestFunc GetByID(const SamplerID &id, std::function<void()> flushForCompile);
NearestFunc GetByID(const SamplerID &id, size_t key, BinManager *binner);
FetchFunc CompileFetch(const SamplerID &id);
NearestFunc CompileNearest(const SamplerID &id);
LinearFunc CompileLinear(const SamplerID &id);
@@ -127,9 +128,29 @@ private:
const u8 *const5551Swizzle_ = nullptr;
const u8 *const5650Swizzle_ = nullptr;
struct LastCache {
size_t key;
NearestFunc func;
int gen = -1;
bool Match(size_t k, int g) const {
return key == k && gen == g;
}
void Set(size_t k, NearestFunc f, int g) {
key = k;
func = f;
gen = g;
}
};
DenseHashMap<size_t, NearestFunc, nullptr> cache_;
std::unordered_map<SamplerID, const u8 *> addresses_;
std::unordered_set<SamplerID> compileQueue_;
int clearGen_ = 0;
static thread_local LastCache lastFetch_;
static thread_local LastCache lastNearest_;
static thread_local LastCache lastLinear_;
};
#if defined(__clang__) || defined(__GNUC__)
+7 -4
View File
@@ -18,6 +18,7 @@
#include "Common/Data/Random/Rng.h"
#include "Common/StringUtils.h"
#include "Core/Config.h"
#include "GPU/Software/BinManager.h"
#include "GPU/Software/DrawPixel.h"
#include "GPU/Software/Sampler.h"
#include "GPU/Software/SoftGpu.h"
@@ -25,21 +26,22 @@
static bool TestSamplerJit() {
using namespace Sampler;
SamplerJitCache *cache = new SamplerJitCache();
BinManager binner;
auto GetLinear = [&](SamplerID &id) {
id.linear = true;
id.fetch = false;
return cache->GetLinear(id, [] {});
return cache->GetLinear(id, &binner);
};
auto GetNearest = [&](SamplerID &id) {
id.linear = false;
id.fetch = false;
return cache->GetNearest(id, [] {});
return cache->GetNearest(id, &binner);
};
auto GetFetch = [&](SamplerID &id) {
id.linear = false;
id.fetch = true;
return cache->GetFetch(id, [] {});
return cache->GetFetch(id, &binner);
};
GMRng rng;
@@ -111,6 +113,7 @@ static bool TestSamplerJit() {
static bool TestPixelJit() {
using namespace Rasterizer;
PixelJitCache *cache = new PixelJitCache();
BinManager binner;
GMRng rng;
int successes = 0;
@@ -134,7 +137,7 @@ static bool TestPixelJit() {
continue;
i++;
SingleFunc func = cache->GetSingle(id, [] {});
SingleFunc func = cache->GetSingle(id, &binner);
SingleFunc genericFunc = cache->GenericSingle(id);
if (func != genericFunc) {
successes++;