mirror of
https://github.com/Lime3DS/Lime3DS.git
synced 2026-07-09 17:24:49 -06:00
core: gsp: General improvements to GSP service accuracy (#2273)
This commit is contained in:
parent
c2dd5f3bea
commit
bda4671be6
@ -39,10 +39,13 @@ namespace ErrCodes {
|
||||
enum {
|
||||
// TODO(purpasmart): Check if this name fits its actual usage
|
||||
OutofRangeOrMisalignedAddress = 513,
|
||||
NoSaveVRAMSysAreaPerm = 518,
|
||||
FirstInitialization = 519,
|
||||
};
|
||||
}
|
||||
|
||||
constexpr Result ResultNoSaveVRAMSysAreaPerm(ErrCodes::NoSaveVRAMSysAreaPerm, ErrorModule::GX,
|
||||
ErrorSummary::NothingHappened, ErrorLevel::Permanent);
|
||||
constexpr Result ResultFirstInitialization(ErrCodes::FirstInitialization, ErrorModule::GX,
|
||||
ErrorSummary::Success, ErrorLevel::Success);
|
||||
constexpr Result ResultRegsOutOfRangeOrMisaligned(ErrCodes::OutofRangeOrMisalignedAddress,
|
||||
@ -71,13 +74,34 @@ CommandBuffer* GSP_GPU::GetCommandBuffer(u32 thread_id) {
|
||||
return reinterpret_cast<CommandBuffer*>(ptr);
|
||||
}
|
||||
|
||||
FrameBufferUpdate* GSP_GPU::GetFrameBufferInfo(u32 thread_id, u32 screen_index) {
|
||||
std::pair<bool, FrameBufferInfo*> GSP_GPU::GetFrameBufferInfo(u32 thread_id, u32 screen_index,
|
||||
bool force_update) {
|
||||
DEBUG_ASSERT_MSG(screen_index < 2, "Invalid screen index");
|
||||
|
||||
bool is_dirty = false;
|
||||
|
||||
// Matches real HW behaviour of a cached copy of
|
||||
// the framebuffer info being maintained that
|
||||
// is only updated with the dirty flag every time
|
||||
// the FB info is accessed.
|
||||
//
|
||||
// If the thread ID is invalid, the last cached
|
||||
// data is used.
|
||||
if (thread_id != std::numeric_limits<u32>::max()) {
|
||||
|
||||
// For each thread there are two FrameBufferUpdate fields
|
||||
const u32 offset = 0x200 + (2 * thread_id + screen_index) * sizeof(FrameBufferUpdate);
|
||||
u8* ptr = shared_memory->GetPointer(offset);
|
||||
return reinterpret_cast<FrameBufferUpdate*>(ptr);
|
||||
FrameBufferUpdate* screen =
|
||||
reinterpret_cast<FrameBufferUpdate*>(shared_memory->GetPointer(offset));
|
||||
|
||||
if (screen->is_dirty || force_update) {
|
||||
cached_framebuffer_infos[screen_index] = screen->framebuffer_info[screen->index];
|
||||
screen->is_dirty.Assign(false);
|
||||
is_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
return {is_dirty, &cached_framebuffer_infos[screen_index]};
|
||||
}
|
||||
|
||||
InterruptRelayQueue* GSP_GPU::GetInterruptRelayQueue(u32 thread_id) {
|
||||
@ -86,10 +110,15 @@ InterruptRelayQueue* GSP_GPU::GetInterruptRelayQueue(u32 thread_id) {
|
||||
}
|
||||
|
||||
void GSP_GPU::ClientDisconnected(std::shared_ptr<Kernel::ServerSession> server_session) {
|
||||
const SessionData* session_data = GetSessionData(server_session);
|
||||
if (active_thread_id == session_data->thread_id) {
|
||||
SessionData* session_data = GetSessionData(server_session);
|
||||
session_data->interrupt_event = nullptr;
|
||||
session_data->registered = false;
|
||||
if (thread_id_with_rights == session_data->thread_id) {
|
||||
ReleaseRight(session_data);
|
||||
}
|
||||
if (True(session_data->relay_queue_flags & RelayEventQueueFlags::AllowSaveVramSysArea)) {
|
||||
vram_backup_handler.ResetState();
|
||||
}
|
||||
SessionRequestHandler::ClientDisconnected(server_session);
|
||||
}
|
||||
|
||||
@ -190,6 +219,41 @@ static Result WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, std::span
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
class GSP_GPU::ThreadCallback : public Kernel::HLERequestContext::WakeupCallback {
|
||||
|
||||
public:
|
||||
enum class Type : u32 {
|
||||
SaveVRAMSysArea,
|
||||
RestoreVRAMSysArea,
|
||||
};
|
||||
|
||||
explicit ThreadCallback(Core::System& system_, std::string name_, Type type_)
|
||||
: system(system_), name(name_), type(type_) {}
|
||||
|
||||
void WakeUp(std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx,
|
||||
Kernel::ThreadWakeupReason reason) {
|
||||
if (type == Type::SaveVRAMSysArea || type == Type::RestoreVRAMSysArea) {
|
||||
IPC::RequestBuilder rb = IPC::RequestBuilder(ctx, 1, 0);
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
std::string name;
|
||||
Type type;
|
||||
|
||||
ThreadCallback() : system(Core::Global<Core::System>()) {}
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<Kernel::HLERequestContext::WakeupCallback>(*this);
|
||||
ar & name;
|
||||
ar & type;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
void GSP_GPU::WriteHWRegs(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
const u32 reg_addr = rp.Pop<u32>();
|
||||
@ -315,7 +379,8 @@ void GSP_GPU::GetPerfLog(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
void GSP_GPU::RegisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
u32 flags = rp.Pop<u32>();
|
||||
|
||||
auto relay_queue_flags = static_cast<RelayEventQueueFlags>(rp.Pop<u32>());
|
||||
|
||||
auto interrupt_event = rp.PopObject<Kernel::Event>();
|
||||
ASSERT_MSG(interrupt_event, "handle is not valid!");
|
||||
@ -325,6 +390,7 @@ void GSP_GPU::RegisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) {
|
||||
SessionData* session_data = GetSessionData(ctx.Session());
|
||||
session_data->interrupt_event = std::move(interrupt_event);
|
||||
session_data->registered = true;
|
||||
session_data->relay_queue_flags = relay_queue_flags;
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
|
||||
|
||||
@ -339,7 +405,7 @@ void GSP_GPU::RegisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(session_data->thread_id);
|
||||
rb.PushCopyObjects(shared_memory);
|
||||
|
||||
LOG_DEBUG(Service_GSP, "called, flags=0x{:08X}", flags);
|
||||
LOG_DEBUG(Service_GSP, "called, relay_queue_flags=0x{:08X}", relay_queue_flags);
|
||||
}
|
||||
|
||||
void GSP_GPU::UnregisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) {
|
||||
@ -521,10 +587,17 @@ void Service::GSP::GSP_GPU::ProcessPendingInterruptImpl(InterruptId interrupt_id
|
||||
// Update framebuffer information if requested
|
||||
const s32 screen_id = (interrupt_id == InterruptId::PDC0) ? 0 : 1;
|
||||
|
||||
auto* info = GetFrameBufferInfo(thread_id, screen_id);
|
||||
if (info->is_dirty) {
|
||||
system.GPU().SetBufferSwap(screen_id, info->framebuffer_info[info->index]);
|
||||
info->is_dirty.Assign(false);
|
||||
auto info = GetFrameBufferInfo(thread_id, screen_id);
|
||||
if (info.first || force_buffer_swap[screen_id]) {
|
||||
force_buffer_swap[screen_id] = false;
|
||||
system.GPU().SetBufferSwap(screen_id, *info.second);
|
||||
}
|
||||
|
||||
// If it's the bottom screen interrupt and a HLE event is pending,
|
||||
// signal it and reset.
|
||||
if (interrupt_id == InterruptId::PDC1 && session_data->on_vblank_event) {
|
||||
session_data->on_vblank_event->Signal();
|
||||
session_data->on_vblank_event = nullptr;
|
||||
}
|
||||
|
||||
} else {
|
||||
@ -549,11 +622,11 @@ void GSP_GPU::SignalInterrupt(InterruptId interrupt_id, u64 wait_delay_ns) {
|
||||
}
|
||||
|
||||
// For normal interrupts, don't do anything if no process has acquired the GPU right.
|
||||
if (active_thread_id == std::numeric_limits<u32>::max()) {
|
||||
if (thread_id_with_rights == std::numeric_limits<u32>::max()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SignalInterruptForThread(interrupt_id, active_thread_id, wait_delay_ns);
|
||||
SignalInterruptForThread(interrupt_id, thread_id_with_rights, wait_delay_ns);
|
||||
}
|
||||
|
||||
void GSP_GPU::SetLcdForceBlack(Kernel::HLERequestContext& ctx) {
|
||||
@ -571,7 +644,14 @@ void GSP_GPU::SetLcdForceBlack(Kernel::HLERequestContext& ctx) {
|
||||
void GSP_GPU::TriggerCmdReqQueue(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
|
||||
auto* command_buffer = GetCommandBuffer(active_thread_id);
|
||||
if (thread_id_with_rights == std::numeric_limits<u32>::max()) {
|
||||
// Even if the active thread ID is not set,
|
||||
// the function always succeeds.
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
auto* command_buffer = GetCommandBuffer(thread_id_with_rights);
|
||||
auto& gpu = system.GPU();
|
||||
|
||||
bool requires_delay = false;
|
||||
@ -628,17 +708,18 @@ void GSP_GPU::ImportDisplayCaptureInfo(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
LOG_DEBUG(Service_GSP, "called");
|
||||
|
||||
if (active_thread_id == std::numeric_limits<u32>::max()) {
|
||||
LOG_WARNING(Service_GSP, "Called without an active thread.");
|
||||
auto top_screen_info = GetFrameBufferInfo(thread_id_with_rights, 0);
|
||||
auto bottom_screen_info = GetFrameBufferInfo(thread_id_with_rights, 1);
|
||||
|
||||
// TODO: Find the right error code.
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(-1);
|
||||
return;
|
||||
if (top_screen_info.first) {
|
||||
force_buffer_swap[0] = true;
|
||||
}
|
||||
if (bottom_screen_info.first) {
|
||||
force_buffer_swap[1] = true;
|
||||
}
|
||||
|
||||
FrameBufferUpdate* top_screen = GetFrameBufferInfo(active_thread_id, 0);
|
||||
FrameBufferUpdate* bottom_screen = GetFrameBufferInfo(active_thread_id, 1);
|
||||
auto top_screen = top_screen_info.second;
|
||||
auto bottom_screen = bottom_screen_info.second;
|
||||
|
||||
struct CaptureInfoEntry {
|
||||
u32_le address_left;
|
||||
@ -649,16 +730,15 @@ void GSP_GPU::ImportDisplayCaptureInfo(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
CaptureInfoEntry top_entry, bottom_entry;
|
||||
// Top Screen
|
||||
top_entry.address_left = top_screen->framebuffer_info[top_screen->index].address_left;
|
||||
top_entry.address_right = top_screen->framebuffer_info[top_screen->index].address_right;
|
||||
top_entry.format = top_screen->framebuffer_info[top_screen->index].format;
|
||||
top_entry.stride = top_screen->framebuffer_info[top_screen->index].stride;
|
||||
top_entry.address_left = top_screen->address_left;
|
||||
top_entry.address_right = top_screen->address_right;
|
||||
top_entry.format = top_screen->format;
|
||||
top_entry.stride = top_screen->stride;
|
||||
// Bottom Screen
|
||||
bottom_entry.address_left = bottom_screen->framebuffer_info[bottom_screen->index].address_left;
|
||||
bottom_entry.address_right =
|
||||
bottom_screen->framebuffer_info[bottom_screen->index].address_right;
|
||||
bottom_entry.format = bottom_screen->framebuffer_info[bottom_screen->index].format;
|
||||
bottom_entry.stride = bottom_screen->framebuffer_info[bottom_screen->index].stride;
|
||||
bottom_entry.address_left = bottom_screen->address_left;
|
||||
bottom_entry.address_right = bottom_screen->address_right;
|
||||
bottom_entry.format = bottom_screen->format;
|
||||
bottom_entry.stride = bottom_screen->stride;
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(9, 0);
|
||||
rb.Push(ResultSuccess);
|
||||
@ -722,26 +802,19 @@ void GSP_GPU::SaveVramSysArea(Kernel::HLERequestContext& ctx) {
|
||||
// Invalid values
|
||||
0, 0, 0};
|
||||
|
||||
if (active_thread_id == std::numeric_limits<u32>::max()) {
|
||||
LOG_WARNING(Service_GSP, "Called without an active thread.");
|
||||
|
||||
// TODO: Find the right error code.
|
||||
const auto session_data = GetSessionData(ctx.Session());
|
||||
if (False(session_data->relay_queue_flags & RelayEventQueueFlags::AllowSaveVramSysArea)) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(-1);
|
||||
rb.Push(ResultNoSaveVRAMSysAreaPerm);
|
||||
return;
|
||||
}
|
||||
|
||||
system.Memory().RasterizerFlushVirtualRegion(Memory::VRAM_VADDR, Memory::VRAM_SIZE,
|
||||
Memory::FlushMode::Flush);
|
||||
const auto vram = system.Memory().GetPointer(Memory::VRAM_VADDR);
|
||||
saved_vram.emplace(std::vector<u8>(Memory::VRAM_SIZE));
|
||||
std::memcpy(saved_vram.get().data(), vram, Memory::VRAM_SIZE);
|
||||
vram_backup_handler.SaveVRAMWithState(VramBackupHandler::State::STATE_0);
|
||||
|
||||
auto top_screen = GetFrameBufferInfo(active_thread_id, 0);
|
||||
auto top_screen = GetFrameBufferInfo(thread_id_with_rights, 0).second;
|
||||
if (top_screen) {
|
||||
u8 bytes_per_pixel =
|
||||
bpp_per_format[top_screen->framebuffer_info[top_screen->index].GetPixelFormat()];
|
||||
const auto top_fb = top_screen->framebuffer_info[top_screen->index];
|
||||
u8 bytes_per_pixel = bpp_per_format[top_screen->GetPixelFormat()];
|
||||
const auto& top_fb = *top_screen;
|
||||
if (top_fb.address_left && bytes_per_pixel != 0 && bytes_per_pixel != 4) {
|
||||
CopyFrameBuffer(system, FRAMEBUFFER_SAVE_AREA_TOP_LEFT, top_fb.address_left,
|
||||
FRAMEBUFFER_WIDTH * bytes_per_pixel, top_fb.stride,
|
||||
@ -761,21 +834,18 @@ void GSP_GPU::SaveVramSysArea(Kernel::HLERequestContext& ctx) {
|
||||
FRAMEBUFFER_WIDTH * bytes_per_pixel, TOP_FRAMEBUFFER_HEIGHT);
|
||||
}
|
||||
|
||||
FrameBufferInfo fb_info = top_screen->framebuffer_info[top_screen->index];
|
||||
|
||||
fb_info.address_left = FRAMEBUFFER_SAVE_AREA_TOP_LEFT;
|
||||
fb_info.address_right = FRAMEBUFFER_SAVE_AREA_TOP_RIGHT;
|
||||
fb_info.stride = FRAMEBUFFER_WIDTH * bytes_per_pixel;
|
||||
system.GPU().SetBufferSwap(0, fb_info);
|
||||
top_screen->address_left = FRAMEBUFFER_SAVE_AREA_TOP_LEFT;
|
||||
top_screen->address_right = FRAMEBUFFER_SAVE_AREA_TOP_RIGHT;
|
||||
top_screen->stride = FRAMEBUFFER_WIDTH * bytes_per_pixel;
|
||||
force_buffer_swap[0] = true;
|
||||
} else {
|
||||
LOG_WARNING(Service_GSP, "No top screen bound, skipping capture.");
|
||||
}
|
||||
|
||||
auto bottom_screen = GetFrameBufferInfo(active_thread_id, 1);
|
||||
auto bottom_screen = GetFrameBufferInfo(thread_id_with_rights, 1).second;
|
||||
if (bottom_screen) {
|
||||
u8 bytes_per_pixel =
|
||||
bpp_per_format[bottom_screen->framebuffer_info[bottom_screen->index].GetPixelFormat()];
|
||||
const auto bottom_fb = bottom_screen->framebuffer_info[bottom_screen->index];
|
||||
u8 bytes_per_pixel = bpp_per_format[bottom_screen->GetPixelFormat()];
|
||||
const auto& bottom_fb = *bottom_screen;
|
||||
if (bottom_fb.address_left && bytes_per_pixel != 0 && bytes_per_pixel != 4) {
|
||||
CopyFrameBuffer(system, FRAMEBUFFER_SAVE_AREA_BOTTOM, bottom_fb.address_left,
|
||||
FRAMEBUFFER_WIDTH * bytes_per_pixel, bottom_fb.stride,
|
||||
@ -785,19 +855,21 @@ void GSP_GPU::SaveVramSysArea(Kernel::HLERequestContext& ctx) {
|
||||
ClearFramebuffer(system, FRAMEBUFFER_SAVE_AREA_BOTTOM,
|
||||
FRAMEBUFFER_WIDTH * bytes_per_pixel, BOTTOM_FRAMEBUFFER_HEIGHT);
|
||||
}
|
||||
FrameBufferInfo fb_info = bottom_screen->framebuffer_info[bottom_screen->index];
|
||||
|
||||
fb_info.address_left = FRAMEBUFFER_SAVE_AREA_BOTTOM;
|
||||
fb_info.stride = FRAMEBUFFER_WIDTH * bytes_per_pixel;
|
||||
system.GPU().SetBufferSwap(1, fb_info);
|
||||
bottom_screen->address_left = FRAMEBUFFER_SAVE_AREA_BOTTOM;
|
||||
bottom_screen->stride = FRAMEBUFFER_WIDTH * bytes_per_pixel;
|
||||
force_buffer_swap[1] = true;
|
||||
} else {
|
||||
LOG_WARNING(Service_GSP, "No bottom screen bound, skipping capture.");
|
||||
}
|
||||
|
||||
// Real GSP waits for VBlank here, but we don't need it (?).
|
||||
vram_backup_handler.SaveVRAMWithState(VramBackupHandler::State::STATE_1);
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ResultSuccess);
|
||||
// Wait for VBlank
|
||||
auto vblank_callback = std::make_shared<ThreadCallback>(system, "GSP_GPU::SaveVRAMSysArea",
|
||||
ThreadCallback::Type::SaveVRAMSysArea);
|
||||
session_data->on_vblank_event = ctx.SleepClientThread(
|
||||
"GSP_GPU::SaveVRAMSysArea", std::chrono::nanoseconds(-1), vblank_callback);
|
||||
}
|
||||
|
||||
void GSP_GPU::RestoreVramSysArea(Kernel::HLERequestContext& ctx) {
|
||||
@ -805,31 +877,35 @@ void GSP_GPU::RestoreVramSysArea(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
LOG_DEBUG(Service_GSP, "called");
|
||||
|
||||
if (saved_vram) {
|
||||
auto vram = system.Memory().GetPointer(Memory::VRAM_VADDR);
|
||||
std::memcpy(vram, saved_vram.get().data(), Memory::VRAM_SIZE);
|
||||
system.Memory().RasterizerFlushVirtualRegion(Memory::VRAM_VADDR, Memory::VRAM_SIZE,
|
||||
Memory::FlushMode::Invalidate);
|
||||
}
|
||||
|
||||
auto top_screen = GetFrameBufferInfo(active_thread_id, 0);
|
||||
if (top_screen) {
|
||||
system.GPU().SetBufferSwap(0, top_screen->framebuffer_info[top_screen->index]);
|
||||
} else {
|
||||
LOG_WARNING(Service_GSP, "No top screen bound, skipping restore.");
|
||||
}
|
||||
|
||||
auto bottom_screen = GetFrameBufferInfo(active_thread_id, 1);
|
||||
if (bottom_screen) {
|
||||
system.GPU().SetBufferSwap(1, bottom_screen->framebuffer_info[top_screen->index]);
|
||||
} else {
|
||||
LOG_WARNING(Service_GSP, "No bottom screen bound, skipping restore.");
|
||||
}
|
||||
|
||||
// Real GSP waits for VBlank here, but we don't need it (?).
|
||||
|
||||
const auto session_data = GetSessionData(ctx.Session());
|
||||
if (False(session_data->relay_queue_flags & RelayEventQueueFlags::AllowSaveVramSysArea)) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(ResultNoSaveVRAMSysAreaPerm);
|
||||
return;
|
||||
}
|
||||
|
||||
vram_backup_handler.RestoreVRAMWithState(VramBackupHandler::State::STATE_1);
|
||||
|
||||
auto top_screen = GetFrameBufferInfo(thread_id_with_rights, 0, true).second;
|
||||
if (!top_screen) {
|
||||
LOG_WARNING(Service_GSP, "No top screen bound, skipping restore.");
|
||||
} else {
|
||||
force_buffer_swap[0] = true;
|
||||
}
|
||||
|
||||
auto bottom_screen = GetFrameBufferInfo(thread_id_with_rights, 1, true).second;
|
||||
if (!bottom_screen) {
|
||||
LOG_WARNING(Service_GSP, "No bottom screen bound, skipping restore.");
|
||||
} else {
|
||||
force_buffer_swap[1] = true;
|
||||
}
|
||||
|
||||
vram_backup_handler.RestoreVRAMWithState(VramBackupHandler::State::STATE_0);
|
||||
|
||||
auto vblank_callback = std::make_shared<ThreadCallback>(
|
||||
system, "GSP_GPU::RestoreVramSysArea", ThreadCallback::Type::RestoreVRAMSysArea);
|
||||
session_data->on_vblank_event = ctx.SleepClientThread(
|
||||
"GSP_GPU::RestoreVramSysArea", std::chrono::nanoseconds(-1), vblank_callback);
|
||||
}
|
||||
|
||||
Result GSP_GPU::AcquireGpuRight(const Kernel::HLERequestContext& ctx,
|
||||
@ -861,7 +937,7 @@ Result GSP_GPU::AcquireGpuRight(const Kernel::HLERequestContext& ctx,
|
||||
gpu.PicaCore().vs_setup.requires_fixup = requires_shader_fixup;
|
||||
gpu.PicaCore().gs_setup.requires_fixup = requires_shader_fixup;
|
||||
|
||||
if (active_thread_id == session_data->thread_id) {
|
||||
if (thread_id_with_rights == session_data->thread_id) {
|
||||
return {ErrorDescription::AlreadyDone, ErrorModule::GX, ErrorSummary::Success,
|
||||
ErrorLevel::Success};
|
||||
}
|
||||
@ -870,14 +946,14 @@ Result GSP_GPU::AcquireGpuRight(const Kernel::HLERequestContext& ctx,
|
||||
|
||||
if (blocking) {
|
||||
// TODO: The thread should be put to sleep until acquired.
|
||||
ASSERT_MSG(active_thread_id == std::numeric_limits<u32>::max(),
|
||||
ASSERT_MSG(thread_id_with_rights == std::numeric_limits<u32>::max(),
|
||||
"Sleeping for GPU right is not yet supported.");
|
||||
} else if (active_thread_id != std::numeric_limits<u32>::max()) {
|
||||
} else if (thread_id_with_rights != std::numeric_limits<u32>::max()) {
|
||||
return {ErrorDescription::Busy, ErrorModule::GX, ErrorSummary::WouldBlock,
|
||||
ErrorLevel::Status};
|
||||
}
|
||||
|
||||
active_thread_id = session_data->thread_id;
|
||||
thread_id_with_rights = session_data->thread_id;
|
||||
active_client_thread_id = ctx.ClientThread()->thread_id;
|
||||
return ResultSuccess;
|
||||
}
|
||||
@ -904,16 +980,17 @@ void GSP_GPU::AcquireRight(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
void GSP_GPU::ReleaseRight(const SessionData* session_data) {
|
||||
ASSERT_MSG(active_thread_id == session_data->thread_id,
|
||||
"Wrong thread tried to release GPU right");
|
||||
active_thread_id = std::numeric_limits<u32>::max();
|
||||
if (thread_id_with_rights == session_data->thread_id) {
|
||||
thread_id_with_rights = std::numeric_limits<u32>::max();
|
||||
active_client_thread_id = std::numeric_limits<u32>::max();
|
||||
}
|
||||
}
|
||||
|
||||
void GSP_GPU::ReleaseRight(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
|
||||
const SessionData* session_data = GetSessionData(ctx.Session());
|
||||
// Success even if wrong thread calls this function.
|
||||
ReleaseRight(session_data);
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
@ -976,14 +1053,16 @@ void GSP_GPU::serialize(Archive& ar, const unsigned int) {
|
||||
DEBUG_SERIALIZATION_POINT;
|
||||
ar& boost::serialization::base_object<Kernel::SessionRequestHandler>(*this);
|
||||
ar & shared_memory;
|
||||
ar & active_thread_id;
|
||||
ar & thread_id_with_rights;
|
||||
ar & active_client_thread_id;
|
||||
ar & first_initialization;
|
||||
ar & used_thread_ids;
|
||||
ar & saved_vram;
|
||||
ar & delay_texture_copy_completion;
|
||||
ar & pending_interrupts;
|
||||
ar & perf_recorder;
|
||||
ar & cached_framebuffer_infos;
|
||||
ar & force_buffer_swap;
|
||||
ar & vram_backup_handler;
|
||||
}
|
||||
SERIALIZE_IMPL(GSP_GPU)
|
||||
|
||||
@ -1051,6 +1130,8 @@ void SessionData::serialize(Archive& ar, const unsigned int) {
|
||||
ar & interrupt_event;
|
||||
ar & thread_id;
|
||||
ar & registered;
|
||||
ar & relay_queue_flags;
|
||||
ar & on_vblank_event;
|
||||
}
|
||||
SERIALIZE_IMPL(SessionData)
|
||||
|
||||
@ -1067,4 +1148,64 @@ SessionData::~SessionData() {
|
||||
gsp->used_thread_ids[thread_id] = false;
|
||||
}
|
||||
|
||||
void VramBackupHandler::SaveVRAMWithState(State state) {
|
||||
|
||||
VRAMBankBackupMask mask{};
|
||||
if (state == State::STATE_0 && curr_state == State::STATE_0) {
|
||||
mask = VRAMBankBackupMask::MASK_1;
|
||||
curr_state = State::STATE_1;
|
||||
} else if (state == State::STATE_1 && curr_state == State::STATE_1) {
|
||||
mask = VRAMBankBackupMask::MASK_2;
|
||||
curr_state = State::STATE_2;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < BANK_COUNT; i++) {
|
||||
if (True(mask & mask_per_bank[i])) {
|
||||
SaveVRAMBank(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VramBackupHandler::RestoreVRAMWithState(State state) {
|
||||
VRAMBankBackupMask mask{};
|
||||
if (state == State::STATE_0 && curr_state == State::STATE_1) {
|
||||
mask = VRAMBankBackupMask::MASK_1;
|
||||
curr_state = State::STATE_0;
|
||||
} else if (state == State::STATE_1 && curr_state == State::STATE_2) {
|
||||
mask = VRAMBankBackupMask::MASK_2;
|
||||
curr_state = State::STATE_1;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < BANK_COUNT; i++) {
|
||||
if (True(mask & mask_per_bank[i])) {
|
||||
RestoreVRAMBank(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VramBackupHandler::SaveVRAMBank(size_t bank_id) {
|
||||
ASSERT(bank_id < BANK_COUNT);
|
||||
|
||||
VAddr bank_vaddr = Memory::VRAM_VADDR + bank_id * VRAM_BACKUP_BANK_SIZE;
|
||||
|
||||
system.Memory().RasterizerFlushVirtualRegion(bank_vaddr, VRAM_BACKUP_BANK_SIZE,
|
||||
Memory::FlushMode::Flush);
|
||||
|
||||
u8* data = system.Memory().GetPointer(bank_vaddr);
|
||||
memcpy(vram_backup[bank_id].data(), data, VRAM_BACKUP_BANK_SIZE);
|
||||
}
|
||||
|
||||
void VramBackupHandler::RestoreVRAMBank(size_t bank_id) {
|
||||
ASSERT(bank_id < BANK_COUNT);
|
||||
|
||||
VAddr bank_vaddr = Memory::VRAM_VADDR + bank_id * VRAM_BACKUP_BANK_SIZE;
|
||||
|
||||
u8* data = system.Memory().GetPointer(bank_vaddr);
|
||||
memcpy(data, vram_backup[bank_id].data(), VRAM_BACKUP_BANK_SIZE);
|
||||
|
||||
system.Memory().RasterizerFlushVirtualRegion(bank_vaddr, VRAM_BACKUP_BANK_SIZE,
|
||||
Memory::FlushMode::Invalidate);
|
||||
}
|
||||
} // namespace Service::GSP
|
||||
|
||||
SERIALIZE_EXPORT_IMPL(Service::GSP::GSP_GPU::ThreadCallback)
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
#include "core/hle/service/gsp/gsp_command.h"
|
||||
#include "core/hle/service/gsp/gsp_interrupt.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
@ -45,6 +46,17 @@ struct FrameBufferInfo {
|
||||
u32 GetPixelFormat() {
|
||||
return format & PIXEL_FORMAT_MASK;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar & active_fb;
|
||||
ar & address_left;
|
||||
ar & address_right;
|
||||
ar & stride;
|
||||
ar & format;
|
||||
ar & shown_fb;
|
||||
ar & unknown;
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(FrameBufferInfo) == 0x1c, "Struct has incorrect size");
|
||||
|
||||
@ -61,6 +73,20 @@ static_assert(sizeof(FrameBufferUpdate) == 0x40, "Struct has incorrect size");
|
||||
static_assert(offsetof(FrameBufferUpdate, framebuffer_info[1]) == 0x20,
|
||||
"FrameBufferInfo element has incorrect alignment");
|
||||
|
||||
enum RelayEventQueueFlags : u32 {
|
||||
AllowSaveVramSysArea = (1 << 0),
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(RelayEventQueueFlags)
|
||||
|
||||
// TODO(PabloMK7): Fill this in when the purpose of
|
||||
// each bit is determined.
|
||||
enum class VRAMBankBackupMask : u8 {
|
||||
MASK_0 = (1 << 0),
|
||||
MASK_1 = (1 << 1),
|
||||
MASK_2 = (1 << 2),
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(VRAMBankBackupMask)
|
||||
|
||||
constexpr u32 FRAMEBUFFER_WIDTH = 240;
|
||||
constexpr u32 FRAMEBUFFER_WIDTH_POW2 = 256;
|
||||
constexpr u32 TOP_FRAMEBUFFER_HEIGHT = 400;
|
||||
@ -72,6 +98,8 @@ constexpr VAddr FRAMEBUFFER_SAVE_AREA_TOP_LEFT = Memory::VRAM_VADDR + 0x273000;
|
||||
constexpr VAddr FRAMEBUFFER_SAVE_AREA_TOP_RIGHT = Memory::VRAM_VADDR + 0x2B9800;
|
||||
constexpr VAddr FRAMEBUFFER_SAVE_AREA_BOTTOM = Memory::VRAM_VADDR + 0x4C7800;
|
||||
|
||||
constexpr size_t VRAM_BACKUP_BANK_SIZE = 0x80000;
|
||||
|
||||
class GSP_GPU;
|
||||
|
||||
class SessionData : public Kernel::SessionRequestHandler::SessionDataBase {
|
||||
@ -88,6 +116,10 @@ public:
|
||||
u32 thread_id;
|
||||
/// Whether RegisterInterruptRelayQueue was called for this session
|
||||
bool registered = false;
|
||||
/// Current relay queue flags
|
||||
RelayEventQueueFlags relay_queue_flags{};
|
||||
/// Event to be signaled on VBlank
|
||||
std::shared_ptr<Kernel::Event> on_vblank_event{};
|
||||
|
||||
private:
|
||||
template <class Archive>
|
||||
@ -95,6 +127,68 @@ private:
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
class VramBackupHandler {
|
||||
public:
|
||||
VramBackupHandler() : system(Core::Global<Core::System>()) {
|
||||
for (auto& v : vram_backup) {
|
||||
v.resize(VRAM_BACKUP_BANK_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(PabloMK7): Figure out what each state means
|
||||
enum class State : u32 { STATE_0, STATE_1, STATE_2 };
|
||||
|
||||
void SaveVRAMWithState(State state);
|
||||
|
||||
void RestoreVRAMWithState(State state);
|
||||
|
||||
void ResetState() {
|
||||
curr_state = State::STATE_0;
|
||||
}
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
|
||||
void SaveVRAMBank(size_t bank_id);
|
||||
|
||||
void RestoreVRAMBank(size_t bank_id);
|
||||
|
||||
static constexpr u32 BANK_COUNT = Memory::VRAM_SIZE / VRAM_BACKUP_BANK_SIZE;
|
||||
|
||||
// A vector is needed because otherwise a stack overflow happens
|
||||
// in the boost serialization code.
|
||||
std::array<std::vector<u8>, BANK_COUNT> vram_backup{};
|
||||
|
||||
// TODO(PabloMK7): Figure out what this means, it's taken from
|
||||
// GSP decompilation.
|
||||
static constexpr std::array<VRAMBankBackupMask, BANK_COUNT> mask_per_bank = {
|
||||
VRAMBankBackupMask::MASK_0, // 0x1F000000 - 0x1F07FFFF
|
||||
VRAMBankBackupMask::MASK_0, // 0x1F080000 - 0x1F0FFFFF
|
||||
VRAMBankBackupMask::MASK_0, // 0x1F100000 - 0x1F17FFFF
|
||||
VRAMBankBackupMask::MASK_0 | VRAMBankBackupMask::MASK_1, // 0x1F180000 - 0x1F1FFFFF
|
||||
VRAMBankBackupMask::MASK_0 | VRAMBankBackupMask::MASK_1, // 0x1F200000 - 0x1F27FFFF
|
||||
VRAMBankBackupMask::MASK_0 | VRAMBankBackupMask::MASK_1, // 0x1F280000 - 0x1F2FFFFF
|
||||
VRAMBankBackupMask::MASK_0, // 0x1F300000 - 0x1F37FFFF
|
||||
VRAMBankBackupMask::MASK_0 | VRAMBankBackupMask::MASK_2, // 0x1F380000 - 0x1F3FFFFF
|
||||
VRAMBankBackupMask::MASK_0 | VRAMBankBackupMask::MASK_2, // 0x1F400000 - 0x1F47FFFF
|
||||
VRAMBankBackupMask::MASK_0 | VRAMBankBackupMask::MASK_1, // 0x1F480000 - 0x1F4FFFFF
|
||||
VRAMBankBackupMask::MASK_0 | VRAMBankBackupMask::MASK_1, // 0x1F500000 - 0x1F57FFFF
|
||||
VRAMBankBackupMask::MASK_0 | VRAMBankBackupMask::MASK_1, // 0x1F580000 - 0x1F5FFFFF
|
||||
};
|
||||
|
||||
State curr_state = State::STATE_0;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
for (size_t i = 0; i < BANK_COUNT; i++) {
|
||||
ar& vram_backup[i];
|
||||
}
|
||||
|
||||
ar & curr_state;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
class GSP_GPU final : public ServiceFramework<GSP_GPU, SessionData> {
|
||||
public:
|
||||
explicit GSP_GPU(Core::System& system);
|
||||
@ -114,9 +208,10 @@ public:
|
||||
* @param thread_id GSP thread id of the process that accesses the structure that we are
|
||||
* requesting.
|
||||
* @param screen_index Index of the screen we are requesting (Top = 0, Bottom = 1).
|
||||
* @returns FramebufferUpdate Information about the specified framebuffer.
|
||||
* @returns A pair containing whether the FB info is new and the framebuffer info itself.
|
||||
*/
|
||||
FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index);
|
||||
std::pair<bool, FrameBufferInfo*> GetFrameBufferInfo(u32 thread_id, u32 screen_index,
|
||||
bool force_update = false);
|
||||
|
||||
/// Gets a pointer to a thread command buffer in GSP shared memory
|
||||
CommandBuffer* GetCommandBuffer(u32 thread_id);
|
||||
@ -128,7 +223,7 @@ public:
|
||||
* Retreives the ID of the thread with GPU rights.
|
||||
*/
|
||||
u32 GetActiveThreadId() {
|
||||
return active_thread_id;
|
||||
return thread_id_with_rights;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -138,6 +233,8 @@ public:
|
||||
return active_client_thread_id;
|
||||
}
|
||||
|
||||
class ThreadCallback;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Signals that the specified interrupt type has occurred to userland code for the specified GSP
|
||||
@ -399,16 +496,13 @@ private:
|
||||
std::shared_ptr<Kernel::SharedMemory> shared_memory;
|
||||
|
||||
/// Thread id that currently has GPU rights or std::numeric_limits<u32>::max() if none.
|
||||
u32 active_thread_id = std::numeric_limits<u32>::max();
|
||||
u32 thread_id_with_rights = std::numeric_limits<u32>::max();
|
||||
|
||||
/// Thread id of the client thread that has GPU rights
|
||||
u32 active_client_thread_id = std::numeric_limits<u32>::max();
|
||||
|
||||
bool first_initialization = true;
|
||||
|
||||
/// VRAM copy saved using SaveVramSysArea.
|
||||
boost::optional<std::vector<u8>> saved_vram;
|
||||
|
||||
/// Maximum number of threads that can be registered at the same time in the GSP module.
|
||||
static constexpr u32 MaxGSPThreads = 4;
|
||||
|
||||
@ -527,6 +621,11 @@ private:
|
||||
|
||||
Core::TimingEventType* SignalInterruptEventType = nullptr;
|
||||
|
||||
std::array<FrameBufferInfo, 2> cached_framebuffer_infos;
|
||||
std::array<bool, 2> force_buffer_swap{};
|
||||
|
||||
VramBackupHandler vram_backup_handler;
|
||||
|
||||
friend class SessionData;
|
||||
|
||||
template <class Archive>
|
||||
@ -539,3 +638,4 @@ private:
|
||||
BOOST_CLASS_EXPORT_KEY(Service::GSP::SessionData)
|
||||
BOOST_CLASS_EXPORT_KEY(Service::GSP::GSP_GPU)
|
||||
SERVICE_CONSTRUCT(Service::GSP::GSP_GPU)
|
||||
BOOST_CLASS_EXPORT_KEY(Service::GSP::GSP_GPU::ThreadCallback)
|
||||
|
||||
@ -486,13 +486,13 @@ void GPU::MemoryTransfer() {
|
||||
}
|
||||
|
||||
void GPU::VBlankCallback(std::uintptr_t user_data, s64 cycles_late) {
|
||||
// Present renderered frame.
|
||||
impl->renderer->SwapBuffers();
|
||||
|
||||
// Signal to GSP that GPU interrupt has occurred
|
||||
impl->signal_interrupt(Service::GSP::InterruptId::PDC0, 0);
|
||||
impl->signal_interrupt(Service::GSP::InterruptId::PDC1, 0);
|
||||
|
||||
// Present renderered frame.
|
||||
impl->renderer->SwapBuffers();
|
||||
|
||||
// Reschedule recurrent event
|
||||
impl->timing.ScheduleEvent(FRAME_TICKS - cycles_late, impl->vblank_event);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user