Nugget
Bare-metal libraries and examples for the original PlayStation
Loading...
Searching...
No Matches
gpu.hh
Go to the documentation of this file.
1/*
2
3MIT License
4
5Copyright (c) 2022 PCSX-Redux authors
6
7Permission is hereby granted, free of charge, to any person obtaining a copy
8of this software and associated documentation files (the "Software"), to deal
9in the Software without restriction, including without limitation the rights
10to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11copies of the Software, and to permit persons to whom the Software is
12furnished to do so, subject to the following conditions:
13
14The above copyright notice and this permission notice shall be included in all
15copies or substantial portions of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23SOFTWARE.
24
25*/
26
27#pragma once
28
29#include <EASTL/array.h>
30#include <EASTL/atomic.h>
31#include <EASTL/fixed_list.h>
32#include <EASTL/functional.h>
33#include <EASTL/utility.h>
34#include <stdint.h>
35
36#include <coroutine>
37
39#include "psyqo/hardware/gpu.hh"
40#include "psyqo/kernel.hh"
46#include "psyqo/shared.hh"
47
48// The vsync counter. The VBlank interrupt increments it: the takeover exception handler
49// (vector.s) directly, the BIOS event through GPU.
50extern "C" uint32_t psyqoFrameCounter;
51
52namespace psyqo {
53
54namespace timer_literals {
55
64consteval uint32_t operator""_ns(unsigned long long int value) { return value / 1'000; }
65consteval uint32_t operator""_us(unsigned long long int value) { return value; }
66consteval uint32_t operator""_ms(unsigned long long int value) { return value * 1'000; }
67consteval uint32_t operator""_s(unsigned long long int value) { return value * 1'000'000; }
68consteval uint32_t operator""_ns(long double value) { return value / 1'000; }
69consteval uint32_t operator""_us(long double value) { return value; }
70consteval uint32_t operator""_ms(long double value) { return value * 1'000; }
71consteval uint32_t operator""_s(long double value) { return value * 1'000'000; }
72
73} // namespace timer_literals
74
83class GPU {
84 struct TimerAwaiter {
85 TimerAwaiter(GPU &gpu, uint32_t deadline) : m_gpu(gpu), m_deadline(deadline) {}
86 ~TimerAwaiter() {}
87 constexpr bool await_ready() const { return false; }
88 void await_suspend(std::coroutine_handle<> handle) {
89 m_gpu.armTimer(m_deadline, [handle](uint32_t) { handle.resume(); });
90 }
91 void await_resume() {}
92 GPU &m_gpu;
93 uintptr_t m_deadline;
94 };
95
96 public:
97 struct Configuration;
98 enum class Resolution { W256, W320, W368, W512, W640 };
99 enum class VideoMode { AUTO, NTSC, PAL };
100 enum class ColorMode { C15BITS, C24BITS };
103 void initialize(const Configuration &config);
104 void reinitialize(const Configuration &config);
105
106 static constexpr uint32_t US_PER_HBLANK = 64;
107 static constexpr unsigned c_chainThreshold = 56;
108
117 static constexpr unsigned c_oversizedGranularity = 16;
121 static constexpr unsigned c_maxOversizedWords = 255 * c_oversizedGranularity;
122
129 unsigned getRefreshRate() const { return m_refreshRate; }
130
143 uint32_t getFrameCount() const { return m_previousFrameCount; }
144
152 uint32_t getLiveFrameCount() const {
153 eastl::atomic_signal_fence(eastl::memory_order_acquire);
154 return *const_cast<const volatile uint32_t *>(&psyqoFrameCounter);
155 }
156
170 unsigned getParity() const { return m_parity; }
171
179 void clear(Color bg = {{0, 0, 0}});
180
191 void getClear(Prim::FastFill &ff, Color bg = {{0, 0, 0}}) const;
192
204 void getNextClear(Prim::FastFill &ff, Color bg = {{0, 0, 0}}) const;
205
217 void uploadToVRAM(const uint16_t *data, Rect region);
218
242 void uploadToVRAM(const uint16_t *data, Rect region, eastl::function<void()> &&callback,
244
251 template <Fragment Frag>
252 void sendFragment(const Frag &fragment) {
253 sendFragment(reinterpret_cast<const uint32_t *>(&fragment.head + 1), fragment.getActualFragmentSize());
254 }
255
265 template <Fragment Frag>
266 void sendFragment(const Frag &fragment, eastl::function<void()> &&callback,
268 sendFragment(reinterpret_cast<const uint32_t *>(&fragment.head + 1), fragment.getActualFragmentSize(),
269 eastl::move(callback), dmaCallback);
270 }
271
275 void disableScissor();
276
283 void enableScissor();
284
292 void getScissor(Prim::Scissor &scissor);
293
302 void getNextScissor(Prim::Scissor &scissor);
303
307 void waitReady();
308
312 void waitFifo();
313
317 static void sendRaw(uint32_t data) { Hardware::GPU::Data = data; }
318
325 template <Primitive Prim>
326 void sendPrimitive(const Prim &primitive) {
327 waitReady();
328 const uint32_t *ptr = reinterpret_cast<const uint32_t *>(&primitive);
329 constexpr size_t size = sizeof(Prim) / sizeof(uint32_t);
330 for (int i = 0; i < size; i++) {
331 if constexpr (sizeof(Prim) > c_chainThreshold) waitFifo();
332 sendRaw(*ptr++);
333 }
334 }
335
349 template <Fragment Frag>
350 void chain(Frag &fragment) {
351 chain(&fragment.head, &fragment.head, fragment.getActualFragmentSize());
352 }
353
363 template <Fragment Frag1, Fragment Frag2>
364 void chain(Frag1 *first, Frag2 *last) {
365 auto count = last->getActualFragmentSize();
366 Kernel::assert(count <= (c_chainThreshold / 4), "Last element of the chain is too big");
367 chain(&first->head, &last->head, last->getActualFragmentSize());
368 }
369
378 template <size_t N, Safe safety = Safe::Yes>
380 chain(&table.m_table[N].head, &table.m_table[0].head, 0);
381 scheduleOTC(&table.m_table[N].head, N + 1);
382 }
383
390 template <Safe safety = Safe::Yes>
392 size_t n = table.size();
393 chain(&table.m_table[n].head, &table.m_table[0].head, 0);
394 scheduleOTC(&table.m_table[n].head, n + 1);
395 }
396
402 void sendChain();
403
411 void sendChain(eastl::function<void()> &&callback, DMA::DmaCallback dmaCallback = DMA::FROM_MAIN_LOOP);
412
418 bool isChainIdle() const;
419
425 bool isChainTransferring() const;
426
432 bool isChainTransferred() const;
433
440 }
441
459 uint32_t now() const { return m_currentTime; }
460
478 uintptr_t armTimer(uint32_t deadline, eastl::function<void(uint32_t)> &&callback);
479
491 TimerAwaiter delay(uint32_t microseconds) { return {*this, now() + microseconds}; }
492
504 uintptr_t armPeriodicTimer(uint32_t period, eastl::function<void(uint32_t)> &&callback);
505
520 void changeTimerPeriod(uintptr_t id, uint32_t period, bool reset = false);
521
531 void pauseTimer(uintptr_t id);
532
542 void resumeTimer(uintptr_t id);
543
551 void cancelTimer(uintptr_t id);
552
561 void pumpCallbacks();
562
563 private:
564 // An oversized node's header stores size >> c_oversizedShift, and bit 0 of the link pointing at
565 // it flags it. That link also carries bit 23, so the DMA engine stops there and never reads the
566 // node; the engine ignores the low two bits of a link while MADR keeps them, so the interrupt
567 // handler sees the flag. tests/dma/dma.c covers that in linked_dma_800001_terminator and
568 // linked_dma_odd_terminator.
569 static constexpr unsigned c_oversizedShift = 4;
570 static constexpr unsigned c_maxNodeWords = 255;
571 static_assert((1u << c_oversizedShift) == c_oversizedGranularity);
572 GPU();
573 GPU(const GPU &) = delete;
574 GPU(GPU &&) = delete;
575 GPU &operator=(const GPU &) = delete;
576 GPU &operator=(GPU &&) = delete;
577 void sendFragment(const uint32_t *data, size_t count);
578 void sendFragment(const uint32_t *data, size_t count, eastl::function<void()> &&callback,
579 DMA::DmaCallback dmaCallback);
580 void scheduleNormalDMA(uintptr_t data, size_t count);
581 void scheduleChainedDMA(uintptr_t head);
582 void chain(uintptr_t *first, uintptr_t *last, size_t count);
583 void scheduleOTC(uintptr_t *start, uint32_t count);
584 void checkOTCAndTriggerCallback();
585 void applyConfiguration(const Configuration &config);
586 void prepareForTakeover();
587
588 eastl::function<void(void)> m_dmaCallback = nullptr;
589 unsigned m_refreshRate = 0;
590 int m_width = 0;
591 int m_height = 0;
592 uint32_t m_currentTime = 0;
593 uint32_t m_previousFrameCount = 0;
594 // Value of psyqoFrameCounter during the last 480i field GPUSTAT.31 was seen set in.
595 uint32_t m_lastLongFieldFrame = 0x80000000;
596 unsigned m_parity = 0;
597 uintptr_t *m_chainHead = nullptr;
598 uintptr_t *m_chainTail = nullptr;
599 size_t m_chainTailCount = 0;
600 enum { CHAIN_IDLE, CHAIN_TRANSFERRING, CHAIN_TRANSFERRED } m_chainStatus = CHAIN_IDLE;
601 struct Timer {
602 eastl::function<void(uint32_t)> callback;
603 uint32_t deadline;
604 uint32_t period;
605 int32_t pausedRemaining;
606 bool periodic;
607 bool paused = false;
608 };
609 eastl::fixed_list<Timer, 32> m_timers;
610 struct ScheduledOTC {
611 uintptr_t *start;
612 uint32_t count;
613 };
614 eastl::fixed_list<ScheduledOTC, 32> m_OTCs[2];
615 uintptr_t *m_chainNext = nullptr;
616
617 uint16_t m_lastHSyncCounter = 0;
618 bool m_interlaced = false;
619 bool m_fromISR = false;
620 bool m_flushCacheAfterDMA = false;
621 // Whether the node m_chainNext points at is an oversized packet. The flag lives in bit 0 of the
622 // link that points at it, so it has to be carried across the state machine rather than re-read.
623 bool m_chainNextOversized = false;
624 bool m_chainHeadOversized = false;
625
626 void flip();
627 friend class Application;
629};
630
631} // namespace psyqo
632
uint32_t start
Definition cdlgetlocp.c:252
The application class.
Definition application.hh:49
An ordering table whose bucket count is chosen at runtime.
Definition ordering-table.hh:128
size_t size() const
Definition ordering-table.hh:158
psyqo::Fragments::ChainEntry * m_table
Definition ordering-table.hh:161
The singleton GPU class.
Definition gpu.hh:83
bool isChainIdle() const
Gets the status of the background DMA transfer operation when initiated by a frame flip.
Definition gpu.cpp:582
unsigned getParity() const
Get the index of the current display buffer.
Definition gpu.hh:170
void sendFragment(const Frag &fragment)
Immediately sends a fragment to the GPU. This is a blocking operation. See the fragments....
Definition gpu.hh:252
static constexpr uint32_t US_PER_HBLANK
Definition gpu.hh:106
void sendPrimitive(const Prim &primitive)
Sends a primitive to the GPU. This is a blocking call.
Definition gpu.hh:326
void waitChainIdle()
Waits until the background DMA transfer operation initiated by a frame flip is complete.
Definition gpu.hh:438
uint32_t getFrameCount() const
Returns the number of frames rendered by the GPU so far.
Definition gpu.hh:143
Interlace
Definition gpu.hh:101
uintptr_t armTimer(uint32_t deadline, eastl::function< void(uint32_t)> &&callback)
Creates a single-use timer.
Definition gpu.cpp:597
void sendFragment(const Frag &fragment, eastl::function< void()> &&callback, DMA::DmaCallback dmaCallback=DMA::FROM_MAIN_LOOP)
Sends a fragment to the GPU as a non-blocking call.
Definition gpu.hh:266
void disableScissor()
Immediately disables the scissoring of the VRAM.
Definition gpu.cpp:339
void chain(DynamicOrderingTable< safety > &table)
Chains a runtime-sized ordering table to the next DMA chain transfer.
Definition gpu.hh:391
static constexpr unsigned c_maxOversizedWords
Largest fragment chain accepts, in words.
Definition gpu.hh:121
void getClear(Prim::FastFill &ff, Color bg={{0, 0, 0}}) const
Sets a FastFill primitive to clear the current drawing buffer.
Definition gpu.cpp:379
void chain(Frag1 *first, Frag2 *last)
Chains an already constructed DMA chain to the next DMA chain transfer.
Definition gpu.hh:364
TimerAwaiter delay(uint32_t microseconds)
Delays the coroutine for a specified amount of time.
Definition gpu.hh:491
uintptr_t armPeriodicTimer(uint32_t period, eastl::function< void(uint32_t)> &&callback)
Creates a periodic timer.
Definition gpu.cpp:602
void enableScissor()
Enables the scissoring of the VRAM.
Definition gpu.cpp:344
void waitReady()
Waits until the GPU is ready to send a command.
Definition gpu.cpp:41
void getNextScissor(Prim::Scissor &scissor)
Gets the next scissoring region.
Definition gpu.cpp:361
void reinitialize(const Configuration &config)
Definition gpu.cpp:72
uint32_t now() const
Gets the current timestamp in microseconds.
Definition gpu.hh:459
void chain(OrderingTable< N, safety > &table)
Chains an ordering table to the next DMA chain transfer.
Definition gpu.hh:379
void initialize(const Configuration &config)
Definition gpu.cpp:148
unsigned getRefreshRate() const
Returns the refresh rate of the GPU.
Definition gpu.hh:129
MiscSetting
Definition gpu.hh:102
void sendChain()
Immediately sends the current DMA chain.
Definition gpu.cpp:528
ColorMode
Definition gpu.hh:100
void changeTimerPeriod(uintptr_t id, uint32_t period, bool reset=false)
Changes the period of a periodic timer.
Definition gpu.cpp:607
void pauseTimer(uintptr_t id)
Pauses a timer.
Definition gpu.cpp:623
void pumpCallbacks()
Runs one round of event processing.
Definition gpu.cpp:651
Resolution
Definition gpu.hh:98
void cancelTimer(uintptr_t id)
Cancels a timer.
Definition gpu.cpp:643
bool isChainTransferred() const
Gets the status of the background DMA transfer operation when initiated by a frame flip.
Definition gpu.cpp:592
VideoMode
Definition gpu.hh:99
void uploadToVRAM(const uint16_t *data, Rect region)
Uploads a buffer to the VRAM as a blocking call.
Definition gpu.cpp:397
void getNextClear(Prim::FastFill &ff, Color bg={{0, 0, 0}}) const
Sets a FastFill primitive to clear the next drawing buffer.
Definition gpu.cpp:387
static void sendRaw(uint32_t data)
Sends a raw 32 bits value to the Data register of the GPU.
Definition gpu.hh:317
uint32_t getLiveFrameCount() const
Returns the number of vsyncs since the GPU was initialized, as of now.
Definition gpu.hh:152
void waitFifo()
Waits until the GPU's FIFO is ready to receive data.
Definition gpu.cpp:47
void resumeTimer(uintptr_t id)
Resumes a paused timer.
Definition gpu.cpp:633
bool isChainTransferring() const
Gets the status of the background DMA transfer operation when initiated by a frame flip.
Definition gpu.cpp:587
void clear(Color bg={{0, 0, 0}})
Immediately clears the drawing buffer.
Definition gpu.cpp:373
void chain(Frag &fragment)
Chains a fragment to the next DMA chain transfer.
Definition gpu.hh:350
static constexpr unsigned c_chainThreshold
Definition gpu.hh:107
void getScissor(Prim::Scissor &scissor)
Gets the current scissoring region.
Definition gpu.cpp:350
static constexpr unsigned c_oversizedGranularity
Granularity of an oversized DMA chain fragment, in words.
Definition gpu.hh:117
The ordering table. Used to sort fragments before sending them to the GPU.
Definition ordering-table.hh:61
psyqo::Fragments::ChainEntry m_table[N+1]
Definition ordering-table.hh:114
#define head
Definition alloc.c:111
static int size
Definition string.h:149
volatile uint32_t * ptr
Definition cop0.c:80
uint32_t psyqoFrameCounter
Definition gpu.hh:50
int i
Definition gte-regio.c:299
#define N
Definition main.c:37
DmaCallback
Definition kernel.hh:38
@ FROM_MAIN_LOOP
Definition kernel.hh:40
Register< 0x0810 > Data
Definition gpu.cpp:29
void assert(bool condition, const char *message, std::source_location location=std::source_location::current())
A simple assert macro.
Definition kernel.hh:366
void takeOverKernel()
Takes over the kernel. Can only be called once inside the main function.
Definition kernel.cpp:140
Definition lua.hh:40
Definition main.c:111
uintptr_t head
Definition fragments.hh:89
Definition configuration.hh:29
A compounded Scissor primitive.
Definition control.hh:156
static int value
Definition syscalls.h:553
static const void size_t count
Definition syscalls.h:196
Definition gpu.h:109
The Color struct.
Definition common.hh:91