mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-07-11 01:25:07 +02:00
Merge pull request #16518 from unknownbrackets/softgpu-fastrect
softgpu: Expand fast path to all fb formats
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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__)
|
||||
|
||||
@@ -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++;
|
||||
|
||||
Reference in New Issue
Block a user