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 {
|
enum {
|
||||||
// TODO(purpasmart): Check if this name fits its actual usage
|
// TODO(purpasmart): Check if this name fits its actual usage
|
||||||
OutofRangeOrMisalignedAddress = 513,
|
OutofRangeOrMisalignedAddress = 513,
|
||||||
|
NoSaveVRAMSysAreaPerm = 518,
|
||||||
FirstInitialization = 519,
|
FirstInitialization = 519,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr Result ResultNoSaveVRAMSysAreaPerm(ErrCodes::NoSaveVRAMSysAreaPerm, ErrorModule::GX,
|
||||||
|
ErrorSummary::NothingHappened, ErrorLevel::Permanent);
|
||||||
constexpr Result ResultFirstInitialization(ErrCodes::FirstInitialization, ErrorModule::GX,
|
constexpr Result ResultFirstInitialization(ErrCodes::FirstInitialization, ErrorModule::GX,
|
||||||
ErrorSummary::Success, ErrorLevel::Success);
|
ErrorSummary::Success, ErrorLevel::Success);
|
||||||
constexpr Result ResultRegsOutOfRangeOrMisaligned(ErrCodes::OutofRangeOrMisalignedAddress,
|
constexpr Result ResultRegsOutOfRangeOrMisaligned(ErrCodes::OutofRangeOrMisalignedAddress,
|
||||||
@ -71,13 +74,34 @@ CommandBuffer* GSP_GPU::GetCommandBuffer(u32 thread_id) {
|
|||||||
return reinterpret_cast<CommandBuffer*>(ptr);
|
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");
|
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
|
// For each thread there are two FrameBufferUpdate fields
|
||||||
const u32 offset = 0x200 + (2 * thread_id + screen_index) * sizeof(FrameBufferUpdate);
|
const u32 offset = 0x200 + (2 * thread_id + screen_index) * sizeof(FrameBufferUpdate);
|
||||||
u8* ptr = shared_memory->GetPointer(offset);
|
FrameBufferUpdate* screen =
|
||||||
return reinterpret_cast<FrameBufferUpdate*>(ptr);
|
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) {
|
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) {
|
void GSP_GPU::ClientDisconnected(std::shared_ptr<Kernel::ServerSession> server_session) {
|
||||||
const SessionData* session_data = GetSessionData(server_session);
|
SessionData* session_data = GetSessionData(server_session);
|
||||||
if (active_thread_id == session_data->thread_id) {
|
session_data->interrupt_event = nullptr;
|
||||||
|
session_data->registered = false;
|
||||||
|
if (thread_id_with_rights == session_data->thread_id) {
|
||||||
ReleaseRight(session_data);
|
ReleaseRight(session_data);
|
||||||
}
|
}
|
||||||
|
if (True(session_data->relay_queue_flags & RelayEventQueueFlags::AllowSaveVramSysArea)) {
|
||||||
|
vram_backup_handler.ResetState();
|
||||||
|
}
|
||||||
SessionRequestHandler::ClientDisconnected(server_session);
|
SessionRequestHandler::ClientDisconnected(server_session);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,6 +219,41 @@ static Result WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, std::span
|
|||||||
return ResultSuccess;
|
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) {
|
void GSP_GPU::WriteHWRegs(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx);
|
IPC::RequestParser rp(ctx);
|
||||||
const u32 reg_addr = rp.Pop<u32>();
|
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) {
|
void GSP_GPU::RegisterInterruptRelayQueue(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(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>();
|
auto interrupt_event = rp.PopObject<Kernel::Event>();
|
||||||
ASSERT_MSG(interrupt_event, "handle is not valid!");
|
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());
|
SessionData* session_data = GetSessionData(ctx.Session());
|
||||||
session_data->interrupt_event = std::move(interrupt_event);
|
session_data->interrupt_event = std::move(interrupt_event);
|
||||||
session_data->registered = true;
|
session_data->registered = true;
|
||||||
|
session_data->relay_queue_flags = relay_queue_flags;
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
|
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.Push(session_data->thread_id);
|
||||||
rb.PushCopyObjects(shared_memory);
|
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) {
|
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
|
// Update framebuffer information if requested
|
||||||
const s32 screen_id = (interrupt_id == InterruptId::PDC0) ? 0 : 1;
|
const s32 screen_id = (interrupt_id == InterruptId::PDC0) ? 0 : 1;
|
||||||
|
|
||||||
auto* info = GetFrameBufferInfo(thread_id, screen_id);
|
auto info = GetFrameBufferInfo(thread_id, screen_id);
|
||||||
if (info->is_dirty) {
|
if (info.first || force_buffer_swap[screen_id]) {
|
||||||
system.GPU().SetBufferSwap(screen_id, info->framebuffer_info[info->index]);
|
force_buffer_swap[screen_id] = false;
|
||||||
info->is_dirty.Assign(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 {
|
} 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.
|
// 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;
|
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) {
|
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) {
|
void GSP_GPU::TriggerCmdReqQueue(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(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();
|
auto& gpu = system.GPU();
|
||||||
|
|
||||||
bool requires_delay = false;
|
bool requires_delay = false;
|
||||||
@ -628,17 +708,18 @@ void GSP_GPU::ImportDisplayCaptureInfo(Kernel::HLERequestContext& ctx) {
|
|||||||
|
|
||||||
LOG_DEBUG(Service_GSP, "called");
|
LOG_DEBUG(Service_GSP, "called");
|
||||||
|
|
||||||
if (active_thread_id == std::numeric_limits<u32>::max()) {
|
auto top_screen_info = GetFrameBufferInfo(thread_id_with_rights, 0);
|
||||||
LOG_WARNING(Service_GSP, "Called without an active thread.");
|
auto bottom_screen_info = GetFrameBufferInfo(thread_id_with_rights, 1);
|
||||||
|
|
||||||
// TODO: Find the right error code.
|
if (top_screen_info.first) {
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
force_buffer_swap[0] = true;
|
||||||
rb.Push(-1);
|
}
|
||||||
return;
|
if (bottom_screen_info.first) {
|
||||||
|
force_buffer_swap[1] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
FrameBufferUpdate* top_screen = GetFrameBufferInfo(active_thread_id, 0);
|
auto top_screen = top_screen_info.second;
|
||||||
FrameBufferUpdate* bottom_screen = GetFrameBufferInfo(active_thread_id, 1);
|
auto bottom_screen = bottom_screen_info.second;
|
||||||
|
|
||||||
struct CaptureInfoEntry {
|
struct CaptureInfoEntry {
|
||||||
u32_le address_left;
|
u32_le address_left;
|
||||||
@ -649,16 +730,15 @@ void GSP_GPU::ImportDisplayCaptureInfo(Kernel::HLERequestContext& ctx) {
|
|||||||
|
|
||||||
CaptureInfoEntry top_entry, bottom_entry;
|
CaptureInfoEntry top_entry, bottom_entry;
|
||||||
// Top Screen
|
// Top Screen
|
||||||
top_entry.address_left = top_screen->framebuffer_info[top_screen->index].address_left;
|
top_entry.address_left = top_screen->address_left;
|
||||||
top_entry.address_right = top_screen->framebuffer_info[top_screen->index].address_right;
|
top_entry.address_right = top_screen->address_right;
|
||||||
top_entry.format = top_screen->framebuffer_info[top_screen->index].format;
|
top_entry.format = top_screen->format;
|
||||||
top_entry.stride = top_screen->framebuffer_info[top_screen->index].stride;
|
top_entry.stride = top_screen->stride;
|
||||||
// Bottom Screen
|
// Bottom Screen
|
||||||
bottom_entry.address_left = bottom_screen->framebuffer_info[bottom_screen->index].address_left;
|
bottom_entry.address_left = bottom_screen->address_left;
|
||||||
bottom_entry.address_right =
|
bottom_entry.address_right = bottom_screen->address_right;
|
||||||
bottom_screen->framebuffer_info[bottom_screen->index].address_right;
|
bottom_entry.format = bottom_screen->format;
|
||||||
bottom_entry.format = bottom_screen->framebuffer_info[bottom_screen->index].format;
|
bottom_entry.stride = bottom_screen->stride;
|
||||||
bottom_entry.stride = bottom_screen->framebuffer_info[bottom_screen->index].stride;
|
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(9, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(9, 0);
|
||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
@ -722,26 +802,19 @@ void GSP_GPU::SaveVramSysArea(Kernel::HLERequestContext& ctx) {
|
|||||||
// Invalid values
|
// Invalid values
|
||||||
0, 0, 0};
|
0, 0, 0};
|
||||||
|
|
||||||
if (active_thread_id == std::numeric_limits<u32>::max()) {
|
const auto session_data = GetSessionData(ctx.Session());
|
||||||
LOG_WARNING(Service_GSP, "Called without an active thread.");
|
if (False(session_data->relay_queue_flags & RelayEventQueueFlags::AllowSaveVramSysArea)) {
|
||||||
|
|
||||||
// TODO: Find the right error code.
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
rb.Push(-1);
|
rb.Push(ResultNoSaveVRAMSysAreaPerm);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
system.Memory().RasterizerFlushVirtualRegion(Memory::VRAM_VADDR, Memory::VRAM_SIZE,
|
vram_backup_handler.SaveVRAMWithState(VramBackupHandler::State::STATE_0);
|
||||||
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);
|
|
||||||
|
|
||||||
auto top_screen = GetFrameBufferInfo(active_thread_id, 0);
|
auto top_screen = GetFrameBufferInfo(thread_id_with_rights, 0).second;
|
||||||
if (top_screen) {
|
if (top_screen) {
|
||||||
u8 bytes_per_pixel =
|
u8 bytes_per_pixel = bpp_per_format[top_screen->GetPixelFormat()];
|
||||||
bpp_per_format[top_screen->framebuffer_info[top_screen->index].GetPixelFormat()];
|
const auto& top_fb = *top_screen;
|
||||||
const auto top_fb = top_screen->framebuffer_info[top_screen->index];
|
|
||||||
if (top_fb.address_left && bytes_per_pixel != 0 && bytes_per_pixel != 4) {
|
if (top_fb.address_left && bytes_per_pixel != 0 && bytes_per_pixel != 4) {
|
||||||
CopyFrameBuffer(system, FRAMEBUFFER_SAVE_AREA_TOP_LEFT, top_fb.address_left,
|
CopyFrameBuffer(system, FRAMEBUFFER_SAVE_AREA_TOP_LEFT, top_fb.address_left,
|
||||||
FRAMEBUFFER_WIDTH * bytes_per_pixel, top_fb.stride,
|
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);
|
FRAMEBUFFER_WIDTH * bytes_per_pixel, TOP_FRAMEBUFFER_HEIGHT);
|
||||||
}
|
}
|
||||||
|
|
||||||
FrameBufferInfo fb_info = top_screen->framebuffer_info[top_screen->index];
|
top_screen->address_left = FRAMEBUFFER_SAVE_AREA_TOP_LEFT;
|
||||||
|
top_screen->address_right = FRAMEBUFFER_SAVE_AREA_TOP_RIGHT;
|
||||||
fb_info.address_left = FRAMEBUFFER_SAVE_AREA_TOP_LEFT;
|
top_screen->stride = FRAMEBUFFER_WIDTH * bytes_per_pixel;
|
||||||
fb_info.address_right = FRAMEBUFFER_SAVE_AREA_TOP_RIGHT;
|
force_buffer_swap[0] = true;
|
||||||
fb_info.stride = FRAMEBUFFER_WIDTH * bytes_per_pixel;
|
|
||||||
system.GPU().SetBufferSwap(0, fb_info);
|
|
||||||
} else {
|
} else {
|
||||||
LOG_WARNING(Service_GSP, "No top screen bound, skipping capture.");
|
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) {
|
if (bottom_screen) {
|
||||||
u8 bytes_per_pixel =
|
u8 bytes_per_pixel = bpp_per_format[bottom_screen->GetPixelFormat()];
|
||||||
bpp_per_format[bottom_screen->framebuffer_info[bottom_screen->index].GetPixelFormat()];
|
const auto& bottom_fb = *bottom_screen;
|
||||||
const auto bottom_fb = bottom_screen->framebuffer_info[bottom_screen->index];
|
|
||||||
if (bottom_fb.address_left && bytes_per_pixel != 0 && bytes_per_pixel != 4) {
|
if (bottom_fb.address_left && bytes_per_pixel != 0 && bytes_per_pixel != 4) {
|
||||||
CopyFrameBuffer(system, FRAMEBUFFER_SAVE_AREA_BOTTOM, bottom_fb.address_left,
|
CopyFrameBuffer(system, FRAMEBUFFER_SAVE_AREA_BOTTOM, bottom_fb.address_left,
|
||||||
FRAMEBUFFER_WIDTH * bytes_per_pixel, bottom_fb.stride,
|
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,
|
ClearFramebuffer(system, FRAMEBUFFER_SAVE_AREA_BOTTOM,
|
||||||
FRAMEBUFFER_WIDTH * bytes_per_pixel, BOTTOM_FRAMEBUFFER_HEIGHT);
|
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;
|
bottom_screen->address_left = FRAMEBUFFER_SAVE_AREA_BOTTOM;
|
||||||
fb_info.stride = FRAMEBUFFER_WIDTH * bytes_per_pixel;
|
bottom_screen->stride = FRAMEBUFFER_WIDTH * bytes_per_pixel;
|
||||||
system.GPU().SetBufferSwap(1, fb_info);
|
force_buffer_swap[1] = true;
|
||||||
} else {
|
} else {
|
||||||
LOG_WARNING(Service_GSP, "No bottom screen bound, skipping capture.");
|
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);
|
// Wait for VBlank
|
||||||
rb.Push(ResultSuccess);
|
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) {
|
void GSP_GPU::RestoreVramSysArea(Kernel::HLERequestContext& ctx) {
|
||||||
@ -805,31 +877,35 @@ void GSP_GPU::RestoreVramSysArea(Kernel::HLERequestContext& ctx) {
|
|||||||
|
|
||||||
LOG_DEBUG(Service_GSP, "called");
|
LOG_DEBUG(Service_GSP, "called");
|
||||||
|
|
||||||
if (saved_vram) {
|
const auto session_data = GetSessionData(ctx.Session());
|
||||||
auto vram = system.Memory().GetPointer(Memory::VRAM_VADDR);
|
if (False(session_data->relay_queue_flags & RelayEventQueueFlags::AllowSaveVramSysArea)) {
|
||||||
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 (?).
|
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
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,
|
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().vs_setup.requires_fixup = requires_shader_fixup;
|
||||||
gpu.PicaCore().gs_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,
|
return {ErrorDescription::AlreadyDone, ErrorModule::GX, ErrorSummary::Success,
|
||||||
ErrorLevel::Success};
|
ErrorLevel::Success};
|
||||||
}
|
}
|
||||||
@ -870,14 +946,14 @@ Result GSP_GPU::AcquireGpuRight(const Kernel::HLERequestContext& ctx,
|
|||||||
|
|
||||||
if (blocking) {
|
if (blocking) {
|
||||||
// TODO: The thread should be put to sleep until acquired.
|
// 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.");
|
"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,
|
return {ErrorDescription::Busy, ErrorModule::GX, ErrorSummary::WouldBlock,
|
||||||
ErrorLevel::Status};
|
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;
|
active_client_thread_id = ctx.ClientThread()->thread_id;
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
@ -904,16 +980,17 @@ void GSP_GPU::AcquireRight(Kernel::HLERequestContext& ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GSP_GPU::ReleaseRight(const SessionData* session_data) {
|
void GSP_GPU::ReleaseRight(const SessionData* session_data) {
|
||||||
ASSERT_MSG(active_thread_id == session_data->thread_id,
|
if (thread_id_with_rights == session_data->thread_id) {
|
||||||
"Wrong thread tried to release GPU right");
|
thread_id_with_rights = std::numeric_limits<u32>::max();
|
||||||
active_thread_id = std::numeric_limits<u32>::max();
|
|
||||||
active_client_thread_id = std::numeric_limits<u32>::max();
|
active_client_thread_id = std::numeric_limits<u32>::max();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GSP_GPU::ReleaseRight(Kernel::HLERequestContext& ctx) {
|
void GSP_GPU::ReleaseRight(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx);
|
IPC::RequestParser rp(ctx);
|
||||||
|
|
||||||
const SessionData* session_data = GetSessionData(ctx.Session());
|
const SessionData* session_data = GetSessionData(ctx.Session());
|
||||||
|
// Success even if wrong thread calls this function.
|
||||||
ReleaseRight(session_data);
|
ReleaseRight(session_data);
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
@ -976,14 +1053,16 @@ void GSP_GPU::serialize(Archive& ar, const unsigned int) {
|
|||||||
DEBUG_SERIALIZATION_POINT;
|
DEBUG_SERIALIZATION_POINT;
|
||||||
ar& boost::serialization::base_object<Kernel::SessionRequestHandler>(*this);
|
ar& boost::serialization::base_object<Kernel::SessionRequestHandler>(*this);
|
||||||
ar & shared_memory;
|
ar & shared_memory;
|
||||||
ar & active_thread_id;
|
ar & thread_id_with_rights;
|
||||||
ar & active_client_thread_id;
|
ar & active_client_thread_id;
|
||||||
ar & first_initialization;
|
ar & first_initialization;
|
||||||
ar & used_thread_ids;
|
ar & used_thread_ids;
|
||||||
ar & saved_vram;
|
|
||||||
ar & delay_texture_copy_completion;
|
ar & delay_texture_copy_completion;
|
||||||
ar & pending_interrupts;
|
ar & pending_interrupts;
|
||||||
ar & perf_recorder;
|
ar & perf_recorder;
|
||||||
|
ar & cached_framebuffer_infos;
|
||||||
|
ar & force_buffer_swap;
|
||||||
|
ar & vram_backup_handler;
|
||||||
}
|
}
|
||||||
SERIALIZE_IMPL(GSP_GPU)
|
SERIALIZE_IMPL(GSP_GPU)
|
||||||
|
|
||||||
@ -1051,6 +1130,8 @@ void SessionData::serialize(Archive& ar, const unsigned int) {
|
|||||||
ar & interrupt_event;
|
ar & interrupt_event;
|
||||||
ar & thread_id;
|
ar & thread_id;
|
||||||
ar & registered;
|
ar & registered;
|
||||||
|
ar & relay_queue_flags;
|
||||||
|
ar & on_vblank_event;
|
||||||
}
|
}
|
||||||
SERIALIZE_IMPL(SessionData)
|
SERIALIZE_IMPL(SessionData)
|
||||||
|
|
||||||
@ -1067,4 +1148,64 @@ SessionData::~SessionData() {
|
|||||||
gsp->used_thread_ids[thread_id] = false;
|
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
|
} // 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_command.h"
|
||||||
#include "core/hle/service/gsp/gsp_interrupt.h"
|
#include "core/hle/service/gsp/gsp_interrupt.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
|
#include "core/memory.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
class System;
|
class System;
|
||||||
@ -45,6 +46,17 @@ struct FrameBufferInfo {
|
|||||||
u32 GetPixelFormat() {
|
u32 GetPixelFormat() {
|
||||||
return format & PIXEL_FORMAT_MASK;
|
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");
|
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,
|
static_assert(offsetof(FrameBufferUpdate, framebuffer_info[1]) == 0x20,
|
||||||
"FrameBufferInfo element has incorrect alignment");
|
"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 = 240;
|
||||||
constexpr u32 FRAMEBUFFER_WIDTH_POW2 = 256;
|
constexpr u32 FRAMEBUFFER_WIDTH_POW2 = 256;
|
||||||
constexpr u32 TOP_FRAMEBUFFER_HEIGHT = 400;
|
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_TOP_RIGHT = Memory::VRAM_VADDR + 0x2B9800;
|
||||||
constexpr VAddr FRAMEBUFFER_SAVE_AREA_BOTTOM = Memory::VRAM_VADDR + 0x4C7800;
|
constexpr VAddr FRAMEBUFFER_SAVE_AREA_BOTTOM = Memory::VRAM_VADDR + 0x4C7800;
|
||||||
|
|
||||||
|
constexpr size_t VRAM_BACKUP_BANK_SIZE = 0x80000;
|
||||||
|
|
||||||
class GSP_GPU;
|
class GSP_GPU;
|
||||||
|
|
||||||
class SessionData : public Kernel::SessionRequestHandler::SessionDataBase {
|
class SessionData : public Kernel::SessionRequestHandler::SessionDataBase {
|
||||||
@ -88,6 +116,10 @@ public:
|
|||||||
u32 thread_id;
|
u32 thread_id;
|
||||||
/// Whether RegisterInterruptRelayQueue was called for this session
|
/// Whether RegisterInterruptRelayQueue was called for this session
|
||||||
bool registered = false;
|
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:
|
private:
|
||||||
template <class Archive>
|
template <class Archive>
|
||||||
@ -95,6 +127,68 @@ private:
|
|||||||
friend class boost::serialization::access;
|
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> {
|
class GSP_GPU final : public ServiceFramework<GSP_GPU, SessionData> {
|
||||||
public:
|
public:
|
||||||
explicit GSP_GPU(Core::System& system);
|
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
|
* @param thread_id GSP thread id of the process that accesses the structure that we are
|
||||||
* requesting.
|
* requesting.
|
||||||
* @param screen_index Index of the screen we are requesting (Top = 0, Bottom = 1).
|
* @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
|
/// Gets a pointer to a thread command buffer in GSP shared memory
|
||||||
CommandBuffer* GetCommandBuffer(u32 thread_id);
|
CommandBuffer* GetCommandBuffer(u32 thread_id);
|
||||||
@ -128,7 +223,7 @@ public:
|
|||||||
* Retreives the ID of the thread with GPU rights.
|
* Retreives the ID of the thread with GPU rights.
|
||||||
*/
|
*/
|
||||||
u32 GetActiveThreadId() {
|
u32 GetActiveThreadId() {
|
||||||
return active_thread_id;
|
return thread_id_with_rights;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -138,6 +233,8 @@ public:
|
|||||||
return active_client_thread_id;
|
return active_client_thread_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ThreadCallback;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* Signals that the specified interrupt type has occurred to userland code for the specified GSP
|
* 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;
|
std::shared_ptr<Kernel::SharedMemory> shared_memory;
|
||||||
|
|
||||||
/// Thread id that currently has GPU rights or std::numeric_limits<u32>::max() if none.
|
/// 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
|
/// Thread id of the client thread that has GPU rights
|
||||||
u32 active_client_thread_id = std::numeric_limits<u32>::max();
|
u32 active_client_thread_id = std::numeric_limits<u32>::max();
|
||||||
|
|
||||||
bool first_initialization = true;
|
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.
|
/// Maximum number of threads that can be registered at the same time in the GSP module.
|
||||||
static constexpr u32 MaxGSPThreads = 4;
|
static constexpr u32 MaxGSPThreads = 4;
|
||||||
|
|
||||||
@ -527,6 +621,11 @@ private:
|
|||||||
|
|
||||||
Core::TimingEventType* SignalInterruptEventType = nullptr;
|
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;
|
friend class SessionData;
|
||||||
|
|
||||||
template <class Archive>
|
template <class Archive>
|
||||||
@ -539,3 +638,4 @@ private:
|
|||||||
BOOST_CLASS_EXPORT_KEY(Service::GSP::SessionData)
|
BOOST_CLASS_EXPORT_KEY(Service::GSP::SessionData)
|
||||||
BOOST_CLASS_EXPORT_KEY(Service::GSP::GSP_GPU)
|
BOOST_CLASS_EXPORT_KEY(Service::GSP::GSP_GPU)
|
||||||
SERVICE_CONSTRUCT(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) {
|
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
|
// Signal to GSP that GPU interrupt has occurred
|
||||||
impl->signal_interrupt(Service::GSP::InterruptId::PDC0, 0);
|
impl->signal_interrupt(Service::GSP::InterruptId::PDC0, 0);
|
||||||
impl->signal_interrupt(Service::GSP::InterruptId::PDC1, 0);
|
impl->signal_interrupt(Service::GSP::InterruptId::PDC1, 0);
|
||||||
|
|
||||||
|
// Present renderered frame.
|
||||||
|
impl->renderer->SwapBuffers();
|
||||||
|
|
||||||
// Reschedule recurrent event
|
// Reschedule recurrent event
|
||||||
impl->timing.ScheduleEvent(FRAME_TICKS - cycles_late, impl->vblank_event);
|
impl->timing.ScheduleEvent(FRAME_TICKS - cycles_late, impl->vblank_event);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user