diff options
Diffstat (limited to 'src')
33 files changed, 726 insertions, 371 deletions
diff --git a/src/core/core.cpp b/src/core/core.cpp index 9f5507a65..ee4af4dcc 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -12,10 +12,13 @@ #include "core/core.h" #include "core/core_timing.h" #include "core/gdbstub/gdbstub.h" +#include "core/hle/kernel/client_port.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/thread.h" #include "core/hle/service/service.h" +#include "core/hle/service/sm/controller.h" +#include "core/hle/service/sm/sm.h" #include "core/hw/hw.h" #include "core/loader/loader.h" #include "core/memory_setup.h" @@ -26,6 +29,8 @@ namespace Core { /*static*/ System System::s_instance; +System::~System() = default; + System::ResultStatus System::RunLoop(bool tight_loop) { status = ResultStatus::Success; if (!cpu_core) { @@ -167,10 +172,12 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { telemetry_session = std::make_unique<Core::TelemetrySession>(); + service_manager = std::make_shared<Service::SM::ServiceManager>(); + HW::Init(); Kernel::Init(system_mode); scheduler = std::make_unique<Kernel::Scheduler>(cpu_core.get()); - Service::Init(); + Service::Init(service_manager); GDBStub::Init(); if (!VideoCore::Init(emu_window)) { @@ -200,17 +207,26 @@ void System::Shutdown() { VideoCore::Shutdown(); GDBStub::Shutdown(); Service::Shutdown(); - scheduler = nullptr; + scheduler.reset(); Kernel::Shutdown(); HW::Shutdown(); - telemetry_session = nullptr; - gpu_core = nullptr; - cpu_core = nullptr; + service_manager.reset(); + telemetry_session.reset(); + gpu_core.reset(); + cpu_core.reset(); CoreTiming::Shutdown(); - app_loader = nullptr; + app_loader.reset(); LOG_DEBUG(Core, "Shutdown OK"); } +Service::SM::ServiceManager& System::ServiceManager() { + return *service_manager; +} + +const Service::SM::ServiceManager& System::ServiceManager() const { + return *service_manager; +} + } // namespace Core diff --git a/src/core/core.h b/src/core/core.h index f497dc022..f81cbfb3c 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -19,10 +19,16 @@ class EmuWindow; class ARM_Interface; +namespace Service::SM { +class ServiceManager; +} + namespace Core { class System { public: + ~System(); + /** * Gets the instance of the System singleton class. * @returns Reference to the instance of the System singleton class. @@ -137,6 +143,9 @@ public: return *app_loader; } + Service::SM::ServiceManager& ServiceManager(); + const Service::SM::ServiceManager& ServiceManager() const; + void SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context) { debug_context = std::move(context); } @@ -171,6 +180,9 @@ private: /// When true, signals that a reschedule should happen bool reschedule_pending{}; + /// Service manager + std::shared_ptr<Service::SM::ServiceManager> service_manager; + /// Telemetry session for this emulation session std::unique_ptr<Core::TelemetrySession> telemetry_session; diff --git a/src/core/hle/kernel/resource_limit.cpp b/src/core/hle/kernel/resource_limit.cpp index 0149a3ed6..88ca8ad7e 100644 --- a/src/core/hle/kernel/resource_limit.cpp +++ b/src/core/hle/kernel/resource_limit.cpp @@ -34,57 +34,57 @@ SharedPtr<ResourceLimit> ResourceLimit::GetForCategory(ResourceLimitCategory cat } } -s32 ResourceLimit::GetCurrentResourceValue(u32 resource) const { +s32 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const { switch (resource) { - case COMMIT: + case ResourceType::Commit: return current_commit; - case THREAD: + case ResourceType::Thread: return current_threads; - case EVENT: + case ResourceType::Event: return current_events; - case MUTEX: + case ResourceType::Mutex: return current_mutexes; - case SEMAPHORE: + case ResourceType::Semaphore: return current_semaphores; - case TIMER: + case ResourceType::Timer: return current_timers; - case SHARED_MEMORY: + case ResourceType::SharedMemory: return current_shared_mems; - case ADDRESS_ARBITER: + case ResourceType::AddressArbiter: return current_address_arbiters; - case CPU_TIME: + case ResourceType::CPUTime: return current_cpu_time; default: - LOG_ERROR(Kernel, "Unknown resource type=%08X", resource); + LOG_ERROR(Kernel, "Unknown resource type=%08X", static_cast<u32>(resource)); UNIMPLEMENTED(); return 0; } } -u32 ResourceLimit::GetMaxResourceValue(u32 resource) const { +u32 ResourceLimit::GetMaxResourceValue(ResourceType resource) const { switch (resource) { - case PRIORITY: + case ResourceType::Priority: return max_priority; - case COMMIT: + case ResourceType::Commit: return max_commit; - case THREAD: + case ResourceType::Thread: return max_threads; - case EVENT: + case ResourceType::Event: return max_events; - case MUTEX: + case ResourceType::Mutex: return max_mutexes; - case SEMAPHORE: + case ResourceType::Semaphore: return max_semaphores; - case TIMER: + case ResourceType::Timer: return max_timers; - case SHARED_MEMORY: + case ResourceType::SharedMemory: return max_shared_mems; - case ADDRESS_ARBITER: + case ResourceType::AddressArbiter: return max_address_arbiters; - case CPU_TIME: + case ResourceType::CPUTime: return max_cpu_time; default: - LOG_ERROR(Kernel, "Unknown resource type=%08X", resource); + LOG_ERROR(Kernel, "Unknown resource type=%08X", static_cast<u32>(resource)); UNIMPLEMENTED(); return 0; } diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h index 1a0ca11f1..cc689a27a 100644 --- a/src/core/hle/kernel/resource_limit.h +++ b/src/core/hle/kernel/resource_limit.h @@ -16,17 +16,17 @@ enum class ResourceLimitCategory : u8 { OTHER = 3 }; -enum ResourceTypes { - PRIORITY = 0, - COMMIT = 1, - THREAD = 2, - EVENT = 3, - MUTEX = 4, - SEMAPHORE = 5, - TIMER = 6, - SHARED_MEMORY = 7, - ADDRESS_ARBITER = 8, - CPU_TIME = 9, +enum class ResourceType { + Priority = 0, + Commit = 1, + Thread = 2, + Event = 3, + Mutex = 4, + Semaphore = 5, + Timer = 6, + SharedMemory = 7, + AddressArbiter = 8, + CPUTime = 9, }; class ResourceLimit final : public Object { @@ -60,14 +60,14 @@ public: * @param resource Requested resource type * @returns The current value of the resource type */ - s32 GetCurrentResourceValue(u32 resource) const; + s32 GetCurrentResourceValue(ResourceType resource) const; /** * Gets the max value for the specified resource. * @param resource Requested resource type * @returns The max value of the resource type */ - u32 GetMaxResourceValue(u32 resource) const; + u32 GetMaxResourceValue(ResourceType resource) const; /// Name of resource limit object. std::string name; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index a3015cf7a..c22da6e47 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -388,7 +388,7 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) { // Note: The kernel uses the current process's resource limit instead of // the one from the thread owner's resource limit. SharedPtr<ResourceLimit>& resource_limit = Core::CurrentProcess()->resource_limit; - if (resource_limit->GetMaxResourceValue(ResourceTypes::PRIORITY) > priority) { + if (resource_limit->GetMaxResourceValue(ResourceType::Priority) > priority) { return ERR_NOT_AUTHORIZED; } @@ -517,7 +517,7 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V } SharedPtr<ResourceLimit>& resource_limit = Core::CurrentProcess()->resource_limit; - if (resource_limit->GetMaxResourceValue(ResourceTypes::PRIORITY) > priority) { + if (resource_limit->GetMaxResourceValue(ResourceType::Priority) > priority) { return ERR_NOT_AUTHORIZED; } diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index 61f22b1a5..aa6c7e8dc 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -13,7 +13,7 @@ namespace Service::Nvidia::Devices { u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { - UNIMPLEMENTED(); + UNIMPLEMENTED_MSG("Unimplemented ioctl"); return 0; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index 71e844959..8e7ca6123 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -27,6 +27,11 @@ u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vecto case IoctlCommand::IocGetVaRegionsCommand: return GetVARegions(input, output); } + + if (static_cast<IoctlCommand>(command.cmd.Value()) == IoctlCommand::IocRemapCommand) + return Remap(input, output); + + UNIMPLEMENTED_MSG("Unimplemented ioctl command"); return 0; } @@ -56,6 +61,36 @@ u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& return 0; } +u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) { + size_t num_entries = input.size() / sizeof(IoctlRemapEntry); + + NGLOG_WARNING(Service_NVDRV, "(STUBBED) called, num_entries=0x{:X}", num_entries); + + std::vector<IoctlRemapEntry> entries(num_entries); + std::memcpy(entries.data(), input.data(), input.size()); + + auto& gpu = Core::System::GetInstance().GPU(); + + for (const auto& entry : entries) { + NGLOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}", + entry.offset, entry.nvmap_handle, entry.pages); + Tegra::GPUVAddr offset = static_cast<Tegra::GPUVAddr>(entry.offset) << 0x10; + + auto object = nvmap_dev->GetObject(entry.nvmap_handle); + ASSERT(object); + + ASSERT(object->status == nvmap::Object::Status::Allocated); + + u64 size = static_cast<u64>(entry.pages) << 0x10; + ASSERT(size <= object->size); + + Tegra::GPUVAddr returned = gpu.memory_manager->MapBufferEx(object->addr, offset, size); + ASSERT(returned == offset); + } + std::memcpy(output.data(), entries.data(), output.size()); + return 0; +} + u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) { IoctlMapBufferEx params{}; std::memcpy(¶ms, input.data(), input.size()); @@ -73,6 +108,16 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou auto object = nvmap_dev->GetObject(params.nvmap_handle); ASSERT(object); + // We can only map objects that have already been assigned a CPU address. + ASSERT(object->status == nvmap::Object::Status::Allocated); + + ASSERT(params.buffer_offset == 0); + + // The real nvservices doesn't make a distinction between handles and ids, and + // object can only have one handle and it will be the same as its id. Assert that this is the + // case to prevent unexpected behavior. + ASSERT(object->id == params.nvmap_handle); + auto& gpu = Core::System::GetInstance().GPU(); if (params.flags & 1) { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index d86c3ebd9..f2dd0c3b3 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h @@ -26,6 +26,7 @@ private: enum class IoctlCommand : u32_le { IocInitalizeExCommand = 0x40284109, IocAllocateSpaceCommand = 0xC0184102, + IocRemapCommand = 0x00000014, IocMapBufferExCommand = 0xC0284106, IocBindChannelCommand = 0x40044101, IocGetVaRegionsCommand = 0xC0404108, @@ -54,6 +55,16 @@ private: }; static_assert(sizeof(IoctlAllocSpace) == 24, "IoctlInitalizeEx is incorrect size"); + struct IoctlRemapEntry { + u16_le flags; + u16_le kind; + u32_le nvmap_handle; + INSERT_PADDING_WORDS(1); + u32_le offset; + u32_le pages; + }; + static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size"); + struct IoctlMapBufferEx { u32_le flags; // bit0: fixed_offset, bit2: cacheable u32_le kind; // -1 is default @@ -91,6 +102,7 @@ private: u32 InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output); u32 AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output); + u32 Remap(const std::vector<u8>& input, std::vector<u8>& output); u32 MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output); u32 BindChannel(const std::vector<u8>& input, std::vector<u8>& output); u32 GetVARegions(const std::vector<u8>& input, std::vector<u8>& output); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index 660a0f665..6e1ba1ac7 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -18,7 +18,7 @@ u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector< case IoctlCommand::IocCtrlEventWaitCommand: return IocCtrlEventWait(input, output); } - UNIMPLEMENTED(); + UNIMPLEMENTED_MSG("Unimplemented ioctl"); return 0; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index 18ea12ef5..b715723d3 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -25,7 +25,7 @@ u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vec case IoctlCommand::IocZcullGetInfo: return ZCullGetInfo(input, output); } - UNIMPLEMENTED(); + UNIMPLEMENTED_MSG("Unimplemented ioctl"); return 0; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index a16e90457..dab6d0533 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -40,7 +40,7 @@ u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u } } - UNIMPLEMENTED(); + UNIMPLEMENTED_MSG("Unimplemented ioctl"); return 0; }; diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index 4bb1f57f6..dcf079d91 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -32,7 +32,7 @@ u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& o return IocParam(input, output); } - UNIMPLEMENTED(); + UNIMPLEMENTED_MSG("Unimplemented ioctl"); return 0; } diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index c5490c1ae..08ce29677 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -145,7 +145,7 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co return ResultCode(ErrorModule::HIPC, ErrorDescription::RemoteProcessDead); } case IPC::CommandType::Control: { - SM::g_service_manager->InvokeControlRequest(context); + Core::System::GetInstance().ServiceManager().InvokeControlRequest(context); break; } case IPC::CommandType::Request: { @@ -170,42 +170,40 @@ void AddNamedPort(std::string name, SharedPtr<ClientPort> port) { } /// Initialize ServiceManager -void Init() { +void Init(std::shared_ptr<SM::ServiceManager>& sm) { // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it // here and pass it into the respective InstallInterfaces functions. auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(); - SM::g_service_manager = std::make_shared<SM::ServiceManager>(); - SM::ServiceManager::InstallInterfaces(SM::g_service_manager); - - Account::InstallInterfaces(*SM::g_service_manager); - AM::InstallInterfaces(*SM::g_service_manager, nv_flinger); - AOC::InstallInterfaces(*SM::g_service_manager); - APM::InstallInterfaces(*SM::g_service_manager); - Audio::InstallInterfaces(*SM::g_service_manager); - Fatal::InstallInterfaces(*SM::g_service_manager); - FileSystem::InstallInterfaces(*SM::g_service_manager); - Friend::InstallInterfaces(*SM::g_service_manager); - HID::InstallInterfaces(*SM::g_service_manager); - LM::InstallInterfaces(*SM::g_service_manager); - NFP::InstallInterfaces(*SM::g_service_manager); - NIFM::InstallInterfaces(*SM::g_service_manager); - NS::InstallInterfaces(*SM::g_service_manager); - Nvidia::InstallInterfaces(*SM::g_service_manager); - PCTL::InstallInterfaces(*SM::g_service_manager); - Sockets::InstallInterfaces(*SM::g_service_manager); - SPL::InstallInterfaces(*SM::g_service_manager); - SSL::InstallInterfaces(*SM::g_service_manager); - Time::InstallInterfaces(*SM::g_service_manager); - VI::InstallInterfaces(*SM::g_service_manager, nv_flinger); - Set::InstallInterfaces(*SM::g_service_manager); + SM::ServiceManager::InstallInterfaces(sm); + + Account::InstallInterfaces(*sm); + AM::InstallInterfaces(*sm, nv_flinger); + AOC::InstallInterfaces(*sm); + APM::InstallInterfaces(*sm); + Audio::InstallInterfaces(*sm); + Fatal::InstallInterfaces(*sm); + FileSystem::InstallInterfaces(*sm); + Friend::InstallInterfaces(*sm); + HID::InstallInterfaces(*sm); + LM::InstallInterfaces(*sm); + NFP::InstallInterfaces(*sm); + NIFM::InstallInterfaces(*sm); + NS::InstallInterfaces(*sm); + Nvidia::InstallInterfaces(*sm); + PCTL::InstallInterfaces(*sm); + Sockets::InstallInterfaces(*sm); + SPL::InstallInterfaces(*sm); + SSL::InstallInterfaces(*sm); + Time::InstallInterfaces(*sm); + VI::InstallInterfaces(*sm, nv_flinger); + Set::InstallInterfaces(*sm); LOG_DEBUG(Service, "initialized OK"); } /// Shutdown ServiceManager void Shutdown() { - SM::g_service_manager = nullptr; g_kernel_named_ports.clear(); LOG_DEBUG(Service, "shutdown OK"); } diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 9c2e826da..fee841d46 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -178,7 +178,7 @@ private: }; /// Initialize ServiceManager -void Init(); +void Init(std::shared_ptr<SM::ServiceManager>& sm); /// Shutdown ServiceManager void Shutdown(); diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 297a4f2c6..4578fc05f 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -14,6 +14,8 @@ namespace Service::SM { +ServiceManager::~ServiceManager() = default; + void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) { controller_interface->InvokeRequest(context); } @@ -72,7 +74,7 @@ ResultVal<Kernel::SharedPtr<Kernel::ClientSession>> ServiceManager::ConnectToSer return client_port->Connect(); } -std::shared_ptr<ServiceManager> g_service_manager; +SM::~SM() = default; /** * SM::Initialize service function diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index 40421cfd5..13f5c4c28 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -23,7 +23,7 @@ namespace Service::SM { class SM final : public ServiceFramework<SM> { public: SM(std::shared_ptr<ServiceManager> service_manager); - ~SM() = default; + ~SM() override; private: void Initialize(Kernel::HLERequestContext& ctx); @@ -44,6 +44,8 @@ class ServiceManager { public: static void InstallInterfaces(std::shared_ptr<ServiceManager> self); + ~ServiceManager(); + ResultVal<Kernel::SharedPtr<Kernel::ServerPort>> RegisterService(std::string name, unsigned int max_sessions); ResultVal<Kernel::SharedPtr<Kernel::ClientPort>> GetServicePort(const std::string& name); @@ -59,6 +61,4 @@ private: std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> registered_services; }; -extern std::shared_ptr<ServiceManager> g_service_manager; - } // namespace Service::SM diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index b697b5f73..36ae2215f 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -582,7 +582,7 @@ public: {2203, nullptr, "SetLayerSize"}, {2204, nullptr, "GetLayerZ"}, {2205, &ISystemDisplayService::SetLayerZ, "SetLayerZ"}, - {2207, nullptr, "SetLayerVisibility"}, + {2207, &ISystemDisplayService::SetLayerVisibility, "SetLayerVisibility"}, {2209, nullptr, "SetLayerAlpha"}, {2312, nullptr, "CreateStrayLayer"}, {2400, nullptr, "OpenIndirectLayer"}, @@ -632,6 +632,16 @@ private: IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); rb.Push(RESULT_SUCCESS); } + + void SetLayerVisibility(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + u64 layer_id = rp.Pop<u64>(); + bool visibility = rp.Pop<bool>(); + IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x%x, visibility=%u", layer_id, + visibility); + } }; class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> { @@ -663,7 +673,7 @@ public: {4206, nullptr, "SetDefaultDisplay"}, {6000, &IManagerDisplayService::AddToLayerStack, "AddToLayerStack"}, {6001, nullptr, "RemoveFromLayerStack"}, - {6002, nullptr, "SetLayerVisibility"}, + {6002, &IManagerDisplayService::SetLayerVisibility, "SetLayerVisibility"}, {6003, nullptr, "SetLayerConfig"}, {6004, nullptr, "AttachLayerPresentationTracer"}, {6005, nullptr, "DetachLayerPresentationTracer"}, @@ -745,6 +755,16 @@ private: rb.Push(RESULT_SUCCESS); } + void SetLayerVisibility(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + u64 layer_id = rp.Pop<u64>(); + bool visibility = rp.Pop<bool>(); + IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); + rb.Push(RESULT_SUCCESS); + LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x%x, visibility=%u", layer_id, + visibility); + } + std::shared_ptr<NVFlinger::NVFlinger> nv_flinger; }; @@ -815,15 +835,15 @@ private: IPC::RequestParser rp{ctx}; u64 display_id = rp.Pop<u64>(); - IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0); + IPC::ResponseBuilder rb = rp.MakeBuilder(6, 0, 0); rb.Push(RESULT_SUCCESS); if (Settings::values.use_docked_mode) { - rb.Push(static_cast<u32>(DisplayResolution::DockedWidth)); - rb.Push(static_cast<u32>(DisplayResolution::DockedHeight)); + rb.Push(static_cast<u64>(DisplayResolution::DockedWidth)); + rb.Push(static_cast<u64>(DisplayResolution::DockedHeight)); } else { - rb.Push(static_cast<u32>(DisplayResolution::UndockedWidth)); - rb.Push(static_cast<u32>(DisplayResolution::UndockedHeight)); + rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth)); + rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight)); } } diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 7cd125f05..5a006aee5 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -4,15 +4,24 @@ #pragma once +#include <bitset> #include <cstring> #include <map> #include <string> +#include <vector> + +#include <boost/optional.hpp> + #include "common/bit_field.h" +#include "common/common_types.h" namespace Tegra { namespace Shader { struct Register { + // Register 255 is special cased to always be 0 + static constexpr size_t ZeroIndex = 255; + constexpr Register() = default; constexpr Register(u64 value) : value(value) {} @@ -86,181 +95,12 @@ union Uniform { BitField<34, 5, u64> index; }; -union OpCode { - enum class Id : u64 { - TEXS = 0x6C, - IPA = 0xE0, - FMUL32_IMM = 0x1E, - FFMA_IMM = 0x65, - FFMA_CR = 0x93, - FFMA_RC = 0xA3, - FFMA_RR = 0xB3, - - FADD_C = 0x98B, - FMUL_C = 0x98D, - MUFU = 0xA10, - FADD_R = 0xB8B, - FMUL_R = 0xB8D, - LD_A = 0x1DFB, - ST_A = 0x1DFE, - - FSETP_R = 0x5BB, - FSETP_C = 0x4BB, - EXIT = 0xE30, - KIL = 0xE33, - - FMUL_IMM = 0x70D, - FMUL_IMM_x = 0x72D, - FADD_IMM = 0x70B, - FADD_IMM_x = 0x72B, - }; - - enum class Type { - Trivial, - Arithmetic, - Ffma, - Flow, - Memory, - Unknown, - }; - - struct Info { - Type type; - std::string name; - }; - - OpCode() = default; - - constexpr OpCode(Id value) : value(static_cast<u64>(value)) {} - - constexpr OpCode(u64 value) : value{value} {} - - constexpr Id EffectiveOpCode() const { - switch (op1) { - case Id::TEXS: - return op1; - } - - switch (op2) { - case Id::IPA: - case Id::FMUL32_IMM: - return op2; - } - - switch (op3) { - case Id::FFMA_IMM: - case Id::FFMA_CR: - case Id::FFMA_RC: - case Id::FFMA_RR: - return op3; - } - - switch (op4) { - case Id::EXIT: - case Id::FSETP_R: - case Id::FSETP_C: - case Id::KIL: - return op4; - } - - switch (op5) { - case Id::MUFU: - case Id::LD_A: - case Id::ST_A: - case Id::FADD_R: - case Id::FADD_C: - case Id::FMUL_R: - case Id::FMUL_C: - return op5; - - case Id::FMUL_IMM: - case Id::FMUL_IMM_x: - return Id::FMUL_IMM; - - case Id::FADD_IMM: - case Id::FADD_IMM_x: - return Id::FADD_IMM; - } - - return static_cast<Id>(value); - } - - static const Info& GetInfo(const OpCode& opcode) { - static const std::map<Id, Info> info_table{BuildInfoTable()}; - const auto& search{info_table.find(opcode.EffectiveOpCode())}; - if (search != info_table.end()) { - return search->second; - } - - static const Info unknown{Type::Unknown, "UNK"}; - return unknown; - } - - constexpr operator Id() const { - return static_cast<Id>(value); - } - - constexpr OpCode operator<<(size_t bits) const { - return value << bits; - } - - constexpr OpCode operator>>(size_t bits) const { - return value >> bits; - } - - template <typename T> - constexpr u64 operator-(const T& oth) const { - return value - oth; - } - - constexpr u64 operator&(const OpCode& oth) const { - return value & oth.value; - } - - constexpr u64 operator~() const { - return ~value; - } - - static std::map<Id, Info> BuildInfoTable() { - std::map<Id, Info> info_table; - info_table[Id::TEXS] = {Type::Memory, "texs"}; - info_table[Id::LD_A] = {Type::Memory, "ld_a"}; - info_table[Id::ST_A] = {Type::Memory, "st_a"}; - info_table[Id::MUFU] = {Type::Arithmetic, "mufu"}; - info_table[Id::FFMA_IMM] = {Type::Ffma, "ffma_imm"}; - info_table[Id::FFMA_CR] = {Type::Ffma, "ffma_cr"}; - info_table[Id::FFMA_RC] = {Type::Ffma, "ffma_rc"}; - info_table[Id::FFMA_RR] = {Type::Ffma, "ffma_rr"}; - info_table[Id::FADD_R] = {Type::Arithmetic, "fadd_r"}; - info_table[Id::FADD_C] = {Type::Arithmetic, "fadd_c"}; - info_table[Id::FADD_IMM] = {Type::Arithmetic, "fadd_imm"}; - info_table[Id::FMUL_R] = {Type::Arithmetic, "fmul_r"}; - info_table[Id::FMUL_C] = {Type::Arithmetic, "fmul_c"}; - info_table[Id::FMUL_IMM] = {Type::Arithmetic, "fmul_imm"}; - info_table[Id::FMUL32_IMM] = {Type::Arithmetic, "fmul32_imm"}; - info_table[Id::FSETP_C] = {Type::Arithmetic, "fsetp_c"}; - info_table[Id::FSETP_R] = {Type::Arithmetic, "fsetp_r"}; - info_table[Id::EXIT] = {Type::Trivial, "exit"}; - info_table[Id::IPA] = {Type::Trivial, "ipa"}; - info_table[Id::KIL] = {Type::Flow, "kil"}; - return info_table; - } - - BitField<57, 7, Id> op1; - BitField<56, 8, Id> op2; - BitField<55, 9, Id> op3; - BitField<52, 12, Id> op4; - BitField<51, 13, Id> op5; - u64 value{}; -}; -static_assert(sizeof(OpCode) == 0x8, "Incorrect structure size"); - } // namespace Shader } // namespace Tegra namespace std { -// TODO(bunne): The below is forbidden by the C++ standard, but works fine. See #330. +// TODO(bunnei): The below is forbidden by the C++ standard, but works fine. See #330. template <> struct make_unsigned<Tegra::Shader::Attribute> { using type = Tegra::Shader::Attribute; @@ -271,11 +111,6 @@ struct make_unsigned<Tegra::Shader::Register> { using type = Tegra::Shader::Register; }; -template <> -struct make_unsigned<Tegra::Shader::OpCode> { - using type = Tegra::Shader::OpCode; -}; - } // namespace std namespace Tegra { @@ -283,7 +118,23 @@ namespace Shader { enum class Pred : u64 { UnusedIndex = 0x7, - NeverExecute = 0xf, + NeverExecute = 0xF, +}; + +enum class PredCondition : u64 { + LessThan = 1, + Equal = 2, + LessEqual = 3, + GreaterThan = 4, + NotEqual = 5, + GreaterEqual = 6, + // TODO(Subv): Other condition types +}; + +enum class PredOperation : u64 { + And = 0, + Or = 1, + Xor = 2, }; enum class SubOp : u64 { @@ -298,18 +149,24 @@ enum class SubOp : u64 { union Instruction { Instruction& operator=(const Instruction& instr) { - hex = instr.hex; + value = instr.value; return *this; } - OpCode opcode; + constexpr Instruction(u64 value) : value{value} {} + BitField<0, 8, Register> gpr0; BitField<8, 8, Register> gpr8; - BitField<16, 4, Pred> pred; + union { + BitField<16, 4, Pred> full_pred; + BitField<16, 3, u64> pred_index; + } pred; + BitField<19, 1, u64> negate_pred; BitField<20, 8, Register> gpr20; BitField<20, 7, SubOp> sub_op; BitField<28, 8, Register> gpr28; BitField<39, 8, Register> gpr39; + BitField<48, 16, u64> opcode; union { BitField<20, 19, u64> imm20_19; @@ -343,6 +200,20 @@ union Instruction { BitField<49, 1, u64> negate_c; } ffma; + union { + BitField<0, 3, u64> pred0; + BitField<3, 3, u64> pred3; + BitField<7, 1, u64> abs_a; + BitField<39, 3, u64> pred39; + BitField<42, 1, u64> neg_pred; + BitField<43, 1, u64> neg_a; + BitField<44, 1, u64> abs_b; + BitField<45, 2, PredOperation> op; + BitField<47, 1, u64> ftz; + BitField<48, 4, PredCondition> cond; + BitField<56, 1, u64> neg_b; + } fsetp; + BitField<61, 1, u64> is_b_imm; BitField<60, 1, u64> is_b_gpr; BitField<59, 1, u64> is_c_gpr; @@ -351,11 +222,218 @@ union Instruction { Uniform uniform; Sampler sampler; - u64 hex; + u64 value; }; static_assert(sizeof(Instruction) == 0x8, "Incorrect structure size"); static_assert(std::is_standard_layout<Instruction>::value, "Structure does not have standard layout"); +class OpCode { +public: + enum class Id { + KIL, + LD_A, + ST_A, + TEXQ, // Texture Query + TEXS, // Texture Fetch with scalar/non-vec4 source/destinations + TLDS, // Texture Load with scalar/non-vec4 source/destinations + EXIT, + IPA, + FFMA_IMM, // Fused Multiply and Add + FFMA_CR, + FFMA_RC, + FFMA_RR, + FADD_C, + FADD_R, + FADD_IMM, + FMUL_C, + FMUL_R, + FMUL_IMM, + FMUL32_IMM, + MUFU, // Multi-Function Operator + RRO, // Range Reduction Operator + F2F_C, + F2F_R, + F2F_IMM, + F2I_C, + F2I_R, + F2I_IMM, + I2F_C, + I2F_R, + I2F_IMM, + LOP32I, + MOV_C, + MOV_R, + MOV_IMM, + MOV32I, + SHR_C, + SHR_R, + SHR_IMM, + FSETP_C, // Set Predicate + FSETP_R, + FSETP_IMM, + ISETP_C, + ISETP_IMM, + ISETP_R, + }; + + enum class Type { + Trivial, + Arithmetic, + Ffma, + Flow, + Memory, + FloatPredicate, + IntegerPredicate, + Unknown, + }; + + class Matcher { + public: + Matcher(const char* const name, u16 mask, u16 expected, OpCode::Id id, OpCode::Type type) + : name{name}, mask{mask}, expected{expected}, id{id}, type{type} {} + + const char* GetName() const { + return name; + } + + u16 GetMask() const { + return mask; + } + + Id GetId() const { + return id; + } + + Type GetType() const { + return type; + } + + /** + * Tests to see if the given instruction is the instruction this matcher represents. + * @param instruction The instruction to test + * @returns true if the given instruction matches. + */ + bool Matches(u16 instruction) const { + return (instruction & mask) == expected; + } + + private: + const char* name; + u16 mask; + u16 expected; + Id id; + Type type; + }; + + static boost::optional<const Matcher&> Decode(Instruction instr) { + static const auto table{GetDecodeTable()}; + + const auto matches_instruction = [instr](const auto& matcher) { + return matcher.Matches(static_cast<u16>(instr.opcode)); + }; + + auto iter = std::find_if(table.begin(), table.end(), matches_instruction); + return iter != table.end() ? boost::optional<const Matcher&>(*iter) : boost::none; + } + +private: + struct Detail { + private: + static constexpr size_t opcode_bitsize = 16; + + /** + * Generates the mask and the expected value after masking from a given bitstring. + * A '0' in a bitstring indicates that a zero must be present at that bit position. + * A '1' in a bitstring indicates that a one must be present at that bit position. + */ + static auto GetMaskAndExpect(const char* const bitstring) { + u16 mask = 0, expect = 0; + for (size_t i = 0; i < opcode_bitsize; i++) { + const size_t bit_position = opcode_bitsize - i - 1; + switch (bitstring[i]) { + case '0': + mask |= 1 << bit_position; + break; + case '1': + expect |= 1 << bit_position; + mask |= 1 << bit_position; + break; + default: + // Ignore + break; + } + } + return std::make_tuple(mask, expect); + } + + public: + /// Creates a matcher that can match and parse instructions based on bitstring. + static auto GetMatcher(const char* const bitstring, OpCode::Id op, OpCode::Type type, + const char* const name) { + const auto mask_expect = GetMaskAndExpect(bitstring); + return Matcher(name, std::get<0>(mask_expect), std::get<1>(mask_expect), op, type); + } + }; + + static std::vector<Matcher> GetDecodeTable() { + std::vector<Matcher> table = { +#define INST(bitstring, op, type, name) Detail::GetMatcher(bitstring, op, type, name) + INST("111000110011----", Id::KIL, Type::Flow, "KIL"), + INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"), + INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"), + INST("1101111101001---", Id::TEXQ, Type::Memory, "TEXQ"), + INST("1101100---------", Id::TEXS, Type::Memory, "TEXS"), + INST("1101101---------", Id::TLDS, Type::Memory, "TLDS"), + INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"), + INST("11100000--------", Id::IPA, Type::Trivial, "IPA"), + INST("001100101-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"), + INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"), + INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"), + INST("010110011-------", Id::FFMA_RR, Type::Ffma, "FFMA_RR"), + INST("0100110001011---", Id::FADD_C, Type::Arithmetic, "FADD_C"), + INST("0101110001011---", Id::FADD_R, Type::Arithmetic, "FADD_R"), + INST("0011100-01011---", Id::FADD_IMM, Type::Arithmetic, "FADD_IMM"), + INST("0100110001101---", Id::FMUL_C, Type::Arithmetic, "FMUL_C"), + INST("0101110001101---", Id::FMUL_R, Type::Arithmetic, "FMUL_R"), + INST("0011100-01101---", Id::FMUL_IMM, Type::Arithmetic, "FMUL_IMM"), + INST("00011110--------", Id::FMUL32_IMM, Type::Arithmetic, "FMUL32_IMM"), + INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"), + INST("0101110010010---", Id::RRO, Type::Arithmetic, "RRO"), + INST("0100110010101---", Id::F2F_C, Type::Arithmetic, "F2F_C"), + INST("0101110010101---", Id::F2F_R, Type::Arithmetic, "F2F_R"), + INST("0011100-10101---", Id::F2F_IMM, Type::Arithmetic, "F2F_IMM"), + INST("0100110010110---", Id::F2I_C, Type::Arithmetic, "F2I_C"), + INST("0101110010110---", Id::F2I_R, Type::Arithmetic, "F2I_R"), + INST("0011100-10110---", Id::F2I_IMM, Type::Arithmetic, "F2I_IMM"), + INST("0100110010111---", Id::I2F_C, Type::Arithmetic, "I2F_C"), + INST("0101110010111---", Id::I2F_R, Type::Arithmetic, "I2F_R"), + INST("0011100-10111---", Id::I2F_IMM, Type::Arithmetic, "I2F_IMM"), + INST("000001----------", Id::LOP32I, Type::Arithmetic, "LOP32I"), + INST("0100110010011---", Id::MOV_C, Type::Arithmetic, "MOV_C"), + INST("0101110010011---", Id::MOV_R, Type::Arithmetic, "MOV_R"), + INST("0011100-10011---", Id::MOV_IMM, Type::Arithmetic, "MOV_IMM"), + INST("000000010000----", Id::MOV32I, Type::Arithmetic, "MOV32I"), + INST("0100110000101---", Id::SHR_C, Type::Arithmetic, "SHR_C"), + INST("0101110000101---", Id::SHR_R, Type::Arithmetic, "SHR_R"), + INST("0011100-00101---", Id::SHR_IMM, Type::Arithmetic, "SHR_IMM"), + INST("010010111011----", Id::FSETP_C, Type::FloatPredicate, "FSETP_C"), + INST("010110111011----", Id::FSETP_R, Type::FloatPredicate, "FSETP_R"), + INST("0011011-1011----", Id::FSETP_IMM, Type::FloatPredicate, "FSETP_IMM"), + INST("010010110110----", Id::ISETP_C, Type::IntegerPredicate, "ISETP_C"), + INST("010110110110----", Id::ISETP_R, Type::IntegerPredicate, "ISETP_R"), + INST("0011011-0110----", Id::ISETP_IMM, Type::IntegerPredicate, "ISETP_IMM"), + }; +#undef INST + std::stable_sort(table.begin(), table.end(), [](const auto& a, const auto& b) { + // If a matcher has more bits in its mask it is more specific, so it + // should come first. + return std::bitset<16>(a.GetMask()).count() > std::bitset<16>(b.GetMask()).count(); + }); + + return table; + } +}; + } // namespace Shader } // namespace Tegra diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index 2789a4ca1..2e1edee03 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/alignment.h" #include "common/assert.h" #include "video_core/memory_manager.h" @@ -11,7 +12,8 @@ PAddr MemoryManager::AllocateSpace(u64 size, u64 align) { boost::optional<PAddr> paddr = FindFreeBlock(size, align); ASSERT(paddr); - for (u64 offset = 0; offset < size; offset += Memory::PAGE_SIZE) { + for (u64 offset = 0; offset < size; offset += PAGE_SIZE) { + ASSERT(PageSlot(*paddr + offset) == static_cast<u64>(PageStatus::Unmapped)); PageSlot(*paddr + offset) = static_cast<u64>(PageStatus::Allocated); } @@ -19,13 +21,8 @@ PAddr MemoryManager::AllocateSpace(u64 size, u64 align) { } PAddr MemoryManager::AllocateSpace(PAddr paddr, u64 size, u64 align) { - for (u64 offset = 0; offset < size; offset += Memory::PAGE_SIZE) { - if (IsPageMapped(paddr + offset)) { - return AllocateSpace(size, align); - } - } - - for (u64 offset = 0; offset < size; offset += Memory::PAGE_SIZE) { + for (u64 offset = 0; offset < size; offset += PAGE_SIZE) { + ASSERT(PageSlot(paddr + offset) == static_cast<u64>(PageStatus::Unmapped)); PageSlot(paddr + offset) = static_cast<u64>(PageStatus::Allocated); } @@ -33,12 +30,11 @@ PAddr MemoryManager::AllocateSpace(PAddr paddr, u64 size, u64 align) { } PAddr MemoryManager::MapBufferEx(VAddr vaddr, u64 size) { - vaddr &= ~Memory::PAGE_MASK; - - boost::optional<PAddr> paddr = FindFreeBlock(size); + boost::optional<PAddr> paddr = FindFreeBlock(size, PAGE_SIZE); ASSERT(paddr); - for (u64 offset = 0; offset < size; offset += Memory::PAGE_SIZE) { + for (u64 offset = 0; offset < size; offset += PAGE_SIZE) { + ASSERT(PageSlot(*paddr + offset) == static_cast<u64>(PageStatus::Unmapped)); PageSlot(*paddr + offset) = vaddr + offset; } @@ -46,16 +42,10 @@ PAddr MemoryManager::MapBufferEx(VAddr vaddr, u64 size) { } PAddr MemoryManager::MapBufferEx(VAddr vaddr, PAddr paddr, u64 size) { - vaddr &= ~Memory::PAGE_MASK; - paddr &= ~Memory::PAGE_MASK; + ASSERT((paddr & PAGE_MASK) == 0); - for (u64 offset = 0; offset < size; offset += Memory::PAGE_SIZE) { - if (PageSlot(paddr + offset) != static_cast<u64>(PageStatus::Allocated)) { - return MapBufferEx(vaddr, size); - } - } - - for (u64 offset = 0; offset < size; offset += Memory::PAGE_SIZE) { + for (u64 offset = 0; offset < size; offset += PAGE_SIZE) { + ASSERT(PageSlot(paddr + offset) == static_cast<u64>(PageStatus::Allocated)); PageSlot(paddr + offset) = vaddr + offset; } @@ -63,23 +53,20 @@ PAddr MemoryManager::MapBufferEx(VAddr vaddr, PAddr paddr, u64 size) { } boost::optional<PAddr> MemoryManager::FindFreeBlock(u64 size, u64 align) { - PAddr paddr{}; - u64 free_space{}; - align = (align + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; + PAddr paddr = 0; + u64 free_space = 0; + align = (align + PAGE_MASK) & ~PAGE_MASK; while (paddr + free_space < MAX_ADDRESS) { if (!IsPageMapped(paddr + free_space)) { - free_space += Memory::PAGE_SIZE; + free_space += PAGE_SIZE; if (free_space >= size) { return paddr; } } else { - paddr += free_space + Memory::PAGE_SIZE; + paddr += free_space + PAGE_SIZE; free_space = 0; - const u64 remainder{paddr % align}; - if (!remainder) { - paddr = (paddr - remainder) + align; - } + paddr = Common::AlignUp(paddr, align); } } @@ -89,7 +76,7 @@ boost::optional<PAddr> MemoryManager::FindFreeBlock(u64 size, u64 align) { VAddr MemoryManager::PhysicalToVirtualAddress(PAddr paddr) { VAddr base_addr = PageSlot(paddr); ASSERT(base_addr != static_cast<u64>(PageStatus::Unmapped)); - return base_addr + (paddr & Memory::PAGE_MASK); + return base_addr + (paddr & PAGE_MASK); } bool MemoryManager::IsPageMapped(PAddr paddr) { @@ -97,14 +84,14 @@ bool MemoryManager::IsPageMapped(PAddr paddr) { } VAddr& MemoryManager::PageSlot(PAddr paddr) { - auto& block = page_table[(paddr >> (Memory::PAGE_BITS + PAGE_TABLE_BITS)) & PAGE_TABLE_MASK]; + auto& block = page_table[(paddr >> (PAGE_BITS + PAGE_TABLE_BITS)) & PAGE_TABLE_MASK]; if (!block) { block = std::make_unique<PageBlock>(); for (unsigned index = 0; index < PAGE_BLOCK_SIZE; index++) { (*block)[index] = static_cast<u64>(PageStatus::Unmapped); } } - return (*block)[(paddr >> Memory::PAGE_BITS) & PAGE_BLOCK_MASK]; + return (*block)[(paddr >> PAGE_BITS) & PAGE_BLOCK_MASK]; } } // namespace Tegra diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index 47da7acd6..b73e283f8 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -24,6 +24,10 @@ public: PAddr MapBufferEx(VAddr vaddr, PAddr paddr, u64 size); VAddr PhysicalToVirtualAddress(PAddr paddr); + static constexpr u64 PAGE_BITS = 16; + static constexpr u64 PAGE_SIZE = 1 << PAGE_BITS; + static constexpr u64 PAGE_MASK = PAGE_SIZE - 1; + private: boost::optional<PAddr> FindFreeBlock(u64 size, u64 align = 1); bool IsPageMapped(PAddr paddr); @@ -35,7 +39,7 @@ private: }; static constexpr u64 MAX_ADDRESS{0x10000000000ULL}; - static constexpr u64 PAGE_TABLE_BITS{14}; + static constexpr u64 PAGE_TABLE_BITS{10}; static constexpr u64 PAGE_TABLE_SIZE{1 << PAGE_TABLE_BITS}; static constexpr u64 PAGE_TABLE_MASK{PAGE_TABLE_SIZE - 1}; static constexpr u64 PAGE_BLOCK_BITS{14}; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 170548528..2d4a0d6db 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -14,7 +14,6 @@ #include "common/math_util.h" #include "common/microprofile.h" #include "common/scope_exit.h" -#include "common/vector_math.h" #include "core/core.h" #include "core/hle/kernel/process.h" #include "core/settings.h" diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 9ece415f7..03e02b52a 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -6,15 +6,10 @@ #include <array> #include <cstddef> -#include <cstring> #include <memory> -#include <unordered_map> #include <vector> #include <glad/glad.h> -#include "common/bit_field.h" #include "common/common_types.h" -#include "common/hash.h" -#include "common/vector_math.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/rasterizer_interface.h" #include "video_core/renderer_opengl/gl_rasterizer_cache.h" diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 6c1c6775a..7410471cc 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -7,7 +7,6 @@ #include <cstring> #include <iterator> #include <memory> -#include <unordered_set> #include <utility> #include <vector> #include <boost/optional.hpp> @@ -20,7 +19,6 @@ #include "common/math_util.h" #include "common/microprofile.h" #include "common/scope_exit.h" -#include "common/vector_math.h" #include "core/core.h" #include "core/frontend/emu_window.h" #include "core/hle/kernel/process.h" @@ -51,6 +49,7 @@ struct FormatTuple { static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_format_tuples = {{ {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, false, 1}, // ABGR8 {GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV, false, 1}, // B5G6R5 + {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, false, 1}, // A2B10G10R10 {GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, true, 16}, // DXT1 {GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true, 16}, // DXT23 {GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true, 16}, // DXT45 @@ -106,9 +105,9 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, VAddr b static constexpr std::array<void (*)(u32, u32, u32, u8*, VAddr, VAddr, VAddr), SurfaceParams::MaxPixelFormat> morton_to_gl_fns = { - MortonCopy<true, PixelFormat::ABGR8>, MortonCopy<true, PixelFormat::B5G6R5>, - MortonCopy<true, PixelFormat::DXT1>, MortonCopy<true, PixelFormat::DXT23>, - MortonCopy<true, PixelFormat::DXT45>, + MortonCopy<true, PixelFormat::ABGR8>, MortonCopy<true, PixelFormat::B5G6R5>, + MortonCopy<true, PixelFormat::A2B10G10R10>, MortonCopy<true, PixelFormat::DXT1>, + MortonCopy<true, PixelFormat::DXT23>, MortonCopy<true, PixelFormat::DXT45>, }; static constexpr std::array<void (*)(u32, u32, u32, u8*, VAddr, VAddr, VAddr), @@ -116,6 +115,7 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, VAddr, VAddr, VAddr), gl_to_morton_fns = { MortonCopy<false, PixelFormat::ABGR8>, MortonCopy<false, PixelFormat::B5G6R5>, + MortonCopy<false, PixelFormat::A2B10G10R10>, // TODO(Subv): Swizzling the DXT1/DXT23/DXT45 formats is not yet supported nullptr, nullptr, @@ -672,7 +672,8 @@ void CachedSurface::DownloadGLTexture(const MathUtil::Rectangle<u32>& rect, GLui glPixelStorei(GL_PACK_ROW_LENGTH, 0); } -enum MatchFlags { +enum class MatchFlags { + None = 0, Invalid = 1, // Flag that can be applied to other match types, invalid matches require // validation before they can be used Exact = 1 << 1, // Surfaces perfectly match @@ -686,6 +687,10 @@ constexpr MatchFlags operator|(MatchFlags lhs, MatchFlags rhs) { return static_cast<MatchFlags>(static_cast<int>(lhs) | static_cast<int>(rhs)); } +constexpr MatchFlags operator&(MatchFlags lhs, MatchFlags rhs) { + return static_cast<MatchFlags>(static_cast<int>(lhs) & static_cast<int>(rhs)); +} + /// Get the best surface match (and its match type) for the given flags template <MatchFlags find_flags> Surface FindMatch(const SurfaceCache& surface_cache, const SurfaceParams& params, @@ -703,15 +708,15 @@ Surface FindMatch(const SurfaceCache& surface_cache, const SurfaceParams& params : (params.res_scale <= surface->res_scale); // validity will be checked in GetCopyableInterval bool is_valid = - find_flags & MatchFlags::Copy + (find_flags & MatchFlags::Copy) != MatchFlags::None ? true : surface->IsRegionValid(validate_interval.value_or(params.GetInterval())); - if (!(find_flags & MatchFlags::Invalid) && !is_valid) + if ((find_flags & MatchFlags::Invalid) == MatchFlags::None && !is_valid) continue; auto IsMatch_Helper = [&](auto check_type, auto match_fn) { - if (!(find_flags & check_type)) + if ((find_flags & check_type) == MatchFlags::None) return; bool matched; diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 6861efe16..bf0fabb29 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -54,9 +54,10 @@ struct SurfaceParams { enum class PixelFormat { ABGR8 = 0, B5G6R5 = 1, - DXT1 = 2, - DXT23 = 3, - DXT45 = 4, + A2B10G10R10 = 2, + DXT1 = 3, + DXT23 = 4, + DXT45 = 5, Max, Invalid = 255, @@ -88,6 +89,7 @@ struct SurfaceParams { constexpr std::array<unsigned int, MaxPixelFormat> bpp_table = { 32, // ABGR8 16, // B5G6R5 + 32, // A2B10G10R10 64, // DXT1 128, // DXT23 128, // DXT45 @@ -104,6 +106,8 @@ struct SurfaceParams { switch (format) { case Tegra::RenderTargetFormat::RGBA8_UNORM: return PixelFormat::ABGR8; + case Tegra::RenderTargetFormat::RGB10_A2_UNORM: + return PixelFormat::A2B10G10R10; default: NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); UNREACHABLE(); @@ -127,6 +131,8 @@ struct SurfaceParams { return PixelFormat::ABGR8; case Tegra::Texture::TextureFormat::B5G6R5: return PixelFormat::B5G6R5; + case Tegra::Texture::TextureFormat::A2B10G10R10: + return PixelFormat::A2B10G10R10; case Tegra::Texture::TextureFormat::DXT1: return PixelFormat::DXT1; case Tegra::Texture::TextureFormat::DXT23: @@ -146,6 +152,8 @@ struct SurfaceParams { return Tegra::Texture::TextureFormat::A8R8G8B8; case PixelFormat::B5G6R5: return Tegra::Texture::TextureFormat::B5G6R5; + case PixelFormat::A2B10G10R10: + return Tegra::Texture::TextureFormat::A2B10G10R10; case PixelFormat::DXT1: return Tegra::Texture::TextureFormat::DXT1; case PixelFormat::DXT23: diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h index 2f0e7ac1a..93f9172e7 100644 --- a/src/video_core/renderer_opengl/gl_resource_manager.h +++ b/src/video_core/renderer_opengl/gl_resource_manager.h @@ -14,13 +14,13 @@ class OGLTexture : private NonCopyable { public: OGLTexture() = default; - OGLTexture(OGLTexture&& o) : handle(std::exchange(o.handle, 0)) {} + OGLTexture(OGLTexture&& o) noexcept : handle(std::exchange(o.handle, 0)) {} ~OGLTexture() { Release(); } - OGLTexture& operator=(OGLTexture&& o) { + OGLTexture& operator=(OGLTexture&& o) noexcept { Release(); handle = std::exchange(o.handle, 0); return *this; @@ -49,13 +49,13 @@ class OGLSampler : private NonCopyable { public: OGLSampler() = default; - OGLSampler(OGLSampler&& o) : handle(std::exchange(o.handle, 0)) {} + OGLSampler(OGLSampler&& o) noexcept : handle(std::exchange(o.handle, 0)) {} ~OGLSampler() { Release(); } - OGLSampler& operator=(OGLSampler&& o) { + OGLSampler& operator=(OGLSampler&& o) noexcept { Release(); handle = std::exchange(o.handle, 0); return *this; @@ -84,13 +84,13 @@ class OGLShader : private NonCopyable { public: OGLShader() = default; - OGLShader(OGLShader&& o) : handle(std::exchange(o.handle, 0)) {} + OGLShader(OGLShader&& o) noexcept : handle(std::exchange(o.handle, 0)) {} ~OGLShader() { Release(); } - OGLShader& operator=(OGLShader&& o) { + OGLShader& operator=(OGLShader&& o) noexcept { Release(); handle = std::exchange(o.handle, 0); return *this; @@ -118,13 +118,13 @@ class OGLProgram : private NonCopyable { public: OGLProgram() = default; - OGLProgram(OGLProgram&& o) : handle(std::exchange(o.handle, 0)) {} + OGLProgram(OGLProgram&& o) noexcept : handle(std::exchange(o.handle, 0)) {} ~OGLProgram() { Release(); } - OGLProgram& operator=(OGLProgram&& o) { + OGLProgram& operator=(OGLProgram&& o) noexcept { Release(); handle = std::exchange(o.handle, 0); return *this; @@ -165,13 +165,12 @@ public: class OGLPipeline : private NonCopyable { public: OGLPipeline() = default; - OGLPipeline(OGLPipeline&& o) { - handle = std::exchange<GLuint>(o.handle, 0); - } + OGLPipeline(OGLPipeline&& o) noexcept : handle{std::exchange<GLuint>(o.handle, 0)} {} + ~OGLPipeline() { Release(); } - OGLPipeline& operator=(OGLPipeline&& o) { + OGLPipeline& operator=(OGLPipeline&& o) noexcept { handle = std::exchange<GLuint>(o.handle, 0); return *this; } @@ -199,13 +198,13 @@ class OGLBuffer : private NonCopyable { public: OGLBuffer() = default; - OGLBuffer(OGLBuffer&& o) : handle(std::exchange(o.handle, 0)) {} + OGLBuffer(OGLBuffer&& o) noexcept : handle(std::exchange(o.handle, 0)) {} ~OGLBuffer() { Release(); } - OGLBuffer& operator=(OGLBuffer&& o) { + OGLBuffer& operator=(OGLBuffer&& o) noexcept { Release(); handle = std::exchange(o.handle, 0); return *this; @@ -234,12 +233,12 @@ class OGLSync : private NonCopyable { public: OGLSync() = default; - OGLSync(OGLSync&& o) : handle(std::exchange(o.handle, nullptr)) {} + OGLSync(OGLSync&& o) noexcept : handle(std::exchange(o.handle, nullptr)) {} ~OGLSync() { Release(); } - OGLSync& operator=(OGLSync&& o) { + OGLSync& operator=(OGLSync&& o) noexcept { Release(); handle = std::exchange(o.handle, nullptr); return *this; @@ -267,13 +266,13 @@ class OGLVertexArray : private NonCopyable { public: OGLVertexArray() = default; - OGLVertexArray(OGLVertexArray&& o) : handle(std::exchange(o.handle, 0)) {} + OGLVertexArray(OGLVertexArray&& o) noexcept : handle(std::exchange(o.handle, 0)) {} ~OGLVertexArray() { Release(); } - OGLVertexArray& operator=(OGLVertexArray&& o) { + OGLVertexArray& operator=(OGLVertexArray&& o) noexcept { Release(); handle = std::exchange(o.handle, 0); return *this; @@ -302,13 +301,13 @@ class OGLFramebuffer : private NonCopyable { public: OGLFramebuffer() = default; - OGLFramebuffer(OGLFramebuffer&& o) : handle(std::exchange(o.handle, 0)) {} + OGLFramebuffer(OGLFramebuffer&& o) noexcept : handle(std::exchange(o.handle, 0)) {} ~OGLFramebuffer() { Release(); } - OGLFramebuffer& operator=(OGLFramebuffer&& o) { + OGLFramebuffer& operator=(OGLFramebuffer&& o) noexcept { Release(); handle = std::exchange(o.handle, 0); return *this; diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index de137558d..086424395 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -97,11 +97,12 @@ private: return exit_method; for (u32 offset = begin; offset != end && offset != PROGRAM_END; ++offset) { - const Instruction instr = {program_code[offset]}; - switch (instr.opcode.EffectiveOpCode()) { - case OpCode::Id::EXIT: { - return exit_method = ExitMethod::AlwaysEnd; - } + if (const auto opcode = OpCode::Decode({program_code[offset]})) { + switch (opcode->GetId()) { + case OpCode::Id::EXIT: { + return exit_method = ExitMethod::AlwaysEnd; + } + } } } return exit_method = ExitMethod::AlwaysReturn; @@ -220,6 +221,8 @@ private: /// Generates code representing a temporary (GPR) register. std::string GetRegister(const Register& reg, unsigned elem = 0) { + if (reg == Register::ZeroIndex) + return "0"; if (stage == Maxwell3D::Regs::ShaderStage::Fragment && reg < 4) { // GPRs 0-3 are output color for the fragment shader return std::string{"color."} + "rgba"[(reg + elem) & 3]; @@ -276,6 +279,52 @@ private: shader.AddLine(dest + " = " + src + ";"); } + /* + * Writes code that assigns a predicate boolean variable. + * @param pred The id of the predicate to write to. + * @param value The expression value to assign to the predicate. + */ + void SetPredicate(u64 pred, const std::string& value) { + using Tegra::Shader::Pred; + // Can't assign to the constant predicate. + ASSERT(pred != static_cast<u64>(Pred::UnusedIndex)); + + std::string variable = 'p' + std::to_string(pred); + shader.AddLine(variable + " = " + value + ';'); + declr_predicates.insert(std::move(variable)); + } + + /* + * Returns the condition to use in the 'if' for a predicated instruction. + * @param instr Instruction to generate the if condition for. + * @returns string containing the predicate condition. + */ + std::string GetPredicateCondition(Instruction instr) const { + using Tegra::Shader::Pred; + ASSERT(instr.pred.pred_index != static_cast<u64>(Pred::UnusedIndex)); + + std::string variable = + 'p' + std::to_string(static_cast<u64>(instr.pred.pred_index.Value())); + + if (instr.negate_pred) { + return "!(" + variable + ')'; + } + + return variable; + } + + /* + * Returns whether the instruction at the specified offset is a 'sched' instruction. + * Sched instructions always appear before a sequence of 3 instructions. + */ + bool IsSchedInstruction(u32 offset) const { + // sched instructions appear once every 4 instructions. + static constexpr size_t SchedPeriod = 4; + u32 absolute_offset = offset - main_offset; + + return (absolute_offset % SchedPeriod) == 0; + } + /** * Compiles a single instruction from Tegra to GLSL. * @param offset the offset of the Tegra shader instruction. @@ -283,11 +332,33 @@ private: * + 1. If the current instruction always terminates the program, returns PROGRAM_END. */ u32 CompileInstr(u32 offset) { + // Ignore sched instructions when generating code. + if (IsSchedInstruction(offset)) { + return offset + 1; + } + const Instruction instr = {program_code[offset]}; + const auto opcode = OpCode::Decode(instr); + + // Decoding failure + if (!opcode) { + NGLOG_CRITICAL(HW_GPU, "Unhandled instruction: {0:x}", instr.value); + UNREACHABLE(); + } + + shader.AddLine("// " + std::to_string(offset) + ": " + opcode->GetName()); - shader.AddLine("// " + std::to_string(offset) + ": " + OpCode::GetInfo(instr.opcode).name); + using Tegra::Shader::Pred; + ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute, + "NeverExecute predicate not implemented"); - switch (OpCode::GetInfo(instr.opcode).type) { + if (instr.pred.pred_index != static_cast<u64>(Pred::UnusedIndex)) { + shader.AddLine("if (" + GetPredicateCondition(instr) + ')'); + shader.AddLine('{'); + ++shader.scope; + } + + switch (opcode->GetType()) { case OpCode::Type::Arithmetic: { std::string dest = GetRegister(instr.gpr0); std::string op_a = instr.alu.negate_a ? "-" : ""; @@ -312,7 +383,7 @@ private: op_b = "abs(" + op_b + ")"; } - switch (instr.opcode.EffectiveOpCode()) { + switch (opcode->GetId()) { case OpCode::Id::FMUL_C: case OpCode::Id::FMUL_R: case OpCode::Id::FMUL_IMM: { @@ -354,16 +425,18 @@ private: SetDest(0, dest, "min(" + op_a + "," + op_b + ")", 1, 1, instr.alu.abs_d); break; default: - NGLOG_CRITICAL(HW_GPU, "Unhandled MUFU sub op: {}", + NGLOG_CRITICAL(HW_GPU, "Unhandled MUFU sub op: {0:x}", static_cast<unsigned>(instr.sub_op.Value())); UNREACHABLE(); } break; } + case OpCode::Id::RRO: { + NGLOG_DEBUG(HW_GPU, "Skipping RRO instruction"); + break; + } default: { - NGLOG_CRITICAL(HW_GPU, "Unhandled arithmetic instruction: {} ({}): {}", - static_cast<unsigned>(instr.opcode.EffectiveOpCode()), - OpCode::GetInfo(instr.opcode).name, instr.hex); + NGLOG_CRITICAL(HW_GPU, "Unhandled arithmetic instruction: {}", opcode->GetName()); UNREACHABLE(); } } @@ -375,7 +448,7 @@ private: std::string op_b = instr.ffma.negate_b ? "-" : ""; std::string op_c = instr.ffma.negate_c ? "-" : ""; - switch (instr.opcode.EffectiveOpCode()) { + switch (opcode->GetId()) { case OpCode::Id::FFMA_CR: { op_b += GetUniform(instr.uniform); op_c += GetRegister(instr.gpr39); @@ -397,9 +470,7 @@ private: break; } default: { - NGLOG_CRITICAL(HW_GPU, "Unhandled FFMA instruction: {} ({}): {}", - static_cast<unsigned>(instr.opcode.EffectiveOpCode()), - OpCode::GetInfo(instr.opcode).name, instr.hex); + NGLOG_CRITICAL(HW_GPU, "Unhandled FFMA instruction: {}", opcode->GetName()); UNREACHABLE(); } } @@ -411,7 +482,7 @@ private: std::string gpr0 = GetRegister(instr.gpr0); const Attribute::Index attribute = instr.attribute.fmt20.index; - switch (instr.opcode.EffectiveOpCode()) { + switch (opcode->GetId()) { case OpCode::Id::LD_A: { ASSERT_MSG(instr.attribute.fmt20.size == 0, "untested"); SetDest(instr.attribute.fmt20.element, gpr0, GetInputAttribute(attribute), 1, 4); @@ -442,22 +513,76 @@ private: break; } default: { - NGLOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {} ({}): {}", - static_cast<unsigned>(instr.opcode.EffectiveOpCode()), - OpCode::GetInfo(instr.opcode).name, instr.hex); + NGLOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {}", opcode->GetName()); UNREACHABLE(); } } break; } + case OpCode::Type::FloatPredicate: { + std::string op_a = instr.fsetp.neg_a ? "-" : ""; + op_a += GetRegister(instr.gpr8); + + if (instr.fsetp.abs_a) { + op_a = "abs(" + op_a + ')'; + } + + std::string op_b{}; + + if (instr.is_b_imm) { + if (instr.fsetp.neg_b) { + // Only the immediate version of fsetp has a neg_b bit. + op_b += '-'; + } + op_b += '(' + GetImmediate19(instr) + ')'; + } else { + if (instr.is_b_gpr) { + op_b += GetRegister(instr.gpr20); + } else { + op_b += GetUniform(instr.uniform); + } + } + + if (instr.fsetp.abs_b) { + op_b = "abs(" + op_b + ')'; + } + + using Tegra::Shader::Pred; + ASSERT_MSG(instr.fsetp.pred0 == static_cast<u64>(Pred::UnusedIndex) && + instr.fsetp.pred39 == static_cast<u64>(Pred::UnusedIndex), + "Compound predicates are not implemented"); + + // We can't use the constant predicate as destination. + ASSERT(instr.fsetp.pred3 != static_cast<u64>(Pred::UnusedIndex)); + using Tegra::Shader::PredCondition; + switch (instr.fsetp.cond) { + case PredCondition::LessThan: + SetPredicate(instr.fsetp.pred3, '(' + op_a + ") < (" + op_b + ')'); + break; + case PredCondition::Equal: + SetPredicate(instr.fsetp.pred3, '(' + op_a + ") == (" + op_b + ')'); + break; + default: + NGLOG_CRITICAL(HW_GPU, "Unhandled predicate condition: {} (a: {}, b: {})", + static_cast<unsigned>(instr.fsetp.cond.Value()), op_a, op_b); + UNREACHABLE(); + } + break; + } default: { - switch (instr.opcode.EffectiveOpCode()) { + switch (opcode->GetId()) { case OpCode::Id::EXIT: { + ASSERT_MSG(instr.pred.pred_index == static_cast<u64>(Pred::UnusedIndex), + "Predicated exits not implemented"); shader.AddLine("return true;"); offset = PROGRAM_END - 1; break; } + case OpCode::Id::KIL: { + shader.AddLine("discard;"); + break; + } case OpCode::Id::IPA: { const auto& attribute = instr.attribute.fmt28; std::string dest = GetRegister(instr.gpr0); @@ -465,9 +590,7 @@ private: break; } default: { - NGLOG_CRITICAL(HW_GPU, "Unhandled instruction: {} ({}): {}", - static_cast<unsigned>(instr.opcode.EffectiveOpCode()), - OpCode::GetInfo(instr.opcode).name, instr.hex); + NGLOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName()); UNREACHABLE(); } } @@ -476,6 +599,12 @@ private: } } + // Close the predicate condition scope. + if (instr.pred.pred_index != static_cast<u64>(Pred::UnusedIndex)) { + --shader.scope; + shader.AddLine('}'); + } + return offset + 1; } @@ -605,6 +734,12 @@ private: declarations.AddNewLine(); ++const_buffer_layout; } + + declarations.AddNewLine(); + for (const auto& pred : declr_predicates) { + declarations.AddLine("bool " + pred + " = false;"); + } + declarations.AddNewLine(); } private: @@ -618,6 +753,7 @@ private: // Declarations std::set<std::string> declr_register; + std::set<std::string> declr_predicates; std::set<Attribute::Index> declr_input_attribute; std::set<Attribute::Index> declr_output_attribute; std::array<ConstBufferEntry, Maxwell3D::Regs::MaxConstBuffers> declr_const_buffers; diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 5e78723a2..ab0acb20a 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -9,13 +9,10 @@ #include <memory> #include <glad/glad.h> #include "common/assert.h" -#include "common/bit_field.h" #include "common/logging/log.h" #include "core/core.h" #include "core/core_timing.h" #include "core/frontend/emu_window.h" -#include "core/hw/hw.h" -#include "core/hw/lcd.h" #include "core/memory.h" #include "core/settings.h" #include "core/tracer/recorder.h" diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index 4df687786..e0509f0ce 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp @@ -53,6 +53,7 @@ u32 BytesPerPixel(TextureFormat format) { // In this case a 'pixel' actually refers to a 4x4 tile. return 16; case TextureFormat::A8R8G8B8: + case TextureFormat::A2B10G10R10: return 4; case TextureFormat::B5G6R5: return 2; @@ -78,6 +79,7 @@ std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width, unswizzled_data.data(), true, block_height); break; case TextureFormat::A8R8G8B8: + case TextureFormat::A2B10G10R10: case TextureFormat::B5G6R5: CopySwizzledData(width, height, bytes_per_pixel, bytes_per_pixel, data, unswizzled_data.data(), true, block_height); @@ -100,6 +102,7 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat case TextureFormat::DXT23: case TextureFormat::DXT45: case TextureFormat::A8R8G8B8: + case TextureFormat::A2B10G10R10: case TextureFormat::B5G6R5: // TODO(Subv): For the time being just forward the same data without any decoding. rgba_data = texture_data; diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h index 86e45aa88..dc004d361 100644 --- a/src/video_core/textures/texture.h +++ b/src/video_core/textures/texture.h @@ -15,6 +15,7 @@ namespace Texture { enum class TextureFormat : u32 { A8R8G8B8 = 0x8, + A2B10G10R10 = 0x9, B5G6R5 = 0x15, DXT1 = 0x24, DXT23 = 0x25, diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp index 1e4844b57..5fada74be 100644 --- a/src/yuzu/debugger/graphics/graphics_surface.cpp +++ b/src/yuzu/debugger/graphics/graphics_surface.cpp @@ -25,6 +25,8 @@ static Tegra::Texture::TextureFormat ConvertToTextureFormat( switch (render_target_format) { case Tegra::RenderTargetFormat::RGBA8_UNORM: return Tegra::Texture::TextureFormat::A8R8G8B8; + case Tegra::RenderTargetFormat::RGB10_A2_UNORM: + return Tegra::Texture::TextureFormat::A2B10G10R10; default: UNIMPLEMENTED_MSG("Unimplemented RT format"); } diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index 3d7cd06a4..36d40a9b5 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp @@ -56,7 +56,28 @@ void EmuWindow_SDL2::OnResize() { UpdateCurrentFramebufferLayout(width, height); } -EmuWindow_SDL2::EmuWindow_SDL2() { +void EmuWindow_SDL2::Fullscreen() { + if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN) == 0) { + return; + } + + NGLOG_ERROR(Frontend, "Fullscreening failed: {}", SDL_GetError()); + + // Try a different fullscreening method + NGLOG_INFO(Frontend, "Attempting to use borderless fullscreen..."); + if (SDL_SetWindowFullscreen(render_window, SDL_WINDOW_FULLSCREEN_DESKTOP) == 0) { + return; + } + + NGLOG_ERROR(Frontend, "Borderless fullscreening failed: {}", SDL_GetError()); + + // Fallback algorithm: Maximise window. + // Works on all systems (unless something is seriously wrong), so no fallback for this one. + NGLOG_INFO(Frontend, "Falling back on a maximised window..."); + SDL_MaximizeWindow(render_window); +} + +EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { InputCommon::Init(); SDL_SetMainReady(); @@ -90,6 +111,10 @@ EmuWindow_SDL2::EmuWindow_SDL2() { exit(1); } + if (fullscreen) { + Fullscreen(); + } + gl_context = SDL_GL_CreateContext(render_window); if (gl_context == nullptr) { diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h index 3664d2fbe..7d5cfffb6 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h @@ -12,7 +12,7 @@ struct SDL_Window; class EmuWindow_SDL2 : public EmuWindow { public: - EmuWindow_SDL2(); + explicit EmuWindow_SDL2(bool fullscreen); ~EmuWindow_SDL2(); /// Swap buffers to display the next frame @@ -43,6 +43,9 @@ private: /// Called by PollEvents when any event that may cause the window to be resized occurs void OnResize(); + /// Called when user passes the fullscreen parameter flag + void Fullscreen(); + /// Called when a configuration change affects the minimal size of the window void OnMinimalClientAreaChangeRequest( const std::pair<unsigned, unsigned>& minimal_size) override; diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index a91140447..39603e881 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -50,6 +50,7 @@ static void PrintHelp(const char* argv0) { std::cout << "Usage: " << argv0 << " [options] <filename>\n" "-g, --gdbport=NUMBER Enable gdb stub on port NUMBER\n" + "-f, --fullscreen Start in fullscreen mode\n" "-h, --help Display this help and exit\n" "-v, --version Output version information and exit\n"; } @@ -76,15 +77,18 @@ int main(int argc, char** argv) { #endif std::string filepath; + bool fullscreen = false; + static struct option long_options[] = { {"gdbport", required_argument, 0, 'g'}, + {"fullscreen", no_argument, 0, 'f'}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'v'}, {0, 0, 0, 0}, }; while (optind < argc) { - char arg = getopt_long(argc, argv, "g:hv", long_options, &option_index); + char arg = getopt_long(argc, argv, "g:fhv", long_options, &option_index); if (arg != -1) { switch (arg) { case 'g': @@ -98,6 +102,10 @@ int main(int argc, char** argv) { exit(1); } break; + case 'f': + fullscreen = true; + NGLOG_INFO(Frontend, "Starting in fullscreen mode..."); + break; case 'h': PrintHelp(argv[0]); return 0; @@ -137,7 +145,7 @@ int main(int argc, char** argv) { Settings::values.use_gdbstub = use_gdbstub; Settings::Apply(); - std::unique_ptr<EmuWindow_SDL2> emu_window{std::make_unique<EmuWindow_SDL2>()}; + std::unique_ptr<EmuWindow_SDL2> emu_window{std::make_unique<EmuWindow_SDL2>(fullscreen)}; Core::System& system{Core::System::GetInstance()}; |