aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/citra_qt/bootmanager.cpp2
-rw-r--r--src/citra_qt/debugger/disassembler.cpp2
-rw-r--r--src/citra_qt/main.cpp27
-rw-r--r--src/citra_qt/main.h2
-rw-r--r--src/common/logging/backend.cpp4
-rw-r--r--src/common/logging/text_formatter.cpp5
-rw-r--r--src/common/string_util.cpp14
-rw-r--r--src/core/arm/disassembler/arm_disasm.cpp141
-rw-r--r--src/core/arm/disassembler/arm_disasm.h18
-rw-r--r--src/core/arm/dyncom/arm_dyncom_interpreter.cpp10
-rw-r--r--src/core/arm/skyeye_common/vfp/vfpinstr.cpp54
-rw-r--r--src/core/hle/applets/applet.cpp9
-rw-r--r--src/core/hle/applets/applet.h3
-rw-r--r--src/core/hle/service/am/am.cpp3
-rw-r--r--src/core/hle/service/am/am_net.cpp3
-rw-r--r--src/core/hle/service/apt/apt.cpp53
-rw-r--r--src/core/hle/service/apt/apt.h30
-rw-r--r--src/core/hle/service/apt/apt_a.cpp1
-rw-r--r--src/core/hle/service/apt/apt_s.cpp4
-rw-r--r--src/core/hle/service/apt/apt_u.cpp2
-rw-r--r--src/core/hle/service/ldr_ro.cpp6
-rw-r--r--src/core/hw/y2r.cpp1
-rw-r--r--src/video_core/command_processor.cpp155
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp7
-rw-r--r--src/video_core/vertex_shader.cpp6
25 files changed, 434 insertions, 128 deletions
diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp
index b12bd858..a96fbea5 100644
--- a/src/citra_qt/bootmanager.cpp
+++ b/src/citra_qt/bootmanager.cpp
@@ -94,7 +94,7 @@ private:
};
GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) :
- QWidget(parent), emu_thread(emu_thread), keyboard_id(0) {
+ QWidget(parent), keyboard_id(0), emu_thread(emu_thread) {
std::string window_title = Common::StringFromFormat("Citra | %s-%s", Common::g_scm_branch, Common::g_scm_desc);
setWindowTitle(QString::fromStdString(window_title));
diff --git a/src/citra_qt/debugger/disassembler.cpp b/src/citra_qt/debugger/disassembler.cpp
index 1e5ef529..d3629bbf 100644
--- a/src/citra_qt/debugger/disassembler.cpp
+++ b/src/citra_qt/debugger/disassembler.cpp
@@ -159,7 +159,7 @@ void DisassemblerModel::SetNextInstruction(unsigned int address) {
}
DisassemblerWidget::DisassemblerWidget(QWidget* parent, EmuThread* emu_thread) :
- QDockWidget(parent), emu_thread(emu_thread), base_addr(0) {
+ QDockWidget(parent), base_addr(0), emu_thread(emu_thread) {
disasm_ui.setupUi(this);
diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp
index 34831f2e..6b030c17 100644
--- a/src/citra_qt/main.cpp
+++ b/src/citra_qt/main.cpp
@@ -122,9 +122,8 @@ GMainWindow::GMainWindow() : emu_thread(nullptr)
y = (screenRect.y() + screenRect.height()) / 2 - h * 55 / 100;
setGeometry(x, y, w, h);
-
// Restore UI state
- QSettings settings(QSettings::IniFormat, QSettings::UserScope, "Citra team", "Citra");
+ QSettings settings;
restoreGeometry(settings.value("geometry").toByteArray());
restoreState(settings.value("state").toByteArray());
render_window->restoreGeometry(settings.value("geometryRenderWindow").toByteArray());
@@ -207,7 +206,7 @@ void GMainWindow::OnDisplayTitleBars(bool show)
}
}
-void GMainWindow::BootGame(std::string filename) {
+void GMainWindow::BootGame(const std::string& filename) {
LOG_INFO(Frontend, "Citra starting...\n");
// Initialize the core emulation
@@ -271,8 +270,13 @@ void GMainWindow::ShutdownGame() {
void GMainWindow::OnMenuLoadFile()
{
- QString filename = QFileDialog::getOpenFileName(this, tr("Load File"), QString(), tr("3DS executable (*.3ds *.3dsx *.elf *.axf *.cci *.cxi)"));
+ QSettings settings;
+ QString rom_path = settings.value("romsPath", QString()).toString();
+
+ QString filename = QFileDialog::getOpenFileName(this, tr("Load File"), rom_path, tr("3DS executable (*.3ds *.3dsx *.elf *.axf *.cci *.cxi)"));
if (filename.size()) {
+ settings.setValue("romsPath", QFileInfo(filename).path());
+
// Shutdown previous session if the emu thread is still active...
if (emu_thread != nullptr)
ShutdownGame();
@@ -282,9 +286,15 @@ void GMainWindow::OnMenuLoadFile()
}
void GMainWindow::OnMenuLoadSymbolMap() {
- QString filename = QFileDialog::getOpenFileName(this, tr("Load Symbol Map"), QString(), tr("Symbol map (*)"));
- if (filename.size())
+ QSettings settings;
+ QString symbol_path = settings.value("symbolsPath", QString()).toString();
+
+ QString filename = QFileDialog::getOpenFileName(this, tr("Load Symbol Map"), symbol_path, tr("Symbol map (*)"));
+ if (filename.size()) {
+ settings.setValue("symbolsPath", QFileInfo(filename).path());
+
LoadSymbolMap(filename.toLatin1().data());
+ }
}
void GMainWindow::OnStartGame()
@@ -375,6 +385,11 @@ int main(int argc, char* argv[])
Log::Filter log_filter(Log::Level::Info);
Log::SetFilter(&log_filter);
+ // Init settings params
+ QSettings::setDefaultFormat(QSettings::IniFormat);
+ QCoreApplication::setOrganizationName("Citra team");
+ QCoreApplication::setApplicationName("Citra");
+
QApplication::setAttribute(Qt::AA_X11InitThreads);
QApplication app(argc, argv);
diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h
index 242b08c3..9fe9e0c9 100644
--- a/src/citra_qt/main.h
+++ b/src/citra_qt/main.h
@@ -55,7 +55,7 @@ signals:
void EmulationStopping();
private:
- void BootGame(std::string filename);
+ void BootGame(const std::string& filename);
void ShutdownGame();
void closeEvent(QCloseEvent* event) override;
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index d85e5837..0a081e7d 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -6,6 +6,7 @@
#include <array>
#include <cstdio>
+#include "common/assert.h"
#include "common/common_funcs.h" // snprintf compatibility define
#include "common/logging/backend.h"
#include "common/logging/filter.h"
@@ -78,8 +79,9 @@ const char* GetLevelName(Level log_level) {
LVL(Warning);
LVL(Error);
LVL(Critical);
+ case Level::Count:
+ UNREACHABLE();
}
- return "Unknown";
#undef LVL
}
diff --git a/src/common/logging/text_formatter.cpp b/src/common/logging/text_formatter.cpp
index 94f3dfc1..de195b0f 100644
--- a/src/common/logging/text_formatter.cpp
+++ b/src/common/logging/text_formatter.cpp
@@ -14,6 +14,7 @@
#include "common/logging/log.h"
#include "common/logging/text_formatter.h"
+#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/string_util.h"
@@ -82,6 +83,8 @@ void PrintColoredMessage(const Entry& entry) {
color = FOREGROUND_RED | FOREGROUND_INTENSITY; break;
case Level::Critical: // Bright magenta
color = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY; break;
+ case Level::Count:
+ UNREACHABLE();
}
SetConsoleTextAttribute(console_handle, color);
@@ -101,6 +104,8 @@ void PrintColoredMessage(const Entry& entry) {
color = ESC "[1;31m"; break;
case Level::Critical: // Bright magenta
color = ESC "[1;35m"; break;
+ case Level::Count:
+ UNREACHABLE();
}
fputs(color, stderr);
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp
index b2f7f7b1..6d6fc591 100644
--- a/src/common/string_util.cpp
+++ b/src/common/string_util.cpp
@@ -296,14 +296,28 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st
std::string UTF16ToUTF8(const std::u16string& input)
{
+#if _MSC_VER >= 1900
+ // Workaround for missing char16_t/char32_t instantiations in MSVC2015
+ std::wstring_convert<std::codecvt_utf8_utf16<__int16>, __int16> convert;
+ std::basic_string<__int16> tmp_buffer(input.cbegin(), input.cend());
+ return convert.to_bytes(tmp_buffer);
+#else
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
return convert.to_bytes(input);
+#endif
}
std::u16string UTF8ToUTF16(const std::string& input)
{
+#if _MSC_VER >= 1900
+ // Workaround for missing char16_t/char32_t instantiations in MSVC2015
+ std::wstring_convert<std::codecvt_utf8_utf16<__int16>, __int16> convert;
+ auto tmp_buffer = convert.from_bytes(input);
+ return std::u16string(tmp_buffer.cbegin(), tmp_buffer.cend());
+#else
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
return convert.from_bytes(input);
+#endif
}
static std::string UTF16ToUTF8(const std::wstring& input)
diff --git a/src/core/arm/disassembler/arm_disasm.cpp b/src/core/arm/disassembler/arm_disasm.cpp
index aaf47b3f..964e3011 100644
--- a/src/core/arm/disassembler/arm_disasm.cpp
+++ b/src/core/arm/disassembler/arm_disasm.cpp
@@ -4,6 +4,7 @@
#include "common/string_util.h"
#include "core/arm/disassembler/arm_disasm.h"
+#include "core/arm/skyeye_common/armsupp.h"
static const char *cond_names[] = {
"eq",
@@ -37,6 +38,7 @@ static const char *opcode_names[] = {
"blx",
"bx",
"cdp",
+ "clrex",
"clz",
"cmn",
"cmp",
@@ -46,6 +48,10 @@ static const char *opcode_names[] = {
"ldr",
"ldrb",
"ldrbt",
+ "ldrex",
+ "ldrexb",
+ "ldrexd",
+ "ldrexh",
"ldrh",
"ldrsb",
"ldrsh",
@@ -58,11 +64,13 @@ static const char *opcode_names[] = {
"msr",
"mul",
"mvn",
+ "nop",
"orr",
"pld",
"rsb",
"rsc",
"sbc",
+ "sev",
"smlal",
"smull",
"stc",
@@ -70,6 +78,10 @@ static const char *opcode_names[] = {
"str",
"strb",
"strbt",
+ "strex",
+ "strexb",
+ "strexd",
+ "strexh",
"strh",
"strt",
"sub",
@@ -80,6 +92,9 @@ static const char *opcode_names[] = {
"tst",
"umlal",
"umull",
+ "wfe",
+ "wfi",
+ "yield",
"undefined",
"adc",
@@ -172,6 +187,8 @@ std::string ARM_Disasm::Disassemble(uint32_t addr, uint32_t insn)
return DisassembleBX(insn);
case OP_CDP:
return "cdp";
+ case OP_CLREX:
+ return "clrex";
case OP_CLZ:
return DisassembleCLZ(insn);
case OP_LDC:
@@ -188,6 +205,15 @@ std::string ARM_Disasm::Disassemble(uint32_t addr, uint32_t insn)
case OP_STRBT:
case OP_STRT:
return DisassembleMem(insn);
+ case OP_LDREX:
+ case OP_LDREXB:
+ case OP_LDREXD:
+ case OP_LDREXH:
+ case OP_STREX:
+ case OP_STREXB:
+ case OP_STREXD:
+ case OP_STREXH:
+ return DisassembleREX(opcode, insn);
case OP_LDRH:
case OP_LDRSB:
case OP_LDRSH:
@@ -204,6 +230,12 @@ std::string ARM_Disasm::Disassemble(uint32_t addr, uint32_t insn)
return DisassembleMSR(insn);
case OP_MUL:
return DisassembleMUL(opcode, insn);
+ case OP_NOP:
+ case OP_SEV:
+ case OP_WFE:
+ case OP_WFI:
+ case OP_YIELD:
+ return DisassembleNoOperands(opcode, insn);
case OP_PLD:
return DisassemblePLD(insn);
case OP_STC:
@@ -646,6 +678,12 @@ std::string ARM_Disasm::DisassembleMSR(uint32_t insn)
cond_to_str(cond), pd ? "spsr" : "cpsr", flags, rm);
}
+std::string ARM_Disasm::DisassembleNoOperands(Opcode opcode, uint32_t insn)
+{
+ uint32_t cond = BITS(insn, 28, 31);
+ return Common::StringFromFormat("%s%s", opcode_names[opcode], cond_to_str(cond));
+}
+
std::string ARM_Disasm::DisassemblePLD(uint32_t insn)
{
uint8_t is_reg = (insn >> 25) & 0x1;
@@ -669,6 +707,36 @@ std::string ARM_Disasm::DisassemblePLD(uint32_t insn)
}
}
+std::string ARM_Disasm::DisassembleREX(Opcode opcode, uint32_t insn) {
+ uint32_t rn = BITS(insn, 16, 19);
+ uint32_t rd = BITS(insn, 12, 15);
+ uint32_t rt = BITS(insn, 0, 3);
+ uint32_t cond = BITS(insn, 28, 31);
+
+ switch (opcode) {
+ case OP_STREX:
+ case OP_STREXB:
+ case OP_STREXH:
+ return Common::StringFromFormat("%s%s\tr%d, r%d, [r%d]", opcode_names[opcode],
+ cond_to_str(cond), rd, rt, rn);
+ case OP_STREXD:
+ return Common::StringFromFormat("%s%s\tr%d, r%d, r%d, [r%d]", opcode_names[opcode],
+ cond_to_str(cond), rd, rt, rt + 1, rn);
+
+ // for LDREX instructions, rd corresponds to Rt from reference manual
+ case OP_LDREX:
+ case OP_LDREXB:
+ case OP_LDREXH:
+ return Common::StringFromFormat("%s%s\tr%d, [r%d]", opcode_names[opcode],
+ cond_to_str(cond), rd, rn);
+ case OP_LDREXD:
+ return Common::StringFromFormat("%s%s\tr%d, r%d, [r%d]", opcode_names[opcode],
+ cond_to_str(cond), rd, rd + 1, rn);
+ default:
+ return opcode_names[OP_UNDEFINED];
+ }
+}
+
std::string ARM_Disasm::DisassembleSWI(uint32_t insn)
{
uint8_t cond = (insn >> 28) & 0xf;
@@ -721,12 +789,9 @@ Opcode ARM_Disasm::Decode00(uint32_t insn) {
}
uint32_t bits7_4 = (insn >> 4) & 0xf;
if (bits7_4 == 0x9) {
- if ((insn & 0x0ff00ff0) == 0x01000090) {
- // Swp instruction
- uint8_t bit22 = (insn >> 22) & 0x1;
- if (bit22)
- return OP_SWPB;
- return OP_SWP;
+ uint32_t bit24 = BIT(insn, 24);
+ if (bit24) {
+ return DecodeSyncPrimitive(insn);
}
// One of the multiply instructions
return DecodeMUL(insn);
@@ -739,6 +804,12 @@ Opcode ARM_Disasm::Decode00(uint32_t insn) {
}
}
+ uint32_t op1 = BITS(insn, 20, 24);
+ if (bit25 && (op1 == 0x12 || op1 == 0x16)) {
+ // One of the MSR (immediate) and hints instructions
+ return DecodeMSRImmAndHints(insn);
+ }
+
// One of the data processing instructions
return DecodeALU(insn);
}
@@ -754,6 +825,10 @@ Opcode ARM_Disasm::Decode01(uint32_t insn) {
// Pre-load
return OP_PLD;
}
+ if (insn == 0xf57ff01f) {
+ // Clear-Exclusive
+ return OP_CLREX;
+ }
if (is_load) {
if (is_byte) {
// Load byte
@@ -836,6 +911,35 @@ Opcode ARM_Disasm::Decode11(uint32_t insn) {
return OP_MCR;
}
+Opcode ARM_Disasm::DecodeSyncPrimitive(uint32_t insn) {
+ uint32_t op = BITS(insn, 20, 23);
+ uint32_t bit22 = BIT(insn, 22);
+ switch (op) {
+ case 0x0:
+ if (bit22)
+ return OP_SWPB;
+ return OP_SWP;
+ case 0x8:
+ return OP_STREX;
+ case 0x9:
+ return OP_LDREX;
+ case 0xA:
+ return OP_STREXD;
+ case 0xB:
+ return OP_LDREXD;
+ case 0xC:
+ return OP_STREXB;
+ case 0xD:
+ return OP_LDREXB;
+ case 0xE:
+ return OP_STREXH;
+ case 0xF:
+ return OP_LDREXH;
+ default:
+ return OP_UNDEFINED;
+ }
+}
+
Opcode ARM_Disasm::DecodeMUL(uint32_t insn) {
uint8_t bit24 = (insn >> 24) & 0x1;
if (bit24 != 0) {
@@ -870,6 +974,31 @@ Opcode ARM_Disasm::DecodeMUL(uint32_t insn) {
return OP_SMLAL;
}
+Opcode ARM_Disasm::DecodeMSRImmAndHints(uint32_t insn) {
+ uint32_t op = BIT(insn, 22);
+ uint32_t op1 = BITS(insn, 16, 19);
+ uint32_t op2 = BITS(insn, 0, 7);
+
+ if (op == 0 && op1 == 0) {
+ switch (op2) {
+ case 0x0:
+ return OP_NOP;
+ case 0x1:
+ return OP_YIELD;
+ case 0x2:
+ return OP_WFE;
+ case 0x3:
+ return OP_WFI;
+ case 0x4:
+ return OP_SEV;
+ default:
+ return OP_UNDEFINED;
+ }
+ }
+
+ return OP_MSR;
+}
+
Opcode ARM_Disasm::DecodeLDRH(uint32_t insn) {
uint8_t is_load = (insn >> 20) & 0x1;
uint8_t bits_65 = (insn >> 5) & 0x3;
diff --git a/src/core/arm/disassembler/arm_disasm.h b/src/core/arm/disassembler/arm_disasm.h
index f94bd466..d04fd21e 100644
--- a/src/core/arm/disassembler/arm_disasm.h
+++ b/src/core/arm/disassembler/arm_disasm.h
@@ -20,6 +20,7 @@ enum Opcode {
OP_BLX,
OP_BX,
OP_CDP,
+ OP_CLREX,
OP_CLZ,
OP_CMN,
OP_CMP,
@@ -29,6 +30,10 @@ enum Opcode {
OP_LDR,
OP_LDRB,
OP_LDRBT,
+ OP_LDREX,
+ OP_LDREXB,
+ OP_LDREXD,
+ OP_LDREXH,
OP_LDRH,
OP_LDRSB,
OP_LDRSH,
@@ -41,11 +46,13 @@ enum Opcode {
OP_MSR,
OP_MUL,
OP_MVN,
+ OP_NOP,
OP_ORR,
OP_PLD,
OP_RSB,
OP_RSC,
OP_SBC,
+ OP_SEV,
OP_SMLAL,
OP_SMULL,
OP_STC,
@@ -53,6 +60,10 @@ enum Opcode {
OP_STR,
OP_STRB,
OP_STRBT,
+ OP_STREX,
+ OP_STREXB,
+ OP_STREXD,
+ OP_STREXH,
OP_STRH,
OP_STRT,
OP_SUB,
@@ -63,6 +74,9 @@ enum Opcode {
OP_TST,
OP_UMLAL,
OP_UMULL,
+ OP_WFE,
+ OP_WFI,
+ OP_YIELD,
// Define thumb opcodes
OP_THUMB_UNDEFINED,
@@ -117,7 +131,9 @@ class ARM_Disasm {
static Opcode Decode01(uint32_t insn);
static Opcode Decode10(uint32_t insn);
static Opcode Decode11(uint32_t insn);
+ static Opcode DecodeSyncPrimitive(uint32_t insn);
static Opcode DecodeMUL(uint32_t insn);
+ static Opcode DecodeMSRImmAndHints(uint32_t insn);
static Opcode DecodeLDRH(uint32_t insn);
static Opcode DecodeALU(uint32_t insn);
@@ -135,7 +151,9 @@ class ARM_Disasm {
static std::string DisassembleMUL(Opcode opcode, uint32_t insn);
static std::string DisassembleMRS(uint32_t insn);
static std::string DisassembleMSR(uint32_t insn);
+ static std::string DisassembleNoOperands(Opcode opcode, uint32_t insn);
static std::string DisassemblePLD(uint32_t insn);
+ static std::string DisassembleREX(Opcode opcode, uint32_t insn);
static std::string DisassembleSWI(uint32_t insn);
static std::string DisassembleSWP(Opcode opcode, uint32_t insn);
};
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
index 0c20c2bc..b88b7475 100644
--- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
+++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp
@@ -3886,7 +3886,6 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
#endif
arm_inst* inst_base;
unsigned int addr;
- unsigned int phys_addr;
unsigned int num_instrs = 0;
int ptr;
@@ -3905,8 +3904,6 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
else
cpu->Reg[15] &= 0xfffffffc;
- phys_addr = cpu->Reg[15];
-
// Find the cached instruction cream, otherwise translate it...
auto itr = cpu->instruction_cache.find(cpu->Reg[15]);
if (itr != cpu->instruction_cache.end()) {
@@ -5997,7 +5994,12 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) {
ldst_inst* inst_cream = (ldst_inst*)inst_base->component;
inst_cream->get_addr(cpu, inst_cream->inst, addr);
- unsigned int value = cpu->Reg[BITS(inst_cream->inst, 12, 15)];
+ unsigned int reg = BITS(inst_cream->inst, 12, 15);
+ unsigned int value = cpu->Reg[reg];
+
+ if (reg == 15)
+ value += 2 * cpu->GetInstructionSize();
+
cpu->WriteMemory32(addr, value);
}
cpu->Reg[15] += cpu->GetInstructionSize();
diff --git a/src/core/arm/skyeye_common/vfp/vfpinstr.cpp b/src/core/arm/skyeye_common/vfp/vfpinstr.cpp
index 9b99fc5b..49298d7b 100644
--- a/src/core/arm/skyeye_common/vfp/vfpinstr.cpp
+++ b/src/core/arm/skyeye_common/vfp/vfpinstr.cpp
@@ -1511,19 +1511,26 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vstm)(unsigned int inst, int index)
#ifdef VFP_INTERPRETER_IMPL
VSTM_INST: /* encoding 1 */
{
- if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
CHECK_VFP_ENABLED;
- vstm_inst *inst_cream = (vstm_inst *)inst_base->component;
+ vstm_inst* inst_cream = (vstm_inst*)inst_base->component;
+
+ u32 address = cpu->Reg[inst_cream->n];
- addr = (inst_cream->add ? cpu->Reg[inst_cream->n] : cpu->Reg[inst_cream->n] - inst_cream->imm32);
+ // Only possible in ARM mode, where PC accesses have an 8 byte offset.
+ if (inst_cream->n == 15)
+ address += 8;
+
+ if (inst_cream->add == 0)
+ address -= inst_cream->imm32;
for (unsigned int i = 0; i < inst_cream->regs; i++)
{
if (inst_cream->single)
{
- cpu->WriteMemory32(addr, cpu->ExtReg[inst_cream->d+i]);
- addr += 4;
+ cpu->WriteMemory32(address, cpu->ExtReg[inst_cream->d+i]);
+ address += 4;
}
else
{
@@ -1531,17 +1538,17 @@ VSTM_INST: /* encoding 1 */
const u32 word2 = cpu->ExtReg[(inst_cream->d+i)*2+1];
if (cpu->InBigEndianMode()) {
- cpu->WriteMemory32(addr + 0, word2);
- cpu->WriteMemory32(addr + 4, word1);
+ cpu->WriteMemory32(address + 0, word2);
+ cpu->WriteMemory32(address + 4, word1);
} else {
- cpu->WriteMemory32(addr + 0, word1);
- cpu->WriteMemory32(addr + 4, word2);
+ cpu->WriteMemory32(address + 0, word1);
+ cpu->WriteMemory32(address + 4, word2);
}
- addr += 8;
+ address += 8;
}
}
- if (inst_cream->wback){
+ if (inst_cream->wback) {
cpu->Reg[inst_cream->n] = (inst_cream->add ? cpu->Reg[inst_cream->n] + inst_cream->imm32 :
cpu->Reg[inst_cream->n] - inst_cream->imm32);
}
@@ -1731,24 +1738,31 @@ static ARM_INST_PTR INTERPRETER_TRANSLATE(vldm)(unsigned int inst, int index)
#ifdef VFP_INTERPRETER_IMPL
VLDM_INST:
{
- if ((inst_base->cond == 0xe) || CondPassed(cpu, inst_base->cond)) {
+ if (inst_base->cond == 0xE || CondPassed(cpu, inst_base->cond)) {
CHECK_VFP_ENABLED;
- vldm_inst *inst_cream = (vldm_inst *)inst_base->component;
+ vldm_inst* inst_cream = (vldm_inst*)inst_base->component;
+
+ u32 address = cpu->Reg[inst_cream->n];
+
+ // Only possible in ARM mode, where PC accesses have an 8 byte offset.
+ if (inst_cream->n == 15)
+ address += 8;
- addr = (inst_cream->add ? cpu->Reg[inst_cream->n] : cpu->Reg[inst_cream->n] - inst_cream->imm32);
+ if (inst_cream->add == 0)
+ address -= inst_cream->imm32;
for (unsigned int i = 0; i < inst_cream->regs; i++)
{
if (inst_cream->single)
{
- cpu->ExtReg[inst_cream->d+i] = cpu->ReadMemory32(addr);
- addr += 4;
+ cpu->ExtReg[inst_cream->d+i] = cpu->ReadMemory32(address);
+ address += 4;
}
else
{
- const u32 word1 = cpu->ReadMemory32(addr + 0);
- const u32 word2 = cpu->ReadMemory32(addr + 4);
+ const u32 word1 = cpu->ReadMemory32(address + 0);
+ const u32 word2 = cpu->ReadMemory32(address + 4);
if (cpu->InBigEndianMode()) {
cpu->ExtReg[(inst_cream->d+i)*2+0] = word2;
@@ -1758,10 +1772,10 @@ VLDM_INST:
cpu->ExtReg[(inst_cream->d+i)*2+1] = word2;
}
- addr += 8;
+ address += 8;
}
}
- if (inst_cream->wback){
+ if (inst_cream->wback) {
cpu->Reg[inst_cream->n] = (inst_cream->add ? cpu->Reg[inst_cream->n] + inst_cream->imm32 :
cpu->Reg[inst_cream->n] - inst_cream->imm32);
}
diff --git a/src/core/hle/applets/applet.cpp b/src/core/hle/applets/applet.cpp
index 826f6cbb..bc2a1829 100644
--- a/src/core/hle/applets/applet.cpp
+++ b/src/core/hle/applets/applet.cpp
@@ -89,12 +89,21 @@ ResultCode Applet::Start(const Service::APT::AppletStartupParameter& parameter)
return result;
}
+bool IsLibraryAppletRunning() {
+ // Check the applets map for instances of any applet
+ for (auto itr = applets.begin(); itr != applets.end(); ++itr)
+ if (itr->second != nullptr)
+ return true;
+ return false;
+}
+
void Init() {
// Register the applet update callback
applet_update_event = CoreTiming::RegisterEvent("HLE Applet Update Event", AppletUpdateEvent);
}
void Shutdown() {
+ CoreTiming::RemoveEvent(applet_update_event);
}
}
diff --git a/src/core/hle/applets/applet.h b/src/core/hle/applets/applet.h
index b235d0b8..af442f81 100644
--- a/src/core/hle/applets/applet.h
+++ b/src/core/hle/applets/applet.h
@@ -67,6 +67,9 @@ protected:
Service::APT::AppletId id; ///< Id of this Applet
};
+/// Returns whether a library applet is currently running
+bool IsLibraryAppletRunning();
+
/// Initializes the HLE applets
void Init();
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 7332478f..7ae4859a 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -5,6 +5,7 @@
#include "common/logging/log.h"
#include "core/hle/service/service.h"
+#include "core/hle/service/am/am.h"
#include "core/hle/service/am/am_app.h"
#include "core/hle/service/am/am_net.h"
#include "core/hle/service/am/am_sys.h"
@@ -35,7 +36,7 @@ void GetTitleIDList(Service::Interface* self) {
cmd_buff[1] = RESULT_SUCCESS.raw;
cmd_buff[2] = 0;
- LOG_WARNING(Service_AM, "(STUBBED) Requested %u titles from media type %u", num_titles, media_type);
+ LOG_WARNING(Service_AM, "(STUBBED) Requested %u titles from media type %u. Address=0x%08X", num_titles, media_type, addr);
}
void GetNumContentInfos(Service::Interface* self) {
diff --git a/src/core/hle/service/am/am_net.cpp b/src/core/hle/service/am/am_net.cpp
index b1af0e9d..aa391f3b 100644
--- a/src/core/hle/service/am/am_net.cpp
+++ b/src/core/hle/service/am/am_net.cpp
@@ -28,7 +28,8 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x08130000, nullptr, "GetTotalContents"},
{0x08140042, nullptr, "GetContentIndexes"},
{0x08150044, nullptr, "GetContentsInfo"},
- {0x08190108, nullptr, "Unknown"},
+ {0x08180042, nullptr, "GetCTCert"},
+ {0x08190108, nullptr, "SetCertificates"},
{0x081B00C2, nullptr, "InstallTitlesFinish"},
};
diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp
index 7b6ab4ce..35402341 100644
--- a/src/core/hle/service/apt/apt.cpp
+++ b/src/core/hle/service/apt/apt.cpp
@@ -101,18 +101,19 @@ void NotifyToWait(Service::Interface* self) {
void GetLockHandle(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
- u32 flags = cmd_buff[1]; // TODO(bunnei): Figure out the purpose of the flag field
+ // Bits [0:2] are the applet type (System, Library, etc)
+ // Bit 5 tells the application that there's a pending APT parameter,
+ // this will cause the app to wait until parameter_event is signaled.
+ u32 applet_attributes = cmd_buff[1];
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- // Not sure what these parameters are used for, but retail apps check that they are 0 after
- // GetLockHandle has been called.
- cmd_buff[2] = 0; // Applet Attributes, this value is passed to Enable.
- cmd_buff[3] = 0;
- cmd_buff[4] = 0;
-
+ cmd_buff[2] = applet_attributes; // Applet Attributes, this value is passed to Enable.
+ cmd_buff[3] = 0; // Least significant bit = power button state
+ cmd_buff[4] = IPC::CopyHandleDesc();
cmd_buff[5] = Kernel::g_handle_table.Create(lock).MoveFrom();
- LOG_TRACE(Service_APT, "called handle=0x%08X", cmd_buff[5]);
+
+ LOG_WARNING(Service_APT, "(STUBBED) called handle=0x%08X applet_attributes=0x%08X", cmd_buff[5], applet_attributes);
}
void Enable(Service::Interface* self) {
@@ -139,13 +140,16 @@ void IsRegistered(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
u32 app_id = cmd_buff[1];
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- /// TODO(Subv): It is currently unknown what this value (0x400) means,
- /// but i believe it is used as a global "LibraryApplet" id, to verify if there's
- /// any LibApplet currently running. This is not verified.
- if (app_id != 0x400)
+
+ // TODO(Subv): An application is considered "registered" if it has already called APT::Enable
+ // handle this properly once we implement multiprocess support.
+ cmd_buff[2] = 0; // Set to not registered by default
+
+ if (app_id == static_cast<u32>(AppletId::AnyLibraryApplet)) {
+ cmd_buff[2] = HLE::Applets::IsLibraryAppletRunning() ? 1 : 0;
+ } else if (auto applet = HLE::Applets::Applet::Get(static_cast<AppletId>(app_id))) {
cmd_buff[2] = 1; // Set to registered
- else
- cmd_buff[2] = 0; // Set to not registered
+ }
LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id);
}
@@ -330,7 +334,26 @@ void GetAppCpuTimeLimit(Service::Interface* self) {
void PrepareToStartLibraryApplet(Service::Interface* self) {
u32* cmd_buff = Kernel::GetCommandBuffer();
AppletId applet_id = static_cast<AppletId>(cmd_buff[1]);
- cmd_buff[1] = HLE::Applets::Applet::Create(applet_id).raw;
+ auto applet = HLE::Applets::Applet::Get(applet_id);
+ if (applet) {
+ LOG_WARNING(Service_APT, "applet has already been started id=%08X", applet_id);
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ } else {
+ cmd_buff[1] = HLE::Applets::Applet::Create(applet_id).raw;
+ }
+ LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id);
+}
+
+void PreloadLibraryApplet(Service::Interface* self) {
+ u32* cmd_buff = Kernel::GetCommandBuffer();
+ AppletId applet_id = static_cast<AppletId>(cmd_buff[1]);
+ auto applet = HLE::Applets::Applet::Get(applet_id);
+ if (applet) {
+ LOG_WARNING(Service_APT, "applet has already been started id=%08X", applet_id);
+ cmd_buff[1] = RESULT_SUCCESS.raw;
+ } else {
+ cmd_buff[1] = HLE::Applets::Applet::Create(applet_id).raw;
+ }
LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id);
}
diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h
index 72972d05..4a72b6b5 100644
--- a/src/core/hle/service/apt/apt.h
+++ b/src/core/hle/service/apt/apt.h
@@ -62,6 +62,7 @@ enum class AppletId : u32 {
Extrapad = 0x208,
Memolib = 0x209,
Application = 0x300,
+ AnyLibraryApplet = 0x400,
SoftwareKeyboard2 = 0x401,
};
@@ -96,8 +97,26 @@ void GetSharedFont(Service::Interface* self);
*/
void NotifyToWait(Service::Interface* self);
+/**
+ * APT::GetLockHandle service function
+ * Inputs:
+ * 1 : Applet attributes
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ * 2 : Applet attributes
+ * 3 : Power button state
+ * 4 : IPC handle descriptor
+ * 5 : APT mutex handle
+ */
void GetLockHandle(Service::Interface* self);
+/**
+ * APT::Enable service function
+ * Inputs:
+ * 1 : Applet attributes
+ * Outputs:
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
void Enable(Service::Interface* self);
/**
@@ -284,6 +303,17 @@ void GetAppCpuTimeLimit(Service::Interface* self);
void PrepareToStartLibraryApplet(Service::Interface* self);
/**
+ * APT::PreloadLibraryApplet service function
+ * Inputs:
+ * 0 : Command header [0x00160040]
+ * 1 : Id of the applet to start
+ * Outputs:
+ * 0 : Return header
+ * 1 : Result of function, 0 on success, otherwise error code
+ */
+void PreloadLibraryApplet(Service::Interface* self);
+
+/**
* APT::StartLibraryApplet service function
* Inputs:
* 0 : Command header [0x001E0084]
diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp
index 88de339f..22800c56 100644
--- a/src/core/hle/service/apt/apt_a.cpp
+++ b/src/core/hle/service/apt/apt_a.cpp
@@ -21,6 +21,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x000D0080, ReceiveParameter, "ReceiveParameter"},
{0x000E0080, GlanceParameter, "GlanceParameter"},
{0x000F0100, CancelParameter, "CancelParameter"},
+ {0x00160040, PreloadLibraryApplet, "PreloadLibraryApplet"},
{0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"},
{0x001E0084, StartLibraryApplet, "StartLibraryApplet"},
{0x003B0040, nullptr, "CancelLibraryApplet?"},
diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp
index 396d1f04..3ac6ff94 100644
--- a/src/core/hle/service/apt/apt_s.cpp
+++ b/src/core/hle/service/apt/apt_s.cpp
@@ -32,9 +32,9 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00130000, nullptr, "GetPreparationState"},
{0x00140040, nullptr, "SetPreparationState"},
{0x00150140, nullptr, "PrepareToStartApplication"},
- {0x00160040, nullptr, "PreloadLibraryApplet"},
+ {0x00160040, PreloadLibraryApplet, "PreloadLibraryApplet"},
{0x00170040, nullptr, "FinishPreloadingLibraryApplet"},
- {0x00180040, nullptr, "PrepareToStartLibraryApplet"},
+ {0x00180040, PrepareToStartLibraryApplet,"PrepareToStartLibraryApplet"},
{0x00190040, nullptr, "PrepareToStartSystemApplet"},
{0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"},
{0x001B00C4, nullptr, "StartApplication"},
diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp
index b724cd72..146bfd59 100644
--- a/src/core/hle/service/apt/apt_u.cpp
+++ b/src/core/hle/service/apt/apt_u.cpp
@@ -33,7 +33,7 @@ const Interface::FunctionInfo FunctionTable[] = {
{0x00130000, nullptr, "GetPreparationState"},
{0x00140040, nullptr, "SetPreparationState"},
{0x00150140, nullptr, "PrepareToStartApplication"},
- {0x00160040, nullptr, "PreloadLibraryApplet"},
+ {0x00160040, PreloadLibraryApplet, "PreloadLibraryApplet"},
{0x00170040, nullptr, "FinishPreloadingLibraryApplet"},
{0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"},
{0x00190040, nullptr, "PrepareToStartSystemApplet"},
diff --git a/src/core/hle/service/ldr_ro.cpp b/src/core/hle/service/ldr_ro.cpp
index 155b97f6..f84ce4d7 100644
--- a/src/core/hle/service/ldr_ro.cpp
+++ b/src/core/hle/service/ldr_ro.cpp
@@ -40,7 +40,8 @@ static void Initialize(Service::Interface* self) {
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- LOG_WARNING(Service_LDR, "(STUBBED) called");
+ LOG_WARNING(Service_LDR, "(STUBBED) called. crs_buffer_ptr=0x%08X, crs_size=0x%08X, address=0x%08X, value=0x%08X, process=0x%08X",
+ crs_buffer_ptr, crs_size, address, value, process);
}
/**
@@ -69,7 +70,8 @@ static void LoadCRR(Service::Interface* self) {
cmd_buff[1] = RESULT_SUCCESS.raw; // No error
- LOG_WARNING(Service_LDR, "(STUBBED) called");
+ LOG_WARNING(Service_LDR, "(STUBBED) called. crs_buffer_ptr=0x%08X, crs_size=0x%08X, value=0x%08X, process=0x%08X",
+ crs_buffer_ptr, crs_size, value, process);
}
const Interface::FunctionInfo FunctionTable[] = {
diff --git a/src/core/hw/y2r.cpp b/src/core/hw/y2r.cpp
index f80e26ec..082a4db8 100644
--- a/src/core/hw/y2r.cpp
+++ b/src/core/hw/y2r.cpp
@@ -14,6 +14,7 @@
#include "common/vector_math.h"
#include "core/hle/service/y2r_u.h"
+#include "core/hw/y2r.h"
#include "core/memory.h"
namespace HW {
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index ef9584ab..243abe84 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -206,92 +206,115 @@ static inline void WritePicaReg(u32 id, u32 value, u32 mask) {
std::map<u32, u32> ranges;
} memory_accesses;
+ // Simple circular-replacement vertex cache
+ // The size has been tuned for optimal balance between hit-rate and the cost of lookup
+ const size_t VERTEX_CACHE_SIZE = 32;
+ std::array<u16, VERTEX_CACHE_SIZE> vertex_cache_ids;
+ std::array<VertexShader::OutputVertex, VERTEX_CACHE_SIZE> vertex_cache;
+
+ unsigned int vertex_cache_pos = 0;
+ vertex_cache_ids.fill(-1);
+
for (unsigned int index = 0; index < regs.num_vertices; ++index)
{
unsigned int vertex = is_indexed ? (index_u16 ? index_address_16[index] : index_address_8[index]) : index;
+ // -1 is a common special value used for primitive restart. Since it's unknown if
+ // the PICA supports it, and it would mess up the caching, guard against it here.
+ ASSERT(vertex != -1);
+
+ bool vertex_cache_hit = false;
+ VertexShader::OutputVertex output;
+
if (is_indexed) {
- // TODO: Implement some sort of vertex cache!
if (g_debug_context && Pica::g_debug_context->recorder) {
int size = index_u16 ? 2 : 1;
memory_accesses.AddAccess(base_address + index_info.offset + size * index, size);
}
- }
-
- // Initialize data for the current vertex
- VertexShader::InputVertex input;
-
- // Load a debugging token to check whether this gets loaded by the running
- // application or not.
- static const float24 debug_token = float24::FromRawFloat24(0x00abcdef);
- input.attr[0].w = debug_token;
-
- for (int i = 0; i < attribute_config.GetNumTotalAttributes(); ++i) {
- // Load the default attribute if we're configured to do so, this data will be overwritten by the loader data if it's set
- if (attribute_config.IsDefaultAttribute(i)) {
- input.attr[i] = g_state.vs.default_attributes[i];
- LOG_TRACE(HW_GPU, "Loaded default attribute %x for vertex %x (index %x): (%f, %f, %f, %f)",
- i, vertex, index,
- input.attr[i][0].ToFloat32(), input.attr[i][1].ToFloat32(),
- input.attr[i][2].ToFloat32(), input.attr[i][3].ToFloat32());
- }
- // Load per-vertex data from the loader arrays
- for (unsigned int comp = 0; comp < vertex_attribute_elements[i]; ++comp) {
- u32 source_addr = vertex_attribute_sources[i] + vertex_attribute_strides[i] * vertex + comp * vertex_attribute_element_size[i];
- const u8* srcdata = Memory::GetPhysicalPointer(source_addr);
-
- if (g_debug_context && Pica::g_debug_context->recorder) {
- memory_accesses.AddAccess(source_addr,
- (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::FLOAT) ? 4
- : (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::SHORT) ? 2 : 1);
+ for (unsigned int i = 0; i < VERTEX_CACHE_SIZE; ++i) {
+ if (vertex == vertex_cache_ids[i]) {
+ output = vertex_cache[i];
+ vertex_cache_hit = true;
+ break;
}
-
- const float srcval = (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::BYTE) ? *(s8*)srcdata :
- (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::UBYTE) ? *(u8*)srcdata :
- (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::SHORT) ? *(s16*)srcdata :
- *(float*)srcdata;
-
- input.attr[i][comp] = float24::FromFloat32(srcval);
- LOG_TRACE(HW_GPU, "Loaded component %x of attribute %x for vertex %x (index %x) from 0x%08x + 0x%08lx + 0x%04lx: %f",
- comp, i, vertex, index,
- attribute_config.GetPhysicalBaseAddress(),
- vertex_attribute_sources[i] - base_address,
- vertex_attribute_strides[i] * vertex + comp * vertex_attribute_element_size[i],
- input.attr[i][comp].ToFloat32());
}
}
- // HACK: Some games do not initialize the vertex position's w component. This leads
- // to critical issues since it messes up perspective division. As a
- // workaround, we force the fourth component to 1.0 if we find this to be the
- // case.
- // To do this, we additionally have to assume that the first input attribute
- // is the vertex position, since there's no information about this other than
- // the empiric observation that this is usually the case.
- if (input.attr[0].w == debug_token)
- input.attr[0].w = float24::FromFloat32(1.0);
+ if (!vertex_cache_hit) {
+ // Initialize data for the current vertex
+ VertexShader::InputVertex input;
+
+ for (int i = 0; i < attribute_config.GetNumTotalAttributes(); ++i) {
+ if (vertex_attribute_elements[i] != 0) {
+ // Default attribute values set if array elements have < 4 components. This
+ // is *not* carried over from the default attribute settings even if they're
+ // enabled for this attribute.
+ static const float24 zero = float24::FromFloat32(0.0f);
+ static const float24 one = float24::FromFloat32(1.0f);
+ input.attr[i] = Math::Vec4<float24>(zero, zero, zero, one);
+
+ // Load per-vertex data from the loader arrays
+ for (unsigned int comp = 0; comp < vertex_attribute_elements[i]; ++comp) {
+ u32 source_addr = vertex_attribute_sources[i] + vertex_attribute_strides[i] * vertex + comp * vertex_attribute_element_size[i];
+ const u8* srcdata = Memory::GetPhysicalPointer(source_addr);
+
+ if (g_debug_context && Pica::g_debug_context->recorder) {
+ memory_accesses.AddAccess(source_addr,
+ (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::FLOAT) ? 4
+ : (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::SHORT) ? 2 : 1);
+ }
+
+ const float srcval = (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::BYTE) ? *(s8*)srcdata :
+ (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::UBYTE) ? *(u8*)srcdata :
+ (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::SHORT) ? *(s16*)srcdata :
+ *(float*)srcdata;
+
+ input.attr[i][comp] = float24::FromFloat32(srcval);
+ LOG_TRACE(HW_GPU, "Loaded component %x of attribute %x for vertex %x (index %x) from 0x%08x + 0x%08lx + 0x%04lx: %f",
+ comp, i, vertex, index,
+ attribute_config.GetPhysicalBaseAddress(),
+ vertex_attribute_sources[i] - base_address,
+ vertex_attribute_strides[i] * vertex + comp * vertex_attribute_element_size[i],
+ input.attr[i][comp].ToFloat32());
+ }
+ } else if (attribute_config.IsDefaultAttribute(i)) {
+ // Load the default attribute if we're configured to do so
+ input.attr[i] = g_state.vs.default_attributes[i];
+ LOG_TRACE(HW_GPU, "Loaded default attribute %x for vertex %x (index %x): (%f, %f, %f, %f)",
+ i, vertex, index,
+ input.attr[i][0].ToFloat32(), input.attr[i][1].ToFloat32(),
+ input.attr[i][2].ToFloat32(), input.attr[i][3].ToFloat32());
+ } else {
+ // TODO(yuriks): In this case, no data gets loaded and the vertex
+ // remains with the last value it had. This isn't currently maintained
+ // as global state, however, and so won't work in Citra yet.
+ }
+ }
- if (g_debug_context)
- g_debug_context->OnEvent(DebugContext::Event::VertexLoaded, (void*)&input);
+ if (g_debug_context)
+ g_debug_context->OnEvent(DebugContext::Event::VertexLoaded, (void*)&input);
#if PICA_DUMP_GEOMETRY
- // NOTE: When dumping geometry, we simply assume that the first input attribute
- // corresponds to the position for now.
- DebugUtils::GeometryDumper::Vertex dumped_vertex = {
- input.attr[0][0].ToFloat32(), input.attr[0][1].ToFloat32(), input.attr[0][2].ToFloat32()
- };
- using namespace std::placeholders;
- dumping_primitive_assembler.SubmitVertex(dumped_vertex,
- std::bind(&DebugUtils::GeometryDumper::AddTriangle,
- &geometry_dumper, _1, _2, _3));
+ // NOTE: When dumping geometry, we simply assume that the first input attribute
+ // corresponds to the position for now.
+ DebugUtils::GeometryDumper::Vertex dumped_vertex = {
+ input.attr[0][0].ToFloat32(), input.attr[0][1].ToFloat32(), input.attr[0][2].ToFloat32()
+ };
+ using namespace std::placeholders;
+ dumping_primitive_assembler.SubmitVertex(dumped_vertex,
+ std::bind(&DebugUtils::GeometryDumper::AddTriangle,
+ &geometry_dumper, _1, _2, _3));
#endif
- // Send to vertex shader
- VertexShader::OutputVertex output = VertexShader::RunShader(input, attribute_config.GetNumTotalAttributes(), g_state.regs.vs, g_state.vs);
+ // Send to vertex shader
+ output = VertexShader::RunShader(input, attribute_config.GetNumTotalAttributes(), g_state.regs.vs, g_state.vs);
- if (is_indexed) {
- // TODO: Add processed vertex to vertex cache!
+ if (is_indexed) {
+ vertex_cache[vertex_cache_pos] = output;
+ vertex_cache_ids[vertex_cache_pos] = vertex;
+ vertex_cache_pos = (vertex_cache_pos + 1) % VERTEX_CACHE_SIZE;
+ }
}
if (Settings::values.use_hw_renderer) {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 2db845da..1fc4e56b 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -7,6 +7,7 @@
#include "common/color.h"
#include "common/math_util.h"
+#include "common/profiler.h"
#include "core/hw/gpu.h"
#include "core/memory.h"
@@ -873,11 +874,15 @@ void RasterizerOpenGL::ReloadDepthBuffer() {
state.Apply();
}
+Common::Profiling::TimingCategory buffer_commit_category("Framebuffer Commit");
+
void RasterizerOpenGL::CommitColorBuffer() {
if (last_fb_color_addr != 0) {
u8* color_buffer = Memory::GetPhysicalPointer(last_fb_color_addr);
if (color_buffer != nullptr) {
+ Common::Profiling::ScopeTimer timer(buffer_commit_category);
+
u32 bytes_per_pixel = Pica::Regs::BytesPerColorPixel(fb_color_texture.format);
std::unique_ptr<u8[]> temp_gl_color_buffer(new u8[fb_color_texture.width * fb_color_texture.height * bytes_per_pixel]);
@@ -913,6 +918,8 @@ void RasterizerOpenGL::CommitDepthBuffer() {
u8* depth_buffer = Memory::GetPhysicalPointer(last_fb_depth_addr);
if (depth_buffer != nullptr) {
+ Common::Profiling::ScopeTimer timer(buffer_commit_category);
+
u32 bytes_per_pixel = Pica::Regs::BytesPerDepthPixel(fb_depth_texture.format);
// OpenGL needs 4 bpp alignment for D24
diff --git a/src/video_core/vertex_shader.cpp b/src/video_core/vertex_shader.cpp
index 960ae577..5f66f345 100644
--- a/src/video_core/vertex_shader.cpp
+++ b/src/video_core/vertex_shader.cpp
@@ -609,6 +609,12 @@ OutputVertex RunShader(const InputVertex& input, int num_attributes, const Regs:
}
}
+ // The hardware takes the absolute and saturates vertex colors like this, *before* doing interpolation
+ for (int i = 0; i < 4; ++i) {
+ ret.color[i] = float24::FromFloat32(
+ std::fmin(std::fabs(ret.color[i].ToFloat32()), 1.0f));
+ }
+
LOG_TRACE(Render_Software, "Output vertex: pos (%.2f, %.2f, %.2f, %.2f), col(%.2f, %.2f, %.2f, %.2f), tc0(%.2f, %.2f)",
ret.pos.x.ToFloat32(), ret.pos.y.ToFloat32(), ret.pos.z.ToFloat32(), ret.pos.w.ToFloat32(),
ret.color.x.ToFloat32(), ret.color.y.ToFloat32(), ret.color.z.ToFloat32(), ret.color.w.ToFloat32(),