GS: Made numerous fixes to the TAG/IMAGE Gif decompressor

This commit is contained in:
Correia 2024-05-21 12:22:41 -03:00
parent c186ce0675
commit f29ccdb34a
18 changed files with 180 additions and 99 deletions

View File

@ -1,6 +1,7 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="ClangTidy" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JniGetFieldID" enabled="true" level="ERROR" enabled_by_default="true" editorAttributes="ERRORS_ATTRIBUTES" />
<inspection_tool class="OCUnusedGlobalDeclaration" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnreachableCallsOfFunction" enabled="false" level="WARNING" enabled_by_default="false" />

1
.idea/misc.xml generated
View File

@ -1,4 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="BuildAttributionWarningsFilter">
<option name="noGCSettingWarning" value="true" />

View File

@ -29,10 +29,10 @@ target_sources(cosmic PRIVATE
${COSMIC_DIR}/engine/ee_plus.cpp
${COSMIC_DIR}/engine/ee_timers.cpp
${COSMIC_DIR}/engine/ee_intc.cpp
${COSMIC_DIR}/engine/copctrl/cop_cache.cpp
${COSMIC_DIR}/engine/copctrl/cop0.cpp
${COSMIC_DIR}/engine/copctrl/cop_misc.cpp
${COSMIC_DIR}/engine/copctrl/cop_dma.cpp
${COSMIC_DIR}/engine/cop_cache.cpp
${COSMIC_DIR}/engine/cop0.cpp
${COSMIC_DIR}/engine/cop_misc.cpp
${COSMIC_DIR}/engine/cop_dma.cpp
${COSMIC_DIR}/engine/cop1_fu.cpp
${COSMIC_DIR}/mio/mmu_tlb.cpp
${COSMIC_DIR}/mio/tlb_mapper.cpp

View File

@ -10,7 +10,7 @@ namespace cosmic {
namespace vm { class EmuVm; }
namespace engine {
class FpuCop;
namespace copctrl { class CtrlCop; }
class CtrlCop;
}
}
namespace cosmic::creeper::ee {
@ -188,7 +188,7 @@ namespace cosmic::creeper::ee {
static Ref<engine::EeMipsCore> cpu;
static Ref<vm::EmuVm> vm;
static Ref<engine::FpuCop> fpu;
static Ref<engine::copctrl::CtrlCop> c0;
static Ref<engine::CtrlCop> c0;
static EeMapSpecial ivSpecial;
static EeRegImm ivRegImm;

View File

@ -176,5 +176,5 @@ namespace cosmic::creeper::ee {
Ref<engine::EeMipsCore> MipsIvInterpreter::cpu;
Ref<vm::EmuVm> MipsIvInterpreter::vm;
Ref<engine::FpuCop> MipsIvInterpreter::fpu;
Ref<engine::copctrl::CtrlCop> MipsIvInterpreter::c0;
Ref<engine::CtrlCop> MipsIvInterpreter::c0;
}

View File

@ -1,7 +1,7 @@
#include <arm_neon.h>
#include <engine/copctrl/cop0.h>
#include <engine/cop0.h>
namespace cosmic::engine::copctrl {
namespace cosmic::engine {
CtrlCop::CtrlCop(std::shared_ptr<mio::DmaController>& ctrl) :
dmac(ctrl) {
// Invalidating all cache lines

View File

@ -7,7 +7,7 @@
namespace cosmic::engine {
class EeMipsCore;
}
namespace cosmic::engine::copctrl {
namespace cosmic::engine {
static constexpr u8 cop0RegsCount{32};
class alignas(16) CopCacheLine {
public:

View File

@ -1,8 +1,8 @@
#include <common/except.h>
#include <engine/copctrl/cop0.h>
#include <engine/cop0.h>
#include <engine/ee_core.h>
namespace cosmic::engine::copctrl {
namespace cosmic::engine {
// We don't check for a cache miss here
os::vec CtrlCop::readCache(u32 address, CacheMode mode) {
u32 tag{getCachePfn(address, mode)};

View File

@ -1,7 +1,7 @@
#include <engine/copctrl/cop0.h>
#include <engine/cop0.h>
#include <mio/mem_pipe.h>
namespace cosmic::engine::copctrl {
namespace cosmic::engine {
bool CtrlCop::getCondition() {
u32 stat{mio::bitBashing<u32>(dmac->performRead(0x1000e10)) & 0x3ff};
u32 pcr{mio::bitBashing<u32>(dmac->performRead(0x1000e020)) & 0x3ff};

View File

@ -1,6 +1,6 @@
#include <engine/copctrl/cop0.h>
#include <engine/cop0.h>
#include <mio/mmu_tlb.h>
namespace cosmic::engine::copctrl {
namespace cosmic::engine {
// Due to the peculiarities of the implementation, the calling function of configureGlobalTlb
// must map and unmap the TLB on its own
void CtrlCop::configureGlobalTlb(mio::TlbPageEntry& entry) {

View File

@ -1,7 +1,7 @@
#include <common/global.h>
#include <engine/ee_core.h>
#include <engine/copctrl/cop0.h>
#include <engine/cop0.h>
#include <creeper/ee/cached_blocks.h>
#include <fishron/ee2arm/jitter_arm64_ee.h>

View File

@ -8,7 +8,7 @@
#include <engine/ee_info.h>
#include <engine/copctrl/cop0.h>
#include <engine/cop0.h>
#include <engine/cop1_fu.h>
#include <engine/ee_timers.h>
#include <vu/v01_cop2vu.h>
@ -125,7 +125,7 @@ namespace cosmic::engine {
bool isABranch{};
u32 delaySlot{};
ExecutionMode cpuMode{ExecutionMode::CachedInterpreter};
copctrl::CtrlCop cop0;
CtrlCop cop0;
FpuCop cop1;
std::unique_ptr<vu::MacroModeCop2> cop2;

View File

@ -15,9 +15,9 @@ namespace cosmic::gs {
if (!queueGetSize())
return;
paths[3].status = Busy;
requestDmac(3);
requestDmac(Gif);
}
void GifBridge::requestDmac(u8 path, bool intPath3) {
void GifBridge::requestDmac(PathsTr path, bool intPath3) {
if (!activatePath || activatePath == path) {
activatePath = path;
if (activatePath == 3 && (!maskedPath3() ||
@ -42,8 +42,32 @@ namespace cosmic::gs {
queueReset();
}
bool GifBridge::isPathActivated(u8 path, bool intPath3) {
if (path != 3 && intPath3) {
void GifBridge::flushDmacFifo() {
feedPathWithData(Gif, queueConsume());
if (!queueGetSize()) {
if (maskedPath3()) {
} else {
// requestDmac(Gif);
}
}
}
void GifBridge::update(u32 cycles) {
if (!maskedPath3() &&
!queueGetSize()) {
requestDmac(Gif);
}
bool isPathMasked{isPathActivated(Gif) && !maskedPath3()};
bool shouldRun{cycles && queueGetSize()};
while (isPathMasked && shouldRun) {
flushDmacFifo();
cycles--;
isPathMasked = isPathActivated(Gif) && !maskedPath3();
shouldRun = cycles && queueGetSize();
}
}
bool GifBridge::isPathActivated(PathsTr path, bool intPath3) {
if (path != Gif && intPath3) {
}
const bool isSelected{activatePath == path};
return isSelected &&
@ -51,10 +75,12 @@ namespace cosmic::gs {
!status.tempStop &&
!gs->privileged(GsBusDir);
}
bool GifBridge::feedPathWithData(u8 path, os::vec data) {
bool GifBridge::feedPathWithData(PathsTr whatPath, os::vec data) {
std::function<void(os::vec&)> feedDev;
switch (path) {
case 1:
switch (whatPath) {
case Vu1:
case Vif1:
case Gif:
feedDev = [&](os::vec& graphics) {
transfer2Gif(graphics);
};
@ -62,8 +88,8 @@ namespace cosmic::gs {
}
if (feedDev)
feedDev(data);
return (path == 1) &&
paths[path].tag.isCompleted();
return (whatPath == 1) &&
paths[whatPath].tag.isCompleted();
}
void GifBridge::transfer2Gif(os::vec packet) {
std::array<u64, 2> package{};
@ -74,13 +100,14 @@ namespace cosmic::gs {
if (!activated->leftRegsData[1]) {
primitiveCounts++;
decodeGifTag(activated, package.data());
// NOTE: The GS Q register is initialized to 1.0f when reading a GIFtag
gsQ = 1.0;
if (activated->leftRegsData[1] != 0) {
}
} else {
switch (activated->dataFormat) {
case PackedFmtTag:
case TagDataFormat::Packed:
// This is an element loop count, like N * M, where N is the count of regs and M is
// the number of times the regs data packet needs to be transferred
activated->leftRegsData[0]--;
@ -91,44 +118,44 @@ namespace cosmic::gs {
activated->leftRegsData[1]--;
}
break;
case RegListFmtTag:
case TagDataFormat::RegList:
break;
case Image2FmtTag:
case Image3FmtTag:
case TagDataFormat::Image2:
case TagDataFormat::Image3:
for (u8 pack{}; pack < 2; pack++)
gs->gsWrite(0x54, package[pack]);
activated->leftRegsData[1]--;
break;
case Unrecognized:
case TagDataFormat::Unrecognized:
break;
}
}
}
void GifBridge::decodeGifTag(Ref<GifTag>& unpacked, u64 packet[2]) {
unpacked->dataFormat = static_cast<TagDataFormat>(packet[0] >> 58 & 0x3);
[[unlikely]] if (unpacked->dataFormat > Image3FmtTag) {
if (unpacked->dataFormat > TagDataFormat::Image3) {
}
// The first transfer from Vif to GS is its Gif-Tag; let's decode it now
// The first transfer from Vif to GS is its Gif-Tag let's decode it now
unpacked->perLoop = packet[0] & 0x7fff;
unpacked->isEndOfPacket = packet[0] & 1 << 0xf;
unpacked->regs = packet[1];
const u16 regs = packet[0] >> 60;
const u8 regs = packet[0] >> 60;
unpacked->regsNum = regs;
if (!regs) {
unpacked->regsNum = 0x10;
}
unpacked->leftRegsData[0] = unpacked->regsNum;
unpacked->leftRegsData[1] = unpacked->perLoop;
}
void GifBridge::deactivatePath(u8 path) {
void GifBridge::deactivatePath(PathsTr path) {
}
bool GifBridge::maskedPath3() {
bool isMasked{};
if (status.path3enbVifMask || status.path3enbGif) {
isMasked = (pathsFormat[3] == TagDataFormat::Unrecognized);
if (isMasked) {
deactivatePath(3);
deactivatePath(Gif);
}
}
return isMasked;

View File

@ -6,10 +6,10 @@ namespace cosmic::gs {
class GsEngine;
enum TagDataFormat {
PackedFmtTag,
RegListFmtTag,
Image2FmtTag,
Image3FmtTag,
Packed,
RegList,
Image2,
Image3,
Unrecognized
};
@ -45,6 +45,11 @@ namespace cosmic::gs {
// PATH1: VU1 via XGKICK instruction; Highest priority
// PATH2: VIF1 via DIRECT/DIRECTHL; Medium priority
// PATH3: GIF DMAC channel (channel 2); Lowest priority
enum PathsTr {
Vu1,
Vif1,
Gif = 3
};
class GifBridge {
public:
GifBridge() = default;
@ -52,18 +57,23 @@ namespace cosmic::gs {
bool downloadGsData(os::vec& put);
void resumeDmacPath();
void requestDmac(u8 path, bool intPath3 = false);
void deactivatePath(u8 path);
bool isPathActivated(u8 path, bool intPath3 = false);
bool feedPathWithData(u8 path, os::vec data);
void requestDmac(PathsTr path, bool intPath3 = false);
void deactivatePath(PathsTr path);
bool isPathActivated(PathsTr path, bool intPath3 = false);
bool feedPathWithData(PathsTr whatPath, os::vec data);
void update(u32 cycles);
private:
void transfer2Gif(os::vec packet);
void decodeGifTag(Ref<GifTag>& unpacked, u64 packet[2]);
void uploadPackedData(Ref<GifTag>& dsTag, u64 packet[2]);
void queueReset();
u32 queueGetSize();
u64 queueGetSize();
os::vec queueConsume();
u64 queueFreePos();
bool maskedPath3();
void flushDmacFifo();
Ref<GsEngine> gs;
std::array<GifPath, 4> paths;
@ -74,14 +84,18 @@ namespace cosmic::gs {
f32 gsQ;
u8 pathQueue;
u32 fifoSize;
u64 fifoSize;
alignas(16) std::array<os::vec, 16> gifFifo;
using QueueIterator = std::array<os::vec, 16>::iterator;
static_assert(sizeof(gifFifo) == 256);
Ref<os::vec> fifoBack, fifoFront;
QueueIterator fifoFront;
std::array<bool, 16> fifoArr;
u64 primitiveCounts;
[[gnu::always_inline]] u8 colorUnzip(u64 v, u8 a) {
return static_cast<u8>((v << a) & 0xff);
template <typename T>
[[gnu::always_inline]] auto extractPair(u64 v, T a, u64 clean) {
return static_cast<T>((v >> a) & clean);
}
};
}

View File

@ -15,24 +15,28 @@ namespace cosmic::gs {
}
switch (reg) {
case PrimitiveOffset:
case RegDesc::Primitive:
gs->gsWrite(0x00, packet[0]);
break;
case RGBAQOffset: {
RGBAQReg color;
color.r = colorUnzip(packet[0], 0 );
color.g = colorUnzip(packet[0], 32);
color.b = colorUnzip(packet[1], 0 );
color.a = colorUnzip(packet[1], 32);
case RegDesc::RGBAQ: {
RGBAQReg color{};
// NOTES: There was a mistake in the type of bitwise operation
// used to extract the values below
color.r = extractPair<u8>(packet[0], 0, 0xff);
color.g = extractPair<u8>(packet[0], 32, 0xff);
color.b = extractPair<u8>(packet[1], 0, 0xff);
color.a = extractPair<u8>(packet[1], 32, 0xff);
// The internal Q register is used here and stays the same
color.gsq = gsQ;
gs->gsWrite(0x01, color.rainbow);
}
break;
case StPosOffset: {
case RegDesc::StPos: {
u64 neoQ;
// Fixing float types, this can be remedied or disabled later...
neoQ = packet[1] & 0x7f800000;
neoQ = extractPair<u64>(packet[1], 0, 0x7f800000);
if ((neoQ & 0x7f800000) == 0x7f800000)
neoQ = (neoQ & 0x80000000) | 0x7f7fffff;
gs->gsWrite(0x02, packet[0]);
@ -40,23 +44,32 @@ namespace cosmic::gs {
gsQ = *reinterpret_cast<f32*>(&neoQ);
}
break;
case UvPosOffset:
gs->gsWrite(0x03, packet[0]);
break;
case Xyz2Offset: {
CoordinatesXyz c;
c.x = packet[0] & 0xffff;
c.y = (packet[0] >> 32) & 0xffff;
c.z = packet[1] & 0xffffffff;
gs->gsWrite(0x05, c.xyz);
case RegDesc::UvPos: {
std::array<u16, 2> uvsCods{};
uvsCods[0] = extractPair<u16>(packet[0], 0, 0x3fff);
uvsCods[1] = extractPair<u16>(packet[0], 32, 0x3fff);
gs->gsWrite(0x03, *reinterpret_cast<u32 *>(uvsCods.data()));
}
case NopOffset:
break;
case FogOffset ... AdOffset: {
case RegDesc::Xyz2: {
CoordinatesXyz c{
.x = extractPair<u16>(packet[0], 0, 0xffff),
.y = extractPair<u16>(packet[0], 32, 0xffff),
.z = extractPair<u32>(packet[1], 0, 0xffffffff)
};
auto disableDraw{(packet[1] >> (111 - 64)) & 1};
auto address{disableDraw ? 0xd : 0x5};
gs->gsWrite(static_cast<u32>(address), c.xyz);
}
case RegDesc::Nop:
break;
case RegDesc::Fog ... RegDesc::Ad: {
u32 addr{static_cast<u32>(packet[1] & 0xff)};
if (addr < 0x7f) {
gs->gsWrite(addr, packet[0]);
if (addr > 0x7f) {
}
gs->gsWrite(addr, packet[0]);
}
break;
default:

View File

@ -1,3 +1,4 @@
#include <range/v3/algorithm.hpp>
#include <gs/gif_bridge.h>
namespace cosmic::gs {
@ -7,22 +8,48 @@ namespace cosmic::gs {
memset(&gifFifo[0], 0xff, sizeof(gifFifo));
#endif
__asm("eor v0.16b, v0.16b, v0.16b");
#define STORE_PACKED_16B(addr)\
__asm("st1 {v0.16b}, [%0]" :: "r" (addr))
for (u32 gifData{}; gifData < gifFifo.size(); ) {
__asm("st1 {v0.16b}, [%0]" :: "r" (&gifFifo[gifData++]));
__asm("st1 {v0.16b}, [%0]" :: "r" (&gifFifo[gifData++]));
__asm("st1 {v0.16b}, [%0]" :: "r" (&gifFifo[gifData++]));
__asm("st1 {v0.16b}, [%0]" :: "r" (&gifFifo[gifData++]));
STORE_PACKED_16B(&gifFifo[gifData++]);
STORE_PACKED_16B(&gifFifo[gifData++]);
STORE_PACKED_16B(&gifFifo[gifData++]);
STORE_PACKED_16B(&gifFifo[gifData++]);
}
fifoFront = std::ref(gifFifo[0]);
fifoBack = std::ref(gifFifo[15]);
fifoFront = std::begin(gifFifo);
ranges::fill(fifoArr, 0);
}
u32 GifBridge::queueGetSize() {
if (fifoBack && fifoFront) {
fifoSize = static_cast<u32>(std::abs(&fifoBack - &fifoFront)) / sizeof(gifFifo[0]);
u64 GifBridge::queueFreePos() {
u64 writable{};
ranges::for_each(fifoArr, [&](const auto pos){
if (!pos)
writable++;
});
return writable;
}
os::vec GifBridge::queueConsume() {
if (fifoFront > std::end(gifFifo)) {
return {};
}
auto front{*fifoFront};
fifoSize = static_cast<u64>(
std::abs(fifoFront - std::begin(gifFifo)));
fifoArr[fifoSize] = {};
fifoFront++;
return front;
}
u64 GifBridge::queueGetSize() {
if (fifoFront) {
fifoSize = static_cast<u64>(
std::abs(fifoFront - std::begin(gifFifo)));
}
// We can pre-load the array values into the L2 cache since we'll be accessing it shortly
for (u32 preload{}; preload < gifFifo.size(); preload++)
__asm("prfm pldl2keep, [%0]" :: "r" (&gifFifo[preload]));
for (u64 preload{}; preload < gifFifo.size(); preload++) {
__asm("prfm pldl2keep, [%0]"::"r" (&gifFifo[preload]));
}
return fifoSize;
}

View File

@ -21,16 +21,16 @@ namespace cosmic::gs {
GsBusDir
};
enum RegDesc {
PrimitiveOffset,
RGBAQOffset,
StPosOffset,
UvPosOffset,
Xyz2Offset,
Primitive,
RGBAQ,
StPos,
UvPos,
Xyz2,
FogOffset = 0xa,
AdOffset = 0xe,
Fog = 0xa,
Ad = 0xe,
NopOffset = 0xf
Nop = 0xf
};
union RGBAQReg {

View File

@ -16,14 +16,14 @@ namespace cosmic::vu {
gifAddr += 16;
quad = *BitCast<os::vec*>(&vecRegion.rw[addr]);
if (vu1Gif.value()->feedPathWithData(1, quad)) {
if (vu1Gif.value()->feedPathWithData(gs::Vu1, quad)) {
if (!path1.stallXgKick) {
// Reactivating the previous interrupted transfer
path1.stallXgKick = {};
gifAddr = gifStallAddr;
vu1Gif.value()->requestDmac(1, true);
vu1Gif.value()->requestDmac(gs::Vu1, true);
} else {
vu1Gif.value()->deactivatePath(1);
vu1Gif.value()->deactivatePath(gs::Vu1);
path1.transferringGif = {};
return;
}
@ -33,9 +33,9 @@ namespace cosmic::vu {
void VectorUnit::startXgKick2Gif() {
if (!vu1Gif.has_value())
return;
vu1Gif.value()->requestDmac(1, true);
vu1Gif.value()->requestDmac(gs::Vu1, true);
while (path1.cycles >= 0x2) {
if (!vu1Gif.value()->isPathActivated(1, true)) {
if (!vu1Gif.value()->isPathActivated(gs::Vu1, true)) {
path1.cycles = 0;
break;
}