diff options
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/arm/dyncom/arm_dyncom_interpreter.cpp | 7 | ||||
-rw-r--r-- | src/core/gdbstub/gdbstub.cpp | 4 | ||||
-rw-r--r-- | src/core/hle/result.h | 1 | ||||
-rw-r--r-- | src/core/hle/service/ac_u.cpp | 26 | ||||
-rw-r--r-- | src/core/hle/service/am/am.cpp | 2 | ||||
-rw-r--r-- | src/core/hle/service/dsp_dsp.cpp | 191 | ||||
-rw-r--r-- | src/core/hle/service/dsp_dsp.h | 19 | ||||
-rw-r--r-- | src/core/hle/service/fs/archive.cpp | 1 | ||||
-rw-r--r-- | src/core/hle/service/fs/fs_user.cpp | 2 | ||||
-rw-r--r-- | src/core/hle/service/gsp_gpu.cpp | 70 | ||||
-rw-r--r-- | src/core/hle/service/y2r_u.cpp | 490 | ||||
-rw-r--r-- | src/core/hle/service/y2r_u.h | 20 | ||||
-rw-r--r-- | src/core/hle/svc.cpp | 8 | ||||
-rw-r--r-- | src/core/hw/gpu.cpp | 327 | ||||
-rw-r--r-- | src/core/hw/gpu.h | 4 | ||||
-rw-r--r-- | src/core/loader/ncch.cpp | 2 | ||||
-rw-r--r-- | src/core/memory.cpp | 140 | ||||
-rw-r--r-- | src/core/memory.h | 16 | ||||
-rw-r--r-- | src/core/settings.cpp | 2 | ||||
-rw-r--r-- | src/core/settings.h | 1 |
20 files changed, 1004 insertions, 329 deletions
diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp index 647784208..8d4b26815 100644 --- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp +++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp @@ -10,7 +10,6 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "common/microprofile.h" -#include "common/profiler.h" #include "core/memory.h" #include "core/hle/svc.h" @@ -25,9 +24,6 @@ #include "core/gdbstub/gdbstub.h" -Common::Profiling::TimingCategory profile_execute("DynCom::Execute"); -Common::Profiling::TimingCategory profile_decode("DynCom::Decode"); - enum { COND = (1 << 0), NON_BRANCH = (1 << 1), @@ -3496,7 +3492,6 @@ static unsigned int InterpreterTranslateInstruction(const ARMul_State* cpu, cons } static int InterpreterTranslateBlock(ARMul_State* cpu, int& bb_start, u32 addr) { - Common::Profiling::ScopeTimer timer_decode(profile_decode); MICROPROFILE_SCOPE(DynCom_Decode); // Decode instruction, get index @@ -3530,7 +3525,6 @@ static int InterpreterTranslateBlock(ARMul_State* cpu, int& bb_start, u32 addr) } static int InterpreterTranslateSingle(ARMul_State* cpu, int& bb_start, u32 addr) { - Common::Profiling::ScopeTimer timer_decode(profile_decode); MICROPROFILE_SCOPE(DynCom_Decode); ARM_INST_PTR inst_base = nullptr; @@ -3565,7 +3559,6 @@ static int clz(unsigned int x) { MICROPROFILE_DEFINE(DynCom_Execute, "DynCom", "Execute", MP_RGB(255, 0, 0)); unsigned InterpreterMainLoop(ARMul_State* cpu) { - Common::Profiling::ScopeTimer timer_execute(profile_execute); MICROPROFILE_SCOPE(DynCom_Execute); GDBStub::BreakpointAddress breakpoint_data; diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index c1a7ec5bf..ae0c116ef 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -529,7 +529,7 @@ static void ReadRegister() { id |= HexCharToValue(command_buffer[2]); } - if (id >= R0_REGISTER && id <= R15_REGISTER) { + if (id <= R15_REGISTER) { IntToGdbHex(reply, Core::g_app_core->GetReg(id)); } else if (id == CPSR_REGISTER) { IntToGdbHex(reply, Core::g_app_core->GetCPSR()); @@ -584,7 +584,7 @@ static void WriteRegister() { id |= HexCharToValue(command_buffer[2]); } - if (id >= R0_REGISTER && id <= R15_REGISTER) { + if (id <= R15_REGISTER) { Core::g_app_core->SetReg(id, GdbHexToInt(buffer_ptr)); } else if (id == CPSR_REGISTER) { Core::g_app_core->SetCPSR(GdbHexToInt(buffer_ptr)); diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 2d22652d9..53931a106 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -18,6 +18,7 @@ /// Detailed description of the error. This listing is likely incomplete. enum class ErrorDescription : u32 { Success = 0, + OS_InvalidBufferDescriptor = 48, WrongAddress = 53, FS_NotFound = 120, FS_AlreadyExists = 190, diff --git a/src/core/hle/service/ac_u.cpp b/src/core/hle/service/ac_u.cpp index d67325506..5241dd3e7 100644 --- a/src/core/hle/service/ac_u.cpp +++ b/src/core/hle/service/ac_u.cpp @@ -3,6 +3,8 @@ // Refer to the license.txt file included. #include "common/logging/log.h" + +#include "core/hle/kernel/event.h" #include "core/hle/service/ac_u.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -11,6 +13,28 @@ namespace AC_U { /** + * AC_U::CloseAsync service function + * Inputs: + * 1 : Always 0x20 + * 3 : Always 0 + * 4 : Event handle, should be signaled when AC connection is closed + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void CloseAsync(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + auto evt = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]); + + if (evt) { + evt->name = "AC_U:close_event"; + evt->Signal(); + } + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + LOG_WARNING(Service_AC, "(STUBBED) called"); +} +/** * AC_U::GetWifiStatus service function * Outputs: * 1 : Result of function, 0 on success, otherwise error code @@ -47,7 +71,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00010000, nullptr, "CreateDefaultConfig"}, {0x00040006, nullptr, "ConnectAsync"}, {0x00050002, nullptr, "GetConnectResult"}, - {0x00080004, nullptr, "CloseAsync"}, + {0x00080004, CloseAsync, "CloseAsync"}, {0x00090002, nullptr, "GetCloseResult"}, {0x000A0000, nullptr, "GetLastErrorCode"}, {0x000D0000, GetWifiStatus, "GetWifiStatus"}, diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 9591522e5..3f71e7f2b 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -43,7 +43,7 @@ void FindContentInfos(Service::Interface* self) { am_content_count[media_type] = cmd_buff[4]; cmd_buff[1] = RESULT_SUCCESS.raw; - LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, title_id=0x%016lx, content_cound=%u, content_ids_pointer=0x%08x, content_info_pointer=0x%08x", + LOG_WARNING(Service_AM, "(STUBBED) media_type=%u, title_id=0x%016llx, content_cound=%u, content_ids_pointer=0x%08x, content_info_pointer=0x%08x", media_type, title_id, am_content_count[media_type], content_ids_pointer, content_info_pointer); } diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp index 08e437125..995bee3f9 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> #include <cinttypes> #include "audio_core/hle/pipe.h" @@ -12,37 +13,80 @@ #include "core/hle/kernel/event.h" #include "core/hle/service/dsp_dsp.h" +using DspPipe = DSP::HLE::DspPipe; + //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace DSP_DSP namespace DSP_DSP { -static u32 read_pipe_count; static Kernel::SharedPtr<Kernel::Event> semaphore_event; -struct PairHash { - template <typename T, typename U> - std::size_t operator()(const std::pair<T, U> &x) const { - // TODO(yuriks): Replace with better hash combining function. - return std::hash<T>()(x.first) ^ std::hash<U>()(x.second); +/// There are three types of interrupts +enum class InterruptType { + Zero, One, Pipe +}; +constexpr size_t NUM_INTERRUPT_TYPE = 3; + +class InterruptEvents final { +public: + void Signal(InterruptType type, DspPipe pipe) { + Kernel::SharedPtr<Kernel::Event>& event = Get(type, pipe); + if (event) { + event->Signal(); + } } + + Kernel::SharedPtr<Kernel::Event>& Get(InterruptType type, DspPipe dsp_pipe) { + switch (type) { + case InterruptType::Zero: + return zero; + case InterruptType::One: + return one; + case InterruptType::Pipe: { + const size_t pipe_index = static_cast<size_t>(dsp_pipe); + ASSERT(pipe_index < DSP::HLE::NUM_DSP_PIPE); + return pipe[pipe_index]; + } + } + + UNREACHABLE_MSG("Invalid interrupt type = %zu", static_cast<size_t>(type)); + } + + bool HasTooManyEventsRegistered() const { + // Actual service implementation only has 6 'slots' for interrupts. + constexpr size_t max_number_of_interrupt_events = 6; + + size_t number = std::count_if(pipe.begin(), pipe.end(), [](const auto& evt) { + return evt != nullptr; + }); + + if (zero != nullptr) + number++; + if (one != nullptr) + number++; + + return number >= max_number_of_interrupt_events; + } + +private: + /// Currently unknown purpose + Kernel::SharedPtr<Kernel::Event> zero = nullptr; + /// Currently unknown purpose + Kernel::SharedPtr<Kernel::Event> one = nullptr; + /// Each DSP pipe has an associated interrupt + std::array<Kernel::SharedPtr<Kernel::Event>, DSP::HLE::NUM_DSP_PIPE> pipe = {{}}; }; -/// Map of (audio interrupt number, channel number) to Kernel::Events. See: RegisterInterruptEvents -static std::unordered_map<std::pair<u32, u32>, Kernel::SharedPtr<Kernel::Event>, PairHash> interrupt_events; +static InterruptEvents interrupt_events; // DSP Interrupts: -// Interrupt #2 occurs every frame tick. Userland programs normally have a thread that's waiting -// for an interrupt event. Immediately after this interrupt event, userland normally updates the -// state in the next region and increments the relevant frame counter by two. -void SignalAllInterrupts() { - // HACK: The other interrupts have currently unknown purpose, we trigger them each tick in any case. - for (auto& interrupt_event : interrupt_events) - interrupt_event.second->Signal(); -} - -void SignalInterrupt(u32 interrupt, u32 channel) { - interrupt_events[std::make_pair(interrupt, channel)]->Signal(); +// The audio-pipe interrupt occurs every frame tick. Userland programs normally have a thread +// that's waiting for an interrupt event. Immediately after this interrupt event, userland +// normally updates the state in the next region and increments the relevant frame counter by +// two. +void SignalPipeInterrupt(DspPipe pipe) { + interrupt_events.Signal(InterruptType::Pipe, pipe); } /** @@ -58,7 +102,10 @@ static void ConvertProcessAddressFromDspDram(Service::Interface* self) { u32 addr = cmd_buff[1]; + cmd_buff[0] = IPC::MakeHeader(0xC, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + // TODO(merry): There is a per-region offset missing in this calculation (that seems to be always zero). cmd_buff[2] = (addr << 1) + (Memory::DSP_RAM_VADDR + 0x40000); LOG_DEBUG(Service_DSP, "addr=0x%08X", addr); @@ -113,7 +160,9 @@ static void LoadComponent(Service::Interface* self) { static void GetSemaphoreEventHandle(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[0] = IPC::MakeHeader(0x16, 1, 2); cmd_buff[1] = RESULT_SUCCESS.raw; // No error + // cmd_buff[2] not set cmd_buff[3] = Kernel::g_handle_table.Create(semaphore_event).MoveFrom(); // Event handle LOG_WARNING(Service_DSP, "(STUBBED) called"); @@ -138,8 +187,7 @@ static void FlushDataCache(Service::Interface* self) { u32 size = cmd_buff[2]; u32 process = cmd_buff[4]; - // TODO(purpasmart96): Verify return header on HW - + cmd_buff[0] = IPC::MakeHeader(0x13, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error LOG_TRACE(Service_DSP, "called address=0x%08X, size=0x%X, process=0x%08X", address, size, process); @@ -148,8 +196,8 @@ static void FlushDataCache(Service::Interface* self) { /** * DSP_DSP::RegisterInterruptEvents service function * Inputs: - * 1 : Interrupt Number - * 2 : Channel Number + * 1 : Interrupt Type + * 2 : Pipe Number * 4 : Interrupt event handle * Outputs: * 1 : Result of function, 0 on success, otherwise error code @@ -157,23 +205,40 @@ static void FlushDataCache(Service::Interface* self) { static void RegisterInterruptEvents(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 interrupt = cmd_buff[1]; - u32 channel = cmd_buff[2]; + u32 type_index = cmd_buff[1]; + u32 pipe_index = cmd_buff[2]; u32 event_handle = cmd_buff[4]; + ASSERT_MSG(type_index < NUM_INTERRUPT_TYPE && pipe_index < DSP::HLE::NUM_DSP_PIPE, + "Invalid type or pipe: type = %u, pipe = %u", type_index, pipe_index); + + InterruptType type = static_cast<InterruptType>(cmd_buff[1]); + DspPipe pipe = static_cast<DspPipe>(cmd_buff[2]); + + cmd_buff[0] = IPC::MakeHeader(0x15, 1, 0); + if (event_handle) { auto evt = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]); - if (evt) { - interrupt_events[std::make_pair(interrupt, channel)] = evt; - cmd_buff[1] = RESULT_SUCCESS.raw; - LOG_INFO(Service_DSP, "Registered interrupt=%u, channel=%u, event_handle=0x%08X", interrupt, channel, event_handle); - } else { - LOG_CRITICAL(Service_DSP, "Invalid event handle! interrupt=%u, channel=%u, event_handle=0x%08X", interrupt, channel, event_handle); - ASSERT(false); // This should really be handled at a IPC translation layer. + + if (!evt) { + LOG_INFO(Service_DSP, "Invalid event handle! type=%u, pipe=%u, event_handle=0x%08X", type_index, pipe_index, event_handle); + ASSERT(false); // TODO: This should really be handled at an IPC translation layer. + } + + if (interrupt_events.HasTooManyEventsRegistered()) { + LOG_INFO(Service_DSP, "Ran out of space to register interrupts (Attempted to register type=%u, pipe=%u, event_handle=0x%08X)", + type_index, pipe_index, event_handle); + cmd_buff[1] = ResultCode(ErrorDescription::InvalidResultValue, ErrorModule::DSP, ErrorSummary::OutOfResource, ErrorLevel::Status).raw; + return; } + + interrupt_events.Get(type, pipe) = evt; + LOG_INFO(Service_DSP, "Registered type=%u, pipe=%u, event_handle=0x%08X", type_index, pipe_index, event_handle); + cmd_buff[1] = RESULT_SUCCESS.raw; } else { - interrupt_events.erase(std::make_pair(interrupt, channel)); - LOG_INFO(Service_DSP, "Unregistered interrupt=%u, channel=%u, event_handle=0x%08X", interrupt, channel, event_handle); + interrupt_events.Get(type, pipe) = nullptr; + LOG_INFO(Service_DSP, "Unregistered interrupt=%u, channel=%u, event_handle=0x%08X", type_index, pipe_index, event_handle); + cmd_buff[1] = RESULT_SUCCESS.raw; } } @@ -187,6 +252,7 @@ static void RegisterInterruptEvents(Service::Interface* self) { static void SetSemaphore(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[0] = IPC::MakeHeader(0x7, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error LOG_WARNING(Service_DSP, "(STUBBED) called"); @@ -195,7 +261,7 @@ static void SetSemaphore(Service::Interface* self) { /** * DSP_DSP::WriteProcessPipe service function * Inputs: - * 1 : Channel + * 1 : Pipe Number * 2 : Size * 3 : (size << 14) | 0x402 * 4 : Buffer @@ -206,24 +272,32 @@ static void SetSemaphore(Service::Interface* self) { static void WriteProcessPipe(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(cmd_buff[1]); + u32 pipe_index = cmd_buff[1]; u32 size = cmd_buff[2]; u32 buffer = cmd_buff[4]; - ASSERT_MSG(IPC::StaticBufferDesc(size, 1) == cmd_buff[3], "IPC static buffer descriptor failed validation (0x%X). pipe=%u, size=0x%X, buffer=0x%08X", cmd_buff[3], pipe, size, buffer); - ASSERT_MSG(Memory::GetPointer(buffer) != nullptr, "Invalid Buffer: pipe=%u, size=0x%X, buffer=0x%08X", pipe, size, buffer); + DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index); - std::vector<u8> message(size); + if (IPC::StaticBufferDesc(size, 1) != cmd_buff[3]) { + LOG_ERROR(Service_DSP, "IPC static buffer descriptor failed validation (0x%X). pipe=%u, size=0x%X, buffer=0x%08X", cmd_buff[3], pipe_index, size, buffer); + cmd_buff[0] = IPC::MakeHeader(0, 1, 0); + cmd_buff[1] = ResultCode(ErrorDescription::OS_InvalidBufferDescriptor, ErrorModule::OS, ErrorSummary::WrongArgument, ErrorLevel::Permanent).raw; + return; + } + + ASSERT_MSG(Memory::GetPointer(buffer) != nullptr, "Invalid Buffer: pipe=%u, size=0x%X, buffer=0x%08X", pipe_index, size, buffer); + std::vector<u8> message(size); for (size_t i = 0; i < size; i++) { message[i] = Memory::Read8(buffer + i); } DSP::HLE::PipeWrite(pipe, message); + cmd_buff[0] = IPC::MakeHeader(0xD, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_DEBUG(Service_DSP, "pipe=%u, size=0x%X, buffer=0x%08X", pipe, size, buffer); + LOG_DEBUG(Service_DSP, "pipe=%u, size=0x%X, buffer=0x%08X", pipe_index, size, buffer); } /** @@ -243,13 +317,16 @@ static void WriteProcessPipe(Service::Interface* self) { static void ReadPipeIfPossible(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(cmd_buff[1]); + u32 pipe_index = cmd_buff[1]; u32 unknown = cmd_buff[2]; u32 size = cmd_buff[3] & 0xFFFF; // Lower 16 bits are size VAddr addr = cmd_buff[0x41]; - ASSERT_MSG(Memory::GetPointer(addr) != nullptr, "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe, unknown, size, addr); + DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index); + + ASSERT_MSG(Memory::GetPointer(addr) != nullptr, "Invalid addr: pipe=%u, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe_index, unknown, size, addr); + cmd_buff[0] = IPC::MakeHeader(0x10, 1, 2); cmd_buff[1] = RESULT_SUCCESS.raw; // No error if (DSP::HLE::GetPipeReadableSize(pipe) >= size) { std::vector<u8> response = DSP::HLE::PipeRead(pipe, size); @@ -260,8 +337,10 @@ static void ReadPipeIfPossible(Service::Interface* self) { } else { cmd_buff[2] = 0; // Return no data } + cmd_buff[3] = IPC::StaticBufferDesc(size, 0); + cmd_buff[4] = addr; - LOG_DEBUG(Service_DSP, "pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X, return cmd_buff[2]=0x%08X", pipe, unknown, size, addr, cmd_buff[2]); + LOG_DEBUG(Service_DSP, "pipe=%u, unknown=0x%08X, size=0x%X, buffer=0x%08X, return cmd_buff[2]=0x%08X", pipe_index, unknown, size, addr, cmd_buff[2]); } /** @@ -278,26 +357,31 @@ static void ReadPipeIfPossible(Service::Interface* self) { static void ReadPipe(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(cmd_buff[1]); + u32 pipe_index = cmd_buff[1]; u32 unknown = cmd_buff[2]; u32 size = cmd_buff[3] & 0xFFFF; // Lower 16 bits are size VAddr addr = cmd_buff[0x41]; - ASSERT_MSG(Memory::GetPointer(addr) != nullptr, "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe, unknown, size, addr); + DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index); + + ASSERT_MSG(Memory::GetPointer(addr) != nullptr, "Invalid addr: pipe=%u, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe_index, unknown, size, addr); if (DSP::HLE::GetPipeReadableSize(pipe) >= size) { std::vector<u8> response = DSP::HLE::PipeRead(pipe, size); Memory::WriteBlock(addr, response.data(), response.size()); + cmd_buff[0] = IPC::MakeHeader(0xE, 2, 2); cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = static_cast<u32>(response.size()); + cmd_buff[3] = IPC::StaticBufferDesc(size, 0); + cmd_buff[4] = addr; } else { // No more data is in pipe. Hardware hangs in this case; this should never happen. UNREACHABLE(); } - LOG_DEBUG(Service_DSP, "pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X, return cmd_buff[2]=0x%08X", pipe, unknown, size, addr, cmd_buff[2]); + LOG_DEBUG(Service_DSP, "pipe=%u, unknown=0x%08X, size=0x%X, buffer=0x%08X, return cmd_buff[2]=0x%08X", pipe_index, unknown, size, addr, cmd_buff[2]); } /** @@ -312,13 +396,16 @@ static void ReadPipe(Service::Interface* self) { static void GetPipeReadableSize(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(cmd_buff[1]); + u32 pipe_index = cmd_buff[1]; u32 unknown = cmd_buff[2]; + DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index); + + cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = DSP::HLE::GetPipeReadableSize(pipe); - LOG_DEBUG(Service_DSP, "pipe=0x%08X, unknown=0x%08X, return cmd_buff[2]=0x%08X", pipe, unknown, cmd_buff[2]); + LOG_DEBUG(Service_DSP, "pipe=%u, unknown=0x%08X, return cmd_buff[2]=0x%08X", pipe_index, unknown, cmd_buff[2]); } /** @@ -333,6 +420,7 @@ static void SetSemaphoreMask(Service::Interface* self) { u32 mask = cmd_buff[1]; + cmd_buff[0] = IPC::MakeHeader(0x17, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error LOG_WARNING(Service_DSP, "(STUBBED) called mask=0x%08X", mask); @@ -350,6 +438,7 @@ static void SetSemaphoreMask(Service::Interface* self) { static void GetHeadphoneStatus(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[0] = IPC::MakeHeader(0x1F, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = 0; // Not using headphones? @@ -376,6 +465,7 @@ static void RecvData(Service::Interface* self) { // Application reads this after requesting DSP shutdown, to verify the DSP has indeed shutdown or slept. + cmd_buff[0] = IPC::MakeHeader(0x1, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; switch (DSP::HLE::GetDspState()) { case DSP::HLE::DspState::On: @@ -411,6 +501,7 @@ static void RecvDataIsReady(Service::Interface* self) { ASSERT_MSG(register_number == 0, "Unknown register_number %u", register_number); + cmd_buff[0] = IPC::MakeHeader(0x2, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = 1; // Ready to read @@ -458,14 +549,14 @@ const Interface::FunctionInfo FunctionTable[] = { Interface::Interface() { semaphore_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "DSP_DSP::semaphore_event"); - read_pipe_count = 0; + interrupt_events = {}; Register(FunctionTable); } Interface::~Interface() { semaphore_event = nullptr; - interrupt_events.clear(); + interrupt_events = {}; } } // namespace diff --git a/src/core/hle/service/dsp_dsp.h b/src/core/hle/service/dsp_dsp.h index 32b89e9bb..22f6687cc 100644 --- a/src/core/hle/service/dsp_dsp.h +++ b/src/core/hle/service/dsp_dsp.h @@ -8,6 +8,12 @@ #include "core/hle/service/service.h" +namespace DSP { +namespace HLE { +enum class DspPipe; +} +} + //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace DSP_DSP @@ -23,15 +29,10 @@ public: } }; -/// Signal all audio related interrupts. -void SignalAllInterrupts(); - /** - * Signal a specific audio related interrupt based on interrupt id and channel id. - * @param interrupt_id The interrupt id - * @param channel_id The channel id - * The significance of various values of interrupt_id and channel_id is not yet known. + * Signal a specific DSP related interrupt of type == InterruptType::Pipe, pipe == pipe. + * @param pipe The DSP pipe for which to signal an interrupt for. */ -void SignalInterrupt(u32 interrupt_id, u32 channel_id); +void SignalPipeInterrupt(DSP::HLE::DspPipe pipe); -} // namespace +} // namespace DSP_DSP diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index e9588cb72..cc51ede0c 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp @@ -114,6 +114,7 @@ ResultVal<bool> File::SyncRequest() { return read.Code(); } cmd_buff[2] = static_cast<u32>(*read); + Memory::RasterizerFlushAndInvalidateRegion(Memory::VirtualToPhysicalAddress(address), length); break; } diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index 3ec7ceb30..7df7da5a4 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -250,7 +250,7 @@ static void CreateFile(Service::Interface* self) { FileSys::Path file_path(filename_type, filename_size, filename_ptr); - LOG_DEBUG(Service_FS, "type=%d size=%llu data=%s", filename_type, filename_size, file_path.DebugStr().c_str()); + LOG_DEBUG(Service_FS, "type=%d size=%llu data=%s", filename_type, file_size, file_path.DebugStr().c_str()); cmd_buff[1] = CreateFileInArchive(archive_handle, file_path, file_size).raw; } diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index 0c655395e..b4c146e08 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp @@ -15,8 +15,6 @@ #include "video_core/gpu_debugger.h" #include "video_core/debug_utils/debug_utils.h" -#include "video_core/renderer_base.h" -#include "video_core/video_core.h" #include "gsp_gpu.h" @@ -45,6 +43,8 @@ Kernel::SharedPtr<Kernel::SharedMemory> g_shared_memory; /// Thread index into interrupt relay queue u32 g_thread_id = 0; +static bool gpu_right_acquired = false; + /// Gets a pointer to a thread command buffer in GSP shared memory static inline u8* GetCommandBuffer(u32 thread_id) { return g_shared_memory->GetPointer(0x800 + (thread_id * sizeof(CommandBuffer))); @@ -291,8 +291,6 @@ static void FlushDataCache(Service::Interface* self) { u32 size = cmd_buff[2]; u32 process = cmd_buff[4]; - VideoCore::g_renderer->Rasterizer()->InvalidateRegion(Memory::VirtualToPhysicalAddress(address), size); - // TODO(purpasmart96): Verify return header on HW cmd_buff[1] = RESULT_SUCCESS.raw; // No error @@ -374,6 +372,9 @@ static void UnregisterInterruptRelayQueue(Service::Interface* self) { * @todo This probably does not belong in the GSP module, instead move to video_core */ void SignalInterrupt(InterruptId interrupt_id) { + if (!gpu_right_acquired) { + return; + } if (nullptr == g_interrupt_event) { LOG_WARNING(Service_GSP, "cannot synchronize until GSP event has been created!"); return; @@ -408,6 +409,8 @@ void SignalInterrupt(InterruptId interrupt_id) { g_interrupt_event->Signal(); } +MICROPROFILE_DEFINE(GPU_GSP_DMA, "GPU", "GSP DMA", MP_RGB(100, 0, 255)); + /// Executes the next GSP command static void ExecuteCommand(const Command& command, u32 thread_id) { // Utility function to convert register ID to address @@ -419,18 +422,21 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { // GX request DMA - typically used for copying memory from GSP heap to VRAM case CommandId::REQUEST_DMA: - VideoCore::g_renderer->Rasterizer()->FlushRegion(Memory::VirtualToPhysicalAddress(command.dma_request.source_address), - command.dma_request.size); + { + MICROPROFILE_SCOPE(GPU_GSP_DMA); + + // TODO: Consider attempting rasterizer-accelerated surface blit if that usage is ever possible/likely + Memory::RasterizerFlushRegion(Memory::VirtualToPhysicalAddress(command.dma_request.source_address), + command.dma_request.size); + Memory::RasterizerFlushAndInvalidateRegion(Memory::VirtualToPhysicalAddress(command.dma_request.dest_address), + command.dma_request.size); memcpy(Memory::GetPointer(command.dma_request.dest_address), Memory::GetPointer(command.dma_request.source_address), command.dma_request.size); SignalInterrupt(InterruptId::DMA); - - VideoCore::g_renderer->Rasterizer()->InvalidateRegion(Memory::VirtualToPhysicalAddress(command.dma_request.dest_address), - command.dma_request.size); break; - + } // TODO: This will need some rework in the future. (why?) case CommandId::SUBMIT_GPU_CMDLIST: { @@ -517,13 +523,8 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { case CommandId::CACHE_FLUSH: { - for (auto& region : command.cache_flush.regions) { - if (region.size == 0) - break; - - VideoCore::g_renderer->Rasterizer()->InvalidateRegion( - Memory::VirtualToPhysicalAddress(region.address), region.size); - } + // NOTE: Rasterizer flushing handled elsewhere in CPU read/write and other GPU handlers + // Use command.cache_flush.regions to implement this handler break; } @@ -628,6 +629,35 @@ static void ImportDisplayCaptureInfo(Service::Interface* self) { LOG_WARNING(Service_GSP, "called"); } +/** + * GSP_GPU::AcquireRight service function + * Outputs: + * 1: Result code + */ +static void AcquireRight(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + gpu_right_acquired = true; + + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_GSP, "called"); +} + +/** + * GSP_GPU::ReleaseRight service function + * Outputs: + * 1: Result code + */ +static void ReleaseRight(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + gpu_right_acquired = false; + + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_GSP, "called"); +} const Interface::FunctionInfo FunctionTable[] = { {0x00010082, WriteHWRegs, "WriteHWRegs"}, @@ -651,8 +681,8 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00130042, RegisterInterruptRelayQueue, "RegisterInterruptRelayQueue"}, {0x00140000, UnregisterInterruptRelayQueue, "UnregisterInterruptRelayQueue"}, {0x00150002, nullptr, "TryAcquireRight"}, - {0x00160042, nullptr, "AcquireRight"}, - {0x00170000, nullptr, "ReleaseRight"}, + {0x00160042, AcquireRight, "AcquireRight"}, + {0x00170000, ReleaseRight, "ReleaseRight"}, {0x00180000, ImportDisplayCaptureInfo, "ImportDisplayCaptureInfo"}, {0x00190000, nullptr, "SaveVramSysArea"}, {0x001A0000, nullptr, "RestoreVramSysArea"}, @@ -673,11 +703,13 @@ Interface::Interface() { g_shared_memory = nullptr; g_thread_id = 0; + gpu_right_acquired = false; } Interface::~Interface() { g_interrupt_event = nullptr; g_shared_memory = nullptr; + gpu_right_acquired = false; } } // namespace diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp index 22f373adf..d16578f87 100644 --- a/src/core/hle/service/y2r_u.cpp +++ b/src/core/hle/service/y2r_u.cpp @@ -4,6 +4,7 @@ #include <cstring> +#include "common/common_funcs.h" #include "common/common_types.h" #include "common/logging/log.h" @@ -12,9 +13,6 @@ #include "core/hle/service/y2r_u.h" #include "core/hw/y2r.h" -#include "video_core/renderer_base.h" -#include "video_core/video_core.h" - //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace Y2R_U @@ -28,13 +26,17 @@ struct ConversionParameters { u16 input_line_width; u16 input_lines; StandardCoefficient standard_coefficient; - u8 reserved; + u8 padding; u16 alpha; }; static_assert(sizeof(ConversionParameters) == 12, "ConversionParameters struct has incorrect size"); static Kernel::SharedPtr<Kernel::Event> completion_event; static ConversionConfiguration conversion; +static DitheringWeightParams dithering_weight_params; +static u32 temporal_dithering_enabled = 0; +static u32 transfer_end_interrupt_enabled = 0; +static u32 spacial_dithering_enabled = 0; static const CoefficientSet standard_coefficients[4] = { {{ 0x100, 0x166, 0xB6, 0x58, 0x1C5, -0x166F, 0x10EE, -0x1C5B }}, // ITU_Rec601 @@ -73,7 +75,7 @@ ResultCode ConversionConfiguration::SetInputLines(u16 lines) { ResultCode ConversionConfiguration::SetStandardCoefficient(StandardCoefficient standard_coefficient) { size_t index = static_cast<size_t>(standard_coefficient); - if (index >= 4) { + if (index >= ARRAY_SIZE(standard_coefficients)) { return ResultCode(ErrorDescription::InvalidEnumValue, ErrorModule::CAM, ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E053ED } @@ -86,44 +88,183 @@ static void SetInputFormat(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); conversion.input_format = static_cast<InputFormat>(cmd_buff[1]); + + cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_DEBUG(Service_Y2R, "called input_format=%hhu", conversion.input_format); +} + +static void GetInputFormat(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[0] = IPC::MakeHeader(0x2, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = static_cast<u32>(conversion.input_format); + + LOG_DEBUG(Service_Y2R, "called input_format=%hhu", conversion.input_format); } static void SetOutputFormat(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); conversion.output_format = static_cast<OutputFormat>(cmd_buff[1]); + + cmd_buff[0] = IPC::MakeHeader(0x3, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_DEBUG(Service_Y2R, "called output_format=%hhu", conversion.output_format); +} + +static void GetOutputFormat(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[0] = IPC::MakeHeader(0x4, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = static_cast<u32>(conversion.output_format); + + LOG_DEBUG(Service_Y2R, "called output_format=%hhu", conversion.output_format); } static void SetRotation(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); conversion.rotation = static_cast<Rotation>(cmd_buff[1]); + + cmd_buff[0] = IPC::MakeHeader(0x5, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_DEBUG(Service_Y2R, "called rotation=%hhu", conversion.rotation); +} + +static void GetRotation(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[0] = IPC::MakeHeader(0x6, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = static_cast<u32>(conversion.rotation); + + LOG_DEBUG(Service_Y2R, "called rotation=%hhu", conversion.rotation); } static void SetBlockAlignment(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); conversion.block_alignment = static_cast<BlockAlignment>(cmd_buff[1]); - LOG_DEBUG(Service_Y2R, "called alignment=%hhu", conversion.block_alignment); + cmd_buff[0] = IPC::MakeHeader(0x7, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_DEBUG(Service_Y2R, "called block_alignment=%hhu", conversion.block_alignment); +} + +static void GetBlockAlignment(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x8, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = static_cast<u32>(conversion.block_alignment); + + LOG_DEBUG(Service_Y2R, "called block_alignment=%hhu", conversion.block_alignment); +} + +/** + * Y2R_U::SetSpacialDithering service function + * Inputs: + * 1 : u8, 0 = Disabled, 1 = Enabled + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void SetSpacialDithering(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + spacial_dithering_enabled = cmd_buff[1] & 0xF; + + cmd_buff[0] = IPC::MakeHeader(0x9, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_Y2R, "(STUBBED) called"); +} + +/** + * Y2R_U::GetSpacialDithering service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : u8, 0 = Disabled, 1 = Enabled + */ +static void GetSpacialDithering(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0xA, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = spacial_dithering_enabled; + + LOG_WARNING(Service_Y2R, "(STUBBED) called"); +} + +/** + * Y2R_U::SetTemporalDithering service function + * Inputs: + * 1 : u8, 0 = Disabled, 1 = Enabled + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void SetTemporalDithering(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + temporal_dithering_enabled = cmd_buff[1] & 0xF; + + cmd_buff[0] = IPC::MakeHeader(0xB, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_Y2R, "(STUBBED) called"); } +/** + * Y2R_U::GetTemporalDithering service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : u8, 0 = Disabled, 1 = Enabled + */ +static void GetTemporalDithering(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0xC, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = temporal_dithering_enabled; + + LOG_WARNING(Service_Y2R, "(STUBBED) called"); +} + +/** + * Y2R_U::SetTransferEndInterrupt service function + * Inputs: + * 1 : u8, 0 = Disabled, 1 = Enabled + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ static void SetTransferEndInterrupt(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + transfer_end_interrupt_enabled = cmd_buff[1] & 0xf; cmd_buff[0] = IPC::MakeHeader(0xD, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; - LOG_DEBUG(Service_Y2R, "(STUBBED) called"); + + LOG_WARNING(Service_Y2R, "(STUBBED) called"); +} + +/** + * Y2R_U::GetTransferEndInterrupt service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : u8, 0 = Disabled, 1 = Enabled + */ +static void GetTransferEndInterrupt(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0xE, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = transfer_end_interrupt_enabled; + + LOG_WARNING(Service_Y2R, "(STUBBED) called"); } /** @@ -135,8 +276,10 @@ static void SetTransferEndInterrupt(Service::Interface* self) { static void GetTransferEndEvent(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[3] = Kernel::g_handle_table.Create(completion_event).MoveFrom(); + LOG_DEBUG(Service_Y2R, "called"); } @@ -147,12 +290,12 @@ static void SetSendingY(Service::Interface* self) { conversion.src_Y.image_size = cmd_buff[2]; conversion.src_Y.transfer_unit = cmd_buff[3]; conversion.src_Y.gap = cmd_buff[4]; - u32 src_process_handle = cmd_buff[6]; - LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " - "src_process_handle=0x%08X", conversion.src_Y.image_size, - conversion.src_Y.transfer_unit, conversion.src_Y.gap, src_process_handle); + cmd_buff[0] = IPC::MakeHeader(0x10, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, src_process_handle=0x%08X", + conversion.src_Y.image_size, conversion.src_Y.transfer_unit, conversion.src_Y.gap, cmd_buff[6]); } static void SetSendingU(Service::Interface* self) { @@ -162,12 +305,12 @@ static void SetSendingU(Service::Interface* self) { conversion.src_U.image_size = cmd_buff[2]; conversion.src_U.transfer_unit = cmd_buff[3]; conversion.src_U.gap = cmd_buff[4]; - u32 src_process_handle = cmd_buff[6]; - LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " - "src_process_handle=0x%08X", conversion.src_U.image_size, - conversion.src_U.transfer_unit, conversion.src_U.gap, src_process_handle); + cmd_buff[0] = IPC::MakeHeader(0x11, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, src_process_handle=0x%08X", + conversion.src_U.image_size, conversion.src_U.transfer_unit, conversion.src_U.gap, cmd_buff[6]); } static void SetSendingV(Service::Interface* self) { @@ -177,12 +320,12 @@ static void SetSendingV(Service::Interface* self) { conversion.src_V.image_size = cmd_buff[2]; conversion.src_V.transfer_unit = cmd_buff[3]; conversion.src_V.gap = cmd_buff[4]; - u32 src_process_handle = cmd_buff[6]; - LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " - "src_process_handle=0x%08X", conversion.src_V.image_size, - conversion.src_V.transfer_unit, conversion.src_V.gap, src_process_handle); + cmd_buff[0] = IPC::MakeHeader(0x12, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, src_process_handle=0x%08X", + conversion.src_V.image_size, conversion.src_V.transfer_unit, conversion.src_V.gap, cmd_buff[6]); } static void SetSendingYUYV(Service::Interface* self) { @@ -192,12 +335,76 @@ static void SetSendingYUYV(Service::Interface* self) { conversion.src_YUYV.image_size = cmd_buff[2]; conversion.src_YUYV.transfer_unit = cmd_buff[3]; conversion.src_YUYV.gap = cmd_buff[4]; - u32 src_process_handle = cmd_buff[6]; - LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " - "src_process_handle=0x%08X", conversion.src_YUYV.image_size, - conversion.src_YUYV.transfer_unit, conversion.src_YUYV.gap, src_process_handle); + cmd_buff[0] = IPC::MakeHeader(0x13, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, src_process_handle=0x%08X", + conversion.src_YUYV.image_size, conversion.src_YUYV.transfer_unit, conversion.src_YUYV.gap, cmd_buff[6]); +} + +/** + * Y2R::IsFinishedSendingYuv service function + * Output: + * 1 : Result of the function, 0 on success, otherwise error code + * 2 : u8, 0 = Not Finished, 1 = Finished + */ +static void IsFinishedSendingYuv(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x14, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = 1; + + LOG_WARNING(Service_Y2R, "(STUBBED) called"); +} + +/** + * Y2R::IsFinishedSendingY service function + * Output: + * 1 : Result of the function, 0 on success, otherwise error code + * 2 : u8, 0 = Not Finished, 1 = Finished + */ +static void IsFinishedSendingY(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x15, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = 1; + + LOG_WARNING(Service_Y2R, "(STUBBED) called"); +} + +/** + * Y2R::IsFinishedSendingU service function + * Output: + * 1 : Result of the function, 0 on success, otherwise error code + * 2 : u8, 0 = Not Finished, 1 = Finished + */ +static void IsFinishedSendingU(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x16, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = 1; + + LOG_WARNING(Service_Y2R, "(STUBBED) called"); +} + +/** + * Y2R::IsFinishedSendingV service function + * Output: + * 1 : Result of the function, 0 on success, otherwise error code + * 2 : u8, 0 = Not Finished, 1 = Finished + */ +static void IsFinishedSendingV(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x17, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = 1; + + LOG_WARNING(Service_Y2R, "(STUBBED) called"); } static void SetReceiving(Service::Interface* self) { @@ -207,27 +414,66 @@ static void SetReceiving(Service::Interface* self) { conversion.dst.image_size = cmd_buff[2]; conversion.dst.transfer_unit = cmd_buff[3]; conversion.dst.gap = cmd_buff[4]; - u32 dst_process_handle = cmd_buff[6]; - LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " - "dst_process_handle=0x%08X", conversion.dst.image_size, - conversion.dst.transfer_unit, conversion.dst.gap, - dst_process_handle); + cmd_buff[0] = IPC::MakeHeader(0x18, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, dst_process_handle=0x%08X", + conversion.dst.image_size, conversion.dst.transfer_unit, conversion.dst.gap, cmd_buff[6]); +} + +/** + * Y2R::IsFinishedReceiving service function + * Output: + * 1 : Result of the function, 0 on success, otherwise error code + * 2 : u8, 0 = Not Finished, 1 = Finished + */ +static void IsFinishedReceiving(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x19, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = 1; + + LOG_WARNING(Service_Y2R, "(STUBBED) called"); } static void SetInputLineWidth(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - LOG_DEBUG(Service_Y2R, "called input_line_width=%u", cmd_buff[1]); + cmd_buff[0] = IPC::MakeHeader(0x1A, 1, 0); cmd_buff[1] = conversion.SetInputLineWidth(cmd_buff[1]).raw; + + LOG_DEBUG(Service_Y2R, "called input_line_width=%u", cmd_buff[1]); +} + +static void GetInputLineWidth(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x1B, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = conversion.input_line_width; + + LOG_DEBUG(Service_Y2R, "called input_line_width=%u", conversion.input_line_width); } static void SetInputLines(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - LOG_DEBUG(Service_Y2R, "called input_line_number=%u", cmd_buff[1]); + cmd_buff[0] = IPC::MakeHeader(0x1C, 1, 0); cmd_buff[1] = conversion.SetInputLines(cmd_buff[1]).raw; + + LOG_DEBUG(Service_Y2R, "called input_lines=%u", cmd_buff[1]); +} + +static void GetInputLines(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x1D, 2, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = static_cast<u32>(conversion.input_lines); + + LOG_DEBUG(Service_Y2R, "called input_lines=%u", conversion.input_lines); } static void SetCoefficient(Service::Interface* self) { @@ -235,45 +481,111 @@ static void SetCoefficient(Service::Interface* self) { const u16* coefficients = reinterpret_cast<const u16*>(&cmd_buff[1]); std::memcpy(conversion.coefficients.data(), coefficients, sizeof(CoefficientSet)); + + cmd_buff[0] = IPC::MakeHeader(0x1E, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_DEBUG(Service_Y2R, "called coefficients=[%hX, %hX, %hX, %hX, %hX, %hX, %hX, %hX]", coefficients[0], coefficients[1], coefficients[2], coefficients[3], coefficients[4], coefficients[5], coefficients[6], coefficients[7]); +} +static void GetCoefficient(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x1F, 5, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + std::memcpy(&cmd_buff[2], conversion.coefficients.data(), sizeof(CoefficientSet)); + + LOG_DEBUG(Service_Y2R, "called"); } static void SetStandardCoefficient(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - LOG_DEBUG(Service_Y2R, "called standard_coefficient=%u", cmd_buff[1]); + u32 index = cmd_buff[1]; + + cmd_buff[0] = IPC::MakeHeader(0x20, 1, 0); + cmd_buff[1] = conversion.SetStandardCoefficient((StandardCoefficient)index).raw; + + LOG_DEBUG(Service_Y2R, "called standard_coefficient=%u", index); +} + +static void GetStandardCoefficient(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 index = cmd_buff[1]; + + if (index < ARRAY_SIZE(standard_coefficients)) { + cmd_buff[0] = IPC::MakeHeader(0x21, 5, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + std::memcpy(&cmd_buff[2], &standard_coefficients[index], sizeof(CoefficientSet)); - cmd_buff[1] = conversion.SetStandardCoefficient((StandardCoefficient)cmd_buff[1]).raw; + LOG_DEBUG(Service_Y2R, "called standard_coefficient=%u ", index); + } else { + cmd_buff[0] = IPC::MakeHeader(0x21, 1, 0); + cmd_buff[1] = -1; // TODO(bunnei): Identify the correct error code for this + + LOG_ERROR(Service_Y2R, "called standard_coefficient=%u The argument is invalid!", index); + } } static void SetAlpha(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); conversion.alpha = cmd_buff[1]; + + cmd_buff[0] = IPC::MakeHeader(0x22, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_DEBUG(Service_Y2R, "called alpha=%hu", conversion.alpha); +} + +static void GetAlpha(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[0] = IPC::MakeHeader(0x23, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = conversion.alpha; + + LOG_DEBUG(Service_Y2R, "called alpha=%hu", conversion.alpha); } -static void StartConversion(Service::Interface* self) { +static void SetDitheringWeightParams(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + std::memcpy(&dithering_weight_params, &cmd_buff[1], sizeof(DitheringWeightParams)); - HW::Y2R::PerformConversion(conversion); + cmd_buff[0] = IPC::MakeHeader(0x24, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; - // dst_image_size would seem to be perfect for this, but it doesn't include the gap :( - u32 total_output_size = conversion.input_lines * - (conversion.dst.transfer_unit + conversion.dst.gap); - VideoCore::g_renderer->Rasterizer()->InvalidateRegion( - Memory::VirtualToPhysicalAddress(conversion.dst.address), total_output_size); + LOG_DEBUG(Service_Y2R, "called"); +} + +static void GetDitheringWeightParams(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x25, 9, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + std::memcpy(&cmd_buff[2], &dithering_weight_params, sizeof(DitheringWeightParams)); LOG_DEBUG(Service_Y2R, "called"); +} + +static void StartConversion(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + // dst_image_size would seem to be perfect for this, but it doesn't include the gap :( + u32 total_output_size = conversion.input_lines * (conversion.dst.transfer_unit + conversion.dst.gap); + Memory::RasterizerFlushAndInvalidateRegion(Memory::VirtualToPhysicalAddress(conversion.dst.address), total_output_size); + + HW::Y2R::PerformConversion(conversion); + completion_event->Signal(); + cmd_buff[0] = IPC::MakeHeader(0x26, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_DEBUG(Service_Y2R, "called"); } static void StopConversion(Service::Interface* self) { @@ -281,6 +593,7 @@ static void StopConversion(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x27, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_DEBUG(Service_Y2R, "called"); } @@ -293,50 +606,61 @@ static void StopConversion(Service::Interface* self) { static void IsBusyConversion(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[0] = IPC::MakeHeader(0x28, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = 0; // StartConversion always finishes immediately + LOG_DEBUG(Service_Y2R, "called"); } /** - * Y2R_U::SetConversionParams service function + * Y2R_U::SetPackageParameter service function */ -static void SetConversionParams(Service::Interface* self) { +static void SetPackageParameter(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); auto params = reinterpret_cast<const ConversionParameters*>(&cmd_buff[1]); - LOG_DEBUG(Service_Y2R, - "called input_format=%hhu output_format=%hhu rotation=%hhu block_alignment=%hhu " - "input_line_width=%hu input_lines=%hu standard_coefficient=%hhu " - "reserved=%hhu alpha=%hX", - params->input_format, params->output_format, params->rotation, params->block_alignment, - params->input_line_width, params->input_lines, params->standard_coefficient, - params->reserved, params->alpha); - - ResultCode result = RESULT_SUCCESS; conversion.input_format = params->input_format; conversion.output_format = params->output_format; conversion.rotation = params->rotation; conversion.block_alignment = params->block_alignment; - result = conversion.SetInputLineWidth(params->input_line_width); - if (result.IsError()) goto cleanup; + + ResultCode result = conversion.SetInputLineWidth(params->input_line_width); + + if (result.IsError()) + goto cleanup; + result = conversion.SetInputLines(params->input_lines); - if (result.IsError()) goto cleanup; + + if (result.IsError()) + goto cleanup; + result = conversion.SetStandardCoefficient(params->standard_coefficient); - if (result.IsError()) goto cleanup; + + if (result.IsError()) + goto cleanup; + + conversion.padding = params->padding; conversion.alpha = params->alpha; cleanup: cmd_buff[0] = IPC::MakeHeader(0x29, 1, 0); cmd_buff[1] = result.raw; + + LOG_DEBUG(Service_Y2R, "called input_format=%hhu output_format=%hhu rotation=%hhu block_alignment=%hhu " + "input_line_width=%hu input_lines=%hu standard_coefficient=%hhu reserved=%hhu alpha=%hX", + params->input_format, params->output_format, params->rotation, params->block_alignment, + params->input_line_width, params->input_lines, params->standard_coefficient, params->padding, params->alpha); } static void PingProcess(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[0] = IPC::MakeHeader(0x2A, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = 0; + LOG_WARNING(Service_Y2R, "(STUBBED) called"); } @@ -362,6 +686,7 @@ static void DriverInitialize(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x2B, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + LOG_DEBUG(Service_Y2R, "called"); } @@ -370,54 +695,67 @@ static void DriverFinalize(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x2C, 1, 0); cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_DEBUG(Service_Y2R, "called"); +} + + +static void GetPackageParameter(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[0] = IPC::MakeHeader(0x2D, 4, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; + std::memcpy(&cmd_buff[2], &conversion, sizeof(ConversionParameters)); + LOG_DEBUG(Service_Y2R, "called"); } const Interface::FunctionInfo FunctionTable[] = { {0x00010040, SetInputFormat, "SetInputFormat"}, - {0x00020000, nullptr, "GetInputFormat"}, + {0x00020000, GetInputFormat, "GetInputFormat"}, {0x00030040, SetOutputFormat, "SetOutputFormat"}, - {0x00040000, nullptr, "GetOutputFormat"}, + {0x00040000, GetOutputFormat, "GetOutputFormat"}, {0x00050040, SetRotation, "SetRotation"}, - {0x00060000, nullptr, "GetRotation"}, + {0x00060000, GetRotation, "GetRotation"}, {0x00070040, SetBlockAlignment, "SetBlockAlignment"}, - {0x00080000, nullptr, "GetBlockAlignment"}, - {0x00090040, nullptr, "SetSpacialDithering"}, - {0x000A0000, nullptr, "GetSpacialDithering"}, - {0x000B0040, nullptr, "SetTemporalDithering"}, - {0x000C0000, nullptr, "GetTemporalDithering"}, + {0x00080000, GetBlockAlignment, "GetBlockAlignment"}, + {0x00090040, SetSpacialDithering, "SetSpacialDithering"}, + {0x000A0000, GetSpacialDithering, "GetSpacialDithering"}, + {0x000B0040, SetTemporalDithering, "SetTemporalDithering"}, + {0x000C0000, GetTemporalDithering, "GetTemporalDithering"}, {0x000D0040, SetTransferEndInterrupt, "SetTransferEndInterrupt"}, + {0x000E0000, GetTransferEndInterrupt, "GetTransferEndInterrupt"}, {0x000F0000, GetTransferEndEvent, "GetTransferEndEvent"}, {0x00100102, SetSendingY, "SetSendingY"}, {0x00110102, SetSendingU, "SetSendingU"}, {0x00120102, SetSendingV, "SetSendingV"}, {0x00130102, SetSendingYUYV, "SetSendingYUYV"}, - {0x00140000, nullptr, "IsFinishedSendingYuv"}, - {0x00150000, nullptr, "IsFinishedSendingY"}, - {0x00160000, nullptr, "IsFinishedSendingU"}, - {0x00170000, nullptr, "IsFinishedSendingV"}, + {0x00140000, IsFinishedSendingYuv, "IsFinishedSendingYuv"}, + {0x00150000, IsFinishedSendingY, "IsFinishedSendingY"}, + {0x00160000, IsFinishedSendingU, "IsFinishedSendingU"}, + {0x00170000, IsFinishedSendingV, "IsFinishedSendingV"}, {0x00180102, SetReceiving, "SetReceiving"}, - {0x00190000, nullptr, "IsFinishedReceiving"}, + {0x00190000, IsFinishedReceiving, "IsFinishedReceiving"}, {0x001A0040, SetInputLineWidth, "SetInputLineWidth"}, - {0x001B0000, nullptr, "GetInputLineWidth"}, + {0x001B0000, GetInputLineWidth, "GetInputLineWidth"}, {0x001C0040, SetInputLines, "SetInputLines"}, - {0x001D0000, nullptr, "GetInputLines"}, + {0x001D0000, GetInputLines, "GetInputLines"}, {0x001E0100, SetCoefficient, "SetCoefficient"}, - {0x001F0000, nullptr, "GetCoefficient"}, + {0x001F0000, GetCoefficient, "GetCoefficient"}, {0x00200040, SetStandardCoefficient, "SetStandardCoefficient"}, - {0x00210040, nullptr, "GetStandardCoefficientParams"}, + {0x00210040, GetStandardCoefficient, "GetStandardCoefficient"}, {0x00220040, SetAlpha, "SetAlpha"}, - {0x00230000, nullptr, "GetAlpha"}, - {0x00240200, nullptr, "SetDitheringWeightParams"}, - {0x00250000, nullptr, "GetDitheringWeightParams"}, + {0x00230000, GetAlpha, "GetAlpha"}, + {0x00240200, SetDitheringWeightParams,"SetDitheringWeightParams"}, + {0x00250000, GetDitheringWeightParams,"GetDitheringWeightParams"}, {0x00260000, StartConversion, "StartConversion"}, {0x00270000, StopConversion, "StopConversion"}, {0x00280000, IsBusyConversion, "IsBusyConversion"}, - {0x002901C0, SetConversionParams, "SetConversionParams"}, + {0x002901C0, SetPackageParameter, "SetPackageParameter"}, {0x002A0000, PingProcess, "PingProcess"}, {0x002B0000, DriverInitialize, "DriverInitialize"}, {0x002C0000, DriverFinalize, "DriverFinalize"}, - {0x002D0000, nullptr, "GetPackageParameter"}, + {0x002D0000, GetPackageParameter, "GetPackageParameter"}, }; //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/service/y2r_u.h b/src/core/hle/service/y2r_u.h index 3965a5545..95fa2fdb7 100644 --- a/src/core/hle/service/y2r_u.h +++ b/src/core/hle/service/y2r_u.h @@ -97,6 +97,7 @@ struct ConversionConfiguration { u16 input_line_width; u16 input_lines; CoefficientSet coefficients; + u8 padding; u16 alpha; /// Input parameters for the Y (luma) plane @@ -109,6 +110,25 @@ struct ConversionConfiguration { ResultCode SetStandardCoefficient(StandardCoefficient standard_coefficient); }; +struct DitheringWeightParams { + u16 w0_xEven_yEven; + u16 w0_xOdd_yEven; + u16 w0_xEven_yOdd; + u16 w0_xOdd_yOdd; + u16 w1_xEven_yEven; + u16 w1_xOdd_yEven; + u16 w1_xEven_yOdd; + u16 w1_xOdd_yOdd; + u16 w2_xEven_yEven; + u16 w2_xOdd_yEven; + u16 w2_xEven_yOdd; + u16 w2_xOdd_yOdd; + u16 w3_xEven_yEven; + u16 w3_xOdd_yEven; + u16 w3_xEven_yOdd; + u16 w3_xOdd_yOdd; +}; + class Interface : public Service::Interface { public: Interface(); diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index ae54afb1c..fb2aecbf2 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -6,7 +6,6 @@ #include "common/logging/log.h" #include "common/microprofile.h" -#include "common/profiler.h" #include "common/string_util.h" #include "common/symbols.h" @@ -860,6 +859,10 @@ static ResultCode GetProcessInfo(s64* out, Handle process_handle, u32 type) { // TODO(yuriks): Type 0 returns a slightly higher number than type 2, but I'm not sure // what's the difference between them. *out = process->heap_used + process->linear_heap_used + process->misc_memory_used; + if(*out % Memory::PAGE_SIZE != 0) { + LOG_ERROR(Kernel_SVC, "called, memory size not page-aligned"); + return ERR_MISALIGNED_SIZE; + } break; case 1: case 3: @@ -1031,8 +1034,6 @@ static const FunctionDef SVC_Table[] = { {0x7D, HLE::Wrap<QueryProcessMemory>, "QueryProcessMemory"}, }; -Common::Profiling::TimingCategory profiler_svc("SVC Calls"); - static const FunctionDef* GetSVCInfo(u32 func_num) { if (func_num >= ARRAY_SIZE(SVC_Table)) { LOG_ERROR(Kernel_SVC, "unknown svc=0x%02X", func_num); @@ -1044,7 +1045,6 @@ static const FunctionDef* GetSVCInfo(u32 func_num) { MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); void CallSVC(u32 immediate) { - Common::Profiling::ScopeTimer timer_svc(profiler_svc); MICROPROFILE_SCOPE(Kernel_SVC); const FunctionDef* info = GetSVCInfo(immediate); diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index 7e2f9cdfa..2fe856293 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp @@ -115,21 +115,39 @@ inline void Write(u32 addr, const T data) { u8* start = Memory::GetPhysicalPointer(config.GetStartAddress()); u8* end = Memory::GetPhysicalPointer(config.GetEndAddress()); - if (config.fill_24bit) { - // fill with 24-bit values - for (u8* ptr = start; ptr < end; ptr += 3) { - ptr[0] = config.value_24bit_r; - ptr[1] = config.value_24bit_g; - ptr[2] = config.value_24bit_b; + // TODO: Consider always accelerating and returning vector of + // regions that the accelerated fill did not cover to + // reduce/eliminate the fill that the cpu has to do. + // This would also mean that the flush below is not needed. + // Fill should first flush all surfaces that touch but are + // not completely within the fill range. + // Then fill all completely covered surfaces, and return the + // regions that were between surfaces or within the touching + // ones for cpu to manually fill here. + if (!VideoCore::g_renderer->Rasterizer()->AccelerateFill(config)) { + Memory::RasterizerFlushAndInvalidateRegion(config.GetStartAddress(), config.GetEndAddress() - config.GetStartAddress()); + + if (config.fill_24bit) { + // fill with 24-bit values + for (u8* ptr = start; ptr < end; ptr += 3) { + ptr[0] = config.value_24bit_r; + ptr[1] = config.value_24bit_g; + ptr[2] = config.value_24bit_b; + } + } else if (config.fill_32bit) { + // fill with 32-bit values + if (end > start) { + u32 value = config.value_32bit; + size_t len = (end - start) / sizeof(u32); + for (size_t i = 0; i < len; ++i) + memcpy(&start[i * sizeof(u32)], &value, sizeof(u32)); + } + } else { + // fill with 16-bit values + u16 value_16bit = config.value_16bit.Value(); + for (u8* ptr = start; ptr < end; ptr += sizeof(u16)) + memcpy(ptr, &value_16bit, sizeof(u16)); } - } else if (config.fill_32bit) { - // fill with 32-bit values - for (u32* ptr = (u32*)start; ptr < (u32*)end; ++ptr) - *ptr = config.value_32bit; - } else { - // fill with 16-bit values - for (u16* ptr = (u16*)start; ptr < (u16*)end; ++ptr) - *ptr = config.value_16bit; } LOG_TRACE(HW_GPU, "MemoryFill from 0x%08x to 0x%08x", config.GetStartAddress(), config.GetEndAddress()); @@ -139,8 +157,6 @@ inline void Write(u32 addr, const T data) { } else { GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PSC1); } - - VideoCore::g_renderer->Rasterizer()->InvalidateRegion(config.GetStartAddress(), config.GetEndAddress() - config.GetStartAddress()); } // Reset "trigger" flag and set the "finish" flag @@ -161,184 +177,185 @@ inline void Write(u32 addr, const T data) { if (Pica::g_debug_context) Pica::g_debug_context->OnEvent(Pica::DebugContext::Event::IncomingDisplayTransfer, nullptr); - u8* src_pointer = Memory::GetPhysicalPointer(config.GetPhysicalInputAddress()); - u8* dst_pointer = Memory::GetPhysicalPointer(config.GetPhysicalOutputAddress()); - - if (config.is_texture_copy) { - u32 input_width = config.texture_copy.input_width * 16; - u32 input_gap = config.texture_copy.input_gap * 16; - u32 output_width = config.texture_copy.output_width * 16; - u32 output_gap = config.texture_copy.output_gap * 16; - - size_t contiguous_input_size = config.texture_copy.size / input_width * (input_width + input_gap); - VideoCore::g_renderer->Rasterizer()->FlushRegion(config.GetPhysicalInputAddress(), contiguous_input_size); - - u32 remaining_size = config.texture_copy.size; - u32 remaining_input = input_width; - u32 remaining_output = output_width; - while (remaining_size > 0) { - u32 copy_size = std::min({ remaining_input, remaining_output, remaining_size }); + if (!VideoCore::g_renderer->Rasterizer()->AccelerateDisplayTransfer(config)) { + u8* src_pointer = Memory::GetPhysicalPointer(config.GetPhysicalInputAddress()); + u8* dst_pointer = Memory::GetPhysicalPointer(config.GetPhysicalOutputAddress()); - std::memcpy(dst_pointer, src_pointer, copy_size); - src_pointer += copy_size; - dst_pointer += copy_size; + if (config.is_texture_copy) { + u32 input_width = config.texture_copy.input_width * 16; + u32 input_gap = config.texture_copy.input_gap * 16; + u32 output_width = config.texture_copy.output_width * 16; + u32 output_gap = config.texture_copy.output_gap * 16; - remaining_input -= copy_size; - remaining_output -= copy_size; - remaining_size -= copy_size; + size_t contiguous_input_size = config.texture_copy.size / input_width * (input_width + input_gap); + Memory::RasterizerFlushRegion(config.GetPhysicalInputAddress(), contiguous_input_size); - if (remaining_input == 0) { - remaining_input = input_width; - src_pointer += input_gap; - } - if (remaining_output == 0) { - remaining_output = output_width; - dst_pointer += output_gap; - } - } + size_t contiguous_output_size = config.texture_copy.size / output_width * (output_width + output_gap); + Memory::RasterizerFlushAndInvalidateRegion(config.GetPhysicalOutputAddress(), contiguous_output_size); - LOG_TRACE(HW_GPU, "TextureCopy: 0x%X bytes from 0x%08X(%u+%u)-> 0x%08X(%u+%u), flags 0x%08X", - config.texture_copy.size, - config.GetPhysicalInputAddress(), input_width, input_gap, - config.GetPhysicalOutputAddress(), output_width, output_gap, - config.flags); + u32 remaining_size = config.texture_copy.size; + u32 remaining_input = input_width; + u32 remaining_output = output_width; + while (remaining_size > 0) { + u32 copy_size = std::min({ remaining_input, remaining_output, remaining_size }); - size_t contiguous_output_size = config.texture_copy.size / output_width * (output_width + output_gap); - VideoCore::g_renderer->Rasterizer()->InvalidateRegion(config.GetPhysicalOutputAddress(), contiguous_output_size); + std::memcpy(dst_pointer, src_pointer, copy_size); + src_pointer += copy_size; + dst_pointer += copy_size; - GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF); - break; - } + remaining_input -= copy_size; + remaining_output -= copy_size; + remaining_size -= copy_size; - if (config.scaling > config.ScaleXY) { - LOG_CRITICAL(HW_GPU, "Unimplemented display transfer scaling mode %u", config.scaling.Value()); - UNIMPLEMENTED(); - break; - } + if (remaining_input == 0) { + remaining_input = input_width; + src_pointer += input_gap; + } + if (remaining_output == 0) { + remaining_output = output_width; + dst_pointer += output_gap; + } + } - if (config.input_linear && config.scaling != config.NoScale) { - LOG_CRITICAL(HW_GPU, "Scaling is only implemented on tiled input"); - UNIMPLEMENTED(); - break; - } + LOG_TRACE(HW_GPU, "TextureCopy: 0x%X bytes from 0x%08X(%u+%u)-> 0x%08X(%u+%u), flags 0x%08X", + config.texture_copy.size, + config.GetPhysicalInputAddress(), input_width, input_gap, + config.GetPhysicalOutputAddress(), output_width, output_gap, + config.flags); - bool horizontal_scale = config.scaling != config.NoScale; - bool vertical_scale = config.scaling == config.ScaleXY; + GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF); + break; + } - u32 output_width = config.output_width >> horizontal_scale; - u32 output_height = config.output_height >> vertical_scale; + if (config.scaling > config.ScaleXY) { + LOG_CRITICAL(HW_GPU, "Unimplemented display transfer scaling mode %u", config.scaling.Value()); + UNIMPLEMENTED(); + break; + } - u32 input_size = config.input_width * config.input_height * GPU::Regs::BytesPerPixel(config.input_format); - u32 output_size = output_width * output_height * GPU::Regs::BytesPerPixel(config.output_format); + if (config.input_linear && config.scaling != config.NoScale) { + LOG_CRITICAL(HW_GPU, "Scaling is only implemented on tiled input"); + UNIMPLEMENTED(); + break; + } - VideoCore::g_renderer->Rasterizer()->FlushRegion(config.GetPhysicalInputAddress(), input_size); + int horizontal_scale = config.scaling != config.NoScale ? 1 : 0; + int vertical_scale = config.scaling == config.ScaleXY ? 1 : 0; - for (u32 y = 0; y < output_height; ++y) { - for (u32 x = 0; x < output_width; ++x) { - Math::Vec4<u8> src_color; + u32 output_width = config.output_width >> horizontal_scale; + u32 output_height = config.output_height >> vertical_scale; - // Calculate the [x,y] position of the input image - // based on the current output position and the scale - u32 input_x = x << horizontal_scale; - u32 input_y = y << vertical_scale; + u32 input_size = config.input_width * config.input_height * GPU::Regs::BytesPerPixel(config.input_format); + u32 output_size = output_width * output_height * GPU::Regs::BytesPerPixel(config.output_format); - if (config.flip_vertically) { - // Flip the y value of the output data, - // we do this after calculating the [x,y] position of the input image - // to account for the scaling options. - y = output_height - y - 1; - } + Memory::RasterizerFlushRegion(config.GetPhysicalInputAddress(), input_size); + Memory::RasterizerFlushAndInvalidateRegion(config.GetPhysicalOutputAddress(), output_size); - u32 dst_bytes_per_pixel = GPU::Regs::BytesPerPixel(config.output_format); - u32 src_bytes_per_pixel = GPU::Regs::BytesPerPixel(config.input_format); - u32 src_offset; - u32 dst_offset; + for (u32 y = 0; y < output_height; ++y) { + for (u32 x = 0; x < output_width; ++x) { + Math::Vec4<u8> src_color; - if (config.input_linear) { - if (!config.dont_swizzle) { - // Interpret the input as linear and the output as tiled - u32 coarse_y = y & ~7; - u32 stride = output_width * dst_bytes_per_pixel; + // Calculate the [x,y] position of the input image + // based on the current output position and the scale + u32 input_x = x << horizontal_scale; + u32 input_y = y << vertical_scale; - src_offset = (input_x + input_y * config.input_width) * src_bytes_per_pixel; - dst_offset = VideoCore::GetMortonOffset(x, y, dst_bytes_per_pixel) + coarse_y * stride; - } else { - // Both input and output are linear - src_offset = (input_x + input_y * config.input_width) * src_bytes_per_pixel; - dst_offset = (x + y * output_width) * dst_bytes_per_pixel; + if (config.flip_vertically) { + // Flip the y value of the output data, + // we do this after calculating the [x,y] position of the input image + // to account for the scaling options. + y = output_height - y - 1; } - } else { - if (!config.dont_swizzle) { - // Interpret the input as tiled and the output as linear - u32 coarse_y = input_y & ~7; - u32 stride = config.input_width * src_bytes_per_pixel; - src_offset = VideoCore::GetMortonOffset(input_x, input_y, src_bytes_per_pixel) + coarse_y * stride; - dst_offset = (x + y * output_width) * dst_bytes_per_pixel; + u32 dst_bytes_per_pixel = GPU::Regs::BytesPerPixel(config.output_format); + u32 src_bytes_per_pixel = GPU::Regs::BytesPerPixel(config.input_format); + u32 src_offset; + u32 dst_offset; + + if (config.input_linear) { + if (!config.dont_swizzle) { + // Interpret the input as linear and the output as tiled + u32 coarse_y = y & ~7; + u32 stride = output_width * dst_bytes_per_pixel; + + src_offset = (input_x + input_y * config.input_width) * src_bytes_per_pixel; + dst_offset = VideoCore::GetMortonOffset(x, y, dst_bytes_per_pixel) + coarse_y * stride; + } else { + // Both input and output are linear + src_offset = (input_x + input_y * config.input_width) * src_bytes_per_pixel; + dst_offset = (x + y * output_width) * dst_bytes_per_pixel; + } } else { - // Both input and output are tiled - u32 out_coarse_y = y & ~7; - u32 out_stride = output_width * dst_bytes_per_pixel; - - u32 in_coarse_y = input_y & ~7; - u32 in_stride = config.input_width * src_bytes_per_pixel; - - src_offset = VideoCore::GetMortonOffset(input_x, input_y, src_bytes_per_pixel) + in_coarse_y * in_stride; - dst_offset = VideoCore::GetMortonOffset(x, y, dst_bytes_per_pixel) + out_coarse_y * out_stride; + if (!config.dont_swizzle) { + // Interpret the input as tiled and the output as linear + u32 coarse_y = input_y & ~7; + u32 stride = config.input_width * src_bytes_per_pixel; + + src_offset = VideoCore::GetMortonOffset(input_x, input_y, src_bytes_per_pixel) + coarse_y * stride; + dst_offset = (x + y * output_width) * dst_bytes_per_pixel; + } else { + // Both input and output are tiled + u32 out_coarse_y = y & ~7; + u32 out_stride = output_width * dst_bytes_per_pixel; + + u32 in_coarse_y = input_y & ~7; + u32 in_stride = config.input_width * src_bytes_per_pixel; + + src_offset = VideoCore::GetMortonOffset(input_x, input_y, src_bytes_per_pixel) + in_coarse_y * in_stride; + dst_offset = VideoCore::GetMortonOffset(x, y, dst_bytes_per_pixel) + out_coarse_y * out_stride; + } } - } - const u8* src_pixel = src_pointer + src_offset; - src_color = DecodePixel(config.input_format, src_pixel); - if (config.scaling == config.ScaleX) { - Math::Vec4<u8> pixel = DecodePixel(config.input_format, src_pixel + src_bytes_per_pixel); - src_color = ((src_color + pixel) / 2).Cast<u8>(); - } else if (config.scaling == config.ScaleXY) { - Math::Vec4<u8> pixel1 = DecodePixel(config.input_format, src_pixel + 1 * src_bytes_per_pixel); - Math::Vec4<u8> pixel2 = DecodePixel(config.input_format, src_pixel + 2 * src_bytes_per_pixel); - Math::Vec4<u8> pixel3 = DecodePixel(config.input_format, src_pixel + 3 * src_bytes_per_pixel); - src_color = (((src_color + pixel1) + (pixel2 + pixel3)) / 4).Cast<u8>(); - } + const u8* src_pixel = src_pointer + src_offset; + src_color = DecodePixel(config.input_format, src_pixel); + if (config.scaling == config.ScaleX) { + Math::Vec4<u8> pixel = DecodePixel(config.input_format, src_pixel + src_bytes_per_pixel); + src_color = ((src_color + pixel) / 2).Cast<u8>(); + } else if (config.scaling == config.ScaleXY) { + Math::Vec4<u8> pixel1 = DecodePixel(config.input_format, src_pixel + 1 * src_bytes_per_pixel); + Math::Vec4<u8> pixel2 = DecodePixel(config.input_format, src_pixel + 2 * src_bytes_per_pixel); + Math::Vec4<u8> pixel3 = DecodePixel(config.input_format, src_pixel + 3 * src_bytes_per_pixel); + src_color = (((src_color + pixel1) + (pixel2 + pixel3)) / 4).Cast<u8>(); + } - u8* dst_pixel = dst_pointer + dst_offset; - switch (config.output_format) { - case Regs::PixelFormat::RGBA8: - Color::EncodeRGBA8(src_color, dst_pixel); - break; + u8* dst_pixel = dst_pointer + dst_offset; + switch (config.output_format) { + case Regs::PixelFormat::RGBA8: + Color::EncodeRGBA8(src_color, dst_pixel); + break; - case Regs::PixelFormat::RGB8: - Color::EncodeRGB8(src_color, dst_pixel); - break; + case Regs::PixelFormat::RGB8: + Color::EncodeRGB8(src_color, dst_pixel); + break; - case Regs::PixelFormat::RGB565: - Color::EncodeRGB565(src_color, dst_pixel); - break; + case Regs::PixelFormat::RGB565: + Color::EncodeRGB565(src_color, dst_pixel); + break; - case Regs::PixelFormat::RGB5A1: - Color::EncodeRGB5A1(src_color, dst_pixel); - break; + case Regs::PixelFormat::RGB5A1: + Color::EncodeRGB5A1(src_color, dst_pixel); + break; - case Regs::PixelFormat::RGBA4: - Color::EncodeRGBA4(src_color, dst_pixel); - break; + case Regs::PixelFormat::RGBA4: + Color::EncodeRGBA4(src_color, dst_pixel); + break; - default: - LOG_ERROR(HW_GPU, "Unknown destination framebuffer format %x", config.output_format.Value()); - break; + default: + LOG_ERROR(HW_GPU, "Unknown destination framebuffer format %x", config.output_format.Value()); + break; + } } } - } - LOG_TRACE(HW_GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), dst format %x, flags 0x%08X", + LOG_TRACE(HW_GPU, "DisplayTriggerTransfer: 0x%08x bytes from 0x%08x(%ux%u)-> 0x%08x(%ux%u), dst format %x, flags 0x%08X", config.output_height * output_width * GPU::Regs::BytesPerPixel(config.output_format), config.GetPhysicalInputAddress(), config.input_width.Value(), config.input_height.Value(), config.GetPhysicalOutputAddress(), output_width, output_height, config.output_format.Value(), config.flags); + } g_regs.display_transfer_config.trigger = 0; GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::PPF); - - VideoCore::g_renderer->Rasterizer()->InvalidateRegion(config.GetPhysicalOutputAddress(), output_size); } break; } diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h index a00adbf53..da4c345b4 100644 --- a/src/core/hw/gpu.h +++ b/src/core/hw/gpu.h @@ -78,7 +78,7 @@ struct Regs { INSERT_PADDING_WORDS(0x4); - struct { + struct MemoryFillConfig { u32 address_start; u32 address_end; @@ -165,7 +165,7 @@ struct Regs { INSERT_PADDING_WORDS(0x169); - struct { + struct DisplayTransferConfig { u32 input_address; u32 output_address; diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index a4b47ef8c..066e91a9e 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -255,7 +255,7 @@ ResultStatus AppLoader_NCCH::Load() { resource_limit_category = exheader_header.arm11_system_local_caps.resource_limit_category; LOG_INFO(Loader, "Name: %s" , exheader_header.codeset_info.name); - LOG_INFO(Loader, "Program ID: %016X" , ncch_header.program_id); + LOG_INFO(Loader, "Program ID: %016llX" , ncch_header.program_id); LOG_DEBUG(Loader, "Code compressed: %s" , is_compressed ? "yes" : "no"); LOG_DEBUG(Loader, "Entry point: 0x%08X", entry_point); LOG_DEBUG(Loader, "Code size: 0x%08X", code_size); diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 7de5bd15d..ee9b69f81 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -15,6 +15,9 @@ #include "core/memory_setup.h" #include "core/mmio.h" +#include "video_core/renderer_base.h" +#include "video_core/video_core.h" + namespace Memory { enum class PageType { @@ -22,8 +25,12 @@ enum class PageType { Unmapped, /// Page is mapped to regular memory. This is the only type you can get pointers to. Memory, + /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and invalidation + RasterizerCachedMemory, /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions. Special, + /// Page is mapped to a I/O region, but also needs to check for rasterizer cache flushing and invalidation + RasterizerCachedSpecial, }; struct SpecialRegion { @@ -57,6 +64,12 @@ struct PageTable { * the corresponding entry in `pointers` MUST be set to null. */ std::array<PageType, NUM_ENTRIES> attributes; + + /** + * Indicates the number of externally cached resources touching a page that should be + * flushed before the memory is accessed + */ + std::array<u8, NUM_ENTRIES> cached_res_count; }; /// Singular page table used for the singleton process @@ -72,8 +85,15 @@ static void MapPages(u32 base, u32 size, u8* memory, PageType type) { while (base != end) { ASSERT_MSG(base < PageTable::NUM_ENTRIES, "out of range mapping at %08X", base); + // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be null here + if (current_page_table->attributes[base] == PageType::RasterizerCachedMemory || + current_page_table->attributes[base] == PageType::RasterizerCachedSpecial) { + RasterizerFlushAndInvalidateRegion(VirtualToPhysicalAddress(base << PAGE_BITS), PAGE_SIZE); + } + current_page_table->attributes[base] = type; current_page_table->pointers[base] = memory; + current_page_table->cached_res_count[base] = 0; base += 1; if (memory != nullptr) @@ -84,6 +104,7 @@ static void MapPages(u32 base, u32 size, u8* memory, PageType type) { void InitMemoryMap() { main_page_table.pointers.fill(nullptr); main_page_table.attributes.fill(PageType::Unmapped); + main_page_table.cached_res_count.fill(0); } void MapMemoryRegion(VAddr base, u32 size, u8* target) { @@ -107,6 +128,28 @@ void UnmapRegion(VAddr base, u32 size) { } /** + * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned) + * using a VMA from the current process + */ +static u8* GetPointerFromVMA(VAddr vaddr) { + u8* direct_pointer = nullptr; + + auto& vma = Kernel::g_current_process->vm_manager.FindVMA(vaddr)->second; + switch (vma.type) { + case Kernel::VMAType::AllocatedMemoryBlock: + direct_pointer = vma.backing_block->data() + vma.offset; + break; + case Kernel::VMAType::BackingMemory: + direct_pointer = vma.backing_memory; + break; + default: + UNREACHABLE(); + } + + return direct_pointer + (vaddr - vma.base); +} + +/** * This function should only be called for virtual addreses with attribute `PageType::Special`. */ static MMIORegionPointer GetMMIOHandler(VAddr vaddr) { @@ -126,6 +169,7 @@ template <typename T> T Read(const VAddr vaddr) { const u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; if (page_pointer) { + // NOTE: Avoid adding any extra logic to this fast-path block T value; std::memcpy(&value, &page_pointer[vaddr & PAGE_MASK], sizeof(T)); return value; @@ -139,8 +183,22 @@ T Read(const VAddr vaddr) { case PageType::Memory: ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr); break; + case PageType::RasterizerCachedMemory: + { + RasterizerFlushRegion(VirtualToPhysicalAddress(vaddr), sizeof(T)); + + T value; + std::memcpy(&value, GetPointerFromVMA(vaddr), sizeof(T)); + return value; + } case PageType::Special: return ReadMMIO<T>(GetMMIOHandler(vaddr), vaddr); + case PageType::RasterizerCachedSpecial: + { + RasterizerFlushRegion(VirtualToPhysicalAddress(vaddr), sizeof(T)); + + return ReadMMIO<T>(GetMMIOHandler(vaddr), vaddr); + } default: UNREACHABLE(); } @@ -153,6 +211,7 @@ template <typename T> void Write(const VAddr vaddr, const T data) { u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; if (page_pointer) { + // NOTE: Avoid adding any extra logic to this fast-path block std::memcpy(&page_pointer[vaddr & PAGE_MASK], &data, sizeof(T)); return; } @@ -165,9 +224,23 @@ void Write(const VAddr vaddr, const T data) { case PageType::Memory: ASSERT_MSG(false, "Mapped memory page without a pointer @ %08X", vaddr); break; + case PageType::RasterizerCachedMemory: + { + RasterizerFlushAndInvalidateRegion(VirtualToPhysicalAddress(vaddr), sizeof(T)); + + std::memcpy(GetPointerFromVMA(vaddr), &data, sizeof(T)); + break; + } case PageType::Special: WriteMMIO<T>(GetMMIOHandler(vaddr), vaddr, data); break; + case PageType::RasterizerCachedSpecial: + { + RasterizerFlushAndInvalidateRegion(VirtualToPhysicalAddress(vaddr), sizeof(T)); + + WriteMMIO<T>(GetMMIOHandler(vaddr), vaddr, data); + break; + } default: UNREACHABLE(); } @@ -179,6 +252,10 @@ u8* GetPointer(const VAddr vaddr) { return page_pointer + (vaddr & PAGE_MASK); } + if (current_page_table->attributes[vaddr >> PAGE_BITS] == PageType::RasterizerCachedMemory) { + return GetPointerFromVMA(vaddr); + } + LOG_ERROR(HW_Memory, "unknown GetPointer @ 0x%08x", vaddr); return nullptr; } @@ -187,6 +264,69 @@ u8* GetPhysicalPointer(PAddr address) { return GetPointer(PhysicalToVirtualAddress(address)); } +void RasterizerMarkRegionCached(PAddr start, u32 size, int count_delta) { + if (start == 0) { + return; + } + + u32 num_pages = ((start + size - 1) >> PAGE_BITS) - (start >> PAGE_BITS) + 1; + PAddr paddr = start; + + for (unsigned i = 0; i < num_pages; ++i) { + VAddr vaddr = PhysicalToVirtualAddress(paddr); + u8& res_count = current_page_table->cached_res_count[vaddr >> PAGE_BITS]; + ASSERT_MSG(count_delta <= UINT8_MAX - res_count, "Rasterizer resource cache counter overflow!"); + ASSERT_MSG(count_delta >= -res_count, "Rasterizer resource cache counter underflow!"); + + // Switch page type to cached if now cached + if (res_count == 0) { + PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS]; + switch (page_type) { + case PageType::Memory: + page_type = PageType::RasterizerCachedMemory; + current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr; + break; + case PageType::Special: + page_type = PageType::RasterizerCachedSpecial; + break; + default: + UNREACHABLE(); + } + } + + res_count += count_delta; + + // Switch page type to uncached if now uncached + if (res_count == 0) { + PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS]; + switch (page_type) { + case PageType::RasterizerCachedMemory: + page_type = PageType::Memory; + current_page_table->pointers[vaddr >> PAGE_BITS] = GetPointerFromVMA(vaddr & ~PAGE_MASK); + break; + case PageType::RasterizerCachedSpecial: + page_type = PageType::Special; + break; + default: + UNREACHABLE(); + } + } + paddr += PAGE_SIZE; + } +} + +void RasterizerFlushRegion(PAddr start, u32 size) { + if (VideoCore::g_renderer != nullptr) { + VideoCore::g_renderer->Rasterizer()->FlushRegion(start, size); + } +} + +void RasterizerFlushAndInvalidateRegion(PAddr start, u32 size) { + if (VideoCore::g_renderer != nullptr) { + VideoCore::g_renderer->Rasterizer()->FlushAndInvalidateRegion(start, size); + } +} + u8 Read8(const VAddr addr) { return Read<u8>(addr); } diff --git a/src/core/memory.h b/src/core/memory.h index 5af72b7a7..9caa3c3f5 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -148,4 +148,20 @@ VAddr PhysicalToVirtualAddress(PAddr addr); */ u8* GetPhysicalPointer(PAddr address); +/** + * Adds the supplied value to the rasterizer resource cache counter of each + * page touching the region. + */ +void RasterizerMarkRegionCached(PAddr start, u32 size, int count_delta); + +/** + * Flushes any externally cached rasterizer resources touching the given region. + */ +void RasterizerFlushRegion(PAddr start, u32 size); + +/** + * Flushes and invalidates any externally cached rasterizer resources touching the given region. + */ +void RasterizerFlushAndInvalidateRegion(PAddr start, u32 size); + } diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 1aa26fbd2..eaf5c8461 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -19,7 +19,7 @@ void Apply() { VideoCore::g_hw_renderer_enabled = values.use_hw_renderer; VideoCore::g_shader_jit_enabled = values.use_shader_jit; - + VideoCore::g_scaled_resolution_enabled = values.use_scaled_resolution; } } // namespace diff --git a/src/core/settings.h b/src/core/settings.h index 4933a516d..d620d8461 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -55,6 +55,7 @@ struct Values { // Renderer bool use_hw_renderer; bool use_shader_jit; + bool use_scaled_resolution; float bg_red; float bg_green; |