Files
SergObsidian/PERSONAL PROJECTS/PS1 DOCS/PS1 Gpu-DMA.md
2025-04-19 01:51:11 +05:00

247 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

В PlayStation 1 (PS1) передача видеокоманд через DMA в GPU — это ключевой механизм для эффективного рендеринга. Разберём процесс детально, включая работу с памятью.
---
## **1. Как передаются видеокоманды через DMA?**
GPU PS1 не имеет собственного набора инструкций — он выполняет **примитивные команды**, переданные CPU. Эти команды организуются в **цепочку DMA** (Display List), которая указывает, что и как рисовать.
### **1.1. Структура цепочки команд**
Каждая команда в цепочке состоит из:
- **Слова-заголовка** (указывает тип примитива: полигон, спрайт, настройка и т. д.).
- **Данных** (координаты, цвета, текстуры).
Пример команды для отрисовки треугольника:
```c
typedef struct {
uint32_t header; // Тип примитива (например, POLY_F3)
uint16_t x1, y1; // Координаты
uint16_t x2, y2;
uint16_t x3, y3;
uint8_t r, g, b; // Цвет
} GPUPrimitive;
```
### **1.2. Передача через DMA**
1. **CPU формирует цепочку команд** в основной RAM (например, массив структур).
2. **Адрес этой цепочки** передаётся в GPU через DMA.
- Используется порт **`GP0`** (для команд) и **`GP1`** (для управления).
3. **DMA-контроллер** (канал **GPU**) копирует данные из RAM в GPU.
**Код на ассемблере (пример):**
```asm
; Запись адреса цепочки в DMA
li a0, 0x1F8010A0 ; Адрес регистра DMA_GPU (канал 2)
la a1, command_list ; Адрес цепочки команд в RAM
sw a1, 0(a0) ; Записываем адрес
; Настройка DMA
li a0, 0x1F8010A4
li a1, 0x01000200 ; Размер блока + флаги (например, 0x200 байт)
sw a1, 0(a0)
; Запуск DMA
li a0, 0x1F8010A8
li a1, 0x01000001 ; Включить DMA
sw a1, 0(a0)
```
---
## **2. Как GPU читает команды?**
### **2.1. Отсутствие собственной памяти для команд**
У GPU **нет отдельной памяти для хранения команд** (в отличие от современных GPU). Он:
1. **Читает команды напрямую из основной RAM** через DMA.
2. **Интерпретирует их "на лету"** (как потоковый процессор).
### **2.2. Роль FIFO-буфера**
GPU имеет **маленький FIFO-буфер** (32 слова):
- DMA заполняет этот буфер, пока GPU читает данные.
- Если буфер переполняется, DMA приостанавливается.
---
## **3. Где хранятся данные?**
### **3.1. Основная RAM (2 МБ)**
- **Цепочки команд** хранятся здесь.
- **Текстуры** тоже могут быть в RAM (но чаще копируются в VRAM).
### **3.2. Видеопамять (VRAM, 1 МБ)**
- **Кадровый буфер** (Frame Buffer) — хранит пиксели.
- **Текстуры** — загружаются сюда для быстрого доступа.
- **CLUT** (Color Look-Up Table) — палитры для 8-битных текстур.
### **3.3. Как GPU обращается к данным?**
- **Через DMA**: Текстуры/данные копируются из RAM в VRAM перед рендерингом.
- **Прямой доступ**: GPU читает VRAM при отрисовке (например, текстуры).
---
## **4. Пример: полный цикл работы**
1. **Подготовка**:
- CPU создаёт в RAM массив команд (например, `DrawPolygon`, `LoadTexture`).
- Текстуры копируются в VRAM через `GP0` или DMA.
2. **Запуск рендеринга**:
- CPU отправляет адрес цепочки в DMA-контроллер GPU.
- DMA копирует команды из RAM в FIFO GPU.
- GPU выполняет команды, рисуя в кадровый буфер (VRAM).
3. **Вывод изображения**:
- Видеоконтроллер выводит кадр из VRAM на экран.
---
## **5. Критические нюансы**
1. **Синхронизация**:
- Если GPU занят, DMA приостанавливается (проверяется через статусный регистр `GP1`).
2. **Оптимизация**:
- **Связные списки команд** (OT — Ordering Table) позволяют сортировать примитивы.
3. **Ограничения**:
- DMA может работать только с выровненными данными (адреса, кратные 4).
- Переполнение FIFO приводит к "пропущенным кадрам".
---
## **6. Псевдокод для работы с DMA и GPU**
```c
// 1. Загрузка текстуры в VRAM
void LoadTexture(uint16_t x, uint16_t y, void* tex_data) {
GP0_SendCommand(0xA0000000 | (y << 11) | x); // Адрес в VRAM
GP0_SendCommand(0x10000000 | (width * height)); // Размер
DMA_Start(GPU_CHANNEL, tex_data, size); // Копируем через DMA
}
// 2. Формирование цепочки команд
struct CommandList {
uint32_t prim_header;
uint32_t data[10];
} __attribute__((aligned(4))); // Выравнивание для DMA
// 3. Запуск рендеринга
void DrawFrame() {
CommandList list = { ... };
DMA_Start(GPU_CHANNEL, &list, sizeof(list));
GPU_TriggerDMA(); // Запуск выполнения
}
```
---
## **Итог**
- **DMA в PS1** передаёт адрес цепочки команд из RAM в GPU.
- **GPU не имеет своей памяти для команд** — читает их напрямую из RAM через DMA/FIFO.
- **VRAM** используется только для хранения текстур и кадрового буфера.
- **Оптимизация DMA-передач** критична для производительности.
Это объясняет, почему в демосценах PS1 так важны **оптимизированные DMA-цепочки** и ручное управление памятью. Для глубокого понимания изучите **документацию PsyQ SDK** или исходники open-source движков (например, [PSn00bSDK](https://github.com/Lameguy64/psn00bsdk)).
----------
# Функции `PutDispEnv` и `PutDrawEnv` в библиотеке PsyQ для PlayStation 1
Эти функции управляют графическим окружением (display и drawing environment) и являются ключевыми для работы с двойной буферизацией на PS1.
## `PutDispEnv` - Установка параметров отображения
```c
void PutDispEnv(DISPENV *env);
```
### Назначение:
Устанавливает параметры **области вывода изображения** на экран:
- Позиция и размер выводимой области
- Режимы интерлейсинга
- Цвет фона за пределами игровой области
### Структура DISPENV:
```c
typedef struct {
u_short disp; // Настройки дисплея
u_short screen; // Размер экрана
u_short isrgb24; // RGB24 режим (0/1)
u_short pad; // Выравнивание
} DISPENV;
```
### Пример использования:
```c
DISPENV dispEnv;
SetDefDispEnv(&dispEnv, 0, 0, 320, 240); // Установка параметров по умолчанию
dispEnv.screen.w = 320; // Ширина выводимой области
dispEnv.screen.h = 240; // Высота выводимой области
PutDispEnv(&dispEnv); // Применение настроек
```
## `PutDrawEnv` - Установка параметров рендеринга
```c
void PutDrawEnv(DRAWENV *env);
```
### Назначение:
Конфигурирует параметры **области рисования**:
- Область отсечения (clipping)
- Смещение рисования
- Цвет фона
- Флаги отрисовки
### Структура DRAWENV:
```c
typedef struct {
RECT clip; // Область отсечения
short ofs[2]; // Смещение рисования
RECT tw; // Область текстуры (если используется)
u_short tpage; // Страница текстуры
u_short dtd; // Флаги (dither, draw background)
} DRAWENV;
```
### Пример использования:
```c
DRAWENV drawEnv;
SetDefDrawEnv(&drawEnv, 0, 0, 320, 240);
drawEnv.clip.x = 0; // Область отсечения
drawEnv.clip.y = 0;
drawEnv.clip.w = 320;
drawEnv.clip.h = 240;
drawEnv.isbg = 1; // Включить очистку фона
setRGB0(&drawEnv, 0, 0, 0); // Черный фон
PutDrawEnv(&drawEnv);
```
## Совместное использование в двойной буферизации
Типичный цикл рендеринга с двойной буферизацией:
```c
DB db[2]; // Double buffer структура
int active = 0;
while(1) {
// Очистка и подготовка OT
ClearOTagR(db[active].ot, OT_LENGTH);
// Добавление примитивов
// ... (код рендеринга)
// Отправка команд на отрисовку
DrawOTag(db[active].ot);
// Переключение буферов
PutDispEnv(&db[active].disp);
PutDrawEnv(&db[active].draw);
active ^= 1; // Смена активного буфера
VSync(0); // Ожидание VBlank
}
```
## Ключевые особенности:
1. **Двойная буферизация** - обычно используют два набора DISPENV/DRAWENV
2. **VSync синхронизация** - переключение лучше делать во время VBlank
3. **Размеры областей** - должны соответствовать выбранному разрешению
4. **Производительность** - правильная настройка уменьшает артефакты
Эти функции являются фундаментальными для управления графическим конвейером PS1 и требуют точной настройки для оптимальной работы.