mirror of
https://github.com/ares-emulator/ares.git
synced 2026-11-12 11:22:59 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e6e9fd36e | ||
|
|
8111bf3163 | ||
|
|
c287c1b225 | ||
|
|
863593ed7f | ||
|
|
c20d405006 | ||
|
|
e1e1bedb2d | ||
|
|
9f060c769b | ||
|
|
e7f9af4bbd | ||
|
|
65c34c2ea4 | ||
|
|
9babe6a02b | ||
|
|
7e5292cd16 | ||
|
|
a8274cd127 | ||
|
|
d99b0a3725 | ||
|
|
e43f3f75f7 | ||
|
|
f894a85253 | ||
|
|
09fbcc4e65 | ||
|
|
73d90e6e7c | ||
|
|
38dd6defa9 | ||
|
|
030488aba7 | ||
|
|
b2175473f0 | ||
|
|
bc2a54800f | ||
|
|
6e182d8542 | ||
|
|
38f64fa70c | ||
|
|
7eb1b28671 | ||
|
|
42c16831b7 | ||
|
|
5982fa8ae3 | ||
|
|
5f435345f1 | ||
|
|
8343f8136c | ||
|
|
4dcba42240 | ||
|
|
81d5cdba32 | ||
|
|
5151003fa4 | ||
|
|
c6a838d6ee | ||
|
|
349f1d281c | ||
|
|
cac4aab301 | ||
|
|
5911338cac | ||
|
|
c0af33d4ce | ||
|
|
ea77623238 | ||
|
|
033752679f | ||
|
|
025fc6450a | ||
|
|
d6858ad821 | ||
|
|
0b46609e95 | ||
|
|
b5a8edc5d0 | ||
|
|
757a7dd670 | ||
|
|
950573df14 | ||
|
|
f43da416fd | ||
|
|
18f45ddc85 | ||
|
|
7135646c44 | ||
|
|
4ab8472a53 | ||
|
|
a47e793b57 | ||
|
|
1ffb4f6d5a | ||
|
|
aa350116d4 | ||
|
|
e8a5579cb7 | ||
|
|
f091fcfacd | ||
|
|
fa9d350a66 |
No files matched your search
@@ -122,9 +122,11 @@ jobs:
|
||||
run: |
|
||||
if [ $ImageOS == "macos14" ]; then
|
||||
sudo xcode-select -s /Applications/Xcode_15.2.app/Contents/Developer
|
||||
export MACOSX_DEPLOYMENT_TARGET=10.13
|
||||
echo "Set Xcode version to 15.2 for macos-latest build."
|
||||
elif [ $ImageOS == "macos12" ]; then
|
||||
sudo xcode-select -s /Applications/Xcode_13.4.app/Contents/Developer
|
||||
export MACOSX_DEPLOYMENT_TARGET=10.9
|
||||
echo "Set Xcode version to 13.4 for macos-compat build."
|
||||
fi
|
||||
brew install make ninja cmake
|
||||
@@ -140,6 +142,7 @@ jobs:
|
||||
pushd thirdparty/MoltenVK
|
||||
./build-moltenvk.sh
|
||||
popd
|
||||
export MACOSX_DEPLOYMENT_TARGET=10.15
|
||||
pushd thirdparty/librashader
|
||||
./build-librashader.sh
|
||||
popd
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ Platform* platform = nullptr;
|
||||
bool _runAhead = false;
|
||||
|
||||
const string Name = "ares";
|
||||
const string Version = "140";
|
||||
const string Version = "141";
|
||||
const string Copyright = "ares team, Near";
|
||||
const string License = "ISC";
|
||||
const string LicenseURI = "https://opensource.org/licenses/ISC";
|
||||
|
||||
@@ -75,8 +75,8 @@ struct GSU {
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
//disassembler.cpp
|
||||
noinline auto disassembleInstruction() -> string;
|
||||
noinline auto disassembleContext() -> string;
|
||||
NALL_NOINLINE auto disassembleInstruction() -> string;
|
||||
NALL_NOINLINE auto disassembleContext() -> string;
|
||||
|
||||
auto disassembleOpcode(char* output) -> void;
|
||||
auto disassembleALT0(char* output) -> void;
|
||||
|
||||
@@ -111,8 +111,8 @@ struct HG51B {
|
||||
auto writeRegister(n7 address, n24 data) -> void;
|
||||
|
||||
//disassembler.cpp
|
||||
noinline auto disassembleInstruction(maybe<n15> pb = {}, maybe<n8> pc = {}) -> string;
|
||||
noinline auto disassembleContext() -> string;
|
||||
NALL_NOINLINE auto disassembleInstruction(maybe<n15> pb = {}, maybe<n8> pc = {}) -> string;
|
||||
NALL_NOINLINE auto disassembleContext() -> string;
|
||||
|
||||
protected:
|
||||
struct Registers {
|
||||
|
||||
@@ -124,8 +124,8 @@ struct I8080 {
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
//disassembler.cpp
|
||||
noinline auto disassembleInstruction(maybe<n16> pc = {}) -> string;
|
||||
noinline auto disassembleContext() -> string;
|
||||
NALL_NOINLINE auto disassembleInstruction(maybe<n16> pc = {}) -> string;
|
||||
NALL_NOINLINE auto disassembleContext() -> string;
|
||||
|
||||
auto disassemble(n16 pc, n8 prefix, n8 code) -> string;
|
||||
|
||||
|
||||
@@ -156,8 +156,8 @@ struct MOS6502 {
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
//disassembler.cpp
|
||||
noinline auto disassembleInstruction(maybe<n16> pc = {}) -> string;
|
||||
noinline auto disassembleContext() -> string;
|
||||
NALL_NOINLINE auto disassembleInstruction(maybe<n16> pc = {}) -> string;
|
||||
NALL_NOINLINE auto disassembleContext() -> string;
|
||||
|
||||
struct PR {
|
||||
bool c; //carry
|
||||
|
||||
@@ -513,19 +513,19 @@ auto SH2::MOVI(u32 i, u32 n) -> void {
|
||||
}
|
||||
|
||||
//MOV.W @(disp,PC),Rn
|
||||
noinline auto SH2::MOVWI(u32 d, u32 n) -> void {
|
||||
NALL_NOINLINE auto SH2::MOVWI(u32 d, u32 n) -> void {
|
||||
u32 pc = inDelaySlot() ? PPC - 2 : PC;
|
||||
R[n] = (s16)readWord(pc + d * 2);
|
||||
}
|
||||
|
||||
//MOV.L @(disp,PC),Rn
|
||||
noinline auto SH2::MOVLI(u32 d, u32 n) -> void {
|
||||
NALL_NOINLINE auto SH2::MOVLI(u32 d, u32 n) -> void {
|
||||
u32 pc = inDelaySlot() ? PPC - 2 : PC;
|
||||
R[n] = readLong((pc & ~3) + d * 4);
|
||||
}
|
||||
|
||||
//MOVA @(disp,PC),R0
|
||||
noinline auto SH2::MOVA(u32 d) -> void {
|
||||
NALL_NOINLINE auto SH2::MOVA(u32 d) -> void {
|
||||
u32 pc = inDelaySlot() ? PPC - 2 : PC;
|
||||
R[0] = (pc & ~3) + d * 4;
|
||||
}
|
||||
|
||||
@@ -29,8 +29,8 @@ struct SM83 {
|
||||
|
||||
//disassembler.cpp
|
||||
virtual auto readDebugger(n16 address) -> n8 { return 0; }
|
||||
noinline auto disassembleInstruction(maybe<n16> pc = {}) -> string;
|
||||
noinline auto disassembleContext() -> string;
|
||||
NALL_NOINLINE auto disassembleInstruction(maybe<n16> pc = {}) -> string;
|
||||
NALL_NOINLINE auto disassembleContext() -> string;
|
||||
|
||||
//memory.cpp
|
||||
auto operand() -> n8;
|
||||
|
||||
@@ -117,9 +117,9 @@ struct SPC700 {
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
//disassembler.cpp
|
||||
noinline auto disassembleInstruction(n16 address, n1 p) -> string;
|
||||
noinline auto disassembleInstruction() -> string;
|
||||
noinline auto disassembleContext() -> string;
|
||||
NALL_NOINLINE auto disassembleInstruction(n16 address, n1 p) -> string;
|
||||
NALL_NOINLINE auto disassembleInstruction() -> string;
|
||||
NALL_NOINLINE auto disassembleContext() -> string;
|
||||
|
||||
struct Flags {
|
||||
bool c; //carry
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
noinline auto TLCS900H::disassembleInstruction() -> string {
|
||||
NALL_NOINLINE auto TLCS900H::disassembleInstruction() -> string {
|
||||
string output;
|
||||
|
||||
auto pc = load(PC);
|
||||
@@ -636,7 +636,7 @@ noinline auto TLCS900H::disassembleInstruction() -> string {
|
||||
return pad(output, -48);
|
||||
}
|
||||
|
||||
noinline auto TLCS900H::disassembleContext() -> string {
|
||||
NALL_NOINLINE auto TLCS900H::disassembleContext() -> string {
|
||||
string output;
|
||||
output.append("XWA:", hex(r.xwa[RFP].l.l0, 8L), " ");
|
||||
output.append("XBC:", hex(r.xbc[RFP].l.l0, 8L), " ");
|
||||
|
||||
@@ -25,8 +25,8 @@ struct uPD96050 {
|
||||
auto writeDP(n12 address, n8 data) -> void;
|
||||
|
||||
//disassembler.cpp
|
||||
noinline auto disassembleInstruction(maybe<n14> ip = {}) -> string;
|
||||
noinline auto disassembleContext() -> string;
|
||||
NALL_NOINLINE auto disassembleInstruction(maybe<n14> ip = {}) -> string;
|
||||
NALL_NOINLINE auto disassembleContext() -> string;
|
||||
|
||||
enum class Revision : u32 { uPD7725, uPD96050 } revision;
|
||||
n24 programROM[16384];
|
||||
|
||||
@@ -235,9 +235,9 @@ struct WDC65816 {
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
//disassembler.cpp
|
||||
noinline auto disassembleInstruction(n24 address, bool e, bool m, bool x) -> string;
|
||||
noinline auto disassembleInstruction() -> string;
|
||||
noinline auto disassembleContext(maybe<bool> e = {}) -> string;
|
||||
NALL_NOINLINE auto disassembleInstruction(n24 address, bool e, bool m, bool x) -> string;
|
||||
NALL_NOINLINE auto disassembleInstruction() -> string;
|
||||
NALL_NOINLINE auto disassembleContext(maybe<bool> e = {}) -> string;
|
||||
|
||||
struct f8 {
|
||||
bool c = 0; //carry
|
||||
|
||||
@@ -211,8 +211,8 @@ struct Z80 {
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
//disassembler.cpp
|
||||
noinline auto disassembleInstruction(maybe<n16> pc = {}) -> string;
|
||||
noinline auto disassembleContext() -> string;
|
||||
NALL_NOINLINE auto disassembleInstruction(maybe<n16> pc = {}) -> string;
|
||||
NALL_NOINLINE auto disassembleContext() -> string;
|
||||
|
||||
auto disassemble(n16 pc, n8 prefix, n8 code) -> string;
|
||||
auto disassembleCB(n16 pc, n8 prefix, n8 code) -> string;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
auto APU::readIO(n32 address) -> n8 {
|
||||
cpu.synchronize(apu);
|
||||
|
||||
switch(address) {
|
||||
|
||||
//NR10
|
||||
@@ -144,6 +146,8 @@ auto APU::readIO(n32 address) -> n8 {
|
||||
}
|
||||
|
||||
auto APU::writeIO(n32 address, n8 data) -> void {
|
||||
cpu.synchronize(apu);
|
||||
|
||||
switch(address) {
|
||||
|
||||
//NR10
|
||||
|
||||
+30
-14
@@ -30,20 +30,25 @@ auto CPU::unload() -> void {
|
||||
}
|
||||
|
||||
auto CPU::main() -> void {
|
||||
ARM7TDMI::irq = irq.ime && (irq.enable & irq.flag);
|
||||
ARM7TDMI::irq = irq.synchronizer[0];
|
||||
|
||||
if(stopped()) {
|
||||
if(!(irq.enable & irq.flag & Interrupt::Keypad)) {
|
||||
Thread::step(16);
|
||||
if(!keypad.conditionMet) {
|
||||
stepIRQ();
|
||||
Thread::step(1);
|
||||
Thread::synchronize();
|
||||
return;
|
||||
}
|
||||
Thread::step(2);
|
||||
Thread::synchronize();
|
||||
context.stopped = false;
|
||||
}
|
||||
|
||||
if(halted()) {
|
||||
if(!(irq.enable & irq.flag)) {
|
||||
return step(16);
|
||||
if(!(irq.enable[0] & irq.flag[0])) {
|
||||
return step(4);
|
||||
}
|
||||
step(2);
|
||||
context.halted = false;
|
||||
}
|
||||
|
||||
@@ -64,6 +69,17 @@ auto CPU::dmaRun() -> void {
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::setInterruptFlag(u32 source) -> void {
|
||||
irq.flag[1] |= source;
|
||||
}
|
||||
|
||||
inline auto CPU::stepIRQ() -> void {
|
||||
irq.synchronizer[0] = irq.synchronizer[1];
|
||||
irq.synchronizer[1] = irq.ime && (irq.enable[0] & irq.flag[0]);
|
||||
irq.enable[0] = irq.enable[1];
|
||||
irq.flag[0] = irq.flag[1];
|
||||
}
|
||||
|
||||
auto CPU::step(u32 clocks) -> void {
|
||||
if(!clocks) return;
|
||||
|
||||
@@ -75,6 +91,7 @@ auto CPU::step(u32 clocks) -> void {
|
||||
dma[3].waiting = max(0, dma[3].waiting - (s32)clocks);
|
||||
|
||||
for(auto _ : range(clocks)) {
|
||||
stepIRQ();
|
||||
timer[0].run();
|
||||
timer[1].run();
|
||||
timer[2].run();
|
||||
@@ -93,17 +110,16 @@ auto CPU::step(u32 clocks) -> void {
|
||||
context.clock++;
|
||||
}
|
||||
|
||||
#if defined(PROFILE_PERFORMANCE)
|
||||
//10-20% speedup by only synchronizing other components every 16 clock cycles
|
||||
Thread::step(clocks);
|
||||
Thread::synchronize(ppu, player);
|
||||
|
||||
//occasionally synchronize with APU in case CPU has not recently interacted with it
|
||||
static u32 counter = 0;
|
||||
counter += clocks;
|
||||
if(counter < 16) return;
|
||||
clocks = counter;
|
||||
counter = 0;
|
||||
#endif
|
||||
|
||||
Thread::step(clocks);
|
||||
Thread::synchronize();
|
||||
if(counter >= 1024) {
|
||||
Thread::synchronize(apu);
|
||||
counter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
auto CPU::power() -> void {
|
||||
|
||||
@@ -47,6 +47,8 @@ struct CPU : ARM7TDMI, Thread, IO {
|
||||
|
||||
auto main() -> void;
|
||||
auto dmaRun() -> void;
|
||||
auto setInterruptFlag(u32 source) -> void;
|
||||
auto stepIRQ() -> void;
|
||||
auto step(u32 clocks) -> void override;
|
||||
auto power() -> void;
|
||||
|
||||
@@ -158,7 +160,8 @@ struct CPU : ARM7TDMI, Thread, IO {
|
||||
n1 transferEnableReceive;
|
||||
n1 transferEnableSend;
|
||||
n1 startBit;
|
||||
n1 transferLength;
|
||||
n4 uartFlags;
|
||||
n2 mode;
|
||||
n1 irqEnable;
|
||||
|
||||
n16 data[4];
|
||||
@@ -172,6 +175,8 @@ struct CPU : ARM7TDMI, Thread, IO {
|
||||
n1 enable;
|
||||
n1 condition;
|
||||
n1 flag[10];
|
||||
|
||||
n1 conditionMet;
|
||||
} keypad;
|
||||
|
||||
struct Joybus {
|
||||
@@ -201,8 +206,9 @@ struct CPU : ARM7TDMI, Thread, IO {
|
||||
|
||||
struct IRQ {
|
||||
n1 ime;
|
||||
n16 enable;
|
||||
n16 flag;
|
||||
n1 synchronizer[2];
|
||||
n16 enable[2];
|
||||
n16 flag[2];
|
||||
} irq;
|
||||
|
||||
struct Wait {
|
||||
|
||||
@@ -2,8 +2,8 @@ inline auto CPU::DMA::run() -> bool {
|
||||
if(!active || waiting) return false;
|
||||
|
||||
transfer();
|
||||
if(irq) cpu.irq.flag |= CPU::Interrupt::DMA0 << id;
|
||||
if(drq && id == 3) cpu.irq.flag |= CPU::Interrupt::Cartridge;
|
||||
if(irq) cpu.setInterruptFlag(CPU::Interrupt::DMA0 << id);
|
||||
if(drq && id == 3) cpu.setInterruptFlag(CPU::Interrupt::Cartridge);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+18
-16
@@ -55,8 +55,9 @@ auto CPU::readIO(n32 address) -> n8 {
|
||||
| serial.startBit << 7
|
||||
);
|
||||
case 0x0400'0129: return (
|
||||
serial.transferLength << 4
|
||||
| serial.irqEnable << 6
|
||||
serial.uartFlags << 0
|
||||
| serial.mode << 4
|
||||
| serial.irqEnable << 6
|
||||
);
|
||||
|
||||
//SIOMLT_SEND (SIODATA8)
|
||||
@@ -183,12 +184,12 @@ auto CPU::readIO(n32 address) -> n8 {
|
||||
case 0x0400'015b: return 0;
|
||||
|
||||
//IE
|
||||
case 0x0400'0200: return irq.enable.byte(0);
|
||||
case 0x0400'0201: return irq.enable.byte(1);
|
||||
case 0x0400'0200: return irq.enable[0].byte(0);
|
||||
case 0x0400'0201: return irq.enable[0].byte(1);
|
||||
|
||||
//IF
|
||||
case 0x0400'0202: return irq.flag.byte(0);
|
||||
case 0x0400'0203: return irq.flag.byte(1);
|
||||
case 0x0400'0202: return irq.flag[0].byte(0);
|
||||
case 0x0400'0203: return irq.flag[0].byte(1);
|
||||
|
||||
//WAITCNT
|
||||
case 0x0400'0204: return (
|
||||
@@ -339,8 +340,9 @@ auto CPU::writeIO(n32 address, n8 data) -> void {
|
||||
serial.startBit = data.bit(7);
|
||||
return;
|
||||
case 0x0400'0129:
|
||||
serial.transferLength = data.bit(4);
|
||||
serial.irqEnable = data.bit(6);
|
||||
serial.uartFlags = data.bit(0,3);
|
||||
serial.mode = data.bit(4,5);
|
||||
serial.irqEnable = data.bit(6);
|
||||
return;
|
||||
|
||||
//SIOMLT_SEND (SIODATA8)
|
||||
@@ -383,10 +385,10 @@ auto CPU::writeIO(n32 address, n8 data) -> void {
|
||||
|
||||
//JOYCNT
|
||||
case 0x0400'0140:
|
||||
joybus.resetSignal = data.bit(0);
|
||||
joybus.receiveComplete = data.bit(1);
|
||||
joybus.sendComplete = data.bit(2);
|
||||
joybus.resetIRQEnable = data.bit(6);
|
||||
joybus.resetSignal &= ~data.bit(0);
|
||||
joybus.receiveComplete &= ~data.bit(1);
|
||||
joybus.sendComplete &= ~data.bit(2);
|
||||
joybus.resetIRQEnable = data.bit(6);
|
||||
return;
|
||||
case 0x0400'0141: return;
|
||||
case 0x0400'0142: return;
|
||||
@@ -415,12 +417,12 @@ auto CPU::writeIO(n32 address, n8 data) -> void {
|
||||
case 0x0400'0159: return;
|
||||
|
||||
//IE
|
||||
case 0x0400'0200: irq.enable.byte(0) = data; return;
|
||||
case 0x0400'0201: irq.enable.byte(1) = data; return;
|
||||
case 0x0400'0200: irq.enable[1].byte(0) = data; return;
|
||||
case 0x0400'0201: irq.enable[1].byte(1) = data; return;
|
||||
|
||||
//IF
|
||||
case 0x0400'0202: irq.flag.byte(0) = irq.flag.byte(0) & ~data; return;
|
||||
case 0x0400'0203: irq.flag.byte(1) = irq.flag.byte(1) & ~data; return;
|
||||
case 0x0400'0202: irq.flag[1].byte(0) = irq.flag[1].byte(0) & ~data; return;
|
||||
case 0x0400'0203: irq.flag[1].byte(1) = irq.flag[1].byte(1) & ~data; return;
|
||||
|
||||
//WAITCNT
|
||||
case 0x0400'0204:
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
auto CPU::Keypad::run() -> void {
|
||||
if(!enable) return;
|
||||
system.controls.poll();
|
||||
|
||||
const bool lookup[] = {
|
||||
system.controls.a->value(),
|
||||
system.controls.b->value(),
|
||||
@@ -15,12 +12,13 @@ auto CPU::Keypad::run() -> void {
|
||||
system.controls.l->value(),
|
||||
};
|
||||
|
||||
bool test = condition; //0 = OR, 1 = AND
|
||||
conditionMet = condition; //0 = OR, 1 = AND
|
||||
for(u32 index : range(10)) {
|
||||
if(!flag[index]) continue;
|
||||
bool input = lookup[index];
|
||||
if(condition == 0) test |= input;
|
||||
if(condition == 1) test &= input;
|
||||
n1 input = lookup[index];
|
||||
if(condition == 0) conditionMet |= input;
|
||||
if(condition == 1) conditionMet &= input;
|
||||
}
|
||||
if(test) cpu.irq.flag |= CPU::Interrupt::Keypad;
|
||||
|
||||
if(conditionMet && enable) cpu.setInterruptFlag(CPU::Interrupt::Keypad);
|
||||
}
|
||||
@@ -52,7 +52,8 @@ auto CPU::serialize(serializer& s) -> void {
|
||||
s(serial.transferEnableReceive);
|
||||
s(serial.transferEnableSend);
|
||||
s(serial.startBit);
|
||||
s(serial.transferLength);
|
||||
s(serial.uartFlags);
|
||||
s(serial.mode);
|
||||
s(serial.irqEnable);
|
||||
for(auto& value : serial.data) s(value);
|
||||
s(serial.data8);
|
||||
@@ -60,6 +61,7 @@ auto CPU::serialize(serializer& s) -> void {
|
||||
s(keypad.enable);
|
||||
s(keypad.condition);
|
||||
for(auto& flag : keypad.flag) s(flag);
|
||||
s(keypad.conditionMet);
|
||||
|
||||
s(joybus.sc);
|
||||
s(joybus.sd);
|
||||
@@ -82,8 +84,9 @@ auto CPU::serialize(serializer& s) -> void {
|
||||
s(joybus.generalFlag);
|
||||
|
||||
s(irq.ime);
|
||||
s(irq.enable);
|
||||
s(irq.flag);
|
||||
for(auto& flag : irq.synchronizer) s(flag);
|
||||
for(auto& flag : irq.enable) s(flag);
|
||||
for(auto& flag : irq.flag) s(flag);
|
||||
|
||||
for(auto& flag : wait.nwait) s(flag);
|
||||
for(auto& flag : wait.swait) s(flag);
|
||||
|
||||
@@ -43,7 +43,7 @@ auto CPU::Timer::step() -> void {
|
||||
if(++period == 0) {
|
||||
period = reload;
|
||||
|
||||
if(irq) cpu.irq.flag |= CPU::Interrupt::Timer0 << id;
|
||||
if(irq) cpu.setInterruptFlag(CPU::Interrupt::Timer0 << id);
|
||||
|
||||
if(apu.fifo[0].timer == id) cpu.runFIFO(0);
|
||||
if(apu.fifo[1].timer == id) cpu.runFIFO(1);
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace ares::GameBoyAdvance {
|
||||
#include <ares/inline.hpp>
|
||||
auto enumerate() -> vector<string>;
|
||||
auto load(Node::System& node, string name) -> bool;
|
||||
auto option(string name, string value) -> bool;
|
||||
|
||||
enum : u32 { //mode flags for bus read, write:
|
||||
Nonsequential = 1, //N cycle
|
||||
|
||||
@@ -63,7 +63,7 @@ auto Player::frame() -> void {
|
||||
&& !cpu.serial.transferEnableReceive
|
||||
&& cpu.serial.transferEnableSend
|
||||
&& cpu.serial.startBit
|
||||
&& cpu.serial.transferLength
|
||||
&& cpu.serial.mode == 0x1
|
||||
&& cpu.serial.irqEnable
|
||||
) {
|
||||
status.packet = (status.packet + 1) % 17;
|
||||
@@ -86,7 +86,7 @@ auto Player::frame() -> void {
|
||||
case 15: status.send = 0x30000003; break;
|
||||
case 16: status.send = 0x30000003; break;
|
||||
}
|
||||
cpu.irq.flag |= CPU::Interrupt::Serial;
|
||||
cpu.setInterruptFlag(CPU::Interrupt::Serial);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,13 +4,19 @@ n1 PPU::Background::IO::frame;
|
||||
n5 PPU::Background::IO::mosaicWidth;
|
||||
n5 PPU::Background::IO::mosaicHeight;
|
||||
|
||||
auto PPU::Background::setEnable(n1 status) -> void {
|
||||
io.enable[3] = status;
|
||||
for(auto& flag : io.enable) flag &= status;
|
||||
}
|
||||
|
||||
auto PPU::Background::scanline(u32 y) -> void {
|
||||
memory::move(io.enable, io.enable + 1, sizeof(io.enable) - 1);
|
||||
mosaicOffset = 0;
|
||||
}
|
||||
|
||||
auto PPU::Background::run(u32 x, u32 y) -> void {
|
||||
output = {};
|
||||
if(ppu.blank() || !io.enable) {
|
||||
if(ppu.blank() || !io.enable[0]) {
|
||||
mosaic = {};
|
||||
return;
|
||||
}
|
||||
@@ -37,10 +43,11 @@ auto PPU::Background::run(u32 x, u32 y) -> void {
|
||||
}
|
||||
|
||||
//horizontal mosaic
|
||||
if(!io.mosaic || ++mosaicOffset >= 1 + io.mosaicWidth) {
|
||||
mosaicOffset = 0;
|
||||
if(!io.mosaic || !mosaicOffset) {
|
||||
mosaicOffset = 1 + io.mosaicWidth;
|
||||
mosaic = output;
|
||||
}
|
||||
mosaicOffset--;
|
||||
}
|
||||
|
||||
auto PPU::Background::linear(u32 x, u32 y) -> void {
|
||||
|
||||
+16
-15
@@ -8,17 +8,17 @@ auto PPU::readIO(n32 address) -> n8 {
|
||||
| Background::IO::frame << 4
|
||||
| objects.io.hblank << 5
|
||||
| objects.io.mapping << 6
|
||||
| io.forceBlank << 7
|
||||
| io.forceBlank[3] << 7
|
||||
);
|
||||
case 0x0400'0001: return (
|
||||
bg0.io.enable << 0
|
||||
| bg1.io.enable << 1
|
||||
| bg2.io.enable << 2
|
||||
| bg3.io.enable << 3
|
||||
| objects.io.enable << 4
|
||||
| window0.io.enable << 5
|
||||
| window1.io.enable << 6
|
||||
| window2.io.enable << 7
|
||||
bg0.io.enable[3] << 0
|
||||
| bg1.io.enable[3] << 1
|
||||
| bg2.io.enable[3] << 2
|
||||
| bg3.io.enable[3] << 3
|
||||
| objects.io.enable[3] << 4
|
||||
| window0.io.enable << 5
|
||||
| window1.io.enable << 6
|
||||
| window2.io.enable << 7
|
||||
);
|
||||
|
||||
//GRSWP
|
||||
@@ -112,14 +112,15 @@ auto PPU::writeIO(n32 address, n8 data) -> void {
|
||||
Background::IO::frame = data.bit(4);
|
||||
objects.io.hblank = data.bit(5);
|
||||
objects.io.mapping = data.bit(6);
|
||||
io.forceBlank = data.bit(7);
|
||||
io.forceBlank[3] = data.bit(7);
|
||||
for(auto& flag : io.forceBlank) flag |= data.bit(7);
|
||||
return;
|
||||
case 0x0400'0001:
|
||||
bg0.io.enable = data.bit(0);
|
||||
bg1.io.enable = data.bit(1);
|
||||
bg2.io.enable = data.bit(2);
|
||||
bg3.io.enable = data.bit(3);
|
||||
objects.io.enable = data.bit(4);
|
||||
bg0.setEnable(data.bit(0));
|
||||
bg1.setEnable(data.bit(1));
|
||||
bg2.setEnable(data.bit(2));
|
||||
bg3.setEnable(data.bit(3));
|
||||
objects.setEnable(data.bit(4));
|
||||
window0.io.enable = data.bit(5);
|
||||
window1.io.enable = data.bit(6);
|
||||
window2.io.enable = data.bit(7);
|
||||
|
||||
+20
-7
@@ -1,7 +1,16 @@
|
||||
auto PPU::Objects::setEnable(n1 status) -> void {
|
||||
io.enable[3] = status;
|
||||
for(auto& flag : io.enable) flag &= status;
|
||||
}
|
||||
|
||||
auto PPU::Objects::scanline(u32 y) -> void {
|
||||
memory::move(io.enable, io.enable + 1, sizeof(io.enable) - 1);
|
||||
if(y >= 160) return;
|
||||
|
||||
mosaicOffset = 0;
|
||||
auto& buffer = lineBuffers[y & 1];
|
||||
for(auto& pixel : buffer) pixel = {};
|
||||
if(ppu.blank() || !io.enable) return;
|
||||
if(ppu.io.forceBlank[1] || cpu.stopped() || !io.enable[1]) return; //checks if display conditions will be met next scanline
|
||||
|
||||
for(auto& object : ppu.object) {
|
||||
n8 py = y - object.y;
|
||||
@@ -60,7 +69,7 @@ auto PPU::Objects::scanline(u32 y) -> void {
|
||||
if(color) {
|
||||
if(object.colors == 0) color = object.palette * 16 + color;
|
||||
buffer[bx].enable = true;
|
||||
buffer[bx].color = ppu.pram[256 + color];
|
||||
buffer[bx].color = 256 + color;
|
||||
buffer[bx].translucent = object.mode == 1;
|
||||
}
|
||||
}
|
||||
@@ -74,26 +83,30 @@ auto PPU::Objects::scanline(u32 y) -> void {
|
||||
|
||||
auto PPU::Objects::run(u32 x, u32 y) -> void {
|
||||
output = {};
|
||||
if(ppu.blank() || !io.enable) {
|
||||
if(ppu.blank() || !io.enable[0]) {
|
||||
mosaic = {};
|
||||
return;
|
||||
}
|
||||
|
||||
auto& buffer = lineBuffers[y & 1];
|
||||
output = buffer[x];
|
||||
output.color = ppu.pram[output.color];
|
||||
|
||||
//horizontal mosaic
|
||||
if(mosaicOffset >= 1 + io.mosaicWidth) {
|
||||
mosaicOffset = 0;
|
||||
if(!mosaicOffset) {
|
||||
mosaicOffset = 1 + io.mosaicWidth;
|
||||
mosaic = output;
|
||||
} else if(!mosaic.mosaic || !output.mosaic) {
|
||||
mosaic = output;
|
||||
}
|
||||
mosaicOffset++;
|
||||
mosaicOffset--;
|
||||
}
|
||||
|
||||
auto PPU::Objects::power() -> void {
|
||||
io = {};
|
||||
for(auto& pixel : buffer) pixel = {};
|
||||
for(auto& buffer : lineBuffers) {
|
||||
for(auto& pixel : buffer) pixel = {};
|
||||
}
|
||||
output = {};
|
||||
mosaic = {};
|
||||
mosaicOffset = 0;
|
||||
|
||||
+44
-21
@@ -23,6 +23,10 @@ PPU ppu;
|
||||
#include "debugger.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto PPU::setAccurate(bool value) -> void {
|
||||
accurate = value;
|
||||
}
|
||||
|
||||
auto PPU::load(Node::Object parent) -> void {
|
||||
vram.allocate(96_KiB);
|
||||
pram.allocate(512);
|
||||
@@ -73,7 +77,7 @@ auto PPU::unload() -> void {
|
||||
}
|
||||
|
||||
inline auto PPU::blank() -> bool {
|
||||
return io.forceBlank || cpu.stopped();
|
||||
return io.forceBlank[0] || cpu.stopped();
|
||||
}
|
||||
|
||||
auto PPU::step(u32 clocks) -> void {
|
||||
@@ -85,7 +89,6 @@ auto PPU::main() -> void {
|
||||
cpu.keypad.run();
|
||||
|
||||
io.vblank = io.vcounter >= 160 && io.vcounter <= 226;
|
||||
io.vcoincidence = io.vcounter == io.vcompare;
|
||||
|
||||
if(io.vcounter == 0) {
|
||||
frame();
|
||||
@@ -97,24 +100,42 @@ auto PPU::main() -> void {
|
||||
bg3.io.ly = bg3.io.y;
|
||||
}
|
||||
|
||||
step(1);
|
||||
|
||||
io.vcoincidence = io.vcounter == io.vcompare;
|
||||
|
||||
if(io.vcounter == 160) {
|
||||
if(io.irqvblank) cpu.setInterruptFlag(CPU::Interrupt::VBlank);
|
||||
}
|
||||
|
||||
step(1);
|
||||
|
||||
if(io.irqvcoincidence) {
|
||||
if(io.vcoincidence) cpu.setInterruptFlag(CPU::Interrupt::VCoincidence);
|
||||
}
|
||||
|
||||
if(io.vcounter == 160) {
|
||||
if(io.irqvblank) cpu.irq.flag |= CPU::Interrupt::VBlank;
|
||||
cpu.dmaVblank();
|
||||
}
|
||||
|
||||
if(io.irqvcoincidence) {
|
||||
if(io.vcoincidence) cpu.irq.flag |= CPU::Interrupt::VCoincidence;
|
||||
step(3);
|
||||
|
||||
if(io.vcounter == 162) {
|
||||
if(videoCapture) cpu.dma[3].enable = 0;
|
||||
videoCapture = !videoCapture && cpu.dma[3].timingMode == 3 && cpu.dma[3].enable;
|
||||
}
|
||||
if(io.vcounter >= 2 && io.vcounter < 162 && videoCapture) cpu.dmaHDMA();
|
||||
|
||||
step(46);
|
||||
step(41);
|
||||
|
||||
if(io.vcounter < 160) {
|
||||
u32 y = io.vcounter;
|
||||
bg0.scanline(y);
|
||||
bg1.scanline(y);
|
||||
bg2.scanline(y);
|
||||
bg3.scanline(y);
|
||||
objects.scanline(y);
|
||||
u32 y = io.vcounter;
|
||||
memory::move(io.forceBlank, io.forceBlank + 1, sizeof(io.forceBlank) - 1);
|
||||
bg0.scanline(y);
|
||||
bg1.scanline(y);
|
||||
bg2.scanline(y);
|
||||
bg3.scanline(y);
|
||||
objects.scanline((y + 1) % 228);
|
||||
if(y < 160) {
|
||||
auto line = screen->pixels().data() + y * 240;
|
||||
for(u32 x : range(240)) {
|
||||
bg0.run(x, y);
|
||||
@@ -128,27 +149,29 @@ auto PPU::main() -> void {
|
||||
window3.output = true;
|
||||
n15 color = dac.run(x, y);
|
||||
line[x] = color;
|
||||
step(4);
|
||||
if(accurate) step(4);
|
||||
}
|
||||
if(!accurate) step(960);
|
||||
} else {
|
||||
step(960);
|
||||
}
|
||||
|
||||
step(1);
|
||||
io.hblank = 1;
|
||||
if(io.irqhblank) cpu.irq.flag |= CPU::Interrupt::HBlank;
|
||||
|
||||
step(1);
|
||||
if(io.irqhblank) cpu.setInterruptFlag(CPU::Interrupt::HBlank);
|
||||
|
||||
step(1);
|
||||
if(io.vcounter < 160) cpu.dmaHblank();
|
||||
|
||||
step(226);
|
||||
step(223);
|
||||
io.hblank = 0;
|
||||
if(++io.vcounter == 228) io.vcounter = 0;
|
||||
if(io.vcounter == 162) {
|
||||
if(videoCapture) cpu.dma[3].enable = 0;
|
||||
videoCapture = !videoCapture && cpu.dma[3].timingMode == 3 && cpu.dma[3].enable;
|
||||
}
|
||||
if(io.vcounter >= 2 && io.vcounter < 162 && videoCapture) cpu.dmaHDMA();
|
||||
}
|
||||
|
||||
auto PPU::frame() -> void {
|
||||
system.controls.poll();
|
||||
screen->frame();
|
||||
scheduler.exit(Event::Frame);
|
||||
}
|
||||
|
||||
+10
-4
@@ -7,6 +7,8 @@ struct PPU : Thread, IO {
|
||||
Memory::Writable<n8 > vram; //96KB
|
||||
Memory::Writable<n16> pram;
|
||||
|
||||
bool accurate;
|
||||
|
||||
struct Debugger {
|
||||
//debugger.cpp
|
||||
auto load(Node::Object) -> void;
|
||||
@@ -27,6 +29,8 @@ struct PPU : Thread, IO {
|
||||
} debugger;
|
||||
|
||||
//ppu.cpp
|
||||
auto setAccurate(bool value) -> void;
|
||||
|
||||
auto load(Node::Object) -> void;
|
||||
auto unload() -> void;
|
||||
|
||||
@@ -70,7 +74,7 @@ private:
|
||||
|
||||
struct IO {
|
||||
n1 gameBoyColorMode;
|
||||
n1 forceBlank;
|
||||
n1 forceBlank[4];
|
||||
n1 greenSwap;
|
||||
|
||||
n1 vblank;
|
||||
@@ -97,6 +101,7 @@ private:
|
||||
|
||||
struct Background {
|
||||
//background.cpp
|
||||
auto setEnable(n1 status) -> void;
|
||||
auto scanline(u32 y) -> void;
|
||||
auto run(u32 x, u32 y) -> void;
|
||||
auto linear(u32 x, u32 y) -> void;
|
||||
@@ -115,7 +120,7 @@ private:
|
||||
static n5 mosaicWidth;
|
||||
static n5 mosaicHeight;
|
||||
|
||||
n1 enable;
|
||||
n1 enable[4];
|
||||
|
||||
n2 priority;
|
||||
n2 characterBase;
|
||||
@@ -162,6 +167,7 @@ private:
|
||||
|
||||
struct Objects {
|
||||
//object.cpp
|
||||
auto setEnable(n1 status) -> void;
|
||||
auto scanline(u32 y) -> void;
|
||||
auto run(u32 x, u32 y) -> void;
|
||||
auto power() -> void;
|
||||
@@ -170,7 +176,7 @@ private:
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
struct IO {
|
||||
n1 enable;
|
||||
n1 enable[4];
|
||||
|
||||
n1 hblank; //1 = allow access to OAM during Hblank
|
||||
n1 mapping; //0 = two-dimensional, 1 = one-dimensional
|
||||
@@ -178,7 +184,7 @@ private:
|
||||
n5 mosaicHeight;
|
||||
} io;
|
||||
|
||||
Pixel buffer[240];
|
||||
Pixel lineBuffers[2][240];
|
||||
Pixel output;
|
||||
Pixel mosaic;
|
||||
u32 mosaicOffset;
|
||||
|
||||
@@ -5,7 +5,7 @@ auto PPU::serialize(serializer& s) -> void {
|
||||
s(pram);
|
||||
|
||||
s(io.gameBoyColorMode);
|
||||
s(io.forceBlank);
|
||||
for(auto& flag : io.forceBlank) s(flag);
|
||||
s(io.greenSwap);
|
||||
s(io.vblank);
|
||||
s(io.hblank);
|
||||
@@ -39,7 +39,7 @@ auto PPU::serialize(serializer& s) -> void {
|
||||
auto PPU::Background::serialize(serializer& s) -> void {
|
||||
s(id);
|
||||
|
||||
s(io.enable);
|
||||
for(auto& flag : io.enable) s(flag);
|
||||
s(io.priority);
|
||||
s(io.characterBase);
|
||||
s(io.unused);
|
||||
@@ -67,7 +67,7 @@ auto PPU::Background::serialize(serializer& s) -> void {
|
||||
}
|
||||
|
||||
auto PPU::Objects::serialize(serializer& s) -> void {
|
||||
s(io.enable);
|
||||
for(auto& flag : io.enable) s(flag);
|
||||
s(io.hblank);
|
||||
s(io.mapping);
|
||||
s(io.mosaicWidth);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
static const string SerializerVersion = "v139.3";
|
||||
static const string SerializerVersion = "v140.3";
|
||||
|
||||
auto System::serialize(bool synchronize) -> serializer {
|
||||
if(synchronize) scheduler.enter(Scheduler::Mode::Synchronize);
|
||||
|
||||
@@ -14,6 +14,13 @@ auto load(Node::System& node, string name) -> bool {
|
||||
return system.load(node, name);
|
||||
}
|
||||
|
||||
auto option(string name, string value) -> bool {
|
||||
if(name == "Pixel Accuracy") {
|
||||
ppu.setAccurate(value.boolean());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Scheduler scheduler;
|
||||
BIOS bios;
|
||||
System system;
|
||||
|
||||
+1
-1
@@ -93,7 +93,7 @@ auto CPU::power(bool reset) -> void {
|
||||
M68000::power();
|
||||
Thread::create(system.frequency() / 7.0, {&CPU::main, this});
|
||||
|
||||
tmssEnable = system.tmss->value();
|
||||
tmssEnable = system.tmss;
|
||||
if(!reset) ram.fill();
|
||||
|
||||
io = {};
|
||||
|
||||
@@ -73,7 +73,7 @@ auto M32X::SH7604::step(u32 clocks) -> void {
|
||||
auto M32X::SH7604::power(bool reset) -> void {
|
||||
Thread::create((system.frequency() / 7.0) * 3.0, {&M32X::SH7604::main, this});
|
||||
SH2::recompilerStepCycles = 20; // Minimum cycles for recompiler to run for each batch of instructions
|
||||
minCyclesBetweenFullSyncs = 100; // Minimum cycles between full sync with the M68K/MD side
|
||||
minCyclesBetweenFullSyncs = 50; // Minimum cycles between full sync with the M68K/MD side
|
||||
minCyclesBetweenSh2Syncs = 5; // Minimum Cycles between sync with the other SH2 (syncOtherSh2)
|
||||
minCyclesBetweenM68kSyncs = 10; // Minimum Cycles between sync with the M68K (syncM68k)
|
||||
SH2::power(reset);
|
||||
|
||||
@@ -35,6 +35,9 @@ auto option(string name, string value) -> bool {
|
||||
m32x.shs.recompiler.enabled = value.boolean();
|
||||
}
|
||||
}
|
||||
if(name == "TMSS") {
|
||||
system.tmss = value.boolean();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -115,8 +118,6 @@ auto System::load(Node::System& root, string name) -> bool {
|
||||
root = node;
|
||||
if(!node->setPak(pak = platform->pak(node))) return false;
|
||||
|
||||
tmss = node->append<Node::Setting::Boolean>("TMSS", false);
|
||||
|
||||
scheduler.reset();
|
||||
controls.load(node);
|
||||
bus.load(node);
|
||||
|
||||
@@ -2,7 +2,7 @@ extern Random random;
|
||||
|
||||
struct System {
|
||||
Node::System node;
|
||||
Node::Setting::Boolean tmss;
|
||||
bool tmss;
|
||||
VFS::Pak pak;
|
||||
|
||||
struct Controls {
|
||||
|
||||
+17
-27
@@ -108,31 +108,25 @@ auto CPU::instruction() -> void {
|
||||
return;
|
||||
}
|
||||
|
||||
if(Accuracy::CPU::Recompiler && recompiler.enabled) {
|
||||
// Fast path: attempt to lookup previously compiled blocks with devirtualizeFast
|
||||
// and fastFetchBlock, this skips exception handling, error checking, and
|
||||
// code emitting pathways for maximum lookup performance.
|
||||
// As memory writes cause recompiler block invalidation, this shouldn't be detectable.
|
||||
if (auto address = devirtualizeFast(ipu.pc)) {
|
||||
if(auto block = recompiler.fastFetchBlock(address)) {
|
||||
block->execute(*this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto access = devirtualize<Read, Word>(ipu.pc);
|
||||
if(!access) return;
|
||||
|
||||
if (auto address = devirtualize(ipu.pc)) {
|
||||
auto block = recompiler.block(ipu.pc, *address, GDB::server.hasBreakpoints());
|
||||
if(Accuracy::CPU::Recompiler && recompiler.enabled && access.cache) {
|
||||
if(vaddrAlignedError<Word>(access.vaddr, false)) return;
|
||||
auto block = recompiler.block(ipu.pc, access.paddr, GDB::server.hasBreakpoints());
|
||||
if(block) {
|
||||
block->execute(*this);
|
||||
}
|
||||
} else {
|
||||
auto data = fetch(ipu.pc);
|
||||
if (!data) return;
|
||||
pipeline.begin();
|
||||
instructionPrologue(ipu.pc, *data);
|
||||
decoderEXECUTE(*data);
|
||||
instructionEpilogue<0>();
|
||||
pipeline.end();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto data = fetch(access);
|
||||
if (!data) return;
|
||||
pipeline.begin();
|
||||
instructionPrologue(ipu.pc, *data);
|
||||
decoderEXECUTE(*data);
|
||||
instructionEpilogue<0>();
|
||||
pipeline.end();
|
||||
}
|
||||
|
||||
auto CPU::instructionPrologue(u64 address, u32 instruction) -> void {
|
||||
@@ -141,11 +135,7 @@ auto CPU::instructionPrologue(u64 address, u32 instruction) -> void {
|
||||
|
||||
template<bool Recompiled>
|
||||
auto CPU::instructionEpilogue() -> void {
|
||||
if constexpr(Recompiled) {
|
||||
//simulates timings without performing actual icache loads
|
||||
icache.step(ipu.pc, devirtualizeFast(ipu.pc));
|
||||
assert(ipu.r[0].u64 == 0);
|
||||
} else {
|
||||
if constexpr(!Recompiled) {
|
||||
ipu.r[0].u64 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
+73
-44
@@ -72,6 +72,17 @@ struct CPU : Thread {
|
||||
}
|
||||
} pipeline{*this};
|
||||
|
||||
struct PhysAccess {
|
||||
enum Direction : u32 { Read, Write };
|
||||
|
||||
explicit operator bool() const { return found; }
|
||||
|
||||
bool found; //this is a valid physical access
|
||||
bool cache; //access must go through cache
|
||||
u32 paddr; //physical address on 32-bit MIPS bus
|
||||
u64 vaddr; //virtual address used on the CPU (64-bit)
|
||||
};
|
||||
|
||||
//context.cpp
|
||||
struct Context {
|
||||
CPU& self;
|
||||
@@ -101,29 +112,46 @@ struct CPU : Thread {
|
||||
struct InstructionCache {
|
||||
CPU& self;
|
||||
struct Line;
|
||||
auto line(u32 vaddr) -> Line& { return lines[vaddr >> 5 & 0x1ff]; }
|
||||
auto line(u64 vaddr) -> Line& { return lines[vaddr >> 5 & 0x1ff]; }
|
||||
|
||||
//used by the recompiler to simulate instruction cache fetch timing
|
||||
auto step(u32 vaddr, u32 address) -> void {
|
||||
auto step(u64 vaddr, u32 paddr) -> void {
|
||||
auto& line = this->line(vaddr);
|
||||
if(!line.hit(address)) {
|
||||
if(!line.hit(paddr)) {
|
||||
self.step(48 * 2);
|
||||
line.valid = 1;
|
||||
line.tag = address & ~0x0000'0fff;
|
||||
line.tag = paddr & ~0x0000'0fff;
|
||||
} else {
|
||||
self.step(1 * 2);
|
||||
}
|
||||
}
|
||||
|
||||
//used by the interpreter to fully emulate the instruction cache
|
||||
auto fetch(u32 vaddr, u32 address, CPU& cpu) -> u32 {
|
||||
auto jitFetch(u64 vaddr, u32 paddr, CPU& cpu) -> void {
|
||||
auto& line = this->line(vaddr);
|
||||
if(!line.hit(address)) {
|
||||
line.fill(address, cpu);
|
||||
} else {
|
||||
cpu.step(1 * 2);
|
||||
if(!line.hit(paddr)) {
|
||||
line.fill(paddr, cpu);
|
||||
}
|
||||
return line.read(address);
|
||||
}
|
||||
|
||||
//used by the interpreter to fully emulate the instruction cache
|
||||
auto fetch(u64 vaddr, u32 paddr, CPU& cpu) -> u32 {
|
||||
auto& line = this->line(vaddr);
|
||||
if(!line.hit(paddr)) {
|
||||
line.fill(paddr, cpu);
|
||||
}
|
||||
return line.read(paddr);
|
||||
}
|
||||
|
||||
auto coherent(u64 vaddr, u32 paddr) -> bool {
|
||||
auto& line = this->line(vaddr);
|
||||
if(!line.hit(paddr))
|
||||
return true;
|
||||
u32 ram[8];
|
||||
self.busReadBurst<ICache>(paddr & ~0x0000'0fff | line.index, ram);
|
||||
for (int i=0; i<8; i++)
|
||||
if (ram[i] != line.words[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto power(bool reset) -> void {
|
||||
@@ -138,11 +166,11 @@ struct CPU : Thread {
|
||||
|
||||
//16KB
|
||||
struct Line {
|
||||
auto hit(u32 address) const -> bool { return valid && tag == (address & ~0x0000'0fff); }
|
||||
auto fill(u32 address, CPU& cpu) -> void {
|
||||
auto hit(u32 paddr) const -> bool { return valid && tag == (paddr & ~0x0000'0fff); }
|
||||
auto fill(u32 paddr, CPU& cpu) -> void {
|
||||
cpu.step(48 * 2);
|
||||
valid = 1;
|
||||
tag = address & ~0x0000'0fff;
|
||||
tag = paddr & ~0x0000'0fff;
|
||||
cpu.busReadBurst<ICache>(tag | index, words);
|
||||
}
|
||||
|
||||
@@ -151,7 +179,7 @@ struct CPU : Thread {
|
||||
cpu.busWriteBurst<ICache>(tag | index, words);
|
||||
}
|
||||
|
||||
auto read(u32 address) const -> u32 { return words[address >> 2 & 7]; }
|
||||
auto read(u32 paddr) const -> u32 { return words[paddr >> 2 & 7]; }
|
||||
|
||||
bool valid;
|
||||
u32 tag;
|
||||
@@ -163,20 +191,21 @@ struct CPU : Thread {
|
||||
//dcache.cpp
|
||||
struct DataCache {
|
||||
struct Line;
|
||||
auto line(u32 vaddr) -> Line&;
|
||||
template<u32 Size> auto read(u32 vaddr, u32 address) -> u64;
|
||||
template<u32 Size> auto write(u32 vaddr, u32 address, u64 data) -> void;
|
||||
auto line(u64 vaddr) -> Line&;
|
||||
template<u32 Size> auto read(u64 vaddr, u32 paddr) -> u64;
|
||||
template<u32 Size> auto write(u64 vaddr, u32 paddr, u64 data) -> void;
|
||||
auto power(bool reset) -> void;
|
||||
|
||||
auto readDebug(u32 vaddr, u32 address) -> u8;
|
||||
auto readDebug(u64 vaddr, u32 paddr) -> u8;
|
||||
auto writeDebug(u64 vaddr, u32 paddr, u8 value) -> void;
|
||||
|
||||
//8KB
|
||||
struct Line {
|
||||
auto hit(u32 address) const -> bool;
|
||||
auto fill(u32 address) -> void;
|
||||
auto hit(u32 paddr) const -> bool;
|
||||
auto fill(u32 paddr) -> void;
|
||||
auto writeBack() -> void;
|
||||
template<u32 Size> auto read(u32 address) const -> u64;
|
||||
template<u32 Size> auto write(u32 address, u64 data) -> void;
|
||||
template<u32 Size> auto read(u32 paddr) const -> u64;
|
||||
template<u32 Size> auto write(u32 paddr, u64 data) -> void;
|
||||
|
||||
bool valid;
|
||||
u16 dirty;
|
||||
@@ -198,14 +227,6 @@ struct CPU : Thread {
|
||||
TLB(CPU& self) : self(self) {}
|
||||
static constexpr u32 Entries = 32;
|
||||
|
||||
struct Match {
|
||||
explicit operator bool() const { return found; }
|
||||
|
||||
bool found;
|
||||
bool cache;
|
||||
u32 address;
|
||||
};
|
||||
|
||||
struct Entry {
|
||||
//scc-tlb.cpp
|
||||
auto synchronize() -> void;
|
||||
@@ -227,12 +248,11 @@ struct CPU : Thread {
|
||||
} entry[TLB::Entries];
|
||||
|
||||
//tlb.cpp
|
||||
auto load(u64 vaddr, bool noExceptions = false) -> Match;
|
||||
auto load(u64 vaddr, const Entry& entry, bool noExceptions = false) -> maybe<Match>;
|
||||
auto load(u64 vaddr, bool noExceptions = false) -> PhysAccess;
|
||||
auto load(u64 vaddr, const Entry& entry, bool noExceptions = false) -> maybe<PhysAccess>;
|
||||
|
||||
auto loadFast(u64 vaddr) -> Match;
|
||||
auto store(u64 vaddr) -> Match;
|
||||
auto store(u64 vaddr, const Entry& entry) -> maybe<Match>;
|
||||
auto store(u64 vaddr, bool noExceptions = false) -> PhysAccess;
|
||||
auto store(u64 vaddr, const Entry& entry, bool noExceptions = false) -> maybe<PhysAccess>;
|
||||
|
||||
struct TlbCache { ;
|
||||
static constexpr int entries = 4;
|
||||
@@ -276,21 +296,30 @@ struct CPU : Thread {
|
||||
auto userSegment64(u64 vaddr) const -> Context::Segment;
|
||||
|
||||
auto segment(u64 vaddr) -> Context::Segment;
|
||||
auto devirtualize(u64 vaddr) -> maybe<u64>;
|
||||
alwaysinline auto devirtualizeFast(u64 vaddr) -> u64;
|
||||
template<u32 Dir, u32 Size> auto devirtualize(u64 vaddr, bool raiseAlignedError = true, bool raiseExceptions = true) -> PhysAccess;
|
||||
auto devirtualizeDebug(u64 vaddr) -> u64;
|
||||
|
||||
auto fetch(u64 vaddr) -> maybe<u32>;
|
||||
auto fetch(PhysAccess access) -> maybe<u32>;
|
||||
auto jitFetch(u64 vaddr, u32 addr) -> void {
|
||||
icache.jitFetch(vaddr, addr, *this);
|
||||
}
|
||||
template<u32 Size> auto busWrite(u32 address, u64 data) -> void;
|
||||
template<u32 Size> auto busRead(u32 address) -> u64;
|
||||
template<u32 Size> auto busWriteBurst(u32 address, u32 *data) -> void;
|
||||
template<u32 Size> auto busReadBurst(u32 address, u32 *data) -> void;
|
||||
template<u32 Size> auto read(u64 vaddr) -> maybe<u64>;
|
||||
template<u32 Size> auto write(u64 vaddr, u64 data, bool alignedError=true) -> bool;
|
||||
template<u32 Size> auto read(PhysAccess access) -> maybe<u64>;
|
||||
template<u32 Size> auto write(PhysAccess access, u64 data) -> bool;
|
||||
template<u32 Size> auto read(u64 vaddr) -> maybe<u64> {
|
||||
return read<Size>(devirtualize<Read, Size>(vaddr));
|
||||
}
|
||||
template<u32 Size> auto write(u64 vaddr, u64 data, bool alignedError = true) -> bool {
|
||||
return write<Size>(devirtualize<Write, Size>(vaddr, alignedError), data);
|
||||
}
|
||||
template<u32 Size> auto vaddrAlignedError(u64 vaddr, bool write) -> bool;
|
||||
auto addressException(u64 vaddr) -> void;
|
||||
|
||||
auto readDebug(u64 vaddr) -> u8;
|
||||
template <u32 Size> auto writeDebug(u64 vaddr, u64 data) -> bool;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
@@ -301,6 +330,7 @@ struct CPU : Thread {
|
||||
Exception(CPU& self) : self(self) {}
|
||||
|
||||
auto trigger(u32 code, u32 coprocessor = 0, bool tlbMiss = 0) -> void;
|
||||
auto reportGDBException(int code, u64 pc) -> void;
|
||||
|
||||
auto interrupt() -> void;
|
||||
auto tlbModification() -> void;
|
||||
@@ -890,10 +920,9 @@ struct CPU : Thread {
|
||||
}
|
||||
|
||||
auto pool(u32 address) -> Pool*;
|
||||
auto block(u32 vaddr, u32 address, bool singleInstruction = false) -> Block*;
|
||||
auto fastFetchBlock(u32 address) -> Block*;
|
||||
auto block(u64 vaddr, u32 address, bool singleInstruction = false) -> Block*;
|
||||
|
||||
auto emit(u32 vaddr, u32 address, bool singleInstruction = false) -> Block*;
|
||||
auto emit(u64 vaddr, u32 address, bool singleInstruction = false) -> Block*;
|
||||
auto emitZeroClear(u32 n) -> void;
|
||||
auto emitEXECUTE(u32 instruction) -> bool;
|
||||
auto emitSPECIAL(u32 instruction) -> bool;
|
||||
|
||||
+40
-31
@@ -1,12 +1,12 @@
|
||||
auto CPU::DataCache::Line::hit(u32 address) const -> bool {
|
||||
return valid && tag == (address & ~0x0000'0fff);
|
||||
auto CPU::DataCache::Line::hit(u32 paddr) const -> bool {
|
||||
return valid && tag == (paddr & ~0x0000'0fff);
|
||||
}
|
||||
|
||||
auto CPU::DataCache::Line::fill(u32 address) -> void {
|
||||
auto CPU::DataCache::Line::fill(u32 paddr) -> void {
|
||||
cpu.step(40 * 2);
|
||||
valid = 1;
|
||||
dirty = 0;
|
||||
tag = address & ~0x0000'0fff;
|
||||
tag = paddr & ~0x0000'0fff;
|
||||
fillPc = cpu.ipu.pc;
|
||||
cpu.busReadBurst<DCache>(tag | index, words);
|
||||
}
|
||||
@@ -17,66 +17,75 @@ auto CPU::DataCache::Line::writeBack() -> void {
|
||||
cpu.busWriteBurst<DCache>(tag | index, words);
|
||||
}
|
||||
|
||||
auto CPU::DataCache::line(u32 vaddr) -> Line& {
|
||||
auto CPU::DataCache::line(u64 vaddr) -> Line& {
|
||||
return lines[vaddr >> 4 & 0x1ff];
|
||||
}
|
||||
|
||||
template<u32 Size>
|
||||
auto CPU::DataCache::Line::read(u32 address) const -> u64 {
|
||||
if constexpr(Size == Byte) { return bytes[address >> 0 & 15 ^ 3]; }
|
||||
if constexpr(Size == Half) { return halfs[address >> 1 & 7 ^ 1]; }
|
||||
if constexpr(Size == Word) { return words[address >> 2 & 3 ^ 0]; }
|
||||
auto CPU::DataCache::Line::read(u32 paddr) const -> u64 {
|
||||
if constexpr(Size == Byte) { return bytes[paddr >> 0 & 15 ^ 3]; }
|
||||
if constexpr(Size == Half) { return halfs[paddr >> 1 & 7 ^ 1]; }
|
||||
if constexpr(Size == Word) { return words[paddr >> 2 & 3 ^ 0]; }
|
||||
if constexpr(Size == Dual) {
|
||||
u64 upper = words[address >> 2 & 2 | 0];
|
||||
u64 lower = words[address >> 2 & 2 | 1];
|
||||
u64 upper = words[paddr >> 2 & 2 | 0];
|
||||
u64 lower = words[paddr >> 2 & 2 | 1];
|
||||
return upper << 32 | lower << 0;
|
||||
}
|
||||
}
|
||||
|
||||
template<u32 Size>
|
||||
auto CPU::DataCache::Line::write(u32 address, u64 data) -> void {
|
||||
if constexpr(Size == Byte) { bytes[address >> 0 & 15 ^ 3] = data; }
|
||||
if constexpr(Size == Half) { halfs[address >> 1 & 7 ^ 1] = data; }
|
||||
if constexpr(Size == Word) { words[address >> 2 & 3 ^ 0] = data; }
|
||||
auto CPU::DataCache::Line::write(u32 paddr, u64 data) -> void {
|
||||
if constexpr(Size == Byte) { bytes[paddr >> 0 & 15 ^ 3] = data; }
|
||||
if constexpr(Size == Half) { halfs[paddr >> 1 & 7 ^ 1] = data; }
|
||||
if constexpr(Size == Word) { words[paddr >> 2 & 3 ^ 0] = data; }
|
||||
if constexpr(Size == Dual) {
|
||||
words[address >> 2 & 2 | 0] = data >> 32;
|
||||
words[address >> 2 & 2 | 1] = data >> 0;
|
||||
words[paddr >> 2 & 2 | 0] = data >> 32;
|
||||
words[paddr >> 2 & 2 | 1] = data >> 0;
|
||||
}
|
||||
dirty |= ((1 << Size) - 1) << (address & 0xF);
|
||||
dirty |= ((1 << Size) - 1) << (paddr & 0xF);
|
||||
dirtyPc = cpu.ipu.pc;
|
||||
}
|
||||
|
||||
template<u32 Size>
|
||||
auto CPU::DataCache::read(u32 vaddr, u32 address) -> u64 {
|
||||
auto CPU::DataCache::read(u64 vaddr, u32 paddr) -> u64 {
|
||||
auto& line = this->line(vaddr);
|
||||
if(!line.hit(address)) {
|
||||
if(!line.hit(paddr)) {
|
||||
if(line.valid && line.dirty) line.writeBack();
|
||||
line.fill(address);
|
||||
line.fill(paddr);
|
||||
} else {
|
||||
cpu.step(1 * 2);
|
||||
}
|
||||
return line.read<Size>(address);
|
||||
return line.read<Size>(paddr);
|
||||
}
|
||||
|
||||
auto CPU::DataCache::readDebug(u32 vaddr, u32 address) -> u8 {
|
||||
auto CPU::DataCache::readDebug(u64 vaddr, u32 paddr) -> u8 {
|
||||
auto& line = this->line(vaddr);
|
||||
if(!line.hit(address)) {
|
||||
if(!line.hit(paddr)) {
|
||||
Thread dummyThread{};
|
||||
return bus.read<Byte>(address, dummyThread, "Ares Debugger");
|
||||
return bus.read<Byte>(paddr, dummyThread, "Ares Debugger");
|
||||
}
|
||||
return line.read<Byte>(address);
|
||||
return line.read<Byte>(paddr);
|
||||
}
|
||||
|
||||
template<u32 Size>
|
||||
auto CPU::DataCache::write(u32 vaddr, u32 address, u64 data) -> void {
|
||||
auto CPU::DataCache::write(u64 vaddr, u32 paddr, u64 data) -> void {
|
||||
auto& line = this->line(vaddr);
|
||||
if(!line.hit(address)) {
|
||||
if(!line.hit(paddr)) {
|
||||
if(line.valid && line.dirty) line.writeBack();
|
||||
line.fill(address);
|
||||
line.fill(paddr);
|
||||
} else {
|
||||
cpu.step(1 * 2);
|
||||
}
|
||||
line.write<Size>(address, data);
|
||||
line.write<Size>(paddr, data);
|
||||
}
|
||||
|
||||
auto CPU::DataCache::writeDebug(u64 vaddr, u32 paddr, u8 data) -> void {
|
||||
auto& line = this->line(vaddr);
|
||||
if(!line.hit(paddr)) {
|
||||
Thread dummyThread{};
|
||||
return bus.write<Byte>(paddr, data, dummyThread, "Ares Debugger");
|
||||
}
|
||||
line.write<Byte>(paddr, data);
|
||||
}
|
||||
|
||||
auto CPU::DataCache::power(bool reset) -> void {
|
||||
@@ -91,4 +100,4 @@ auto CPU::DataCache::power(bool reset) -> void {
|
||||
}
|
||||
|
||||
template
|
||||
auto CPU::DataCache::Line::write<Byte>(u32 address, u64 data) -> void;
|
||||
auto CPU::DataCache::Line::write<Byte>(u32 paddr, u64 data) -> void;
|
||||
@@ -1,10 +1,6 @@
|
||||
auto CPU::Exception::trigger(u32 code, u32 coprocessor, bool tlbMiss) -> void {
|
||||
self.debugger.exception(code);
|
||||
|
||||
if(code != 0) {
|
||||
auto sig = (code == 2 || code == 3) ? GDB::Signal::SEGV : GDB::Signal::TRAP;
|
||||
GDB::server.reportSignal(sig, self.ipu.pc);
|
||||
}
|
||||
reportGDBException(code, self.ipu.pc);
|
||||
|
||||
u64 vectorBase = !self.scc.status.vectorLocation ? (s32)0x8000'0000 : (s32)0xbfc0'0200;
|
||||
|
||||
@@ -64,3 +60,24 @@ auto CPU::Exception::nmi() -> void {
|
||||
self.scc.epcError = self.ipu.pc;
|
||||
self.pipeline.setPc(0xffff'ffff'bfc0'0000);
|
||||
}
|
||||
|
||||
auto CPU::Exception::reportGDBException(int code, u64 pc) -> void {
|
||||
switch(code) {
|
||||
case 0: GDB::server.reportSignal(GDB::Signal::WINCH, pc); break; // ignored by default
|
||||
case 1: GDB::server.reportSignal(GDB::Signal::SEGV, pc); break;
|
||||
case 2: GDB::server.reportSignal(GDB::Signal::SEGV, pc); break;
|
||||
case 3: GDB::server.reportSignal(GDB::Signal::SEGV, pc); break;
|
||||
case 4: GDB::server.reportSignal(GDB::Signal::BUS, pc); break;
|
||||
case 5: GDB::server.reportSignal(GDB::Signal::BUS, pc); break;
|
||||
case 6: GDB::server.reportSignal(GDB::Signal::ABORT, pc); break;
|
||||
case 7: GDB::server.reportSignal(GDB::Signal::ABORT, pc); break;
|
||||
case 8: GDB::server.reportSignal(GDB::Signal::SYS, pc); break;
|
||||
case 9: GDB::server.reportSignal(GDB::Signal::STOP, pc); break;
|
||||
case 10: GDB::server.reportSignal(GDB::Signal::ILL, pc); break;
|
||||
case 11: GDB::server.reportSignal(GDB::Signal::URG, pc); break; // ignored by default
|
||||
case 12: GDB::server.reportSignal(GDB::Signal::FPE, pc); break;
|
||||
case 13: GDB::server.reportSignal(GDB::Signal::TRAP, pc); break;
|
||||
case 15: GDB::server.reportSignal(GDB::Signal::FPE, pc); break;
|
||||
case 23: GDB::server.reportSignal(GDB::Signal::LOST, pc); break;
|
||||
}
|
||||
}
|
||||
@@ -250,7 +250,7 @@ auto CPU::checkFPUExceptions() -> bool {
|
||||
|
||||
#define CHECK_FPE_IMPL(type, res, operation, convert) \
|
||||
fenv.clearExcept(); \
|
||||
volatile type v##res = [&]() noinline -> type { return operation; }(); \
|
||||
volatile type v##res = [&]() NALL_NOINLINE -> type { return operation; }(); \
|
||||
if (checkFPUExceptions<convert>()) return; \
|
||||
type res = v##res;
|
||||
|
||||
|
||||
@@ -118,27 +118,29 @@ auto CPU::BREAK() -> void {
|
||||
}
|
||||
|
||||
auto CPU::CACHE(u8 operation, cr64& rs, s16 imm) -> void {
|
||||
u32 address;
|
||||
if (auto phys = devirtualize(rs.u64 + imm)) address = *phys;
|
||||
else return;
|
||||
auto access = devirtualize<Read, Word>(rs.u64 + imm);
|
||||
if (!access) return;
|
||||
if(!access.cache) {
|
||||
debug(unusual, "[CPU] CACHE access to non-cacheable address ", hex(access.vaddr, 8L), " at PC ", hex(PC, 16L));
|
||||
}
|
||||
|
||||
switch(operation) {
|
||||
|
||||
case 0x00: { //icache index invalidate
|
||||
auto& line = icache.line(address);
|
||||
auto& line = icache.line(access.vaddr);
|
||||
line.valid = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x04: { //icache load tag
|
||||
auto& line = icache.line(address);
|
||||
auto& line = icache.line(access.vaddr);
|
||||
scc.tagLo.primaryCacheState = line.valid << 1;
|
||||
scc.tagLo.physicalAddress = line.tag;
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x08: { //icache store tag
|
||||
auto& line = icache.line(address);
|
||||
auto& line = icache.line(access.vaddr);
|
||||
line.valid = scc.tagLo.primaryCacheState.bit(1);
|
||||
line.tag = scc.tagLo.physicalAddress;
|
||||
if(scc.tagLo.primaryCacheState == 0b01) debug(unusual, "[CPU] CACHE CPCS=1");
|
||||
@@ -147,39 +149,39 @@ auto CPU::CACHE(u8 operation, cr64& rs, s16 imm) -> void {
|
||||
}
|
||||
|
||||
case 0x10: { //icache hit invalidate
|
||||
auto& line = icache.line(address);
|
||||
if(line.hit(address)) line.valid = 0;
|
||||
auto& line = icache.line(access.vaddr);
|
||||
if(line.hit(access.paddr)) line.valid = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x14: { //icache fill
|
||||
auto& line = icache.line(address);
|
||||
line.fill(address, cpu);
|
||||
auto& line = icache.line(access.vaddr);
|
||||
line.fill(access.paddr, cpu);
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x18: { //icache hit write back
|
||||
auto& line = icache.line(address);
|
||||
if(line.hit(address)) line.writeBack(cpu);
|
||||
auto& line = icache.line(access.vaddr);
|
||||
if(line.hit(access.paddr)) line.writeBack(cpu);
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x01: { //dcache index write back invalidate
|
||||
auto& line = dcache.line(address);
|
||||
auto& line = dcache.line(access.vaddr);
|
||||
if(line.valid && line.dirty) line.writeBack();
|
||||
line.valid = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x05: { //dcache index load tag
|
||||
auto& line = dcache.line(address);
|
||||
auto& line = dcache.line(access.vaddr);
|
||||
scc.tagLo.primaryCacheState = line.valid << 1 | line.dirty << 0;
|
||||
scc.tagLo.physicalAddress = line.tag;
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x09: { //dcache index store tag
|
||||
auto& line = dcache.line(address);
|
||||
auto& line = dcache.line(access.vaddr);
|
||||
line.valid = scc.tagLo.primaryCacheState.bit(1);
|
||||
line.dirty = scc.tagLo.primaryCacheState.bit(0);
|
||||
line.tag = scc.tagLo.physicalAddress;
|
||||
@@ -189,17 +191,17 @@ auto CPU::CACHE(u8 operation, cr64& rs, s16 imm) -> void {
|
||||
}
|
||||
|
||||
case 0x0d: { //dcache create dirty exclusive
|
||||
auto& line = dcache.line(address);
|
||||
if(!line.hit(address) && line.dirty) line.writeBack();
|
||||
line.tag = address & ~0xfff;
|
||||
auto& line = dcache.line(access.vaddr);
|
||||
if(!line.hit(access.paddr) && line.dirty) line.writeBack();
|
||||
line.tag = access.paddr & ~0xfff;
|
||||
line.valid = 1;
|
||||
line.dirty = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x11: { //dcache hit invalidate
|
||||
auto& line = dcache.line(address);
|
||||
if(line.hit(address)) {
|
||||
auto& line = dcache.line(access.vaddr);
|
||||
if(line.hit(access.paddr)) {
|
||||
line.valid = 0;
|
||||
line.dirty = 0;
|
||||
}
|
||||
@@ -207,8 +209,8 @@ auto CPU::CACHE(u8 operation, cr64& rs, s16 imm) -> void {
|
||||
}
|
||||
|
||||
case 0x15: { //dcache hit write back invalidate
|
||||
auto& line = dcache.line(address);
|
||||
if(line.hit(address)) {
|
||||
auto& line = dcache.line(access.vaddr);
|
||||
if(line.hit(access.paddr)) {
|
||||
if(line.dirty) line.writeBack();
|
||||
line.valid = 0;
|
||||
}
|
||||
@@ -216,8 +218,8 @@ auto CPU::CACHE(u8 operation, cr64& rs, s16 imm) -> void {
|
||||
}
|
||||
|
||||
case 0x19: { //dcache hit write back
|
||||
auto& line = dcache.line(address);
|
||||
if(line.hit(address)) {
|
||||
auto& line = dcache.line(access.vaddr);
|
||||
if(line.hit(access.paddr)) {
|
||||
if(line.dirty) line.writeBack();
|
||||
}
|
||||
break;
|
||||
@@ -610,10 +612,10 @@ auto CPU::LHU(r64& rt, cr64& rs, s16 imm) -> void {
|
||||
}
|
||||
|
||||
auto CPU::LL(r64& rt, cr64& rs, s16 imm) -> void {
|
||||
if(auto address = devirtualize(rs.u64 + imm)) {
|
||||
if (auto data = read<Word>(rs.u64 + imm)) {
|
||||
if(auto access = devirtualize<Read, Word>(rs.u64 + imm)) {
|
||||
if (auto data = read<Word>(access.vaddr)) {
|
||||
rt.u64 = s32(*data);
|
||||
scc.ll = *address >> 4;
|
||||
scc.ll = access.paddr >> 4;
|
||||
scc.llbit = 1;
|
||||
}
|
||||
}
|
||||
@@ -621,10 +623,10 @@ auto CPU::LL(r64& rt, cr64& rs, s16 imm) -> void {
|
||||
|
||||
auto CPU::LLD(r64& rt, cr64& rs, s16 imm) -> void {
|
||||
if(!context.kernelMode() && context.bits == 32) return exception.reservedInstruction();
|
||||
if(auto address = devirtualize(rs.u64 + imm)) {
|
||||
if (auto data = read<Dual>(rs.u64 + imm)) {
|
||||
if(auto access = devirtualize<Read, Word>(rs.u64 + imm)) {
|
||||
if (auto data = read<Dual>(access.vaddr)) {
|
||||
rt.u64 = *data;
|
||||
scc.ll = *address >> 4;
|
||||
scc.ll = access.paddr >> 4;
|
||||
scc.llbit = 1;
|
||||
}
|
||||
}
|
||||
|
||||
+56
-156
@@ -88,60 +88,42 @@ auto CPU::segment(u64 vaddr) -> Context::Segment {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto CPU::devirtualize(u64 vaddr) -> maybe<u64> {
|
||||
if(vaddrAlignedError<Word>(vaddr, false)) return nothing;
|
||||
template <u32 Dir, u32 Size>
|
||||
auto CPU::devirtualize(u64 vaddr, bool raiseAlignedError, bool raiseExceptions) -> PhysAccess {
|
||||
if (raiseAlignedError && vaddrAlignedError<Size>(vaddr, Dir == Write)) {
|
||||
return PhysAccess{false};
|
||||
}
|
||||
//fast path for RDRAM, which is by far the most accessed memory region
|
||||
if (vaddr >= 0xffff'ffff'8000'0000ull && vaddr <= 0xffff'ffff'83ef'ffffull) {
|
||||
if constexpr(Dir == Read) return PhysAccess{true, true, (u32)vaddr & 0x3eff'ffff, vaddr};
|
||||
if constexpr(Dir == Write) return PhysAccess{true, true, (u32)vaddr & 0x3eff'ffff, vaddr};
|
||||
}
|
||||
switch(segment(vaddr)) {
|
||||
case Context::Segment::Unused:
|
||||
addressException(vaddr);
|
||||
exception.addressLoad();
|
||||
return nothing;
|
||||
if(raiseExceptions) {
|
||||
addressException(vaddr);
|
||||
if constexpr(Dir == Read) exception.addressLoad();
|
||||
if constexpr(Dir == Write) exception.addressStore();
|
||||
}
|
||||
return PhysAccess{false};
|
||||
case Context::Segment::Mapped:
|
||||
if(auto match = tlb.load(vaddr)) return match.address & context.physMask;
|
||||
addressException(vaddr);
|
||||
return nothing;
|
||||
if constexpr(Dir == Read) if(auto access = tlb.load (vaddr, !raiseExceptions)) return access;
|
||||
if constexpr(Dir == Write) if(auto access = tlb.store(vaddr, !raiseExceptions)) return access;
|
||||
return PhysAccess{false};
|
||||
case Context::Segment::Cached:
|
||||
return PhysAccess{true, true, (u32)(vaddr & 0x1fff'ffff), vaddr};
|
||||
case Context::Segment::Direct:
|
||||
return vaddr & 0x1fff'ffff;
|
||||
return PhysAccess{true, false, (u32)(vaddr & 0x1fff'ffff), vaddr};
|
||||
case Context::Segment::Cached32:
|
||||
return PhysAccess{true, true, (u32)(vaddr & 0xffff'ffff), vaddr};
|
||||
case Context::Segment::Direct32:
|
||||
return vaddr & 0xffff'ffff;
|
||||
return PhysAccess{true, false, (u32)(vaddr & 0xffff'ffff), vaddr};
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
|
||||
// Fast(er) version of devirtualize for icache lookups
|
||||
// avoids handling unmapped regions/exceptions as these should have already
|
||||
// been handled by instruction fetch, also ignores tlb match failure
|
||||
auto CPU::devirtualizeFast(u64 vaddr) -> u64 {
|
||||
// Assume address space is mapped into pages that are 4kb in size
|
||||
// If we have a cached physical address for this page, use it
|
||||
// This cache is purged on any writes to the TLB so should never become stale
|
||||
auto vbase = vaddr >> 12;
|
||||
if(devirtualizeCache.vbase == vbase && devirtualizeCache.pbase) {
|
||||
auto offset = vaddr & 0xfff;
|
||||
return (devirtualizeCache.pbase & ~0xfff) + offset;
|
||||
}
|
||||
|
||||
// Cache the physical address of this page for the next call
|
||||
devirtualizeCache.vbase = vaddr >> 12;
|
||||
|
||||
switch(segment(vaddr)) {
|
||||
case Context::Segment::Mapped: {
|
||||
auto match = tlb.loadFast(vaddr);
|
||||
return devirtualizeCache.pbase = match.address & context.physMask;
|
||||
}
|
||||
case Context::Segment::Cached:
|
||||
case Context::Segment::Direct:
|
||||
return devirtualizeCache.pbase = vaddr & 0x1fff'ffff;
|
||||
case Context::Segment::Cached32:
|
||||
case Context::Segment::Direct32:
|
||||
return devirtualizeCache.pbase = vaddr & 0xffff'ffff;
|
||||
}
|
||||
return devirtualizeCache.pbase = 0;
|
||||
}
|
||||
|
||||
auto CPU::devirtualizeDebug(u64 vaddr) -> u64 {
|
||||
return devirtualizeFast(vaddr); // this wrapper preserves the inlining of 'devirtualizeFast'
|
||||
return devirtualize<Read, Byte>(vaddr, false).paddr; // this wrapper preserves the inlining of 'devirtualizeFast'
|
||||
}
|
||||
|
||||
template<u32 Size>
|
||||
@@ -164,132 +146,45 @@ inline auto CPU::busReadBurst(u32 address, u32 *data) -> void {
|
||||
return bus.readBurst<Size>(address, data, *this);
|
||||
}
|
||||
|
||||
auto CPU::fetch(u64 vaddr) -> maybe<u32> {
|
||||
if(vaddrAlignedError<Word>(vaddr, false)) return nothing;
|
||||
switch(segment(vaddr)) {
|
||||
case Context::Segment::Unused:
|
||||
step(1 * 2);
|
||||
addressException(vaddr);
|
||||
exception.addressLoad();
|
||||
return nothing;
|
||||
case Context::Segment::Mapped:
|
||||
if(auto match = tlb.load(vaddr)) {
|
||||
if(match.cache) return icache.fetch(vaddr, match.address & context.physMask, cpu);
|
||||
step(1 * 2);
|
||||
return busRead<Word>(match.address & context.physMask);
|
||||
}
|
||||
step(1 * 2);
|
||||
addressException(vaddr);
|
||||
return nothing;
|
||||
case Context::Segment::Cached:
|
||||
return icache.fetch(vaddr, vaddr & 0x1fff'ffff, cpu);
|
||||
case Context::Segment::Cached32:
|
||||
return icache.fetch(vaddr, vaddr & 0xffff'ffff, cpu);
|
||||
case Context::Segment::Direct:
|
||||
step(1 * 2);
|
||||
return busRead<Word>(vaddr & 0x1fff'ffff);
|
||||
case Context::Segment::Direct32:
|
||||
step(1 * 2);
|
||||
return busRead<Word>(vaddr & 0xffff'ffff);
|
||||
}
|
||||
|
||||
unreachable;
|
||||
auto CPU::fetch(PhysAccess access) -> maybe<u32> {
|
||||
step(1 * 2);
|
||||
if(!access) return nothing;
|
||||
if(access.cache) return icache.fetch(access.vaddr, access.paddr, cpu);
|
||||
return busRead<Word>(access.paddr);
|
||||
}
|
||||
|
||||
template<u32 Size>
|
||||
auto CPU::read(u64 vaddr) -> maybe<u64> {
|
||||
if(vaddrAlignedError<Size>(vaddr, false)) return nothing;
|
||||
GDB::server.reportMemRead(vaddr, Size);
|
||||
|
||||
switch(segment(vaddr)) {
|
||||
case Context::Segment::Unused:
|
||||
step(1 * 2);
|
||||
addressException(vaddr);
|
||||
exception.addressLoad();
|
||||
return nothing;
|
||||
case Context::Segment::Mapped:
|
||||
if(auto match = tlb.load(vaddr)) {
|
||||
if(match.cache) return dcache.read<Size>(vaddr, match.address & context.physMask);
|
||||
step(1 * 2);
|
||||
return busRead<Size>(match.address & context.physMask);
|
||||
}
|
||||
step(1 * 2);
|
||||
addressException(vaddr);
|
||||
return nothing;
|
||||
case Context::Segment::Cached:
|
||||
return dcache.read<Size>(vaddr, vaddr & 0x1fff'ffff);
|
||||
case Context::Segment::Cached32:
|
||||
return dcache.read<Size>(vaddr, vaddr & 0xffff'ffff);
|
||||
case Context::Segment::Direct:
|
||||
step(1 * 2);
|
||||
return busRead<Size>(vaddr & 0x1fff'ffff);
|
||||
case Context::Segment::Direct32:
|
||||
step(1 * 2);
|
||||
return busRead<Size>(vaddr & 0xffff'ffff);
|
||||
}
|
||||
|
||||
unreachable;
|
||||
auto CPU::read(PhysAccess access) -> maybe<u64> {
|
||||
if(!access) return nothing;
|
||||
GDB::server.reportMemRead(access.vaddr, Size);
|
||||
if(access.cache) return dcache.read<Size>(access.vaddr, access.paddr);
|
||||
return busRead<Size>(access.paddr);
|
||||
}
|
||||
|
||||
auto CPU::readDebug(u64 vaddr) -> u8 {
|
||||
Thread dummyThread{};
|
||||
auto access = devirtualize<Read, Byte>(vaddr, false, false);
|
||||
if(!access) return 0;
|
||||
if(access.cache) return dcache.readDebug(access.vaddr, access.paddr);
|
||||
return bus.read<Byte>(access.paddr, dummyThread, "Ares Debugger");
|
||||
}
|
||||
|
||||
switch(segment(vaddr)) {
|
||||
case Context::Segment::Unused: return 0;
|
||||
case Context::Segment::Mapped:
|
||||
if(auto match = tlb.load(vaddr, true)) {
|
||||
if(match.cache) return dcache.readDebug(vaddr, match.address & context.physMask);
|
||||
return bus.read<Byte>(match.address & context.physMask, dummyThread, "Ares Debugger");
|
||||
}
|
||||
return 0;
|
||||
case Context::Segment::Cached:
|
||||
return dcache.readDebug(vaddr, vaddr & 0x1fff'ffff);
|
||||
case Context::Segment::Cached32:
|
||||
return dcache.readDebug(vaddr, vaddr & 0xffff'ffff);
|
||||
case Context::Segment::Direct:
|
||||
return bus.read<Byte>(vaddr & 0x1fff'ffff, dummyThread, "Ares Debugger");
|
||||
case Context::Segment::Direct32:
|
||||
return bus.read<Byte>(vaddr & 0xffff'ffff, dummyThread, "Ares Debugger");
|
||||
}
|
||||
|
||||
unreachable;
|
||||
template<u32 Size>
|
||||
auto CPU::write(PhysAccess access, u64 data) -> bool {
|
||||
if(!access) return false;
|
||||
GDB::server.reportMemWrite(access.vaddr, Size);
|
||||
if(access.cache) return dcache.write<Size>(access.vaddr, access.paddr, data), true;
|
||||
return busWrite<Size>(access.paddr, data), true;
|
||||
}
|
||||
|
||||
template<u32 Size>
|
||||
auto CPU::write(u64 vaddr0, u64 data, bool alignedError) -> bool {
|
||||
if(alignedError && vaddrAlignedError<Size>(vaddr0, true)) return false;
|
||||
u64 vaddr = vaddr0 & ~((u64)Size - 1);
|
||||
|
||||
GDB::server.reportMemWrite(vaddr0, Size);
|
||||
|
||||
switch(segment(vaddr)) {
|
||||
case Context::Segment::Unused:
|
||||
step(1 * 2);
|
||||
addressException(vaddr0);
|
||||
exception.addressStore();
|
||||
return false;
|
||||
case Context::Segment::Mapped:
|
||||
if(auto match = tlb.store(vaddr)) {
|
||||
if(match.cache) return dcache.write<Size>(vaddr, match.address & context.physMask, data), true;
|
||||
step(1 * 2);
|
||||
return busWrite<Size>(match.address & context.physMask, data), true;
|
||||
}
|
||||
step(1 * 2);
|
||||
addressException(vaddr0);
|
||||
return false;
|
||||
case Context::Segment::Cached:
|
||||
return dcache.write<Size>(vaddr, vaddr & 0x1fff'ffff, data), true;
|
||||
case Context::Segment::Cached32:
|
||||
return dcache.write<Size>(vaddr, vaddr & 0xffff'ffff, data), true;
|
||||
case Context::Segment::Direct:
|
||||
step(1 * 2);
|
||||
return busWrite<Size>(vaddr & 0x1fff'ffff, data), true;
|
||||
case Context::Segment::Direct32:
|
||||
step(1 * 2);
|
||||
return busWrite<Size>(vaddr & 0xffff'ffff, data), true;
|
||||
}
|
||||
|
||||
unreachable;
|
||||
auto CPU::writeDebug(u64 vaddr, u64 data) -> bool {
|
||||
auto access = devirtualize<Write, Size>(vaddr, false, false);
|
||||
if(!access) return false;
|
||||
GDB::server.reportMemWrite(access.vaddr, Size);
|
||||
if(access.cache) return dcache.write<Size>(access.vaddr, access.paddr, data), true;
|
||||
return busWrite<Size>(access.paddr, data), true;
|
||||
}
|
||||
|
||||
template<u32 Size>
|
||||
@@ -321,3 +216,8 @@ auto CPU::addressException(u64 vaddr) -> void {
|
||||
scc.xcontext.badVirtualAddress = vaddr >> 13;
|
||||
scc.xcontext.region = vaddr >> 62;
|
||||
}
|
||||
|
||||
template auto CPU::writeDebug<Byte>(u64, u64) -> bool;
|
||||
template auto CPU::writeDebug<Half>(u64, u64) -> bool;
|
||||
template auto CPU::writeDebug<Word>(u64, u64) -> bool;
|
||||
template auto CPU::writeDebug<Dual>(u64, u64) -> bool;
|
||||
+34
-15
@@ -9,36 +9,39 @@ auto CPU::Recompiler::pool(u32 address) -> Pool* {
|
||||
return pool;
|
||||
}
|
||||
|
||||
auto CPU::Recompiler::block(u32 vaddr, u32 address, bool singleInstruction) -> Block* {
|
||||
auto CPU::Recompiler::block(u64 vaddr, u32 address, bool singleInstruction) -> Block* {
|
||||
if(auto block = pool(address)->blocks[address >> 2 & 0x3f]) return block;
|
||||
auto block = emit(vaddr, address, singleInstruction);
|
||||
pool(address)->blocks[address >> 2 & 0x3f] = block;
|
||||
memory::jitprotect(true);
|
||||
if(block) {
|
||||
pool(address)->blocks[address >> 2 & 0x3f] = block;
|
||||
memory::jitprotect(true);
|
||||
}
|
||||
return block;
|
||||
}
|
||||
|
||||
auto CPU::Recompiler::fastFetchBlock(u32 address) -> Block* {
|
||||
auto& pool = pools[address >> 8 & 0x1fffff];
|
||||
if(pool) return pool->blocks[address >> 2 & 0x3f];
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#define IpuBase offsetof(IPU, r[16])
|
||||
#define IpuReg(r) sreg(1), offsetof(IPU, r) - IpuBase
|
||||
#define PipelineReg(x) mem(sreg(0), offsetof(CPU, pipeline) + offsetof(Pipeline, x))
|
||||
|
||||
auto CPU::Recompiler::emit(u32 vaddr, u32 address, bool singleInstruction) -> Block* {
|
||||
auto CPU::Recompiler::emit(u64 vaddr, u32 address, bool singleInstruction) -> Block* {
|
||||
if(unlikely(allocator.available() < 1_MiB)) {
|
||||
print("CPU allocator flush\n");
|
||||
allocator.release();
|
||||
reset();
|
||||
}
|
||||
|
||||
auto block = (Block*)allocator.acquire(sizeof(Block));
|
||||
// abort compilation of block asap if the instruction cache is not coherent
|
||||
if(!self.icache.coherent(vaddr, address))
|
||||
return nullptr;
|
||||
|
||||
bool abort = false;
|
||||
beginFunction(3);
|
||||
|
||||
Thread thread;
|
||||
bool hasBranched = 0;
|
||||
int numInsn = 0;
|
||||
constexpr u32 branchToSelf = 0x1000'ffff; //beq 0,0,<pc>
|
||||
u32 jumpToSelf = 2 << 26 | vaddr >> 2 & 0x3ff'ffff; //j <pc>
|
||||
while(true) {
|
||||
u32 instruction = bus.read<Word>(address, thread, "Ares Recompiler");
|
||||
mov32(PipelineReg(nstate), imm(0));
|
||||
@@ -46,30 +49,46 @@ auto CPU::Recompiler::emit(u32 vaddr, u32 address, bool singleInstruction) -> Bl
|
||||
mov64(PipelineReg(pc), reg(0));
|
||||
add64(PipelineReg(nextpc), reg(0), imm(4));
|
||||
if(callInstructionPrologue) {
|
||||
mov32(reg(1), imm(instruction));
|
||||
mov64(reg(1), imm(vaddr));
|
||||
mov32(reg(2), imm(instruction));
|
||||
call(&CPU::instructionPrologue);
|
||||
}
|
||||
if(numInsn == 0 || (vaddr&0x1f)==0){
|
||||
//abort compilation of block if the instruction cache is not coherent
|
||||
if(!self.icache.coherent(vaddr, address)) {
|
||||
resetCompiler();
|
||||
return nullptr;
|
||||
}
|
||||
mov64(reg(1), imm(vaddr));
|
||||
mov32(reg(2), imm(address));
|
||||
call(&CPU::jitFetch);
|
||||
}
|
||||
numInsn++;
|
||||
bool branched = emitEXECUTE(instruction);
|
||||
if(unlikely(instruction == 0x1000'ffff //beq 0,0,<pc>
|
||||
|| instruction == (2 << 26 | vaddr >> 2 & 0x3ff'ffff))) { //j <pc>
|
||||
if(unlikely(instruction == branchToSelf || instruction == jumpToSelf)) {
|
||||
//accelerate idle loops
|
||||
mov32(reg(1), imm(64 * 2));
|
||||
call(&CPU::step);
|
||||
} else {
|
||||
mov32(reg(1), imm(1 * 2));
|
||||
call(&CPU::step);
|
||||
}
|
||||
call(&CPU::instructionEpilogue<1>);
|
||||
test32(PipelineReg(state), imm(Pipeline::EndBlock), set_z);
|
||||
mov32(PipelineReg(state), PipelineReg(nstate));
|
||||
mov64(mem(IpuReg(pc)), PipelineReg(pc));
|
||||
|
||||
vaddr += 4;
|
||||
address += 4;
|
||||
jumpToSelf += 4;
|
||||
if(hasBranched || (address & 0xfc) == 0 || singleInstruction) break; //block boundary
|
||||
hasBranched = branched;
|
||||
jumpEpilog(flag_nz);
|
||||
}
|
||||
|
||||
jumpEpilog();
|
||||
|
||||
memory::jitprotect(false);
|
||||
auto block = (Block*)allocator.acquire(sizeof(Block));
|
||||
block->code = endFunction();
|
||||
|
||||
//print(hex(PC, 8L), " ", instructions, " ", size(), "\n");
|
||||
|
||||
+26
-36
@@ -1,23 +1,23 @@
|
||||
|
||||
auto CPU::TLB::load(u64 vaddr, const Entry& entry, bool noExceptions) -> maybe<Match> {
|
||||
auto CPU::TLB::load(u64 vaddr, const Entry& entry, bool noExceptions) -> maybe<PhysAccess> {
|
||||
if(!entry.globals && entry.addressSpaceID != self.scc.tlb.addressSpaceID) return nothing;
|
||||
if((vaddr & entry.addressMaskHi) != entry.virtualAddress) return nothing;
|
||||
if(vaddr >> 62 != entry.region) return nothing;
|
||||
bool lo = vaddr & entry.addressSelect;
|
||||
if(!entry.valid[lo]) {
|
||||
if(noExceptions)return Match{false};
|
||||
if(noExceptions)return PhysAccess{false};
|
||||
|
||||
self.addressException(vaddr);
|
||||
self.debugger.tlbLoadInvalid(vaddr);
|
||||
self.exception.tlbLoadInvalid();
|
||||
return Match{false};
|
||||
return PhysAccess{false};
|
||||
}
|
||||
physicalAddress = entry.physicalAddress[lo] + (vaddr & entry.addressMaskLo);
|
||||
self.debugger.tlbLoad(vaddr, physicalAddress);
|
||||
return Match{true, entry.cacheAlgorithm[lo] != 2, physicalAddress};
|
||||
return PhysAccess{true, entry.cacheAlgorithm[lo] != 2, physicalAddress, vaddr};
|
||||
}
|
||||
|
||||
auto CPU::TLB::load(u64 vaddr, bool noExceptions) -> Match {
|
||||
auto CPU::TLB::load(u64 vaddr, bool noExceptions) -> PhysAccess {
|
||||
for(auto& entry : this->tlbCache.entry) {
|
||||
if(!entry.entry) continue;
|
||||
if(auto match = load(vaddr, *entry.entry, noExceptions)) {
|
||||
@@ -41,45 +41,33 @@ auto CPU::TLB::load(u64 vaddr, bool noExceptions) -> Match {
|
||||
return {false};
|
||||
}
|
||||
|
||||
// Fast(er) version of load for recompiler icache lookups
|
||||
// avoids exceptions/debug checks
|
||||
auto CPU::TLB::loadFast(u64 vaddr) -> Match {
|
||||
for(auto& entry : this->entry) {
|
||||
if(!entry.globals && entry.addressSpaceID != self.scc.tlb.addressSpaceID) continue;
|
||||
if((vaddr & entry.addressMaskHi) != entry.virtualAddress) continue;
|
||||
if(vaddr >> 62 != entry.region) continue;
|
||||
bool lo = vaddr & entry.addressSelect;
|
||||
if(!entry.valid[lo]) return { false, 0, 0 };
|
||||
physicalAddress = entry.physicalAddress[lo] + (vaddr & entry.addressMaskLo);
|
||||
return {true, entry.cacheAlgorithm[lo] != 2, physicalAddress};
|
||||
}
|
||||
|
||||
return {false, 0, 0};
|
||||
}
|
||||
|
||||
auto CPU::TLB::store(u64 vaddr, const Entry& entry) -> maybe<Match> {
|
||||
auto CPU::TLB::store(u64 vaddr, const Entry& entry, bool noExceptions) -> maybe<PhysAccess> {
|
||||
if(!entry.globals && entry.addressSpaceID != self.scc.tlb.addressSpaceID) return nothing;
|
||||
if((vaddr & entry.addressMaskHi) != entry.virtualAddress) return nothing;
|
||||
if(vaddr >> 62 != entry.region) return nothing;
|
||||
bool lo = vaddr & entry.addressSelect;
|
||||
if(!entry.valid[lo]) {
|
||||
self.addressException(vaddr);
|
||||
self.debugger.tlbStoreInvalid(vaddr);
|
||||
self.exception.tlbStoreInvalid();
|
||||
return Match{false};
|
||||
if(!noExceptions) {
|
||||
self.addressException(vaddr);
|
||||
self.debugger.tlbStoreInvalid(vaddr);
|
||||
self.exception.tlbStoreInvalid();
|
||||
}
|
||||
return PhysAccess{false};
|
||||
}
|
||||
if(!entry.dirty[lo]) {
|
||||
self.addressException(vaddr);
|
||||
self.debugger.tlbModification(vaddr);
|
||||
self.exception.tlbModification();
|
||||
return Match{false};
|
||||
if(!noExceptions) {
|
||||
self.addressException(vaddr);
|
||||
self.debugger.tlbModification(vaddr);
|
||||
self.exception.tlbModification();
|
||||
}
|
||||
return PhysAccess{false};
|
||||
}
|
||||
physicalAddress = entry.physicalAddress[lo] + (vaddr & entry.addressMaskLo);
|
||||
self.debugger.tlbStore(vaddr, physicalAddress);
|
||||
return Match{true, entry.cacheAlgorithm[lo] != 2, physicalAddress};
|
||||
return PhysAccess{true, entry.cacheAlgorithm[lo] != 2, physicalAddress, vaddr};
|
||||
}
|
||||
|
||||
auto CPU::TLB::store(u64 vaddr) -> Match {
|
||||
auto CPU::TLB::store(u64 vaddr, bool noExceptions) -> PhysAccess {
|
||||
for(auto& entry : this->tlbCache.entry) {
|
||||
if(!entry.entry) continue;
|
||||
if(auto match = store(vaddr, *entry.entry)) {
|
||||
@@ -89,15 +77,17 @@ auto CPU::TLB::store(u64 vaddr) -> Match {
|
||||
}
|
||||
|
||||
for(auto& entry : this->entry) {
|
||||
if(auto match = store(vaddr, entry)) {
|
||||
if(auto match = store(vaddr, entry, noExceptions)) {
|
||||
this->tlbCache.insert(entry);
|
||||
return *match;
|
||||
}
|
||||
}
|
||||
|
||||
self.addressException(vaddr);
|
||||
self.debugger.tlbStoreMiss(vaddr);
|
||||
self.exception.tlbStoreMiss();
|
||||
if(!noExceptions) {
|
||||
self.addressException(vaddr);
|
||||
self.debugger.tlbStoreMiss(vaddr);
|
||||
self.exception.tlbStoreMiss();
|
||||
}
|
||||
return {false};
|
||||
}
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ auto System::initDebugHooks() -> void {
|
||||
};
|
||||
|
||||
GDB::server.hooks.read = [](u64 address, u32 byteCount) -> string {
|
||||
address |= 0xFFFFFFFF'00000000ull;
|
||||
address = (s32)address;
|
||||
|
||||
string res{};
|
||||
res.resize(byteCount * 2);
|
||||
@@ -152,16 +152,48 @@ auto System::initDebugHooks() -> void {
|
||||
return res;
|
||||
};
|
||||
|
||||
GDB::server.hooks.write = [](u64 address, u32 unitSize, u64 value) {
|
||||
address |= 0xFFFFFFFF'00000000ull;
|
||||
switch(unitSize) {
|
||||
case Byte: cpu.write<Byte>(address, value, false); break;
|
||||
case Half: cpu.write<Half>(address, value, false); break;
|
||||
case Word: cpu.write<Word>(address, value, false); break;
|
||||
case Dual: cpu.write<Dual>(address, value, false); break;
|
||||
GDB::server.hooks.write = [](u64 address, vector<u8> data) {
|
||||
address = (s32)address;
|
||||
|
||||
// Handle writes of different/unaligned sizes only within the RDRAM area,
|
||||
// where we are sure that the write size does not really matter
|
||||
if(address >= 0xffff'ffff'8000'0000ull && address <= 0xffff'ffff'83ef'ffffull) {
|
||||
for(auto b : data) {
|
||||
cpu.dcache.writeDebug(address, address & 0x1fff'ffff, b);
|
||||
address++;
|
||||
}
|
||||
} else if (address >= 0xffff'ffff'a000'0000ull && address <= 0xffff'ffff'a3ef'ffffull) {
|
||||
Thread dummyThread{};
|
||||
for(auto b : data) {
|
||||
bus.write<Byte>(address & 0x1fff'ffff, b, dummyThread, "Ares Debugger");
|
||||
address++;
|
||||
}
|
||||
} else {
|
||||
// Otherwise, the write is expected to be of a set size to an aligned address.
|
||||
u64 value;
|
||||
switch(data.size()) {
|
||||
case Byte:
|
||||
value = (u64)data[0];
|
||||
cpu.writeDebug<Byte>(address, value);
|
||||
break;
|
||||
case Half:
|
||||
value = ((u64)data[0]<<8) | ((u64)data[1]<<0);
|
||||
cpu.writeDebug<Half>(address, value);
|
||||
break;
|
||||
case Word:
|
||||
value = ((u64)data[0]<<24) | ((u64)data[1]<<16) | ((u64)data[2]<<8) | ((u64)data[3]<<0);
|
||||
cpu.writeDebug<Word>(address, value);
|
||||
break;
|
||||
case Dual:
|
||||
value = ((u64)data[0]<<56) | ((u64)data[1]<<48) | ((u64)data[2]<<40) | ((u64)data[3]<<32);
|
||||
value |= ((u64)data[4]<<24) | ((u64)data[5]<<16) | ((u64)data[6]<< 8) | ((u64)data[7]<< 0);
|
||||
cpu.writeDebug<Dual>(address, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
GDB::server.hooks.regRead = [](u32 regIdx) {
|
||||
if(regIdx < 32) {
|
||||
return hex(cpu.ipu.r[regIdx].u64, 16, '0');
|
||||
|
||||
@@ -1 +1 @@
|
||||
43bc31642cc70d04adb828a285e68cdbde7110a9
|
||||
1f69c762be68feb9fcd5276d75acc6e5a6160a19
|
||||
@@ -201,6 +201,11 @@ bool Renderer::init_caps()
|
||||
LOGW("Current proprietary Intel Windows driver is tested to perform much better without 8/16-bit integer support.\n");
|
||||
allow_small_types = false;
|
||||
}
|
||||
else if (features.driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY_KHR)
|
||||
{
|
||||
LOGW("Current proprietary Qcom driver is known to be buggy with 8/16-bit integer arithmetic, disabling support for time being.\n");
|
||||
allow_small_types = false;
|
||||
}
|
||||
|
||||
// Intel ANV *must* use small integer arithmetic, or it doesn't pass test suite.
|
||||
}
|
||||
@@ -716,7 +721,7 @@ static int normalize_dzpix(int dz)
|
||||
else if (dz == 0)
|
||||
return 1;
|
||||
|
||||
unsigned bit = 31 - leading_zeroes(dz);
|
||||
unsigned bit = 31 - Util::leading_zeroes(dz);
|
||||
return 1 << (bit + 1);
|
||||
}
|
||||
|
||||
@@ -1680,7 +1685,7 @@ void Renderer::submit_span_setup_jobs(Vulkan::CommandBuffer &cmd, bool upscale)
|
||||
cmd.set_buffer_view(1, 0, *instance.gpu.span_info_jobs_view);
|
||||
cmd.set_specialization_constant_mask(3);
|
||||
cmd.set_specialization_constant(0, (upscale ? caps.upscaling : 1) * ImplementationConstants::DefaultWorkgroupSize);
|
||||
cmd.set_specialization_constant(1, upscale ? trailing_zeroes(caps.upscaling) : 0u);
|
||||
cmd.set_specialization_constant(1, upscale ? Util::trailing_zeroes(caps.upscaling) : 0u);
|
||||
|
||||
Vulkan::QueryPoolHandle begin_ts, end_ts;
|
||||
if (caps.timestamp >= 2)
|
||||
@@ -1780,7 +1785,7 @@ void Renderer::submit_rasterization(Vulkan::CommandBuffer &cmd, Vulkan::Buffer &
|
||||
if (caps.timestamp >= 2)
|
||||
start_ts = cmd.write_timestamp(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
|
||||
|
||||
uint32_t scale_log2_bit = (upscaling ? trailing_zeroes(caps.upscaling) : 0u) << RASTERIZATION_UPSCALING_LOG2_BIT_OFFSET;
|
||||
uint32_t scale_log2_bit = (upscaling ? Util::trailing_zeroes(caps.upscaling) : 0u) << RASTERIZATION_UPSCALING_LOG2_BIT_OFFSET;
|
||||
|
||||
for (size_t i = 0; i < stream.static_raster_state_cache.size(); i++)
|
||||
{
|
||||
@@ -1891,7 +1896,7 @@ void Renderer::submit_tile_binning_combined(Vulkan::CommandBuffer &cmd, bool ups
|
||||
if (supports_subgroup_size_control(32, subgroup_size))
|
||||
{
|
||||
cmd.enable_subgroup_size_control(true);
|
||||
cmd.set_subgroup_size_log2(true, 5, trailing_zeroes(subgroup_size));
|
||||
cmd.set_subgroup_size_log2(true, 5, Util::trailing_zeroes(subgroup_size));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -2092,7 +2097,7 @@ void Renderer::submit_depth_blend(Vulkan::CommandBuffer &cmd, Vulkan::Buffer &tm
|
||||
cmd.set_specialization_constant(5, Limits::MaxPrimitives);
|
||||
cmd.set_specialization_constant(6, upscaled ? caps.max_width : Limits::MaxWidth);
|
||||
cmd.set_specialization_constant(7, uint32_t(force_write_mask || (!is_host_coherent && !upscaled)) |
|
||||
((upscaled ? trailing_zeroes(caps.upscaling) : 0u) << 1u));
|
||||
((upscaled ? Util::trailing_zeroes(caps.upscaling) : 0u) << 1u));
|
||||
|
||||
if (upscaled)
|
||||
cmd.set_storage_buffer(0, 0, *upscaling_multisampled_rdram);
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -484,7 +484,7 @@ Vulkan::ImageHandle VideoInterface::vram_fetch_stage(const Registers ®s, unsi
|
||||
async_cmd->set_specialization_constant_mask(7);
|
||||
async_cmd->set_specialization_constant(0, uint32_t(rdram_size));
|
||||
async_cmd->set_specialization_constant(1, regs.status & (VI_CONTROL_TYPE_MASK | VI_CONTROL_META_AA_BIT));
|
||||
async_cmd->set_specialization_constant(2, trailing_zeroes(scaling_factor));
|
||||
async_cmd->set_specialization_constant(2, Util::trailing_zeroes(scaling_factor));
|
||||
|
||||
async_cmd->push_constants(&push, 0, sizeof(push));
|
||||
async_cmd->dispatch((extract_width + 15) / 16,
|
||||
|
||||
@@ -40,14 +40,18 @@ struct AlignedAllocation
|
||||
static void *operator new(size_t size)
|
||||
{
|
||||
void *ret = ::Util::memalign_alloc(alignof(T), size);
|
||||
#ifdef __EXCEPTIONS
|
||||
if (!ret) throw std::bad_alloc();
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *operator new[](size_t size)
|
||||
{
|
||||
void *ret = ::Util::memalign_alloc(alignof(T), size);
|
||||
#ifdef __EXCEPTIONS
|
||||
if (!ret) throw std::bad_alloc();
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,13 +29,22 @@
|
||||
namespace Util
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
#define leading_zeroes(x) ((x) == 0 ? 32 : __builtin_clz(x))
|
||||
#define trailing_zeroes(x) ((x) == 0 ? 32 : __builtin_ctz(x))
|
||||
#define trailing_ones(x) __builtin_ctz(~uint32_t(x))
|
||||
#define leading_zeroes64(x) ((x) == 0 ? 64 : __builtin_clzll(x))
|
||||
#define trailing_zeroes64(x) ((x) == 0 ? 64 : __builtin_ctzll(x))
|
||||
#define trailing_ones64(x) __builtin_ctzll(~uint64_t(x))
|
||||
#define popcount32(x) __builtin_popcount(x)
|
||||
#define leading_zeroes_(x) ((x) == 0 ? 32 : __builtin_clz(x))
|
||||
#define trailing_zeroes_(x) ((x) == 0 ? 32 : __builtin_ctz(x))
|
||||
#define trailing_ones_(x) __builtin_ctz(~uint32_t(x))
|
||||
#define leading_zeroes64_(x) ((x) == 0 ? 64 : __builtin_clzll(x))
|
||||
#define trailing_zeroes64_(x) ((x) == 0 ? 64 : __builtin_ctzll(x))
|
||||
#define trailing_ones64_(x) __builtin_ctzll(~uint64_t(x))
|
||||
#define popcount32_(x) __builtin_popcount(x)
|
||||
|
||||
static inline uint32_t leading_zeroes(uint32_t x) { return leading_zeroes_(x); }
|
||||
static inline uint32_t trailing_zeroes(uint32_t x) { return trailing_zeroes_(x); }
|
||||
static inline uint32_t trailing_ones(uint32_t x) { return trailing_ones_(x); }
|
||||
static inline uint32_t leading_zeroes64(uint64_t x) { return leading_zeroes64_(x); }
|
||||
static inline uint32_t trailing_zeroes64(uint64_t x) { return trailing_zeroes64_(x); }
|
||||
static inline uint32_t trailing_ones64(uint64_t x) { return trailing_ones64_(x); }
|
||||
static inline uint32_t popcount32(uint32_t x) { return popcount32_(x); }
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
namespace Internal
|
||||
{
|
||||
@@ -81,13 +90,13 @@ static inline uint32_t ctz64(uint64_t x)
|
||||
}
|
||||
}
|
||||
|
||||
#define popcount32(x) ::Util::Internal::popcount32(x)
|
||||
#define leading_zeroes(x) ::Util::Internal::clz(x)
|
||||
#define trailing_zeroes(x) ::Util::Internal::ctz(x)
|
||||
#define trailing_ones(x) ::Util::Internal::ctz(~uint32_t(x))
|
||||
#define leading_zeroes64(x) ::Util::Internal::clz64(x)
|
||||
#define trailing_zeroes64(x) ::Util::Internal::ctz64(x)
|
||||
#define trailing_ones64(x) ::Util::Internal::ctz64(~uint64_t(x))
|
||||
static inline uint32_t leading_zeroes(uint32_t x) { return Internal::clz(x); }
|
||||
static inline uint32_t trailing_zeroes(uint32_t x) { return Internal::ctz(x); }
|
||||
static inline uint32_t trailing_ones(uint32_t x) { return Internal::ctz(~x); }
|
||||
static inline uint32_t leading_zeroes64(uint64_t x) { return Internal::clz64(x); }
|
||||
static inline uint32_t trailing_zeroes64(uint64_t x) { return Internal::ctz64(x); }
|
||||
static inline uint32_t trailing_ones64(uint64_t x) { return Internal::ctz64(~x); }
|
||||
static inline uint32_t popcount32(uint32_t x) { return Internal::popcount32(x); }
|
||||
#else
|
||||
#error "Implement me."
|
||||
#endif
|
||||
|
||||
@@ -107,7 +107,7 @@ int64_t get_current_time_nsecs()
|
||||
return int64_t(double(li.QuadPart) * static_qpc_freq.inv_freq);
|
||||
#else
|
||||
struct timespec ts = {};
|
||||
#ifdef ANDROID
|
||||
#if defined(ANDROID) || defined(__FreeBSD__)
|
||||
constexpr auto timebase = CLOCK_MONOTONIC;
|
||||
#else
|
||||
constexpr auto timebase = CLOCK_MONOTONIC_RAW;
|
||||
@@ -128,4 +128,4 @@ double Timer::end()
|
||||
auto nt = get_current_time_nsecs();
|
||||
return double(nt - t) * 1e-9;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -767,7 +767,7 @@ void CommandBuffer::blit_image(const Image &dst, const Image &src,
|
||||
void CommandBuffer::begin_context()
|
||||
{
|
||||
dirty = ~0u;
|
||||
dirty_sets = ~0u;
|
||||
dirty_sets_realloc = ~0u;
|
||||
dirty_vbos = ~0u;
|
||||
current_pipeline = {};
|
||||
current_pipeline_layout = VK_NULL_HANDLE;
|
||||
@@ -1291,9 +1291,9 @@ Pipeline CommandBuffer::build_graphics_pipeline(Device *device, const DeferredPi
|
||||
|
||||
// Depth state
|
||||
VkPipelineDepthStencilStateCreateInfo ds = { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO };
|
||||
ds.stencilTestEnable = compile.compatible_render_pass->has_stencil(compile.subpass_index) && compile.static_state.state.stencil_test;
|
||||
ds.depthTestEnable = compile.compatible_render_pass->has_depth(compile.subpass_index) && compile.static_state.state.depth_test;
|
||||
ds.depthWriteEnable = compile.compatible_render_pass->has_depth(compile.subpass_index) && compile.static_state.state.depth_write;
|
||||
ds.stencilTestEnable = compile.compatible_render_pass->has_stencil(compile.subpass_index) && compile.static_state.state.stencil_test != 0;
|
||||
ds.depthTestEnable = compile.compatible_render_pass->has_depth(compile.subpass_index) && compile.static_state.state.depth_test != 0;
|
||||
ds.depthWriteEnable = compile.compatible_render_pass->has_depth(compile.subpass_index) && compile.static_state.state.depth_write != 0;
|
||||
|
||||
if (ds.depthTestEnable)
|
||||
ds.depthCompareOp = static_cast<VkCompareOp>(compile.static_state.state.depth_compare);
|
||||
@@ -1391,7 +1391,7 @@ Pipeline CommandBuffer::build_graphics_pipeline(Device *device, const DeferredPi
|
||||
VkPipelineShaderStageRequiredSubgroupSizeCreateInfo subgroup_size_info_task;
|
||||
VkPipelineShaderStageRequiredSubgroupSizeCreateInfo subgroup_size_info_mesh;
|
||||
|
||||
for (unsigned i = 0; i < Util::ecast(ShaderStage::Count); i++)
|
||||
for (int i = 0; i < Util::ecast(ShaderStage::Count); i++)
|
||||
{
|
||||
auto mask = compile.layout->get_resource_layout().spec_constant_mask[i] &
|
||||
get_combined_spec_constant_mask(compile);
|
||||
@@ -1414,7 +1414,7 @@ Pipeline CommandBuffer::build_graphics_pipeline(Device *device, const DeferredPi
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < Util::ecast(ShaderStage::Count); i++)
|
||||
for (int i = 0; i < Util::ecast(ShaderStage::Count); i++)
|
||||
{
|
||||
auto stage = static_cast<ShaderStage>(i);
|
||||
if (compile.program->get_shader(stage))
|
||||
@@ -2089,7 +2089,7 @@ void CommandBuffer::set_program_layout(const PipelineLayout *layout)
|
||||
VK_ASSERT(layout);
|
||||
if (!pipeline_state.layout)
|
||||
{
|
||||
dirty_sets = ~0u;
|
||||
dirty_sets_realloc = ~0u;
|
||||
set_dirty(COMMAND_BUFFER_DIRTY_PUSH_CONSTANTS_BIT);
|
||||
}
|
||||
else if (layout->get_hash() != pipeline_state.layout->get_hash())
|
||||
@@ -2097,11 +2097,20 @@ void CommandBuffer::set_program_layout(const PipelineLayout *layout)
|
||||
auto &new_layout = layout->get_resource_layout();
|
||||
auto &old_layout = pipeline_state.layout->get_resource_layout();
|
||||
|
||||
uint32_t first_invalidated_set_index = VULKAN_NUM_DESCRIPTOR_SETS;
|
||||
uint32_t new_push_set = layout->get_push_set_index();
|
||||
uint32_t old_push_set = pipeline_state.layout->get_push_set_index();
|
||||
if (new_push_set == old_push_set)
|
||||
{
|
||||
new_push_set = UINT32_MAX;
|
||||
old_push_set = UINT32_MAX;
|
||||
}
|
||||
|
||||
// If the push constant layout changes, all descriptor sets
|
||||
// are invalidated.
|
||||
if (new_layout.push_constant_layout_hash != old_layout.push_constant_layout_hash)
|
||||
{
|
||||
dirty_sets = ~0u;
|
||||
first_invalidated_set_index = 0;
|
||||
set_dirty(COMMAND_BUFFER_DIRTY_PUSH_CONSTANTS_BIT);
|
||||
}
|
||||
else
|
||||
@@ -2109,13 +2118,28 @@ void CommandBuffer::set_program_layout(const PipelineLayout *layout)
|
||||
// Find the first set whose descriptor set layout differs.
|
||||
for (unsigned set = 0; set < VULKAN_NUM_DESCRIPTOR_SETS; set++)
|
||||
{
|
||||
if (layout->get_allocator(set) != pipeline_state.layout->get_allocator(set))
|
||||
if (layout->get_allocator(set) != pipeline_state.layout->get_allocator(set) ||
|
||||
set == new_push_set || set == old_push_set)
|
||||
{
|
||||
dirty_sets |= ~((1u << set) - 1);
|
||||
first_invalidated_set_index = set;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (first_invalidated_set_index < VULKAN_NUM_DESCRIPTOR_SETS)
|
||||
{
|
||||
dirty_sets_rebind |= ~((1u << first_invalidated_set_index) - 1u);
|
||||
|
||||
for (unsigned set = first_invalidated_set_index; set < VULKAN_NUM_DESCRIPTOR_SETS; set++)
|
||||
{
|
||||
if (layout->get_allocator(set) != pipeline_state.layout->get_allocator(set) ||
|
||||
set == new_push_set || set == old_push_set)
|
||||
{
|
||||
dirty_sets_realloc |= 1u << set;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pipeline_state.layout = layout;
|
||||
@@ -2241,21 +2265,21 @@ void CommandBuffer::set_uniform_buffer(unsigned set, unsigned binding, const Buf
|
||||
VK_ASSERT(buffer.get_create_info().usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
|
||||
auto &b = bindings.bindings[set][binding];
|
||||
|
||||
if (buffer.get_cookie() == bindings.cookies[set][binding] && b.buffer.range == range)
|
||||
if (buffer.get_cookie() == bindings.cookies[set][binding] && b.buffer.dynamic.range == range)
|
||||
{
|
||||
if (b.dynamic_offset != offset)
|
||||
if (b.buffer.push.offset != offset)
|
||||
{
|
||||
dirty_sets_dynamic |= 1u << set;
|
||||
b.dynamic_offset = offset;
|
||||
dirty_sets_rebind |= 1u << set;
|
||||
b.buffer.push.offset = offset;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
b.buffer = { buffer.get_buffer(), 0, range };
|
||||
b.dynamic_offset = offset;
|
||||
b.buffer.dynamic = { buffer.get_buffer(), 0, range };
|
||||
b.buffer.push = { buffer.get_buffer(), offset, range };
|
||||
bindings.cookies[set][binding] = buffer.get_cookie();
|
||||
bindings.secondary_cookies[set][binding] = 0;
|
||||
dirty_sets |= 1u << set;
|
||||
dirty_sets_realloc |= 1u << set;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2267,14 +2291,14 @@ void CommandBuffer::set_storage_buffer(unsigned set, unsigned binding, const Buf
|
||||
VK_ASSERT(buffer.get_create_info().usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
|
||||
auto &b = bindings.bindings[set][binding];
|
||||
|
||||
if (buffer.get_cookie() == bindings.cookies[set][binding] && b.buffer.offset == offset && b.buffer.range == range)
|
||||
if (buffer.get_cookie() == bindings.cookies[set][binding] && b.buffer.dynamic.offset == offset && b.buffer.dynamic.range == range)
|
||||
return;
|
||||
|
||||
b.buffer = { buffer.get_buffer(), offset, range };
|
||||
b.dynamic_offset = 0;
|
||||
b.buffer.dynamic = { buffer.get_buffer(), offset, range };
|
||||
b.buffer.push = b.buffer.dynamic;
|
||||
bindings.cookies[set][binding] = buffer.get_cookie();
|
||||
bindings.secondary_cookies[set][binding] = 0;
|
||||
dirty_sets |= 1u << set;
|
||||
dirty_sets_realloc |= 1u << set;
|
||||
}
|
||||
|
||||
void CommandBuffer::set_uniform_buffer(unsigned set, unsigned binding, const Buffer &buffer)
|
||||
@@ -2297,7 +2321,7 @@ void CommandBuffer::set_sampler(unsigned set, unsigned binding, const Sampler &s
|
||||
auto &b = bindings.bindings[set][binding];
|
||||
b.image.fp.sampler = sampler.get_sampler();
|
||||
b.image.integer.sampler = sampler.get_sampler();
|
||||
dirty_sets |= 1u << set;
|
||||
dirty_sets_realloc |= 1u << set;
|
||||
bindings.secondary_cookies[set][binding] = sampler.get_cookie();
|
||||
}
|
||||
|
||||
@@ -2311,7 +2335,7 @@ void CommandBuffer::set_buffer_view_common(unsigned set, unsigned binding, const
|
||||
b.buffer_view = view.get_view();
|
||||
bindings.cookies[set][binding] = view.get_cookie();
|
||||
bindings.secondary_cookies[set][binding] = 0;
|
||||
dirty_sets |= 1u << set;
|
||||
dirty_sets_realloc |= 1u << set;
|
||||
}
|
||||
|
||||
void CommandBuffer::set_buffer_view(unsigned set, unsigned binding, const BufferView &view)
|
||||
@@ -2353,7 +2377,7 @@ void CommandBuffer::set_input_attachments(unsigned set, unsigned start_binding)
|
||||
b.image.fp.imageView = view->get_float_view();
|
||||
b.image.integer.imageView = view->get_integer_view();
|
||||
bindings.cookies[set][start_binding + i] = view->get_cookie();
|
||||
dirty_sets |= 1u << set;
|
||||
dirty_sets_realloc |= 1u << set;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2374,14 +2398,14 @@ void CommandBuffer::set_texture(unsigned set, unsigned binding,
|
||||
b.image.integer.imageLayout = layout;
|
||||
b.image.integer.imageView = integer_view;
|
||||
bindings.cookies[set][binding] = cookie;
|
||||
dirty_sets |= 1u << set;
|
||||
dirty_sets_realloc |= 1u << set;
|
||||
}
|
||||
|
||||
void CommandBuffer::set_bindless(unsigned set, VkDescriptorSet desc_set)
|
||||
{
|
||||
VK_ASSERT(set < VULKAN_NUM_DESCRIPTOR_SETS);
|
||||
bindless_sets[set] = desc_set;
|
||||
dirty_sets |= 1u << set;
|
||||
dirty_sets_realloc |= 1u << set;
|
||||
}
|
||||
|
||||
void CommandBuffer::set_texture(unsigned set, unsigned binding, const ImageView &view)
|
||||
@@ -2452,180 +2476,225 @@ void CommandBuffer::set_unorm_storage_texture(unsigned set, unsigned binding, co
|
||||
view.get_image().get_layout(VK_IMAGE_LAYOUT_GENERAL), view.get_cookie() | COOKIE_BIT_UNORM);
|
||||
}
|
||||
|
||||
void CommandBuffer::rebind_descriptor_set(uint32_t set)
|
||||
void CommandBuffer::flush_descriptor_binds(const VkDescriptorSet *sets,
|
||||
uint32_t &first_set, uint32_t &set_count,
|
||||
uint32_t *dynamic_offsets, uint32_t &num_dynamic_offsets)
|
||||
{
|
||||
auto &layout = pipeline_state.layout->get_resource_layout();
|
||||
if (layout.bindless_descriptor_set_mask & (1u << set))
|
||||
{
|
||||
VK_ASSERT(bindless_sets[set]);
|
||||
table.vkCmdBindDescriptorSets(cmd, actual_render_pass ? VK_PIPELINE_BIND_POINT_GRAPHICS : VK_PIPELINE_BIND_POINT_COMPUTE,
|
||||
current_pipeline_layout, set, 1, &bindless_sets[set], 0, nullptr);
|
||||
if (!set_count)
|
||||
return;
|
||||
}
|
||||
|
||||
auto &set_layout = layout.sets[set];
|
||||
uint32_t num_dynamic_offsets = 0;
|
||||
uint32_t dynamic_offsets[VULKAN_NUM_BINDINGS];
|
||||
table.vkCmdBindDescriptorSets(
|
||||
cmd, actual_render_pass ? VK_PIPELINE_BIND_POINT_GRAPHICS : VK_PIPELINE_BIND_POINT_COMPUTE,
|
||||
current_pipeline_layout, first_set, set_count, sets, num_dynamic_offsets, dynamic_offsets);
|
||||
|
||||
// UBOs
|
||||
for_each_bit(set_layout.uniform_buffer_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
VK_ASSERT(num_dynamic_offsets < VULKAN_NUM_BINDINGS);
|
||||
dynamic_offsets[num_dynamic_offsets++] = bindings.bindings[set][binding + i].dynamic_offset;
|
||||
}
|
||||
});
|
||||
|
||||
table.vkCmdBindDescriptorSets(cmd, actual_render_pass ? VK_PIPELINE_BIND_POINT_GRAPHICS : VK_PIPELINE_BIND_POINT_COMPUTE,
|
||||
current_pipeline_layout, set, 1, &allocated_sets[set], num_dynamic_offsets, dynamic_offsets);
|
||||
set_count = 0;
|
||||
num_dynamic_offsets = 0;
|
||||
}
|
||||
|
||||
void CommandBuffer::flush_descriptor_set(uint32_t set)
|
||||
void CommandBuffer::rebind_descriptor_set(uint32_t set, VkDescriptorSet *sets, uint32_t &first_set, uint32_t &set_count,
|
||||
uint32_t *dynamic_offsets, uint32_t &num_dynamic_offsets)
|
||||
{
|
||||
if (set_count == 0)
|
||||
first_set = set;
|
||||
else if (first_set + set_count != set)
|
||||
{
|
||||
flush_descriptor_binds(sets, first_set, set_count, dynamic_offsets, num_dynamic_offsets);
|
||||
first_set = set;
|
||||
}
|
||||
|
||||
auto &layout = pipeline_state.layout->get_resource_layout();
|
||||
if (layout.bindless_descriptor_set_mask & (1u << set))
|
||||
{
|
||||
VK_ASSERT(bindless_sets[set]);
|
||||
table.vkCmdBindDescriptorSets(cmd, actual_render_pass ? VK_PIPELINE_BIND_POINT_GRAPHICS : VK_PIPELINE_BIND_POINT_COMPUTE,
|
||||
current_pipeline_layout, set, 1, &bindless_sets[set], 0, nullptr);
|
||||
sets[set_count++] = bindless_sets[set];
|
||||
return;
|
||||
}
|
||||
|
||||
auto &set_layout = layout.sets[set];
|
||||
uint32_t num_dynamic_offsets = 0;
|
||||
uint32_t dynamic_offsets[VULKAN_NUM_BINDINGS];
|
||||
Hasher h;
|
||||
|
||||
h.u32(set_layout.fp_mask);
|
||||
|
||||
// UBOs
|
||||
for_each_bit(set_layout.uniform_buffer_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
h.u64(bindings.cookies[set][binding + i]);
|
||||
h.u32(bindings.bindings[set][binding + i].buffer.range);
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].buffer.buffer != VK_NULL_HANDLE);
|
||||
|
||||
VK_ASSERT(num_dynamic_offsets < VULKAN_NUM_BINDINGS);
|
||||
dynamic_offsets[num_dynamic_offsets++] = bindings.bindings[set][binding + i].dynamic_offset;
|
||||
VK_ASSERT(num_dynamic_offsets < VULKAN_NUM_DYNAMIC_UBOS);
|
||||
dynamic_offsets[num_dynamic_offsets++] = bindings.bindings[set][binding + i].buffer.push.offset;
|
||||
}
|
||||
});
|
||||
|
||||
sets[set_count++] = allocated_sets[set];
|
||||
}
|
||||
|
||||
void CommandBuffer::validate_descriptor_binds(uint32_t set)
|
||||
{
|
||||
#ifdef VULKAN_DEBUG
|
||||
auto &layout = pipeline_state.layout->get_resource_layout();
|
||||
auto &set_layout = layout.sets[set];
|
||||
|
||||
for_each_bit(set_layout.uniform_buffer_mask,
|
||||
[&](uint32_t binding)
|
||||
{
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].buffer.dynamic.buffer != VK_NULL_HANDLE);
|
||||
});
|
||||
|
||||
// SSBOs
|
||||
for_each_bit(set_layout.storage_buffer_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
h.u64(bindings.cookies[set][binding + i]);
|
||||
h.u32(bindings.bindings[set][binding + i].buffer.offset);
|
||||
h.u32(bindings.bindings[set][binding + i].buffer.range);
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].buffer.buffer != VK_NULL_HANDLE);
|
||||
}
|
||||
});
|
||||
for_each_bit(set_layout.storage_buffer_mask,
|
||||
[&](uint32_t binding)
|
||||
{
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].buffer.dynamic.buffer != VK_NULL_HANDLE);
|
||||
});
|
||||
|
||||
// Texel buffers
|
||||
for_each_bit(set_layout.sampled_texel_buffer_mask | set_layout.storage_texel_buffer_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
h.u64(bindings.cookies[set][binding + i]);
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].buffer_view != VK_NULL_HANDLE);
|
||||
}
|
||||
});
|
||||
for_each_bit(set_layout.sampled_texel_buffer_mask | set_layout.storage_texel_buffer_mask,
|
||||
[&](uint32_t binding)
|
||||
{
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].buffer_view != VK_NULL_HANDLE);
|
||||
});
|
||||
|
||||
// Sampled images
|
||||
for_each_bit(set_layout.sampled_image_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
h.u64(bindings.cookies[set][binding + i]);
|
||||
if ((set_layout.immutable_sampler_mask & (1u << (binding + i))) == 0)
|
||||
{
|
||||
h.u64(bindings.secondary_cookies[set][binding + i]);
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.sampler != VK_NULL_HANDLE);
|
||||
}
|
||||
h.u32(bindings.bindings[set][binding + i].image.fp.imageLayout);
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.imageView != VK_NULL_HANDLE);
|
||||
}
|
||||
});
|
||||
for_each_bit(set_layout.sampled_image_mask,
|
||||
[&](uint32_t binding)
|
||||
{
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
if ((set_layout.immutable_sampler_mask & (1u << (binding + i))) == 0)
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.sampler != VK_NULL_HANDLE);
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.imageView != VK_NULL_HANDLE);
|
||||
}
|
||||
});
|
||||
|
||||
// Separate images
|
||||
for_each_bit(set_layout.separate_image_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
h.u64(bindings.cookies[set][binding + i]);
|
||||
h.u32(bindings.bindings[set][binding + i].image.fp.imageLayout);
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.imageView != VK_NULL_HANDLE);
|
||||
}
|
||||
});
|
||||
for_each_bit(set_layout.separate_image_mask,
|
||||
[&](uint32_t binding)
|
||||
{
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.imageView != VK_NULL_HANDLE);
|
||||
});
|
||||
|
||||
// Separate samplers
|
||||
for_each_bit(set_layout.sampler_mask & ~set_layout.immutable_sampler_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
h.u64(bindings.secondary_cookies[set][binding + i]);
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.sampler != VK_NULL_HANDLE);
|
||||
}
|
||||
});
|
||||
for_each_bit(set_layout.sampler_mask & ~set_layout.immutable_sampler_mask,
|
||||
[&](uint32_t binding)
|
||||
{
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.sampler != VK_NULL_HANDLE);
|
||||
});
|
||||
|
||||
// Storage images
|
||||
for_each_bit(set_layout.storage_image_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
h.u64(bindings.cookies[set][binding + i]);
|
||||
h.u32(bindings.bindings[set][binding + i].image.fp.imageLayout);
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.imageView != VK_NULL_HANDLE);
|
||||
}
|
||||
});
|
||||
for_each_bit(set_layout.storage_image_mask,
|
||||
[&](uint32_t binding)
|
||||
{
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.imageView != VK_NULL_HANDLE);
|
||||
});
|
||||
|
||||
// Input attachments
|
||||
for_each_bit(set_layout.input_attachment_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
h.u64(bindings.cookies[set][binding + i]);
|
||||
h.u32(bindings.bindings[set][binding + i].image.fp.imageLayout);
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.imageView != VK_NULL_HANDLE);
|
||||
}
|
||||
});
|
||||
for_each_bit(set_layout.input_attachment_mask,
|
||||
[&](uint32_t binding)
|
||||
{
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
VK_ASSERT(bindings.bindings[set][binding + i].image.fp.imageView != VK_NULL_HANDLE);
|
||||
});
|
||||
#else
|
||||
(void)set;
|
||||
#endif
|
||||
}
|
||||
|
||||
Hash hash = h.get();
|
||||
auto allocated = pipeline_state.layout->get_allocator(set)->find(thread_index, hash);
|
||||
void CommandBuffer::push_descriptor_set(uint32_t set)
|
||||
{
|
||||
#ifdef VULKAN_DEBUG
|
||||
validate_descriptor_binds(set);
|
||||
#endif
|
||||
|
||||
// The descriptor set was not successfully cached, rebuild.
|
||||
if (!allocated.second)
|
||||
VkDescriptorUpdateTemplate update_template = pipeline_state.layout->get_update_template(set);
|
||||
VK_ASSERT(update_template);
|
||||
table.vkCmdPushDescriptorSetWithTemplateKHR(
|
||||
cmd, update_template,
|
||||
pipeline_state.layout->get_layout(), set, bindings.bindings[set]);
|
||||
}
|
||||
|
||||
void CommandBuffer::flush_descriptor_set(uint32_t set, VkDescriptorSet *sets,
|
||||
uint32_t &first_set, uint32_t &set_count,
|
||||
uint32_t *dynamic_offsets, uint32_t &num_dynamic_offsets)
|
||||
{
|
||||
if (set_count == 0)
|
||||
first_set = set;
|
||||
else if (first_set + set_count != set)
|
||||
{
|
||||
auto update_template = pipeline_state.layout->get_update_template(set);
|
||||
VK_ASSERT(update_template);
|
||||
table.vkUpdateDescriptorSetWithTemplate(device->get_device(), allocated.first,
|
||||
update_template, bindings.bindings[set]);
|
||||
flush_descriptor_binds(sets, first_set, set_count, dynamic_offsets, num_dynamic_offsets);
|
||||
first_set = set;
|
||||
}
|
||||
|
||||
table.vkCmdBindDescriptorSets(cmd, actual_render_pass ? VK_PIPELINE_BIND_POINT_GRAPHICS : VK_PIPELINE_BIND_POINT_COMPUTE,
|
||||
current_pipeline_layout, set, 1, &allocated.first, num_dynamic_offsets, dynamic_offsets);
|
||||
allocated_sets[set] = allocated.first;
|
||||
auto &layout = pipeline_state.layout->get_resource_layout();
|
||||
if (layout.bindless_descriptor_set_mask & (1u << set))
|
||||
{
|
||||
VK_ASSERT(bindless_sets[set]);
|
||||
sets[set_count++] = bindless_sets[set];
|
||||
return;
|
||||
}
|
||||
|
||||
auto &set_layout = layout.sets[set];
|
||||
|
||||
#ifdef VULKAN_DEBUG
|
||||
validate_descriptor_binds(set);
|
||||
#endif
|
||||
|
||||
// UBOs
|
||||
for_each_bit(set_layout.uniform_buffer_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
dynamic_offsets[num_dynamic_offsets++] = bindings.bindings[set][binding + i].buffer.push.offset;
|
||||
});
|
||||
|
||||
auto vk_set = pipeline_state.layout->get_allocator(set)->request_descriptor_set(thread_index, device->frame_context_index);
|
||||
|
||||
VkDescriptorUpdateTemplate update_template = pipeline_state.layout->get_update_template(set);
|
||||
VK_ASSERT(update_template);
|
||||
table.vkUpdateDescriptorSetWithTemplate(device->get_device(), vk_set, update_template, bindings.bindings[set]);
|
||||
sets[set_count++] = vk_set;
|
||||
allocated_sets[set] = vk_set;
|
||||
}
|
||||
|
||||
void CommandBuffer::flush_descriptor_sets()
|
||||
{
|
||||
auto &layout = pipeline_state.layout->get_resource_layout();
|
||||
|
||||
uint32_t set_update = layout.descriptor_set_mask & dirty_sets;
|
||||
for_each_bit(set_update, [&](uint32_t set) { flush_descriptor_set(set); });
|
||||
dirty_sets &= ~set_update;
|
||||
uint32_t first_set = 0;
|
||||
uint32_t set_count = 0;
|
||||
VkDescriptorSet sets[VULKAN_NUM_DESCRIPTOR_SETS];
|
||||
uint32_t dynamic_offsets[VULKAN_NUM_DYNAMIC_UBOS];
|
||||
uint32_t num_dynamic_offsets = 0;
|
||||
|
||||
// If we update a set, we also bind dynamically.
|
||||
dirty_sets_dynamic &= ~set_update;
|
||||
dirty_sets_rebind |= dirty_sets_realloc;
|
||||
uint32_t set_update_mask = layout.descriptor_set_mask & dirty_sets_rebind;
|
||||
|
||||
// If we only rebound UBOs, we might get away with just rebinding descriptor sets, no hashing and lookup required.
|
||||
uint32_t dynamic_set_update = layout.descriptor_set_mask & dirty_sets_dynamic;
|
||||
for_each_bit(dynamic_set_update, [&](uint32_t set) { rebind_descriptor_set(set); });
|
||||
dirty_sets_dynamic &= ~dynamic_set_update;
|
||||
uint32_t push_set_index = pipeline_state.layout->get_push_set_index();
|
||||
if (push_set_index != UINT32_MAX && (dirty_sets_rebind & (1u << push_set_index)) != 0)
|
||||
{
|
||||
push_descriptor_set(push_set_index);
|
||||
set_update_mask &= ~(1u << push_set_index);
|
||||
}
|
||||
|
||||
for_each_bit(set_update_mask, [&](uint32_t set) {
|
||||
if ((dirty_sets_realloc & (1u << set)) != 0)
|
||||
flush_descriptor_set(set, sets, first_set, set_count, dynamic_offsets, num_dynamic_offsets);
|
||||
else
|
||||
rebind_descriptor_set(set, sets, first_set, set_count, dynamic_offsets, num_dynamic_offsets);
|
||||
});
|
||||
|
||||
dirty_sets_realloc = 0;
|
||||
dirty_sets_rebind = 0;
|
||||
flush_descriptor_binds(sets, first_set, set_count, dynamic_offsets, num_dynamic_offsets);
|
||||
}
|
||||
|
||||
void CommandBuffer::draw(uint32_t vertex_count, uint32_t instance_count, uint32_t first_vertex, uint32_t first_instance)
|
||||
@@ -2967,7 +3036,7 @@ void CommandBuffer::restore_state(const CommandBufferSavedState &state)
|
||||
memcpy(bindings.bindings[i], state.bindings.bindings[i], sizeof(bindings.bindings[i]));
|
||||
memcpy(bindings.cookies[i], state.bindings.cookies[i], sizeof(bindings.cookies[i]));
|
||||
memcpy(bindings.secondary_cookies[i], state.bindings.secondary_cookies[i], sizeof(bindings.secondary_cookies[i]));
|
||||
dirty_sets |= 1u << i;
|
||||
dirty_sets_realloc |= 1u << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -846,8 +846,8 @@ private:
|
||||
VkRect2D scissor = {};
|
||||
|
||||
CommandBufferDirtyFlags dirty = ~0u;
|
||||
uint32_t dirty_sets = 0;
|
||||
uint32_t dirty_sets_dynamic = 0;
|
||||
uint32_t dirty_sets_realloc = 0;
|
||||
uint32_t dirty_sets_rebind = 0;
|
||||
uint32_t dirty_vbos = 0;
|
||||
uint32_t active_vbos = 0;
|
||||
VkPipelineStageFlags2 uses_swapchain_in_stages = 0;
|
||||
@@ -883,8 +883,20 @@ private:
|
||||
bool flush_compute_pipeline(bool synchronous);
|
||||
void flush_descriptor_sets();
|
||||
void begin_graphics();
|
||||
void flush_descriptor_set(uint32_t set);
|
||||
void rebind_descriptor_set(uint32_t set);
|
||||
void flush_descriptor_set(
|
||||
uint32_t set, VkDescriptorSet *sets,
|
||||
uint32_t &first_set, uint32_t &set_count,
|
||||
uint32_t *dynamic_offsets, uint32_t &num_dynamic_offsets);
|
||||
void push_descriptor_set(uint32_t set);
|
||||
void rebind_descriptor_set(
|
||||
uint32_t set, VkDescriptorSet *sets,
|
||||
uint32_t &first_set, uint32_t &set_count,
|
||||
uint32_t *dynamic_offsets, uint32_t &num_dynamic_offsets);
|
||||
void flush_descriptor_binds(const VkDescriptorSet *sets,
|
||||
uint32_t &first_set, uint32_t &set_count,
|
||||
uint32_t *dynamic_offsets, uint32_t &num_dynamic_offsets);
|
||||
void validate_descriptor_binds(uint32_t set);
|
||||
|
||||
void begin_compute();
|
||||
void begin_context();
|
||||
|
||||
|
||||
@@ -203,10 +203,10 @@ PFN_vkGetInstanceProcAddr Context::get_instance_proc_addr()
|
||||
return instance_proc_addr;
|
||||
}
|
||||
|
||||
bool Context::init_loader(PFN_vkGetInstanceProcAddr addr)
|
||||
bool Context::init_loader(PFN_vkGetInstanceProcAddr addr, bool force_reload)
|
||||
{
|
||||
std::lock_guard<std::mutex> holder(loader_init_lock);
|
||||
if (loader_init_once && !addr)
|
||||
if (loader_init_once && !force_reload && !addr)
|
||||
return true;
|
||||
|
||||
if (!addr)
|
||||
@@ -608,12 +608,6 @@ bool Context::create_instance(const char * const *instance_ext, uint32_t instanc
|
||||
ext.supports_surface_capabilities2 = true;
|
||||
}
|
||||
|
||||
if (ext.supports_surface_capabilities2 && has_extension(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME))
|
||||
{
|
||||
instance_exts.push_back(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME);
|
||||
ext.supports_surface_maintenance1 = true;
|
||||
}
|
||||
|
||||
if ((flags & CONTEXT_CREATION_ENABLE_ADVANCED_WSI_BIT) != 0 &&
|
||||
has_surface_extension &&
|
||||
has_extension(VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME))
|
||||
@@ -630,8 +624,6 @@ bool Context::create_instance(const char * const *instance_ext, uint32_t instanc
|
||||
return layer_itr != end(queried_layers);
|
||||
};
|
||||
|
||||
VkValidationFeaturesEXT validation_features = { VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT };
|
||||
|
||||
force_no_validation = Util::get_environment_bool("GRANITE_VULKAN_NO_VALIDATION", false);
|
||||
|
||||
if (!force_no_validation && has_layer("VK_LAYER_KHRONOS_validation"))
|
||||
@@ -644,6 +636,10 @@ bool Context::create_instance(const char * const *instance_ext, uint32_t instanc
|
||||
std::vector<VkExtensionProperties> layer_exts(layer_ext_count);
|
||||
vkEnumerateInstanceExtensionProperties("VK_LAYER_KHRONOS_validation", &layer_ext_count, layer_exts.data());
|
||||
|
||||
#if 0
|
||||
VkValidationFeaturesEXT validation_features = { VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT };
|
||||
|
||||
// Tons of false positives around timeline semaphores atm, so don't bother.
|
||||
if (find_if(begin(layer_exts), end(layer_exts), [](const VkExtensionProperties &e) {
|
||||
return strcmp(e.extensionName, VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME) == 0;
|
||||
}) != end(layer_exts))
|
||||
@@ -657,6 +653,7 @@ bool Context::create_instance(const char * const *instance_ext, uint32_t instanc
|
||||
validation_features.pEnabledValidationFeatures = validation_sync_features;
|
||||
info.pNext = &validation_features;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!ext.supports_debug_utils &&
|
||||
find_if(begin(layer_exts), end(layer_exts), [](const VkExtensionProperties &e) {
|
||||
@@ -669,6 +666,22 @@ bool Context::create_instance(const char * const *instance_ext, uint32_t instanc
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ext.supports_surface_capabilities2 && has_extension(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME))
|
||||
{
|
||||
#ifdef VULKAN_DEBUG
|
||||
// It seems like there are some bugs with EXT_swapchain_maint1 in VVL atm.
|
||||
const bool support_maint1 = force_no_validation;
|
||||
#else
|
||||
constexpr bool support_maint1 = true;
|
||||
#endif
|
||||
|
||||
if (support_maint1)
|
||||
{
|
||||
instance_exts.push_back(VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME);
|
||||
ext.supports_surface_maintenance1 = true;
|
||||
}
|
||||
}
|
||||
|
||||
info.enabledExtensionCount = instance_exts.size();
|
||||
info.ppEnabledExtensionNames = instance_exts.empty() ? nullptr : instance_exts.data();
|
||||
info.enabledLayerCount = instance_layers.size();
|
||||
@@ -1302,7 +1315,14 @@ bool Context::create_device(VkPhysicalDevice gpu_, VkSurfaceKHR surface,
|
||||
ADD_CHAIN(ext.storage_8bit_features, 8BIT_STORAGE_FEATURES_KHR);
|
||||
enabled_extensions.push_back(VK_KHR_8BIT_STORAGE_EXTENSION_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
if (ext.device_api_core_version >= VK_API_VERSION_1_3)
|
||||
{
|
||||
ADD_CHAIN(ext.vk13_features, VULKAN_1_3_FEATURES);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (has_extension(VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME))
|
||||
{
|
||||
ADD_CHAIN(ext.subgroup_size_control_features, SUBGROUP_SIZE_CONTROL_FEATURES_EXT);
|
||||
@@ -1310,9 +1330,6 @@ bool Context::create_device(VkPhysicalDevice gpu_, VkSurfaceKHR surface,
|
||||
}
|
||||
}
|
||||
|
||||
if (ext.device_api_core_version >= VK_API_VERSION_1_3)
|
||||
ADD_CHAIN(ext.vk13_features, VULKAN_1_3_FEATURES);
|
||||
|
||||
if (has_extension(VK_NV_COMPUTE_SHADER_DERIVATIVES_EXTENSION_NAME))
|
||||
{
|
||||
enabled_extensions.push_back(VK_NV_COMPUTE_SHADER_DERIVATIVES_EXTENSION_NAME);
|
||||
@@ -1362,6 +1379,12 @@ bool Context::create_device(VkPhysicalDevice gpu_, VkSurfaceKHR surface,
|
||||
ADD_CHAIN(ext.device_generated_commands_compute_features, DEVICE_GENERATED_COMMANDS_COMPUTE_FEATURES_NV);
|
||||
}
|
||||
|
||||
if (has_extension(VK_NV_DESCRIPTOR_POOL_OVERALLOCATION_EXTENSION_NAME))
|
||||
{
|
||||
enabled_extensions.push_back(VK_NV_DESCRIPTOR_POOL_OVERALLOCATION_EXTENSION_NAME);
|
||||
ADD_CHAIN(ext.descriptor_pool_overallocation_features, DESCRIPTOR_POOL_OVERALLOCATION_FEATURES_NV);
|
||||
}
|
||||
|
||||
if (has_extension(VK_EXT_MESH_SHADER_EXTENSION_NAME))
|
||||
{
|
||||
enabled_extensions.push_back(VK_EXT_MESH_SHADER_EXTENSION_NAME);
|
||||
@@ -1374,6 +1397,12 @@ bool Context::create_device(VkPhysicalDevice gpu_, VkSurfaceKHR surface,
|
||||
ADD_CHAIN(ext.index_type_uint8_features, INDEX_TYPE_UINT8_FEATURES_EXT);
|
||||
}
|
||||
|
||||
if (has_extension(VK_EXT_RGBA10X6_FORMATS_EXTENSION_NAME))
|
||||
{
|
||||
enabled_extensions.push_back(VK_EXT_RGBA10X6_FORMATS_EXTENSION_NAME);
|
||||
ADD_CHAIN(ext.rgba10x6_formats_features, RGBA10X6_FORMATS_FEATURES_EXT);
|
||||
}
|
||||
|
||||
if (has_extension(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME))
|
||||
{
|
||||
ext.supports_external_memory_host = true;
|
||||
@@ -1398,6 +1427,18 @@ bool Context::create_device(VkPhysicalDevice gpu_, VkSurfaceKHR surface,
|
||||
ext.supports_push_descriptor = true;
|
||||
}
|
||||
|
||||
if (has_extension(VK_EXT_IMAGE_COMPRESSION_CONTROL_EXTENSION_NAME))
|
||||
{
|
||||
enabled_extensions.push_back(VK_EXT_IMAGE_COMPRESSION_CONTROL_EXTENSION_NAME);
|
||||
ADD_CHAIN(ext.image_compression_control_features, IMAGE_COMPRESSION_CONTROL_FEATURES_EXT);
|
||||
}
|
||||
|
||||
if (has_extension(VK_EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_EXTENSION_NAME))
|
||||
{
|
||||
enabled_extensions.push_back(VK_EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_EXTENSION_NAME);
|
||||
ADD_CHAIN(ext.image_compression_control_swapchain_features, IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_FEATURES_EXT);
|
||||
}
|
||||
|
||||
if (ext.device_api_core_version >= VK_API_VERSION_1_3)
|
||||
{
|
||||
ext.supports_store_op_none = true;
|
||||
@@ -1581,14 +1622,14 @@ bool Context::create_device(VkPhysicalDevice gpu_, VkSurfaceKHR surface,
|
||||
{
|
||||
if (has_extension(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME))
|
||||
ADD_CHAIN(driver_properties, DRIVER_PROPERTIES);
|
||||
if (has_extension(VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME))
|
||||
ADD_CHAIN(size_control_props, SUBGROUP_SIZE_CONTROL_PROPERTIES);
|
||||
ADD_CHAIN(id_properties, ID_PROPERTIES);
|
||||
ADD_CHAIN(subgroup_properties, SUBGROUP_PROPERTIES);
|
||||
}
|
||||
|
||||
if (ext.device_api_core_version >= VK_API_VERSION_1_3)
|
||||
ADD_CHAIN(ext.vk13_props, VULKAN_1_3_PROPERTIES);
|
||||
else if (has_extension(VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME))
|
||||
ADD_CHAIN(size_control_props, SUBGROUP_SIZE_CONTROL_PROPERTIES);
|
||||
|
||||
if (ext.supports_external_memory_host)
|
||||
ADD_CHAIN(ext.host_memory_properties, EXTERNAL_MEMORY_HOST_PROPERTIES_EXT);
|
||||
|
||||
@@ -100,12 +100,16 @@ struct DeviceFeatures
|
||||
VkPhysicalDeviceMeshShaderFeaturesEXT mesh_shader_features = {};
|
||||
VkPhysicalDeviceMeshShaderPropertiesEXT mesh_shader_properties = {};
|
||||
VkPhysicalDeviceIndexTypeUint8FeaturesEXT index_type_uint8_features = {};
|
||||
VkPhysicalDeviceRGBA10X6FormatsFeaturesEXT rgba10x6_formats_features = {};
|
||||
VkPhysicalDeviceImageCompressionControlFeaturesEXT image_compression_control_features = {};
|
||||
VkPhysicalDeviceImageCompressionControlSwapchainFeaturesEXT image_compression_control_swapchain_features = {};
|
||||
|
||||
// Vendor
|
||||
VkPhysicalDeviceComputeShaderDerivativesFeaturesNV compute_shader_derivative_features = {};
|
||||
VkPhysicalDeviceDeviceGeneratedCommandsFeaturesNV device_generated_commands_features = {};
|
||||
VkPhysicalDeviceDeviceGeneratedCommandsComputeFeaturesNV device_generated_commands_compute_features = {};
|
||||
VkPhysicalDeviceDeviceGeneratedCommandsPropertiesNV device_generated_commands_properties = {};
|
||||
VkPhysicalDeviceDescriptorPoolOverallocationFeaturesNV descriptor_pool_overallocation_features = {};
|
||||
|
||||
// Fallback feature structs (Vulkan 1.1)
|
||||
VkPhysicalDeviceHostQueryResetFeatures host_query_reset_features = {};
|
||||
@@ -239,7 +243,7 @@ public:
|
||||
Context();
|
||||
Context(const Context &) = delete;
|
||||
void operator=(const Context &) = delete;
|
||||
static bool init_loader(PFN_vkGetInstanceProcAddr addr);
|
||||
static bool init_loader(PFN_vkGetInstanceProcAddr addr, bool force_reload = false);
|
||||
static PFN_vkGetInstanceProcAddr get_instance_proc_addr();
|
||||
|
||||
~Context();
|
||||
|
||||
@@ -40,9 +40,8 @@ DescriptorSetAllocator::DescriptorSetAllocator(Hash hash, Device *device_, const
|
||||
|
||||
if (!bindless)
|
||||
{
|
||||
unsigned count = device_->num_thread_indices;
|
||||
for (unsigned i = 0; i < count; i++)
|
||||
per_thread.emplace_back(new PerThread);
|
||||
unsigned count = device_->num_thread_indices * device_->per_frame.size();
|
||||
per_thread_and_frame.resize(count);
|
||||
}
|
||||
|
||||
if (bindless && !device->get_device_features().vk12_features.descriptorIndexing)
|
||||
@@ -176,11 +175,27 @@ DescriptorSetAllocator::DescriptorSetAllocator(Hash hash, Device *device_, const
|
||||
#ifdef VULKAN_DEBUG
|
||||
LOGI("Creating descriptor set layout.\n");
|
||||
#endif
|
||||
if (table.vkCreateDescriptorSetLayout(device->get_device(), &info, nullptr, &set_layout) != VK_SUCCESS)
|
||||
if (table.vkCreateDescriptorSetLayout(device->get_device(), &info, nullptr, &set_layout_pool) != VK_SUCCESS)
|
||||
LOGE("Failed to create descriptor set layout.");
|
||||
|
||||
#ifdef GRANITE_VULKAN_FOSSILIZE
|
||||
device->register_descriptor_set_layout(set_layout, get_hash(), info);
|
||||
if (set_layout_pool)
|
||||
device->register_descriptor_set_layout(set_layout_pool, get_hash(), info);
|
||||
#endif
|
||||
|
||||
if (!bindless && device->get_device_features().supports_push_descriptor && !device->workarounds.broken_push_descriptors)
|
||||
{
|
||||
info.flags |= VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR;
|
||||
for (auto &b : bindings)
|
||||
if (b.descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC)
|
||||
b.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||
if (table.vkCreateDescriptorSetLayout(device->get_device(), &info, nullptr, &set_layout_push) != VK_SUCCESS)
|
||||
LOGE("Failed to create descriptor set layout.");
|
||||
#ifdef GRANITE_VULKAN_FOSSILIZE
|
||||
if (set_layout_push)
|
||||
device->register_descriptor_set_layout(set_layout_push, get_hash(), info);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void DescriptorSetAllocator::reset_bindless_pool(VkDescriptorPool pool)
|
||||
@@ -196,7 +211,7 @@ VkDescriptorSet DescriptorSetAllocator::allocate_bindless_set(VkDescriptorPool p
|
||||
VkDescriptorSetAllocateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
|
||||
info.descriptorPool = pool;
|
||||
info.descriptorSetCount = 1;
|
||||
info.pSetLayouts = &set_layout;
|
||||
info.pSetLayouts = &set_layout_pool;
|
||||
|
||||
VkDescriptorSetVariableDescriptorCountAllocateInfoEXT count_info =
|
||||
{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO_EXT };
|
||||
@@ -225,12 +240,6 @@ VkDescriptorPool DescriptorSetAllocator::allocate_bindless_pool(unsigned num_set
|
||||
info.poolSizeCount = 1;
|
||||
|
||||
VkDescriptorPoolSize size = pool_size[0];
|
||||
if (num_descriptors > size.descriptorCount)
|
||||
{
|
||||
LOGE("Trying to allocate more than max bindless descriptors for descriptor layout.\n");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
size.descriptorCount = num_descriptors;
|
||||
info.pPoolSizes = &size;
|
||||
|
||||
@@ -247,82 +256,98 @@ void DescriptorSetAllocator::begin_frame()
|
||||
{
|
||||
if (!bindless)
|
||||
{
|
||||
for (auto &thr : per_thread)
|
||||
thr->should_begin = true;
|
||||
// This can only be called in a situation where no command buffers are alive,
|
||||
// so we don't need to consider any locks here.
|
||||
if (device->per_frame.size() * device->num_thread_indices != per_thread_and_frame.size())
|
||||
per_thread_and_frame.resize(device->per_frame.size() * device->num_thread_indices);
|
||||
|
||||
// It would be safe to set all offsets to 0 here, but that's a little wasteful.
|
||||
for (uint32_t i = 0; i < device->num_thread_indices; i++)
|
||||
per_thread_and_frame[i * device->per_frame.size() + device->frame_context_index].offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<VkDescriptorSet, bool> DescriptorSetAllocator::find(unsigned thread_index, Hash hash)
|
||||
VkDescriptorSet DescriptorSetAllocator::request_descriptor_set(unsigned thread_index, unsigned frame_index)
|
||||
{
|
||||
VK_ASSERT(!bindless);
|
||||
|
||||
auto &state = *per_thread[thread_index];
|
||||
if (state.should_begin)
|
||||
size_t flattened_index = thread_index * device->per_frame.size() + frame_index;
|
||||
|
||||
auto &state = per_thread_and_frame[flattened_index];
|
||||
|
||||
unsigned pool_index = state.offset / VULKAN_NUM_SETS_PER_POOL;
|
||||
unsigned pool_offset = state.offset % VULKAN_NUM_SETS_PER_POOL;
|
||||
|
||||
if (pool_index >= state.pools.size())
|
||||
{
|
||||
state.set_nodes.begin_frame();
|
||||
state.should_begin = false;
|
||||
Pool *pool = state.object_pool.allocate();
|
||||
|
||||
VkDescriptorPoolCreateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
|
||||
info.maxSets = VULKAN_NUM_SETS_PER_POOL;
|
||||
if (!pool_size.empty())
|
||||
{
|
||||
info.poolSizeCount = pool_size.size();
|
||||
info.pPoolSizes = pool_size.data();
|
||||
}
|
||||
|
||||
bool overallocation =
|
||||
device->get_device_features().descriptor_pool_overallocation_features.descriptorPoolOverallocation ==
|
||||
VK_TRUE;
|
||||
|
||||
if (overallocation)
|
||||
{
|
||||
// No point in allocating new pools if we can keep using the existing one.
|
||||
info.flags |= VK_DESCRIPTOR_POOL_CREATE_ALLOW_OVERALLOCATION_POOLS_BIT_NV |
|
||||
VK_DESCRIPTOR_POOL_CREATE_ALLOW_OVERALLOCATION_SETS_BIT_NV;
|
||||
}
|
||||
|
||||
bool need_alloc = !overallocation || state.pools.empty();
|
||||
|
||||
pool->pool = VK_NULL_HANDLE;
|
||||
if (need_alloc && table.vkCreateDescriptorPool(device->get_device(), &info, nullptr, &pool->pool) != VK_SUCCESS)
|
||||
{
|
||||
LOGE("Failed to create descriptor pool.\n");
|
||||
state.object_pool.free(pool);
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
VkDescriptorSetLayout layouts[VULKAN_NUM_SETS_PER_POOL];
|
||||
std::fill(std::begin(layouts), std::end(layouts), set_layout_pool);
|
||||
|
||||
VkDescriptorSetAllocateInfo alloc = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
|
||||
alloc.descriptorPool = pool->pool != VK_NULL_HANDLE ? pool->pool : state.pools.front()->pool;
|
||||
alloc.descriptorSetCount = VULKAN_NUM_SETS_PER_POOL;
|
||||
alloc.pSetLayouts = layouts;
|
||||
|
||||
if (table.vkAllocateDescriptorSets(device->get_device(), &alloc, pool->sets) != VK_SUCCESS)
|
||||
LOGE("Failed to allocate descriptor sets.\n");
|
||||
state.pools.push_back(pool);
|
||||
}
|
||||
|
||||
auto *node = state.set_nodes.request(hash);
|
||||
if (node)
|
||||
return { node->set, true };
|
||||
|
||||
node = state.set_nodes.request_vacant(hash);
|
||||
if (node)
|
||||
return { node->set, false };
|
||||
|
||||
VkDescriptorPool pool;
|
||||
VkDescriptorPoolCreateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO };
|
||||
info.maxSets = VULKAN_NUM_SETS_PER_POOL;
|
||||
if (!pool_size.empty())
|
||||
{
|
||||
info.poolSizeCount = pool_size.size();
|
||||
info.pPoolSizes = pool_size.data();
|
||||
}
|
||||
|
||||
if (table.vkCreateDescriptorPool(device->get_device(), &info, nullptr, &pool) != VK_SUCCESS)
|
||||
{
|
||||
LOGE("Failed to create descriptor pool.\n");
|
||||
return { VK_NULL_HANDLE, false };
|
||||
}
|
||||
|
||||
VkDescriptorSet sets[VULKAN_NUM_SETS_PER_POOL];
|
||||
VkDescriptorSetLayout layouts[VULKAN_NUM_SETS_PER_POOL];
|
||||
std::fill(std::begin(layouts), std::end(layouts), set_layout);
|
||||
|
||||
VkDescriptorSetAllocateInfo alloc = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO };
|
||||
alloc.descriptorPool = pool;
|
||||
alloc.descriptorSetCount = VULKAN_NUM_SETS_PER_POOL;
|
||||
alloc.pSetLayouts = layouts;
|
||||
|
||||
if (table.vkAllocateDescriptorSets(device->get_device(), &alloc, sets) != VK_SUCCESS)
|
||||
LOGE("Failed to allocate descriptor sets.\n");
|
||||
state.pools.push_back(pool);
|
||||
|
||||
for (auto set : sets)
|
||||
state.set_nodes.make_vacant(set);
|
||||
|
||||
return { state.set_nodes.request_vacant(hash)->set, false };
|
||||
VkDescriptorSet vk_set = state.pools[pool_index]->sets[pool_offset];
|
||||
state.offset++;
|
||||
return vk_set;
|
||||
}
|
||||
|
||||
void DescriptorSetAllocator::clear()
|
||||
{
|
||||
for (auto &thr : per_thread)
|
||||
for (auto &state : per_thread_and_frame)
|
||||
{
|
||||
thr->set_nodes.clear();
|
||||
for (auto &pool : thr->pools)
|
||||
for (auto *obj : state.pools)
|
||||
{
|
||||
table.vkResetDescriptorPool(device->get_device(), pool, 0);
|
||||
table.vkDestroyDescriptorPool(device->get_device(), pool, nullptr);
|
||||
table.vkDestroyDescriptorPool(device->get_device(), obj->pool, nullptr);
|
||||
state.object_pool.free(obj);
|
||||
}
|
||||
thr->pools.clear();
|
||||
state.pools.clear();
|
||||
state.offset = 0;
|
||||
state.object_pool = {};
|
||||
}
|
||||
}
|
||||
|
||||
DescriptorSetAllocator::~DescriptorSetAllocator()
|
||||
{
|
||||
if (set_layout != VK_NULL_HANDLE)
|
||||
table.vkDestroyDescriptorSetLayout(device->get_device(), set_layout, nullptr);
|
||||
table.vkDestroyDescriptorSetLayout(device->get_device(), set_layout_pool, nullptr);
|
||||
table.vkDestroyDescriptorSetLayout(device->get_device(), set_layout_push, nullptr);
|
||||
clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -56,8 +56,8 @@ struct DescriptorSetLayout
|
||||
|
||||
// Avoid -Wclass-memaccess warnings since we hash DescriptorSetLayout.
|
||||
|
||||
static const unsigned VULKAN_NUM_SETS_PER_POOL = 16;
|
||||
static const unsigned VULKAN_DESCRIPTOR_RING_SIZE = 8;
|
||||
static const unsigned VULKAN_NUM_SETS_PER_POOL = 64;
|
||||
static const unsigned VULKAN_DESCRIPTOR_RING_SIZE = 16;
|
||||
|
||||
class DescriptorSetAllocator;
|
||||
class BindlessDescriptorPool;
|
||||
@@ -121,11 +121,16 @@ public:
|
||||
DescriptorSetAllocator(const DescriptorSetAllocator &) = delete;
|
||||
|
||||
void begin_frame();
|
||||
std::pair<VkDescriptorSet, bool> find(unsigned thread_index, Util::Hash hash);
|
||||
VkDescriptorSet request_descriptor_set(unsigned thread_index, unsigned frame_context);
|
||||
|
||||
VkDescriptorSetLayout get_layout() const
|
||||
VkDescriptorSetLayout get_layout_for_pool() const
|
||||
{
|
||||
return set_layout;
|
||||
return set_layout_pool;
|
||||
}
|
||||
|
||||
VkDescriptorSetLayout get_layout_for_push() const
|
||||
{
|
||||
return set_layout_push;
|
||||
}
|
||||
|
||||
void clear();
|
||||
@@ -140,27 +145,25 @@ public:
|
||||
void reset_bindless_pool(VkDescriptorPool pool);
|
||||
|
||||
private:
|
||||
struct DescriptorSetNode : Util::TemporaryHashmapEnabled<DescriptorSetNode>, Util::IntrusiveListEnabled<DescriptorSetNode>
|
||||
{
|
||||
explicit DescriptorSetNode(VkDescriptorSet set_)
|
||||
: set(set_)
|
||||
{
|
||||
}
|
||||
|
||||
VkDescriptorSet set;
|
||||
};
|
||||
|
||||
Device *device;
|
||||
const VolkDeviceTable &table;
|
||||
VkDescriptorSetLayout set_layout = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout set_layout_pool = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout set_layout_push = VK_NULL_HANDLE;
|
||||
|
||||
struct PerThread
|
||||
struct Pool
|
||||
{
|
||||
Util::TemporaryHashmap<DescriptorSetNode, VULKAN_DESCRIPTOR_RING_SIZE, true> set_nodes;
|
||||
std::vector<VkDescriptorPool> pools;
|
||||
bool should_begin = true;
|
||||
VkDescriptorPool pool;
|
||||
VkDescriptorSet sets[VULKAN_NUM_SETS_PER_POOL];
|
||||
};
|
||||
std::vector<std::unique_ptr<PerThread>> per_thread;
|
||||
|
||||
struct PerThreadAndFrame
|
||||
{
|
||||
std::vector<Pool *> pools;
|
||||
Util::ObjectPool<Pool> object_pool;
|
||||
uint32_t offset = 0;
|
||||
};
|
||||
|
||||
std::vector<PerThreadAndFrame> per_thread_and_frame;
|
||||
std::vector<VkDescriptorPoolSize> pool_size;
|
||||
bool bindless = false;
|
||||
};
|
||||
|
||||
@@ -502,6 +502,8 @@ const PipelineLayout *Device::request_pipeline_layout(const CombinedResourceLayo
|
||||
h.data(layout.spec_constant_mask, sizeof(layout.spec_constant_mask));
|
||||
h.u32(layout.attribute_mask);
|
||||
h.u32(layout.render_target_mask);
|
||||
// Drivers with and without push descriptor support need to observe different hashes for Fossilize.
|
||||
h.s32(int(ext.supports_push_descriptor && !workarounds.broken_push_descriptors));
|
||||
for (unsigned set = 0; set < VULKAN_NUM_DESCRIPTOR_SETS; set++)
|
||||
{
|
||||
Util::for_each_bit(layout.sets[set].immutable_sampler_mask, [&](unsigned bit) {
|
||||
@@ -853,7 +855,11 @@ void Device::init_workarounds()
|
||||
// Events are not supported in MoltenVK.
|
||||
// TODO: Use VK_KHR_portability_subset to determine this.
|
||||
workarounds.emulate_event_as_pipeline_barrier = true;
|
||||
// MoltenVK is broken with push descriptor templates.
|
||||
// KhronosGroup/MoltenVK issue 2323.
|
||||
workarounds.broken_push_descriptors = true;
|
||||
LOGW("Emulating events as pipeline barriers on Metal emulation.\n");
|
||||
LOGW("Disabling push descriptors on Metal emulation.\n");
|
||||
#else
|
||||
bool sync2_workarounds = false;
|
||||
const bool mesa_driver = ext.driver_id == VK_DRIVER_ID_MESA_RADV ||
|
||||
@@ -874,11 +880,21 @@ void Device::init_workarounds()
|
||||
workarounds.emulate_event_as_pipeline_barrier = true;
|
||||
}
|
||||
|
||||
if (ext.driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY && gpu_props.driverVersion < VK_VERSION_MAJOR(535))
|
||||
// For whatever ridiculous reason, pipeline cache control causes GPU hangs on Pascal cards in parallel-rdp.
|
||||
// Use mesh shaders as the sentinel to check for that.
|
||||
if (ext.driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY &&
|
||||
(gpu_props.driverVersion < VK_VERSION_MAJOR(535) ||
|
||||
!ext.mesh_shader_features.meshShader))
|
||||
{
|
||||
LOGW("Disabling pipeline cache control.\n");
|
||||
workarounds.broken_pipeline_cache_control = true;
|
||||
}
|
||||
else if (ext.driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY_KHR)
|
||||
{
|
||||
// Seems broken on this driver too. Compilation stutter galore ...
|
||||
LOGW("Disabling pipeline cache control.\n");
|
||||
workarounds.broken_pipeline_cache_control = true;
|
||||
}
|
||||
|
||||
if (sync2_workarounds)
|
||||
{
|
||||
@@ -890,6 +906,12 @@ void Device::init_workarounds()
|
||||
// Avoids having to add workaround path to events as well, just fallback to plain barriers.
|
||||
workarounds.emulate_event_as_pipeline_barrier = true;
|
||||
}
|
||||
|
||||
// I cannot reproduce this myself, but there are several users experiencing GPU hangs with push descriptors
|
||||
// on AMD drivers (not RADV), so :shrug:.
|
||||
// https://github.com/simple64/simple64/issues/449
|
||||
if (ext.driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE || ext.driver_id == VK_DRIVER_ID_AMD_PROPRIETARY)
|
||||
workarounds.broken_push_descriptors = true;
|
||||
#endif
|
||||
|
||||
if (ext.supports_tooling_info && vkGetPhysicalDeviceToolPropertiesEXT)
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
namespace Vulkan
|
||||
{
|
||||
constexpr unsigned VULKAN_NUM_DESCRIPTOR_SETS = 4;
|
||||
constexpr unsigned VULKAN_NUM_DYNAMIC_UBOS = 8; // Vulkan min-spec
|
||||
constexpr unsigned VULKAN_NUM_BINDINGS = 32;
|
||||
constexpr unsigned VULKAN_NUM_BINDINGS_BINDLESS_VARYING = 16 * 1024;
|
||||
constexpr unsigned VULKAN_NUM_ATTACHMENTS = 8;
|
||||
|
||||
@@ -46,5 +46,6 @@ struct ImplementationWorkarounds
|
||||
bool broken_pipeline_cache_control = false;
|
||||
bool force_host_cached = false;
|
||||
bool force_sync1_access = false;
|
||||
bool broken_push_descriptors = false;
|
||||
};
|
||||
}
|
||||
@@ -210,7 +210,7 @@ private:
|
||||
uint32_t height = 0;
|
||||
};
|
||||
|
||||
static const unsigned VULKAN_FRAMEBUFFER_RING_SIZE = 8;
|
||||
static const unsigned VULKAN_FRAMEBUFFER_RING_SIZE = 16;
|
||||
class FramebufferAllocator
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -65,16 +65,24 @@ PipelineLayout::PipelineLayout(Hash hash, Device *device_, const CombinedResourc
|
||||
{
|
||||
set_allocators[i] = device->request_descriptor_set_allocator(layout.sets[i], layout.stages_for_bindings[i],
|
||||
immutable_samplers ? immutable_samplers->samplers[i] : nullptr);
|
||||
layouts[i] = set_allocators[i]->get_layout();
|
||||
layouts[i] = set_allocators[i]->get_layout_for_pool();
|
||||
if (layout.descriptor_set_mask & (1u << i))
|
||||
{
|
||||
num_sets = i + 1;
|
||||
|
||||
// Assume the last set index in layout is the highest frequency update one, make that push descriptor if possible.
|
||||
// Only one descriptor set can be push descriptor.
|
||||
bool has_push_layout = set_allocators[i]->get_layout_for_push() != VK_NULL_HANDLE;
|
||||
if (has_push_layout)
|
||||
push_set_index = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (num_sets > device->get_gpu_properties().limits.maxBoundDescriptorSets)
|
||||
{
|
||||
LOGE("Number of sets %u exceeds device limit of %u.\n",
|
||||
num_sets, device->get_gpu_properties().limits.maxBoundDescriptorSets);
|
||||
}
|
||||
if (push_set_index != UINT32_MAX)
|
||||
layouts[push_set_index] = set_allocators[push_set_index]->get_layout_for_push();
|
||||
|
||||
if (num_sets > VULKAN_NUM_DESCRIPTOR_SETS)
|
||||
LOGE("Number of sets %u exceeds limit of %u.\n", num_sets, VULKAN_NUM_DESCRIPTOR_SETS);
|
||||
|
||||
VkPipelineLayoutCreateInfo info = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
|
||||
if (num_sets)
|
||||
@@ -120,127 +128,169 @@ void PipelineLayout::create_update_templates()
|
||||
for_each_bit(set_layout.uniform_buffer_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
VK_ASSERT(update_count < VULKAN_NUM_BINDINGS);
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = 0;
|
||||
entry.descriptorCount = array_size;
|
||||
entry.offset = offsetof(ResourceBinding, buffer) + sizeof(ResourceBinding) * binding;
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
// Work around a RenderDoc capture bug where descriptorCount > 1 is not handled correctly.
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = desc_set == push_set_index ?
|
||||
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = i;
|
||||
entry.descriptorCount = 1;
|
||||
if (desc_set == push_set_index)
|
||||
entry.offset = offsetof(ResourceBinding, buffer.push) + sizeof(ResourceBinding) * (binding + i);
|
||||
else
|
||||
entry.offset = offsetof(ResourceBinding, buffer.dynamic) + sizeof(ResourceBinding) * (binding + i);
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
}
|
||||
});
|
||||
|
||||
for_each_bit(set_layout.storage_buffer_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
VK_ASSERT(update_count < VULKAN_NUM_BINDINGS);
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = 0;
|
||||
entry.descriptorCount = array_size;
|
||||
entry.offset = offsetof(ResourceBinding, buffer) + sizeof(ResourceBinding) * binding;
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = i;
|
||||
entry.descriptorCount = 1;
|
||||
entry.offset = offsetof(ResourceBinding, buffer.dynamic) + sizeof(ResourceBinding) * (binding + i);
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
}
|
||||
});
|
||||
|
||||
for_each_bit(set_layout.sampled_texel_buffer_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
VK_ASSERT(update_count < VULKAN_NUM_BINDINGS);
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = 0;
|
||||
entry.descriptorCount = array_size;
|
||||
entry.offset = offsetof(ResourceBinding, buffer_view) + sizeof(ResourceBinding) * binding;
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = i;
|
||||
entry.descriptorCount = 1;
|
||||
entry.offset = offsetof(ResourceBinding, buffer_view) + sizeof(ResourceBinding) * (binding + i);
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
}
|
||||
});
|
||||
|
||||
for_each_bit(set_layout.storage_texel_buffer_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
VK_ASSERT(update_count < VULKAN_NUM_BINDINGS);
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = 0;
|
||||
entry.descriptorCount = array_size;
|
||||
entry.offset = offsetof(ResourceBinding, buffer_view) + sizeof(ResourceBinding) * binding;
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = i;
|
||||
entry.descriptorCount = 1;
|
||||
entry.offset = offsetof(ResourceBinding, buffer_view) + sizeof(ResourceBinding) * (binding + i);
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
}
|
||||
});
|
||||
|
||||
for_each_bit(set_layout.sampled_image_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
VK_ASSERT(update_count < VULKAN_NUM_BINDINGS);
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = 0;
|
||||
entry.descriptorCount = array_size;
|
||||
if (set_layout.fp_mask & (1u << binding))
|
||||
entry.offset = offsetof(ResourceBinding, image.fp) + sizeof(ResourceBinding) * binding;
|
||||
else
|
||||
entry.offset = offsetof(ResourceBinding, image.integer) + sizeof(ResourceBinding) * binding;
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = i;
|
||||
entry.descriptorCount = 1;
|
||||
if (set_layout.fp_mask & (1u << binding))
|
||||
entry.offset = offsetof(ResourceBinding, image.fp) + sizeof(ResourceBinding) * (binding + i);
|
||||
else
|
||||
entry.offset = offsetof(ResourceBinding, image.integer) + sizeof(ResourceBinding) * (binding + i);
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
}
|
||||
});
|
||||
|
||||
for_each_bit(set_layout.separate_image_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
VK_ASSERT(update_count < VULKAN_NUM_BINDINGS);
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = 0;
|
||||
entry.descriptorCount = array_size;
|
||||
if (set_layout.fp_mask & (1u << binding))
|
||||
entry.offset = offsetof(ResourceBinding, image.fp) + sizeof(ResourceBinding) * binding;
|
||||
else
|
||||
entry.offset = offsetof(ResourceBinding, image.integer) + sizeof(ResourceBinding) * binding;
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = i;
|
||||
entry.descriptorCount = 1;
|
||||
if (set_layout.fp_mask & (1u << binding))
|
||||
entry.offset = offsetof(ResourceBinding, image.fp) + sizeof(ResourceBinding) * (binding + i);
|
||||
else
|
||||
entry.offset = offsetof(ResourceBinding, image.integer) + sizeof(ResourceBinding) * (binding + i);
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
}
|
||||
});
|
||||
|
||||
for_each_bit(set_layout.sampler_mask & ~set_layout.immutable_sampler_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
VK_ASSERT(update_count < VULKAN_NUM_BINDINGS);
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = 0;
|
||||
entry.descriptorCount = array_size;
|
||||
entry.offset = offsetof(ResourceBinding, image.fp) + sizeof(ResourceBinding) * binding;
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = i;
|
||||
entry.descriptorCount = 1;
|
||||
entry.offset = offsetof(ResourceBinding, image.fp) + sizeof(ResourceBinding) * (binding + i);
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
}
|
||||
});
|
||||
|
||||
for_each_bit(set_layout.storage_image_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
VK_ASSERT(update_count < VULKAN_NUM_BINDINGS);
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = 0;
|
||||
entry.descriptorCount = array_size;
|
||||
if (set_layout.fp_mask & (1u << binding))
|
||||
entry.offset = offsetof(ResourceBinding, image.fp) + sizeof(ResourceBinding) * binding;
|
||||
else
|
||||
entry.offset = offsetof(ResourceBinding, image.integer) + sizeof(ResourceBinding) * binding;
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = i;
|
||||
entry.descriptorCount = 1;
|
||||
if (set_layout.fp_mask & (1u << binding))
|
||||
entry.offset = offsetof(ResourceBinding, image.fp) + sizeof(ResourceBinding) * (binding + i);
|
||||
else
|
||||
entry.offset = offsetof(ResourceBinding, image.integer) + sizeof(ResourceBinding) * (binding + i);
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
}
|
||||
});
|
||||
|
||||
for_each_bit(set_layout.input_attachment_mask, [&](uint32_t binding) {
|
||||
unsigned array_size = set_layout.array_size[binding];
|
||||
VK_ASSERT(update_count < VULKAN_NUM_BINDINGS);
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = 0;
|
||||
entry.descriptorCount = array_size;
|
||||
if (set_layout.fp_mask & (1u << binding))
|
||||
entry.offset = offsetof(ResourceBinding, image.fp) + sizeof(ResourceBinding) * binding;
|
||||
else
|
||||
entry.offset = offsetof(ResourceBinding, image.integer) + sizeof(ResourceBinding) * binding;
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
for (unsigned i = 0; i < array_size; i++)
|
||||
{
|
||||
auto &entry = update_entries[update_count++];
|
||||
entry.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
|
||||
entry.dstBinding = binding;
|
||||
entry.dstArrayElement = i;
|
||||
entry.descriptorCount = 1;
|
||||
if (set_layout.fp_mask & (1u << binding))
|
||||
entry.offset = offsetof(ResourceBinding, image.fp) + sizeof(ResourceBinding) * (binding + i);
|
||||
else
|
||||
entry.offset = offsetof(ResourceBinding, image.integer) + sizeof(ResourceBinding) * (binding + i);
|
||||
entry.stride = sizeof(ResourceBinding);
|
||||
}
|
||||
});
|
||||
|
||||
VkDescriptorUpdateTemplateCreateInfo info = {VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO };
|
||||
VkDescriptorUpdateTemplateCreateInfo info = { VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO };
|
||||
info.pipelineLayout = pipe_layout;
|
||||
info.descriptorSetLayout = set_allocators[desc_set]->get_layout();
|
||||
info.templateType = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET;
|
||||
|
||||
if (desc_set == push_set_index)
|
||||
{
|
||||
info.descriptorSetLayout = set_allocators[desc_set]->get_layout_for_push();
|
||||
info.templateType = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR;
|
||||
}
|
||||
else
|
||||
{
|
||||
info.descriptorSetLayout = set_allocators[desc_set]->get_layout_for_pool();
|
||||
info.templateType = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET;
|
||||
}
|
||||
|
||||
info.set = desc_set;
|
||||
info.descriptorUpdateEntryCount = update_count;
|
||||
info.pDescriptorUpdateEntries = update_entries;
|
||||
|
||||
@@ -83,18 +83,21 @@ struct CombinedResourceLayout
|
||||
Util::Hash push_constant_layout_hash = 0;
|
||||
};
|
||||
|
||||
struct ResourceBinding
|
||||
union ResourceBinding
|
||||
{
|
||||
union {
|
||||
VkDescriptorBufferInfo buffer;
|
||||
struct
|
||||
{
|
||||
VkDescriptorImageInfo fp;
|
||||
VkDescriptorImageInfo integer;
|
||||
} image;
|
||||
VkBufferView buffer_view;
|
||||
};
|
||||
VkDeviceSize dynamic_offset;
|
||||
struct
|
||||
{
|
||||
VkDescriptorBufferInfo dynamic;
|
||||
VkDescriptorBufferInfo push;
|
||||
} buffer;
|
||||
|
||||
struct
|
||||
{
|
||||
VkDescriptorImageInfo fp;
|
||||
VkDescriptorImageInfo integer;
|
||||
} image;
|
||||
|
||||
VkBufferView buffer_view;
|
||||
};
|
||||
|
||||
struct ResourceBindings
|
||||
@@ -138,12 +141,18 @@ public:
|
||||
return update_template[set];
|
||||
}
|
||||
|
||||
uint32_t get_push_set_index() const
|
||||
{
|
||||
return push_set_index;
|
||||
}
|
||||
|
||||
private:
|
||||
Device *device;
|
||||
VkPipelineLayout pipe_layout = VK_NULL_HANDLE;
|
||||
CombinedResourceLayout layout;
|
||||
DescriptorSetAllocator *set_allocators[VULKAN_NUM_DESCRIPTOR_SETS] = {};
|
||||
VkDescriptorUpdateTemplate update_template[VULKAN_NUM_DESCRIPTOR_SETS] = {};
|
||||
uint32_t push_set_index = UINT32_MAX;
|
||||
void create_update_templates();
|
||||
};
|
||||
|
||||
|
||||
@@ -35,11 +35,16 @@
|
||||
#include "logging.hpp"
|
||||
#include <utility>
|
||||
|
||||
#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
|
||||
// Workaround silly Xlib headers that define macros for these globally :(
|
||||
#ifdef None
|
||||
#undef None
|
||||
#endif
|
||||
#ifdef Bool
|
||||
#undef Bool
|
||||
#endif
|
||||
#ifdef Status
|
||||
#undef Status
|
||||
#endif
|
||||
|
||||
#ifdef VULKAN_DEBUG
|
||||
#define VK_ASSERT(x) \
|
||||
|
||||
@@ -222,6 +222,9 @@ bool WSI::init_surface_swapchain_dxgi(unsigned width, unsigned height)
|
||||
case BackbufferFormat::HDR10:
|
||||
format = { VK_FORMAT_A2B10G10R10_UNORM_PACK32, VK_COLOR_SPACE_HDR10_ST2084_EXT };
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr unsigned num_images = 3;
|
||||
@@ -411,11 +414,13 @@ void WSI::nonblock_delete_swapchains()
|
||||
{
|
||||
if (!swap.fence || swap.fence->wait_timeout(0))
|
||||
{
|
||||
platform->destroy_swapchain_resources(swap.swapchain);
|
||||
table->vkDestroySwapchainKHR(device->get_device(), swap.swapchain, nullptr);
|
||||
}
|
||||
else if (pending >= 2)
|
||||
{
|
||||
swap.fence->wait();
|
||||
platform->destroy_swapchain_resources(swap.swapchain);
|
||||
table->vkDestroySwapchainKHR(device->get_device(), swap.swapchain, nullptr);
|
||||
}
|
||||
else
|
||||
@@ -448,6 +453,7 @@ void WSI::drain_swapchain(bool in_tear_down)
|
||||
{
|
||||
if (old_swap.fence)
|
||||
old_swap.fence->wait();
|
||||
platform->destroy_swapchain_resources(old_swap.swapchain);
|
||||
table->vkDestroySwapchainKHR(context->get_device(), old_swap.swapchain, nullptr);
|
||||
}
|
||||
|
||||
@@ -475,6 +481,7 @@ void WSI::tear_down_swapchain()
|
||||
|
||||
drain_swapchain(true);
|
||||
platform->event_swapchain_destroyed();
|
||||
platform->destroy_swapchain_resources(swapchain);
|
||||
table->vkDestroySwapchainKHR(context->get_device(), swapchain, nullptr);
|
||||
swapchain = VK_NULL_HANDLE;
|
||||
has_acquired_swapchain_index = false;
|
||||
@@ -1664,6 +1671,7 @@ WSI::SwapchainError WSI::init_swapchain(unsigned width, unsigned height)
|
||||
|
||||
platform->event_swapchain_destroyed();
|
||||
auto res = table->vkCreateSwapchainKHR(context->get_device(), &info, nullptr, &swapchain);
|
||||
platform->destroy_swapchain_resources(old_swapchain);
|
||||
table->vkDestroySwapchainKHR(context->get_device(), old_swapchain, nullptr);
|
||||
has_acquired_swapchain_index = false;
|
||||
present_id = 0;
|
||||
@@ -1749,6 +1757,7 @@ void WSIPlatform::event_swapchain_created(Device *, VkSwapchainKHR, unsigned, un
|
||||
VkFormat, VkColorSpaceKHR,
|
||||
VkSurfaceTransformFlagBitsKHR) {}
|
||||
void WSIPlatform::event_swapchain_destroyed() {}
|
||||
void WSIPlatform::destroy_swapchain_resources(VkSwapchainKHR) {}
|
||||
void WSIPlatform::event_frame_tick(double, double) {}
|
||||
void WSIPlatform::event_swapchain_index(Device *, unsigned) {}
|
||||
void WSIPlatform::begin_drop_event() {}
|
||||
|
||||
@@ -118,6 +118,7 @@ public:
|
||||
float aspect_ratio, size_t num_swapchain_images,
|
||||
VkFormat format, VkColorSpaceKHR color_space,
|
||||
VkSurfaceTransformFlagBitsKHR pre_rotate);
|
||||
virtual void destroy_swapchain_resources(VkSwapchainKHR swapchain);
|
||||
virtual void event_swapchain_destroyed();
|
||||
virtual void event_frame_tick(double frame, double elapsed);
|
||||
virtual void event_swapchain_index(Device *device, unsigned index);
|
||||
|
||||
@@ -50,10 +50,10 @@ auto NTTDataKeypad::data() -> n2 {
|
||||
case 10: return l->value();
|
||||
case 11: return r->value();
|
||||
|
||||
case 12: return 1; //4-bit device signature
|
||||
case 13: return 0;
|
||||
case 14: return 1;
|
||||
case 15: return 1;
|
||||
case 12: return 0; //4-bit device signature
|
||||
case 13: return 1;
|
||||
case 14: return 0;
|
||||
case 15: return 0;
|
||||
|
||||
case 16: return zero->value();
|
||||
case 17: return one->value();
|
||||
|
||||
@@ -8,7 +8,7 @@ struct DIP {
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
n8 value = 0x00;
|
||||
n8 value = 0x03; // default game time used for the competition is 6 minutes
|
||||
};
|
||||
|
||||
extern DIP dip;
|
||||
@@ -3,7 +3,7 @@ auto SA1::dmaNormal() -> void {
|
||||
while(io.dtc--) {
|
||||
n8 data = r.mdr;
|
||||
n24 source = io.dsa++;
|
||||
n16 target = io.dda++;
|
||||
n24 target = io.dda++;
|
||||
|
||||
if(io.sd == DMA::SourceROM && io.dd == DMA::DestBWRAM) {
|
||||
step();
|
||||
|
||||
@@ -152,7 +152,7 @@ auto SDD1::Decompressor::PEM::getBit(n8 context) -> n8 {
|
||||
const State& s = SDD1::Decompressor::PEM::evolutionTable[currentStatus];
|
||||
|
||||
n8 bit;
|
||||
bool endOfRun;
|
||||
bool endOfRun = false;
|
||||
switch(s.codeNumber) {
|
||||
case 0: bit = self.bg0.getBit(endOfRun); break;
|
||||
case 1: bit = self.bg1.getBit(endOfRun); break;
|
||||
|
||||
@@ -49,7 +49,7 @@ auto SuperFX::rpix(n8 x, n8 y) -> n8 {
|
||||
flushPixelCache(pixelcache[1]);
|
||||
flushPixelCache(pixelcache[0]);
|
||||
|
||||
u32 cn; //character number
|
||||
u32 cn = 0; //character number
|
||||
switch(regs.por.obj ? 3 : regs.scmr.ht) {
|
||||
case 0: cn = ((x & 0xf8) << 1) + ((y & 0xf8) >> 3); break;
|
||||
case 1: cn = ((x & 0xf8) << 1) + ((x & 0xf8) >> 1) + ((y & 0xf8) >> 3); break;
|
||||
@@ -76,7 +76,7 @@ auto SuperFX::flushPixelCache(PixelCache& cache) -> void {
|
||||
n8 x = cache.offset << 3;
|
||||
n8 y = cache.offset >> 5;
|
||||
|
||||
u32 cn; //character number
|
||||
u32 cn = 0; //character number
|
||||
switch(regs.por.obj ? 3 : regs.scmr.ht) {
|
||||
case 0: cn = ((x & 0xf8) << 1) + ((y & 0xf8) >> 3); break;
|
||||
case 1: cn = ((x & 0xf8) << 1) + ((x & 0xf8) >> 1) + ((y & 0xf8) >> 3); break;
|
||||
|
||||
@@ -38,7 +38,8 @@ auto PPU::Window::render(Layer& layer, bool enable, bool output[448]) -> void {
|
||||
}
|
||||
|
||||
auto PPU::Window::render(Color& color, u32 mask, bool output[448]) -> void {
|
||||
bool set, clear;
|
||||
bool set = 0;
|
||||
bool clear = 0;
|
||||
switch(mask) {
|
||||
case 0: memory::fill<bool>(output, 256, 1); return; //always
|
||||
case 1: set = 1, clear = 0; break; //inside
|
||||
|
||||
@@ -90,12 +90,12 @@ struct PPU : PPUBase::Implementation, PPUcounter {
|
||||
|
||||
//main.cpp
|
||||
auto main() -> void;
|
||||
noinline auto cycleObjectEvaluate() -> void;
|
||||
template<u32 Cycle> noinline auto cycleBackgroundFetch() -> void;
|
||||
noinline auto cycleBackgroundBegin() -> void;
|
||||
noinline auto cycleBackgroundBelow() -> void;
|
||||
noinline auto cycleBackgroundAbove() -> void;
|
||||
noinline auto cycleRenderPixel() -> void;
|
||||
NALL_NOINLINE auto cycleObjectEvaluate() -> void;
|
||||
template<u32 Cycle> NALL_NOINLINE auto cycleBackgroundFetch() -> void;
|
||||
NALL_NOINLINE auto cycleBackgroundBegin() -> void;
|
||||
NALL_NOINLINE auto cycleBackgroundBelow() -> void;
|
||||
NALL_NOINLINE auto cycleBackgroundAbove() -> void;
|
||||
NALL_NOINLINE auto cycleRenderPixel() -> void;
|
||||
template<u32> auto cycle() -> void;
|
||||
|
||||
//io.cpp
|
||||
|
||||
@@ -138,15 +138,17 @@ endif
|
||||
$(call rcopy,$(mia.path)/Database/*,$(output.path)/$(name).app/Contents/Resources/Database/)
|
||||
sips -s format icns resource/$(name).png --out $(output.path)/$(name).app/Contents/Resources/$(name).icns
|
||||
codesign --force --deep --options runtime --entitlements resource/$(name).selfsigned.entitlements --sign - $(output.path)/$(name).app
|
||||
else ifeq ($(platform),windows)
|
||||
else ifneq ($(filter $(platform),windows linux bsd),)
|
||||
$(call mkdir,$(output.path)/Shaders/)
|
||||
$(call mkdir,$(output.path)/Database/)
|
||||
$(call rcopy,$(thirdparty.path)/slang-shaders/*,$(output.path)/Shaders/)
|
||||
$(call rcopy,$(mia.path)/Database/*,$(output.path)/Database/)
|
||||
ifeq ($(librashader), true)
|
||||
ifdef librashader.path
|
||||
$(call copy,$(librashader.path),$(output.path)/)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
verbose: nall.verbose ruby.verbose hiro.verbose all;
|
||||
|
||||
|
||||
@@ -138,7 +138,7 @@ auto nall::main(Arguments arguments) -> void {
|
||||
|
||||
program.startGameLoad.reset();
|
||||
for(auto argument : arguments) {
|
||||
if(file::exists(argument)) program.startGameLoad.append(argument);
|
||||
if(file::exists(argument) || directory::exists(argument)) program.startGameLoad.append(argument);
|
||||
}
|
||||
|
||||
Instances::presentation.construct();
|
||||
|
||||
@@ -46,6 +46,8 @@ struct Emulator {
|
||||
vector<Firmware> firmware;
|
||||
shared_pointer<mia::Pak> system;
|
||||
shared_pointer<mia::Pak> game;
|
||||
shared_pointer<mia::Pak> gamepad;
|
||||
shared_pointer<mia::Pak> gb;
|
||||
vector<InputPort> ports;
|
||||
vector<string> inputBlacklist;
|
||||
vector<string> portBlacklist;
|
||||
|
||||
@@ -39,6 +39,7 @@ namespace ares::Atari2600 {
|
||||
#ifdef CORE_GBA
|
||||
namespace ares::GameBoyAdvance {
|
||||
auto load(Node::System& node, string name) -> bool;
|
||||
auto option(string name, string value) -> bool;
|
||||
}
|
||||
#include "game-boy-advance.cpp"
|
||||
#endif
|
||||
|
||||
@@ -57,6 +57,8 @@ auto GameBoyAdvance::load() -> bool {
|
||||
system = mia::System::create("Game Boy Advance");
|
||||
if(!system->load(firmware[0].location)) return errorFirmware(firmware[0]), false;
|
||||
|
||||
ares::GameBoyAdvance::option("Pixel Accuracy", settings.video.pixelAccuracy);
|
||||
|
||||
if(!ares::GameBoyAdvance::load(root, "[Nintendo] Game Boy Advance")) return false;
|
||||
|
||||
if(auto port = root->find<ares::Node::Port>("Cartridge Slot")) {
|
||||
|
||||
@@ -84,6 +84,8 @@ auto MegaDrive::load() -> bool {
|
||||
if(!system->load()) return false;
|
||||
}
|
||||
|
||||
ares::MegaDrive::option("TMSS", settings.megadrive.tmss);
|
||||
|
||||
if(!ares::MegaDrive::load(root, {"[Sega] ", name, " (", region, ")"})) return false;
|
||||
|
||||
if(auto port = root->find<ares::Node::Port>("Cartridge Slot")) {
|
||||
|
||||
@@ -6,9 +6,7 @@ struct Nintendo64 : Emulator {
|
||||
auto save() -> bool override;
|
||||
auto pak(ares::Node::Object) -> shared_pointer<vfs::directory> override;
|
||||
|
||||
shared_pointer<mia::Pak> gamepad;
|
||||
shared_pointer<mia::Pak> disk;
|
||||
shared_pointer<mia::Pak> gb;
|
||||
u32 regionID = 0;
|
||||
sTimer diskInsertTimer;
|
||||
};
|
||||
|
||||
@@ -6,7 +6,6 @@ struct Nintendo64DD : Emulator {
|
||||
auto save() -> bool override;
|
||||
auto pak(ares::Node::Object) -> shared_pointer<vfs::directory> override;
|
||||
|
||||
shared_pointer<mia::Pak> gamepad;
|
||||
u32 regionID = 0;
|
||||
sTimer diskInsertTimer;
|
||||
};
|
||||
@@ -95,16 +94,39 @@ auto Nintendo64DD::load() -> bool {
|
||||
if(auto port = root->find<ares::Node::Port>({"Controller Port ", 1 + id})) {
|
||||
auto peripheral = port->allocate("Gamepad");
|
||||
port->connect();
|
||||
bool transferPakConnected = false;
|
||||
if(auto port = peripheral->find<ares::Node::Port>("Pak")) {
|
||||
if(id == 0 && game->pak->attribute("cpak").boolean()) {
|
||||
gamepad = mia::Pak::create("Nintendo 64");
|
||||
gamepad->pak->append("save.pak", 32_KiB);
|
||||
gamepad->load("save.pak", ".pak", game->location);
|
||||
port->allocate("Controller Pak");
|
||||
port->connect();
|
||||
} else if(game->pak->attribute("rpak").boolean()) {
|
||||
port->allocate("Rumble Pak");
|
||||
if(id == 0 && game->pak->attribute("tpak").boolean()) {
|
||||
#if defined(CORE_GB)
|
||||
auto transferPak = port->allocate("Transfer Pak");
|
||||
port->connect();
|
||||
|
||||
if(auto slot = transferPak->find<ares::Node::Port>("Cartridge Slot")) {
|
||||
gb = mia::Medium::create("Game Boy");
|
||||
string tmpPath;
|
||||
if(gb->load(Emulator::load(gb, tmpPath))) {
|
||||
slot->allocate();
|
||||
slot->connect();
|
||||
transferPakConnected = true;
|
||||
} else {
|
||||
port->disconnect();
|
||||
gb.reset();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if(!transferPakConnected) {
|
||||
if(id == 0 && game->pak->attribute("cpak").boolean()) {
|
||||
gamepad = mia::Pak::create("Nintendo 64");
|
||||
gamepad->pak->append("save.pak", 32_KiB);
|
||||
gamepad->load("save.pak", ".pak", game->location);
|
||||
port->allocate("Controller Pak");
|
||||
port->connect();
|
||||
} else if(game->pak->attribute("rpak").boolean()) {
|
||||
port->allocate("Rumble Pak");
|
||||
port->connect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,36 @@ SuperFamicom::SuperFamicom() {
|
||||
device.digital ("Right", virtualPorts[id].mouse.right);
|
||||
port.append(device); }
|
||||
|
||||
{ InputDevice device{"NTT Data Keypad"};
|
||||
device.digital("Up", virtualPorts[id].pad.up);
|
||||
device.digital("Down", virtualPorts[id].pad.down);
|
||||
device.digital("Left", virtualPorts[id].pad.left);
|
||||
device.digital("Right", virtualPorts[id].pad.right);
|
||||
device.digital("B", virtualPorts[id].pad.south);
|
||||
device.digital("A", virtualPorts[id].pad.east);
|
||||
device.digital("Y", virtualPorts[id].pad.west);
|
||||
device.digital("X", virtualPorts[id].pad.north);
|
||||
device.digital("L", virtualPorts[id].pad.l_bumper);
|
||||
device.digital("R", virtualPorts[id].pad.r_bumper);
|
||||
device.digital("Back", virtualPorts[id].pad.select);
|
||||
device.digital("Next", virtualPorts[id].pad.start);
|
||||
device.digital("1", virtualPorts[id].pad.one);
|
||||
device.digital("2", virtualPorts[id].pad.two);
|
||||
device.digital("3", virtualPorts[id].pad.three);
|
||||
device.digital("4", virtualPorts[id].pad.four);
|
||||
device.digital("5", virtualPorts[id].pad.five);
|
||||
device.digital("6", virtualPorts[id].pad.six);
|
||||
device.digital("7", virtualPorts[id].pad.seven);
|
||||
device.digital("8", virtualPorts[id].pad.eight);
|
||||
device.digital("9", virtualPorts[id].pad.nine);
|
||||
device.digital("0", virtualPorts[id].pad.zero);
|
||||
device.digital("*", virtualPorts[id].pad.star);
|
||||
device.digital("C", virtualPorts[id].pad.clear);
|
||||
device.digital("#", virtualPorts[id].pad.pound);
|
||||
device.digital(".", virtualPorts[id].pad.point);
|
||||
device.digital("End", virtualPorts[id].pad.end);
|
||||
port.append(device); }
|
||||
|
||||
{ InputDevice device{"Super Scope"};
|
||||
device.relative("X", virtualPorts[id].mouse.x);
|
||||
device.relative("Y", virtualPorts[id].mouse.y);
|
||||
@@ -60,7 +90,7 @@ SuperFamicom::SuperFamicom() {
|
||||
ports.append(port);
|
||||
}
|
||||
|
||||
inputBlacklist = {"Justifiers", "NTT Data Keypad", "Super Multitap"};
|
||||
inputBlacklist = {"Justifiers", "Super Multitap"};
|
||||
}
|
||||
|
||||
auto SuperFamicom::load() -> bool {
|
||||
|
||||
@@ -162,6 +162,21 @@ struct VirtualPad : InputDevice {
|
||||
InputAnalog rstick_left;
|
||||
InputAnalog rstick_right;
|
||||
InputRumble rumble;
|
||||
InputDigital one;
|
||||
InputDigital two;
|
||||
InputDigital three;
|
||||
InputDigital four;
|
||||
InputDigital five;
|
||||
InputDigital six;
|
||||
InputDigital seven;
|
||||
InputDigital eight;
|
||||
InputDigital nine;
|
||||
InputDigital zero;
|
||||
InputDigital star;
|
||||
InputDigital clear;
|
||||
InputDigital pound;
|
||||
InputDigital point;
|
||||
InputDigital end;
|
||||
};
|
||||
|
||||
struct VirtualMouse : InputDevice {
|
||||
|
||||
@@ -532,6 +532,115 @@ auto Presentation::refreshSystemMenu() -> void {
|
||||
if(peripheralItem.text() == connected->name()) peripheralItem.setChecked();
|
||||
}
|
||||
}
|
||||
|
||||
// Allow for additional paks for N64 gamepads
|
||||
if((emulator->name == "Nintendo 64" || emulator->name == "Nintendo 64DD")
|
||||
&& port->type() == "Controller") {
|
||||
const string portNum = port->name()[port->name().length() - 1];
|
||||
|
||||
// remove this check to enable pak menu option for all 4 controllers
|
||||
if(portNum == "1") {
|
||||
|
||||
if(portMenu.actionCount() > 0) portMenu.append(MenuSeparator());
|
||||
Menu pakMenu{&portMenu};
|
||||
pakMenu.setText("Pak");
|
||||
Group pakGroup;
|
||||
MenuRadioItem nothing{&pakMenu};;
|
||||
nothing.setText("Nothing");
|
||||
nothing.setAttribute<ares::Node::Port>("port", port);
|
||||
nothing.onActivate([=] {
|
||||
auto port = nothing.attribute<ares::Node::Port>("port");
|
||||
const string portName = port->name();
|
||||
if(auto port = emulator->root->find<ares::Node::Port>(portName)) {
|
||||
port->disconnect();
|
||||
auto peripheral = port->allocate("Gamepad");
|
||||
port->connect();
|
||||
}
|
||||
});
|
||||
pakGroup.append(nothing);
|
||||
|
||||
MenuRadioItem cpak{&pakMenu};
|
||||
cpak.setAttribute<ares::Node::Port>("port", port);
|
||||
cpak.setText("Controller Pak");
|
||||
cpak.onActivate([=] {
|
||||
auto port = cpak.attribute<ares::Node::Port>("port");
|
||||
const string portName = port->name();
|
||||
if(auto port = emulator->root->find<ares::Node::Port>(portName)) {
|
||||
port->disconnect();
|
||||
auto peripheral = port->allocate("Gamepad");
|
||||
port->connect();
|
||||
if(auto port = peripheral->find<ares::Node::Port>("Pak")) {
|
||||
emulator->gamepad = mia::Pak::create("Nintendo 64");
|
||||
emulator->gamepad->pak->append("save.pak", 32_KiB);
|
||||
string pakExt = ".pak";
|
||||
if(portNum != "1") { pakExt = string(".", portNum, ".pak");}
|
||||
emulator->gamepad->load("save.pak", pakExt, emulator->game->location);
|
||||
port->allocate("Controller Pak");
|
||||
port->connect();
|
||||
}
|
||||
}
|
||||
});
|
||||
pakGroup.append(cpak);
|
||||
|
||||
MenuRadioItem rpak{&pakMenu};
|
||||
rpak.setAttribute<ares::Node::Port>("port", port);
|
||||
rpak.setText("Rumble Pak");
|
||||
rpak.onActivate([=] {
|
||||
auto port = rpak.attribute<ares::Node::Port>("port");
|
||||
const string portName = port->name();
|
||||
if(auto port = emulator->root->find<ares::Node::Port>(portName)) {
|
||||
port->disconnect();
|
||||
auto peripheral = port->allocate("Gamepad");
|
||||
port->connect();
|
||||
if(auto port = peripheral->find<ares::Node::Port>("Pak")) {
|
||||
port->allocate("Rumble Pak");
|
||||
port->connect();
|
||||
}
|
||||
}
|
||||
});
|
||||
pakGroup.append(rpak);
|
||||
|
||||
MenuRadioItem tpak{&pakMenu};
|
||||
tpak.setAttribute<ares::Node::Port>("port", port);
|
||||
tpak.setText("Transfer Pak");
|
||||
tpak.onActivate([=] {
|
||||
auto port = tpak.attribute<ares::Node::Port>("port");
|
||||
const string portName = port->name();
|
||||
if(auto port = emulator->root->find<ares::Node::Port>(portName)) {
|
||||
port->disconnect();
|
||||
auto peripheral = port->allocate("Gamepad");
|
||||
port->connect();
|
||||
if(auto port = peripheral->find<ares::Node::Port>("Pak")) {
|
||||
#if defined(CORE_GB)
|
||||
emulator->gb.reset();
|
||||
auto transferPak = port->allocate("Transfer Pak");
|
||||
port->connect();
|
||||
|
||||
if(auto slot = transferPak->find<ares::Node::Port>("Cartridge Slot")) {
|
||||
emulator->gb = mia::Medium::create("Game Boy");
|
||||
string tmpPath;
|
||||
if(emulator->gb->load(emulator->load(emulator->gb, tmpPath))) {
|
||||
slot->allocate();
|
||||
slot->connect();
|
||||
} else {
|
||||
port->disconnect();
|
||||
emulator->gb.reset();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
});
|
||||
pakGroup.append(tpak);
|
||||
|
||||
// set currently enabled pak
|
||||
// Note: based on initialization routine in desktop-ui/emulator/nintendo-64.cpp & nintedo-64dd.cpp
|
||||
if(emulator->game->pak->attribute("tpak").boolean() && portNum == "1") tpak.setChecked();
|
||||
else if(emulator->game->pak->attribute("cpak").boolean() && portNum == "1") cpak.setChecked();
|
||||
else if(emulator->game->pak->attribute("rpak").boolean()) rpak.setChecked();
|
||||
else nothing.setChecked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(portsFound > 0) systemMenu.append(MenuSeparator());
|
||||
|
||||
@@ -43,5 +43,13 @@ auto OptionSettings::construct() -> void {
|
||||
});
|
||||
nintendo64ExpansionPakLayout.setAlignment(1).setPadding(12_sx, 0);
|
||||
nintendo64ExpansionPakHint.setText("Enable/Disable the 4MB Expansion Pak").setFont(Font().setSize(7.0)).setForegroundColor(SystemColor::Sublabel);
|
||||
|
||||
|
||||
megaDriveSettingsLabel.setText("Mega Drive Settings").setFont(Font().setBold());
|
||||
|
||||
megaDriveTmssOption.setText("TMSS Boot Rom").setChecked(settings.megadrive.tmss).onToggle([&] {
|
||||
settings.megadrive.tmss = megaDriveTmssOption.checked();
|
||||
});
|
||||
megaDriveTmssLayout.setAlignment(1).setPadding(12_sx, 0);
|
||||
megaDriveTmssHint.setText("Enable/Disable the TMSS Boot Rom at system initialization").setFont(Font().setSize(7.0)).setForegroundColor(SystemColor::Sublabel);
|
||||
|
||||
}
|
||||
@@ -124,6 +124,8 @@ auto Settings::process(bool load) -> void {
|
||||
|
||||
bind(boolean, "Nintendo64/ExpansionPak", nintendo64.expansionPak);
|
||||
|
||||
bind(boolean, "MegaDrive/TMSS", megadrive.tmss);
|
||||
|
||||
for(u32 index : range(9)) {
|
||||
string name = {"Recent/Game-", 1 + index};
|
||||
bind(string, name, recent.game[index]);
|
||||
|
||||
@@ -104,6 +104,10 @@ struct Settings : Markup::Node {
|
||||
struct Nintendo64 {
|
||||
bool expansionPak = true;
|
||||
} nintendo64;
|
||||
|
||||
struct MegaDrive {
|
||||
bool tmss = false;
|
||||
} megadrive;
|
||||
};
|
||||
|
||||
struct VideoSettings : VerticalLayout {
|
||||
@@ -258,6 +262,10 @@ struct OptionSettings : VerticalLayout {
|
||||
HorizontalLayout nintendo64ExpansionPakLayout{this, Size{~0, 0}, 5};
|
||||
CheckLabel nintendo64ExpansionPakOption{&nintendo64ExpansionPakLayout, Size{0, 0}, 5};
|
||||
Label nintendo64ExpansionPakHint{&nintendo64ExpansionPakLayout, Size{0, 0}};
|
||||
Label megaDriveSettingsLabel{this, Size{~0, 0}, 5};
|
||||
HorizontalLayout megaDriveTmssLayout{this, Size{~0, 0}, 5};
|
||||
CheckLabel megaDriveTmssOption{&megaDriveTmssLayout, Size{0, 0}, 5};
|
||||
Label megaDriveTmssHint{&megaDriveTmssLayout, Size{0, 0}};
|
||||
};
|
||||
|
||||
struct FirmwareSettings : VerticalLayout {
|
||||
|
||||
@@ -162,7 +162,10 @@
|
||||
NSPoint localPoint = [self convertPoint:event.locationInWindow fromView:nil];
|
||||
NSInteger row = [self rowAtPoint:localPoint];
|
||||
NSInteger column = [self columnAtPoint:localPoint];
|
||||
|
||||
|
||||
if (row < 0 || row >= tableView->state.items.size()) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (row >= 0 && ![self isRowSelected:row]) {
|
||||
[self selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
|
||||
|
||||
@@ -676,7 +676,7 @@ board: EVENT-CC92
|
||||
memory type=ROM content=Level-3
|
||||
dip
|
||||
processor manufacturer=NEC architecture=uPD7725
|
||||
map address=20-3f,a0-bf:8000-ffff mask=0x7fff
|
||||
map address=20-3f,a0-bf:8000-ffff mask=0x3fff
|
||||
memory type=ROM content=Program architecture=uPD7725
|
||||
memory type=ROM content=Data architecture=uPD7725
|
||||
memory type=RAM content=Data architecture=uPD7725
|
||||
|
||||
@@ -287,6 +287,60 @@ game
|
||||
size: 0x80000
|
||||
content: Download
|
||||
|
||||
//sha256 is without firmware
|
||||
game
|
||||
sha256: 67c1a4917f4cd0fd704b83330a7e91763736c2d2a10a7e12318fdac54cd9c6c6
|
||||
title: Campus Challenge '92
|
||||
name: Super Nintendo Campus Challenge 1992
|
||||
region: Japan
|
||||
revision: 1.0
|
||||
board: EVENT-CC92
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x40000
|
||||
content: Program
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x80000
|
||||
content: Level-1
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x80000
|
||||
content: Level-2
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x80000
|
||||
content: Level-3
|
||||
memory
|
||||
type: RAM
|
||||
size: 0x2000
|
||||
content: Save
|
||||
volatile
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x1800
|
||||
content: Program
|
||||
manufacturer: NEC
|
||||
architecture: uPD7725
|
||||
identifier: DSP1
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x800
|
||||
content: Data
|
||||
manufacturer: NEC
|
||||
architecture: uPD7725
|
||||
identifier: DSP1
|
||||
memory
|
||||
type: RAM
|
||||
size: 0x200
|
||||
content: Data
|
||||
manufacturer: NEC
|
||||
architecture: uPD7725
|
||||
identifier: DSP1
|
||||
volatile
|
||||
oscillator
|
||||
frequency: 8000000
|
||||
|
||||
game
|
||||
sha256: 6e7dcbb4df32903d6ff5da1e308342c0a72f5af3f11479cf49391dc3a17d5d7b
|
||||
title: キャプテンコマンドー
|
||||
@@ -12126,6 +12180,60 @@ game
|
||||
size: 0x200000
|
||||
content: Program
|
||||
|
||||
//sha256 is without firmware
|
||||
game
|
||||
sha256: 5443a97e9c40e25821a8fb8c91b63c7bd8c3060d9ff888ee6c573b87b53935f4
|
||||
title: PowerFest '94
|
||||
name: PowerFest '94
|
||||
region: USA
|
||||
revision: 1.0
|
||||
board: EVENT-PF94
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x40000
|
||||
content: Program
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x80000
|
||||
content: Level-1
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x80000
|
||||
content: Level-2
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x100000
|
||||
content: Level-3
|
||||
memory
|
||||
type: RAM
|
||||
size: 0x2000
|
||||
content: Save
|
||||
volatile
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x1800
|
||||
content: Program
|
||||
manufacturer: NEC
|
||||
architecture: uPD7725
|
||||
identifier: DSP1
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x800
|
||||
content: Data
|
||||
manufacturer: NEC
|
||||
architecture: uPD7725
|
||||
identifier: DSP1
|
||||
memory
|
||||
type: RAM
|
||||
size: 0x200
|
||||
content: Data
|
||||
manufacturer: NEC
|
||||
architecture: uPD7725
|
||||
identifier: DSP1
|
||||
volatile
|
||||
oscillator
|
||||
frequency: 8000000
|
||||
|
||||
game
|
||||
sha256: 06c8fc466805f97c9147711b2d8370d4f4d05d9fa3a916f17aa1682f73c9a63b
|
||||
title: Power Instinct
|
||||
|
||||
@@ -182,18 +182,25 @@ auto Nintendo64::analyze(vector<u8>& data) -> string {
|
||||
string region = "NTSC";
|
||||
switch(data[0x3e]) {
|
||||
case 'A': region = "NTSC"; break; //North America + Japan
|
||||
case 'B': region = "NTSC"; break; //Brazil
|
||||
case 'C': region = "NTSC"; break; //China
|
||||
case 'D': region = "PAL"; break; //Germany
|
||||
case 'E': region = "NTSC"; break; //North America
|
||||
case 'F': region = "PAL"; break; //France
|
||||
case 'G': region = "NTSC"; break; //Gateway 64 (NTSC)
|
||||
case 'H': region = "PAL"; break; //Netherlands
|
||||
case 'I': region = "PAL"; break; //Italy
|
||||
case 'J': region = "NTSC"; break; //Japan
|
||||
case 'K': region = "NTSC"; break; //Korea
|
||||
case 'L': region = "PAL"; break; //Gateway 64 (PAL)
|
||||
case 'N': region = "NTSC"; break; //Canada
|
||||
case 'P': region = "PAL"; break; //Europe
|
||||
case 'S': region = "PAL"; break; //Spain
|
||||
case 'U': region = "PAL"; break; //Australia
|
||||
case 'W': region = "PAL"; break; //Scandinavia
|
||||
case 'X': region = "PAL"; break; //Europe
|
||||
case 'Y': region = "PAL"; break; //Europe
|
||||
case 'Z': region = "PAL"; break; //Europe
|
||||
}
|
||||
|
||||
string id;
|
||||
@@ -431,6 +438,7 @@ auto Nintendo64::analyze(vector<u8>& data) -> string {
|
||||
if(id == "NDP") {flash = 128_KiB;} //Dinosaur Planet (Unlicensed)
|
||||
|
||||
//Controller Pak
|
||||
if(id == "N4W") {cpak = true; rpak = true;} //40 Winks (Aftermarket)
|
||||
if(id == "NO7") {cpak = true; rpak = true;} //The World Is Not Enough
|
||||
if(id == "NAY") {cpak = true;} //Aidyn Chronicles - The First Mage
|
||||
if(id == "NBS") {cpak = true; rpak = true;} //All-Star Baseball '99
|
||||
@@ -642,6 +650,7 @@ auto Nintendo64::analyze(vector<u8>& data) -> string {
|
||||
if(id == "N8W") {cpak = true;} //World Cup '98
|
||||
if(id == "NWO") {cpak = true; rpak = true;} //World Driver Championship
|
||||
if(id == "NXF") {cpak = true; rpak = true;} //Xena Warrior Princess - The Talisman of Fate
|
||||
if(id == "NXC") {cpak = true; rpak = true;} //Xeno Crisis (Aftermarket)
|
||||
|
||||
//Rumble Pak
|
||||
if(id == "NJQ") {rpak = true;} //Batman Beyond - Return of the Joker [Batman of the Future - Return of the Joker (E)]
|
||||
|
||||
@@ -36,21 +36,13 @@ protected:
|
||||
auto SuperFamicom::load(string location) -> bool {
|
||||
vector<u8> rom;
|
||||
string directory = location;
|
||||
if(directory::exists(location)) {
|
||||
auto files = directory::files(location, "*.rom");
|
||||
append(rom, {location, "program.rom" });
|
||||
append(rom, {location, "data.rom" });
|
||||
append(rom, {location, "expansion.rom"});
|
||||
for(auto& file : files.match("*.program.rom")) append(rom, {location, file});
|
||||
for(auto& file : files.match("*.data.rom" )) append(rom, {location, file});
|
||||
for(auto& file : files.match("*.boot.rom" )) append(rom, {location, file});
|
||||
} else if(rom = Medium::read(location)) {
|
||||
directory = Location::dir(location);
|
||||
//append firmware to the ROM if it is missing
|
||||
auto manifest = analyze(rom);
|
||||
auto document = BML::unserialize(manifest);
|
||||
bool local_firmware = false;
|
||||
bool folder = false;
|
||||
|
||||
auto append_missing_firmware = [&](auto document)
|
||||
{
|
||||
if(auto identifier = document["game/board/memory/identifier"]) {
|
||||
if(!firmwareRomSize()) {
|
||||
if(!firmwareRomSize() && !local_firmware) {
|
||||
auto id = identifier.string();
|
||||
array_view<u8> view;
|
||||
if(id == "Cx4" ) view = Resource::SuperFamicom::Cx4;
|
||||
@@ -66,15 +58,48 @@ auto SuperFamicom::load(string location) -> bool {
|
||||
if(id == "ST018") view = Resource::SuperFamicom::ST018;
|
||||
while(view) rom.append(*view++);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if(directory::exists(location)) {
|
||||
auto files = directory::files(location, "*.rom");
|
||||
append(rom, {location, "program.rom" });
|
||||
append(rom, {location, "data.rom" });
|
||||
append(rom, {location, "expansion.rom"});
|
||||
for(auto& file : files.match("slot-*.rom" )) { append(rom, {location, file}); }
|
||||
for(auto& file : files.match("*.program.rom")) { append(rom, {location, file}); local_firmware = true; }
|
||||
for(auto& file : files.match("*.data.rom" )) { append(rom, {location, file}); local_firmware = true; }
|
||||
for(auto& file : files.match("*.boot.rom" )) { append(rom, {location, file}); }
|
||||
folder = true;
|
||||
} else if(rom = Medium::read(location)) {
|
||||
directory = Location::dir(location);
|
||||
}
|
||||
|
||||
if(!rom) return false;
|
||||
|
||||
|
||||
//append firmware to the ROM if it is missing
|
||||
auto tmp_manifest = analyze(rom);
|
||||
auto document = BML::unserialize(tmp_manifest);
|
||||
append_missing_firmware(document);
|
||||
|
||||
this->sha256 = Hash::SHA256(rom).digest();
|
||||
this->location = location;
|
||||
this->manifest = Medium::manifestDatabase(sha256);
|
||||
|
||||
if(!manifest) {
|
||||
auto local_manifest = location.replace({".", location.split(".").last()}, ".bml");
|
||||
if (folder)
|
||||
local_manifest = directory.append("manifest.bml");
|
||||
if(file::exists(local_manifest)) {
|
||||
manifest = file::read(local_manifest);
|
||||
}
|
||||
}
|
||||
|
||||
document = BML::unserialize(manifest);
|
||||
append_missing_firmware(document); // if auto-detect failed, use chips from the manifest
|
||||
|
||||
if(!manifest) manifest = analyze(rom);
|
||||
auto document = BML::unserialize(manifest);
|
||||
document = BML::unserialize(manifest);
|
||||
if(!document) return false;
|
||||
|
||||
pak = new vfs::directory;
|
||||
@@ -529,6 +554,13 @@ auto SuperFamicom::board() const -> string {
|
||||
if(headerAddress == 0x40ffb0) mode = "EXHIROM-";
|
||||
}
|
||||
|
||||
//the Sufami Turbo has the non-descriptive label "ADD-ON BASE CASSETE"
|
||||
//(yes, missing a T), and its serial "A9PJ" is shared with
|
||||
//Bishoujo Senshi Sailor Moon SuperS - Fuwafuwa Panic (Japan)
|
||||
//so we identify it with this embedded string
|
||||
string sufamiSignature = "BANDAI SFC-ADX";
|
||||
if(string(data.view(0, sufamiSignature.length())) == sufamiSignature) board.append("ST-", mode);
|
||||
|
||||
//this game's title overwrite the map mode with '!' (0x21), but is a LOROM game
|
||||
if(label() == "YUYU NO QUIZ DE GO!GO") mode = "LOROM-";
|
||||
|
||||
@@ -538,10 +570,7 @@ auto SuperFamicom::board() const -> string {
|
||||
bool epsonRTC = false;
|
||||
bool sharpRTC = false;
|
||||
|
||||
if(serial() == "A9PJ") {
|
||||
//Sufami Turbo (JPN)
|
||||
board.append("ST-", mode);
|
||||
} else if(serial() == "ZBSJ") {
|
||||
if(serial() == "ZBSJ") {
|
||||
//BS-X: Sore wa Namae o Nusumareta Machi no Monogatari (JPN)
|
||||
board.append("BS-MCC-");
|
||||
} else if(serial() == "042J") {
|
||||
@@ -752,8 +781,8 @@ auto SuperFamicom::firmwareRomSize() const -> u32 {
|
||||
}
|
||||
|
||||
auto SuperFamicom::ramSize() const -> u32 {
|
||||
auto ramSize = data[headerAddress + 0x28] & 7;
|
||||
if(ramSize) return 1024 << ramSize;
|
||||
auto ramSize = data[headerAddress + 0x28];
|
||||
if (ramSize) return (1 << (((ramSize - 1) & 7) + 1)) << 10;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -84,8 +84,8 @@ inline auto CHD::load(const string& location) -> bool {
|
||||
|
||||
int track_no;
|
||||
int frames;
|
||||
int pregap_frames;
|
||||
int postgap_frames;
|
||||
int pregap_frames = 0;
|
||||
int postgap_frames = 0;
|
||||
|
||||
// First, attempt to fetch CDROMv2 metadata
|
||||
err = chd_get_metadata(chd, CDROM_TRACK_METADATA2_TAG, tracks.size(), metadata, sizeof(metadata), &metadata_size, nullptr, nullptr);
|
||||
|
||||
+9
-4
@@ -192,10 +192,15 @@ namespace nall::GDB {
|
||||
u32 sepIdx = sepIdxMaybe ? sepIdxMaybe.get() : 1;
|
||||
|
||||
u64 address = cmdName.slice(1, sepIdx-1).hex();
|
||||
u64 unitSize = cmdName.slice(sepIdx+1, 1).hex();
|
||||
u64 value = cmdParts.size() > 1 ? cmdParts[1].hex() : 0;
|
||||
|
||||
hooks.write(address, unitSize, value);
|
||||
u64 byteSize = cmdName.slice(sepIdx+1, 1).hex();
|
||||
string hexvalue = cmdParts.size() > 1 ? cmdParts[1] : string{};
|
||||
vector<u8> value;
|
||||
string hexbyte;
|
||||
for (int i=0; i<hexvalue.size(); i+=2) {
|
||||
hexbyte = hexvalue.slice(i, 2);
|
||||
value.append((u8)toHex(hexbyte.data()));
|
||||
}
|
||||
hooks.write(address, value);
|
||||
return "OK";
|
||||
}
|
||||
|
||||
|
||||
+21
-2
@@ -9,10 +9,29 @@ enum class Signal : u8 {
|
||||
HANGUP = 1,
|
||||
INT = 2,
|
||||
QUIT = 3,
|
||||
ILLEGAL = 4,
|
||||
ILL = 4,
|
||||
TRAP = 5,
|
||||
ABORT = 6,
|
||||
EMT = 7,
|
||||
FPE = 8,
|
||||
KILL = 9,
|
||||
BUS = 10,
|
||||
SEGV = 11,
|
||||
SYS = 12,
|
||||
PIPE = 13,
|
||||
ALRM = 14,
|
||||
TERM = 15,
|
||||
URG = 16,
|
||||
STOP = 17,
|
||||
TSTP = 18,
|
||||
CONT = 19,
|
||||
CHLD = 20,
|
||||
TTIN = 21,
|
||||
TTOU = 22,
|
||||
IO = 23,
|
||||
PROF = 27,
|
||||
WINCH = 28,
|
||||
LOST = 29,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -48,7 +67,7 @@ class Server : public nall::TCPText::Server {
|
||||
struct {
|
||||
// Memory
|
||||
function<string(u64 address, u32 byteCount)> read{};
|
||||
function<void(u64 address, u32 unitSize, u64 value)> write{};
|
||||
function<void(u64 address, vector<u8> value)> write{};
|
||||
function<u64(u64 address)> normalizeAddress{};
|
||||
|
||||
// Registers
|
||||
|
||||
+3
-3
@@ -161,15 +161,15 @@ namespace nall {
|
||||
|
||||
#if defined(COMPILER_CLANG) || defined(COMPILER_GCC)
|
||||
#define no_optimize __attribute__((optnone))
|
||||
#define noinline __attribute__((noinline))
|
||||
#define NALL_NOINLINE __attribute__((noinline))
|
||||
#define alwaysinline inline __attribute__((always_inline))
|
||||
#elif defined(COMPILER_MICROSOFT)
|
||||
#define no_optimize
|
||||
#define noinline __declspec(noinline)
|
||||
#define NALL_NOINLINE __declspec(noinline)
|
||||
#define alwaysinline inline __forceinline
|
||||
#else
|
||||
#define no_optimize
|
||||
#define noinline
|
||||
#define NALL_NOINLINE
|
||||
#define alwaysinline inline
|
||||
#endif
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace nall::recompiler {
|
||||
if(args >= 1) options |= SLJIT_ARG_VALUE(SLJIT_ARG_TYPE_W, 1);
|
||||
if(args >= 2) options |= SLJIT_ARG_VALUE(SLJIT_ARG_TYPE_W, 2);
|
||||
if(args >= 3) options |= SLJIT_ARG_VALUE(SLJIT_ARG_TYPE_W, 3);
|
||||
sljit_emit_enter(compiler, 0, options, 4, 3, 0, 0, 0);
|
||||
sljit_emit_enter(compiler, 0, options, 4, 3, 0);
|
||||
sljit_jump* entry = sljit_emit_jump(compiler, SLJIT_JUMP);
|
||||
epilogue = sljit_emit_label(compiler);
|
||||
sljit_emit_return_void(compiler);
|
||||
|
||||
+13
-10
@@ -122,10 +122,14 @@ struct VideoMetal : VideoDriver, Metal {
|
||||
}
|
||||
|
||||
auto isVRRSupported() -> bool {
|
||||
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 120000
|
||||
if (@available(macOS 12.0, *)) {
|
||||
NSTimeInterval minInterval = view.window.screen.minimumRefreshInterval;
|
||||
NSTimeInterval maxInterval = view.window.screen.maximumRefreshInterval;
|
||||
_vrrIsSupported = minInterval != maxInterval;
|
||||
#else
|
||||
_vrrIsSupported = false;
|
||||
#endif
|
||||
return _vrrIsSupported;
|
||||
} else {
|
||||
return false;
|
||||
@@ -139,6 +143,7 @@ struct VideoMetal : VideoDriver, Metal {
|
||||
CFTimeInterval refreshRate = CGDisplayModeGetRefreshRate(displayMode);
|
||||
_presentInterval = (1.0 / refreshRate);
|
||||
} else {
|
||||
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 120000
|
||||
if (@available(macOS 12.0, *)) {
|
||||
CFTimeInterval minimumInterval = view.window.screen.minimumRefreshInterval;
|
||||
if (_refreshRateHint != 0) {
|
||||
@@ -149,6 +154,7 @@ struct VideoMetal : VideoDriver, Metal {
|
||||
_presentInterval = minimumInterval;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,13 +168,15 @@ struct VideoMetal : VideoDriver, Metal {
|
||||
}
|
||||
|
||||
if(file::exists(pathname)) {
|
||||
if (_libra.preset_create(pathname.data(), &_preset) != NULL) {
|
||||
if (auto error = _libra.preset_create(pathname.data(), &_preset)) {
|
||||
print(string{"Metal: Failed to load shader: ", pathname, "\n"});
|
||||
_libra.error_print(error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_libra.mtl_filter_chain_create(&_preset, _commandQueue, nil, &_filterChain) != NULL) {
|
||||
if (auto error = _libra.mtl_filter_chain_create(&_preset, _commandQueue, nil, &_filterChain)) {
|
||||
print(string{"Metal: Failed to create filter chain for: ", pathname, "\n"});
|
||||
_libra.error_print(error);
|
||||
return false;
|
||||
};
|
||||
} else {
|
||||
@@ -280,11 +288,6 @@ struct VideoMetal : VideoDriver, Metal {
|
||||
|
||||
_viewportSize.x = width;
|
||||
_viewportSize.y = height;
|
||||
|
||||
_libraViewport.width = (uint32_t) width;
|
||||
_libraViewport.height = (uint32_t) height;
|
||||
_libraViewport.x = 0;
|
||||
_libraViewport.y = 0;
|
||||
|
||||
_outputX = (_viewWidth - width) / 2;
|
||||
_outputY = (_viewHeight - height) / 2;
|
||||
@@ -341,8 +344,8 @@ private:
|
||||
/// consisting of the pixel buffer from the emulator, onto a texture the same size as our eventual output,
|
||||
/// `_renderTargetTexture`. Then it calls into librashader, which performs postprocessing onto the same
|
||||
/// output texture. Then for the second render pass here, we composite the output texture within ares's viewport.
|
||||
/// We need this last pass because librashader expects the viewport to be the same size as the output texture,
|
||||
/// which is not the case for ares.
|
||||
/// We defer this second pass because it is performed directly onto the drawable texture, which we want to delay
|
||||
/// acquiring for as long as possible.
|
||||
|
||||
dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);
|
||||
|
||||
@@ -382,7 +385,7 @@ private:
|
||||
[renderEncoder endEncoding];
|
||||
|
||||
if (_filterChain) {
|
||||
_libra.mtl_filter_chain_frame(&_filterChain, commandBuffer, frameCount, sourceTexture, _libraViewport, _renderTargetTexture, nil, nil);
|
||||
_libra.mtl_filter_chain_frame(&_filterChain, commandBuffer, frameCount, sourceTexture, _renderTargetTexture, nil, nil, nil);
|
||||
}
|
||||
|
||||
//this call will block the current thread/queue if a drawable is not yet available
|
||||
|
||||
@@ -99,6 +99,5 @@ struct Metal {
|
||||
libra_instance_t _libra;
|
||||
libra_shader_preset_t _preset;
|
||||
libra_mtl_filter_chain_t _filterChain;
|
||||
libra_viewport_t _libraViewport;
|
||||
bool initialized = false;
|
||||
};
|
||||
Loaded 100 of 744 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user