citra/src/core/hle/service/gsp/gsp_gpu.h

404 lines
13 KiB
C++

// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <cstddef>
#include <memory>
#include <string>
#include <boost/optional/optional.hpp>
#include <boost/serialization/export.hpp>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/gsp/gsp_command.h"
#include "core/hle/service/gsp/gsp_interrupt.h"
#include "core/hle/service/service.h"
namespace Core {
class System;
}
namespace Kernel {
class HLERequestContext;
class Process;
class SharedMemory;
} // namespace Kernel
namespace Service::GSP {
struct FrameBufferInfo {
u32 active_fb; // 0 = first, 1 = second
u32 address_left;
u32 address_right;
u32 stride; // maps to 0x1EF00X90 ?
u32 format; // maps to 0x1EF00X70 ?
u32 shown_fb; // maps to 0x1EF00X78 ?
u32 unknown;
};
static_assert(sizeof(FrameBufferInfo) == 0x1c, "Struct has incorrect size");
struct FrameBufferUpdate {
BitField<0, 1, u8> index; // Index used for GSP::SetBufferSwap
BitField<0, 1, u8> is_dirty; // true if GSP should update GPU framebuffer registers
u16 pad1;
FrameBufferInfo framebuffer_info[2];
u32 pad2;
};
static_assert(sizeof(FrameBufferUpdate) == 0x40, "Struct has incorrect size");
static_assert(offsetof(FrameBufferUpdate, framebuffer_info[1]) == 0x20,
"FrameBufferInfo element has incorrect alignment");
constexpr u32 FRAMEBUFFER_WIDTH = 240;
constexpr u32 FRAMEBUFFER_WIDTH_POW2 = 256;
constexpr u32 TOP_FRAMEBUFFER_HEIGHT = 400;
constexpr u32 BOTTOM_FRAMEBUFFER_HEIGHT = 320;
constexpr u32 FRAMEBUFFER_HEIGHT_POW2 = 512;
// These are the VRAM addresses that GSP copies framebuffers to in SaveVramSysArea.
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;
class GSP_GPU;
class SessionData : public Kernel::SessionRequestHandler::SessionDataBase {
public:
SessionData() = default;
SessionData(GSP_GPU* gsp);
~SessionData();
GSP_GPU* gsp;
/// Event triggered when GSP interrupt has been signalled
std::shared_ptr<Kernel::Event> interrupt_event;
/// Thread index into interrupt relay queue
u32 thread_id;
/// Whether RegisterInterruptRelayQueue was called for this session
bool registered = false;
private:
template <class Archive>
void serialize(Archive& ar, const unsigned int);
friend class boost::serialization::access;
};
class GSP_GPU final : public ServiceFramework<GSP_GPU, SessionData> {
public:
explicit GSP_GPU(Core::System& system);
~GSP_GPU() = default;
void ClientDisconnected(std::shared_ptr<Kernel::ServerSession> server_session) override;
/**
* Signals that the specified interrupt type has occurred to userland code
* @param interrupt_id ID of interrupt that is being signalled
*/
void SignalInterrupt(InterruptId interrupt_id);
/**
* Retrieves the framebuffer info stored in the GSP shared memory for the
* specified screen index and thread id.
* @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.
*/
FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index);
/// Gets a pointer to a thread command buffer in GSP shared memory
CommandBuffer* GetCommandBuffer(u32 thread_id);
/// Gets a pointer to the interrupt relay queue for a given thread index
InterruptRelayQueue* GetInterruptRelayQueue(u32 thread_id);
/**
* Retreives the ID of the thread with GPU rights.
*/
u32 GetActiveThreadId() {
return active_thread_id;
}
private:
/**
* Signals that the specified interrupt type has occurred to userland code for the specified GSP
* thread id.
* @param interrupt_id ID of interrupt that is being signalled.
* @param thread_id GSP thread that will receive the interrupt.
*/
void SignalInterruptForThread(InterruptId interrupt_id, u32 thread_id);
/**
* GSP_GPU::WriteHWRegs service function
*
* Writes sequential GSP GPU hardware registers
*
* Inputs:
* 1 : address of first GPU register
* 2 : number of registers to write sequentially
* 4 : pointer to source data array
*/
void WriteHWRegs(Kernel::HLERequestContext& ctx);
/**
* GSP_GPU::WriteHWRegsWithMask service function
*
* Updates sequential GSP GPU hardware registers using masks
*
* Inputs:
* 1 : address of first GPU register
* 2 : number of registers to update sequentially
* 4 : pointer to source data array
* 6 : pointer to mask array
*/
void WriteHWRegsWithMask(Kernel::HLERequestContext& ctx);
/// Read a GSP GPU hardware register
void ReadHWRegs(Kernel::HLERequestContext& ctx);
/**
* GSP_GPU::SetBufferSwap service function
*
* Updates GPU display framebuffer configuration using the specified parameters.
*
* Inputs:
* 1 : Screen ID (0 = top screen, 1 = bottom screen)
* 2-7 : FrameBufferInfo structure
* Outputs:
* 1: Result code
*/
void SetBufferSwap(Kernel::HLERequestContext& ctx);
/**
* GSP_GPU::FlushDataCache service function
*
* This Function is a no-op, We aren't emulating the CPU cache any time soon.
*
* Inputs:
* 1 : Address
* 2 : Size
* 3 : Value 0, some descriptor for the KProcess Handle
* 4 : KProcess handle
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void FlushDataCache(Kernel::HLERequestContext& ctx);
/**
* GSP_GPU::InvalidateDataCache service function
*
* This Function is a no-op, We aren't emulating the CPU cache any time soon.
*
* Inputs:
* 1 : Address
* 2 : Size
* 3 : Value 0, some descriptor for the KProcess Handle
* 4 : KProcess handle
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void InvalidateDataCache(Kernel::HLERequestContext& ctx);
/**
* GSP_GPU::SetLcdForceBlack service function
*
* Enable or disable REG_LCDCOLORFILL with the color black.
*
* Inputs:
* 1: Black color fill flag (0 = don't fill, !0 = fill)
* Outputs:
* 1: Result code
*/
void SetLcdForceBlack(Kernel::HLERequestContext& ctx);
/// This triggers handling of the GX command written to the command buffer in shared memory.
void TriggerCmdReqQueue(Kernel::HLERequestContext& ctx);
/**
* GSP_GPU::SetAxiConfigQoSMode service function
* Inputs:
* 1 : Mode, unused in emulator
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void SetAxiConfigQoSMode(Kernel::HLERequestContext& ctx);
/**
* GSP_GPU::RegisterInterruptRelayQueue service function
* Inputs:
* 1 : "Flags" field, purpose is unknown
* 3 : Handle to GSP synchronization event
* Outputs:
* 1 : Result of function, 0x2A07 on success, otherwise error code
* 2 : Thread index into GSP command buffer
* 4 : Handle to GSP shared memory
*/
void RegisterInterruptRelayQueue(Kernel::HLERequestContext& ctx);
/**
* GSP_GPU::UnregisterInterruptRelayQueue service function
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void UnregisterInterruptRelayQueue(Kernel::HLERequestContext& ctx);
/**
* GSP_GPU::TryAcquireRight service function
* Inputs:
* 0 : Header code [0x00150002]
* 1 : Handle translate header (0x0)
* 2 : Process handle
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void TryAcquireRight(Kernel::HLERequestContext& ctx);
/**
* GSP_GPU::AcquireRight service function
* Inputs:
* 0 : Header code [0x00160042]
* 1 : Flags
* 2 : Handle translate header (0x0)
* 3 : Process handle
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void AcquireRight(Kernel::HLERequestContext& ctx);
/**
* GSP_GPU::ReleaseRight service function
* Outputs:
* 1: Result code
*/
void ReleaseRight(Kernel::HLERequestContext& ctx);
/**
* Releases rights to the GPU.
* Will fail if the session_data doesn't have the GPU right
*/
void ReleaseRight(const SessionData* session_data);
/**
* GSP_GPU::ImportDisplayCaptureInfo service function
*
* Returns information about the current framebuffer state
*
* Inputs:
* 0: Header 0x00180000
* Outputs:
* 0: Header Code[0x00180240]
* 1: Result code
* 2: Left framebuffer virtual address for the main screen
* 3: Right framebuffer virtual address for the main screen
* 4: Main screen framebuffer format
* 5: Main screen framebuffer width
* 6: Left framebuffer virtual address for the bottom screen
* 7: Right framebuffer virtual address for the bottom screen
* 8: Bottom screen framebuffer format
* 9: Bottom screen framebuffer width
*/
void ImportDisplayCaptureInfo(Kernel::HLERequestContext& ctx);
/**
* GSP_GPU::SaveVramSysArea service function
*
* Returns information about the current framebuffer state
*
* Inputs:
* 0: Header 0x00190000
* Outputs:
* 0: Header Code[0x00190040]
* 1: Result code
*/
void SaveVramSysArea(Kernel::HLERequestContext& ctx);
/**
* GSP_GPU::RestoreVramSysArea service function
*
* Returns information about the current framebuffer state
*
* Inputs:
* 0: Header 0x001A0000
* Outputs:
* 0: Header Code[0x001A0040]
* 1: Result code
*/
void RestoreVramSysArea(Kernel::HLERequestContext& ctx);
/**
* GSP_GPU::StoreDataCache service function
*
* This Function is a no-op, We aren't emulating the CPU cache any time soon.
*
* Inputs:
* 0 : Header code [0x001F0082]
* 1 : Address
* 2 : Size
* 3 : Value 0, some descriptor for the KProcess Handle
* 4 : KProcess handle
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void StoreDataCache(Kernel::HLERequestContext& ctx);
/// Force the 3D LED State (0 = On, Non-Zero = Off)
void SetLedForceOff(Kernel::HLERequestContext& ctx);
/**
* GSP_GPU::SetInternalPriorities service function
* Inputs:
* 0 : Header code [0x001E0080]
* 1 : Session thread priority
* 2 : Session thread priority with rights
* Outputs:
* 1 : Result of function, 0 on success, otherwise error code
*/
void SetInternalPriorities(Kernel::HLERequestContext& ctx);
/// Returns the session data for the specified registered thread id, or nullptr if not found.
SessionData* FindRegisteredThreadData(u32 thread_id);
u32 GetUnusedThreadId() const;
std::unique_ptr<Kernel::SessionRequestHandler::SessionDataBase> MakeSessionData() override;
ResultCode AcquireGpuRight(const Kernel::HLERequestContext& ctx,
const std::shared_ptr<Kernel::Process>& process, u32 flag,
bool blocking);
Core::System& system;
/// GSP shared memory
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();
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;
/// Thread ids currently in use by the sessions connected to the GSPGPU service.
std::array<bool, MaxGSPThreads> used_thread_ids{};
friend class SessionData;
template <class Archive>
void serialize(Archive& ar, const unsigned int);
friend class boost::serialization::access;
};
} // namespace Service::GSP
BOOST_CLASS_EXPORT_KEY(Service::GSP::SessionData)
BOOST_CLASS_EXPORT_KEY(Service::GSP::GSP_GPU)
SERVICE_CONSTRUCT(Service::GSP::GSP_GPU)