Files
ppsspp/GPU/Common/VertexDecoderRiscV.cpp
T

153 lines
4.6 KiB
C++

// Copyright (c) 2023- 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 "ppsspp_config.h"
#if PPSSPP_ARCH(RISCV64)
#include "Common/CPUDetect.h"
#include "Common/Log.h"
#include "Common/RiscVEmitter.h"
#include "Core/MIPS/JitCommon/JitCommon.h"
#include "GPU/GPUState.h"
#include "GPU/Common/VertexDecoderCommon.h"
using namespace RiscVGen;
static const RiscVReg srcReg = X10;
static const RiscVReg dstReg = X11;
static const RiscVReg counterReg = X12;
static const RiscVReg tempReg1 = X13;
static const RiscVReg tempReg2 = X14;
static const RiscVReg tempReg3 = X15;
static const RiscVReg scratchReg = X16;
static const RiscVReg fullAlphaReg = X17;
static const RiscVReg boundsMinUReg = X28;
static const RiscVReg boundsMinVReg = X29;
static const RiscVReg boundsMaxUReg = X30;
static const RiscVReg boundsMaxVReg = X31;
static const RiscVReg fpScratchReg1 = F10;
static const RiscVReg fpScratchReg2 = F11;
static const RiscVReg fpScratchReg3 = F12;
static const RiscVReg fpScratchReg4 = F13;
static const JitLookup jitLookup[] = {
{&VertexDecoder::Step_TcFloat, &VertexDecoderJitCache::Jit_TcFloat},
};
JittedVertexDecoder VertexDecoderJitCache::Compile(const VertexDecoder &dec, int32_t *jittedSize) {
dec_ = &dec;
BeginWrite(4096);
const u8 *start = AlignCode16();
SetAutoCompress(true);
bool log = false;
if (dec.col) {
// Or LDB and skip the conditional? This is probably cheaper.
LI(fullAlphaReg, 0xFF);
}
if (dec.tc && dec.throughmode) {
// TODO: Smarter, only when doing bounds.
LI(tempReg1, &gstate_c.vertBounds.minU);
LH(boundsMinUReg, tempReg1, offsetof(KnownVertexBounds, minU));
LH(boundsMaxUReg, tempReg1, offsetof(KnownVertexBounds, maxU));
LH(boundsMinVReg, tempReg1, offsetof(KnownVertexBounds, minV));
LH(boundsMaxVReg, tempReg1, offsetof(KnownVertexBounds, maxV));
}
// TODO: Skipping, prescale.
const u8 *loopStart = GetCodePtr();
for (int i = 0; i < dec.numSteps_; i++) {
if (!CompileStep(dec, i)) {
EndWrite();
// Reset the code ptr (effectively undoing what we generated) and return zero to indicate that we failed.
ResetCodePtr(GetOffset(start));
char temp[1024]{};
dec.ToString(temp);
ERROR_LOG(G3D, "Could not compile vertex decoder, failed at step %d: %s", i, temp);
return nullptr;
}
}
ADDI(srcReg, srcReg, dec.VertexSize());
ADDI(dstReg, dstReg, dec.decFmt.stride);
ADDI(counterReg, counterReg, -1);
BLT(R_ZERO, counterReg, loopStart);
if (dec.col) {
LI(tempReg1, &gstate_c.vertexFullAlpha);
FixupBranch skip = BNE(R_ZERO, fullAlphaReg);
SB(fullAlphaReg, tempReg1, 0);
SetJumpTarget(skip);
}
if (dec.tc && dec.throughmode) {
// TODO: Smarter, only when doing bounds.
LI(tempReg1, &gstate_c.vertBounds.minU);
SH(boundsMinUReg, tempReg1, offsetof(KnownVertexBounds, minU));
SH(boundsMaxUReg, tempReg1, offsetof(KnownVertexBounds, maxU));
SH(boundsMinVReg, tempReg1, offsetof(KnownVertexBounds, minV));
SH(boundsMaxVReg, tempReg1, offsetof(KnownVertexBounds, maxV));
}
RET();
FlushIcache();
if (log) {
char temp[1024]{};
dec.ToString(temp);
INFO_LOG(JIT, "=== %s (%d bytes) ===", temp, (int)(GetCodePtr() - start));
std::vector<std::string> lines = DisassembleRV64(start, (int)(GetCodePtr() - start));
for (auto line : lines) {
INFO_LOG(JIT, "%s", line.c_str());
}
INFO_LOG(JIT, "==========");
}
*jittedSize = (int)(GetCodePtr() - start);
EndWrite();
return (JittedVertexDecoder)start;
}
bool VertexDecoderJitCache::CompileStep(const VertexDecoder &dec, int step) {
// See if we find a matching JIT function.
for (size_t i = 0; i < ARRAY_SIZE(jitLookup); i++) {
if (dec.steps_[step] == jitLookup[i].func) {
((*this).*jitLookup[i].jitFunc)();
return true;
}
}
return false;
}
void VertexDecoderJitCache::Jit_TcFloat() {
// Just copy 64 bits. Might be nice if we could detect misaligned load perf.
LW(tempReg1, srcReg, dec_->tcoff);
LW(tempReg2, srcReg, dec_->tcoff + 4);
SW(tempReg1, dstReg, dec_->decFmt.uvoff);
SW(tempReg2, dstReg, dec_->decFmt.uvoff + 4);
}
#endif // PPSSPP_ARCH(RISCV64)