aboutsummaryrefslogtreecommitdiff
path: root/src/core/hle/service/gsp/gsp_gpu.h
blob: e1bc5eb4fd56f01601d02001420659bbc9810e40 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
// 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;

    Result 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)