2 poin oleh GN⁺ 2024-09-03 | 1 komentar | Bagikan ke WhatsApp
  • paraLLEl-GS adalah proyek yang mereproduksi GS (Graphics Synthesizer) milik PS2 dengan komputasi Vulkan, untuk mengatasi keterbatasan akurasi dan upscaling yang ditinggalkan GSdx, yang selama hampir 20 tahun menjadi standar de facto
  • GS bekerja dengan VRAM 4MiB dan fillrate tinggi, tetapi karakteristik pipeline piksel seperti destination alpha test, blending kondisional, serta alpha/warna di atas 1.0 membuatnya sulit dicocokkan dengan API grafis umum
  • Implementasinya melacak VRAM per halaman dan blok 256 byte, lalu menggabungkan snapshot CLUT, unswizzle tekstur, dan penjadwalan render pass untuk menangani umpan balik framebuffer/tekstur
  • Kasus seperti Tales of the Abyss, Final Fantasy X, MGS2, Valkyrie Profile 2, dan Shadow of the Colossus dipakai untuk membandingkan masalah upscaling, UI, blending presisi tinggi, dan umpan balik tekstur, termasuk adegan 8x dan 16x SSAA
  • Saat ini validasi terutama masih bergantung pada pemutaran GS dump; meski sudah ada patch peretasan PCSX2 dan uji real-time mkfifo, integrasi ke emulator tetap diperlukan agar bisa menjangkau pengguna nyata

Tujuan dan titik awal paraLLEl-GS

  • paraLLEl-GS adalah proyek yang mengemulasikan GS (Graphics Synthesizer) PlayStation 2 dengan komputasi Vulkan
  • Karya penulis yang sama pada 2020, paraLLEl-RDP, mengimplementasikan RDP N64 dengan komputasi Vulkan dan menargetkan hasil yang mendekati akurasi bit serta upscaling dengan Angrylion sebagai acuan
  • Di PS2, GSdx selama sekitar 20 tahun tetap menjadi implementasi tercanggih secara de facto
  • Sekitar 2014 sempat ada percobaan implementasi komputasi GS PS2 berbasis OpenCL, tetapi tidak pernah selesai, dan kini sulit ditemukan bahkan di repositori upstream
  • Di PS2, alasan memakai raster komputasi shader tidak sekuat di N64
    • PCSX2 memiliki software renderer yang dioptimalkan dengan baik dan renderer berbasis grafis yang relatif tangguh
    • Software renderer tidak mendukung upscaling
    • Renderer berbasis grafis menunjukkan berbagai bug dan glitch, terutama saat upscaling
  • paraLLEl-GS lebih berfokus pada menghindari masalah akurasi yang jelas daripada mengejar akurasi bit terhadap perangkat keras
    • Software renderer GSdx sendiri juga tampaknya bukan implementasi yang akurat hingga tingkat bit terhadap perangkat keras, sehingga pengujian berbasis perbandingan langsung cepat menemui batas

Mengapa PS2 GS sulit

  • GS adalah perangkat yang pada tahun 2000 secara teoretis mampu memproses lebih dari 1 miliar piksel per detik berkat fillrate dan bandwidth-nya
  • VRAM-nya kecil, hanya 4MiB, tetapi dirancang untuk terus di-stream melalui beberapa mesin DMA
  • Pipeline pikselnya sendiri dalam beberapa hal lebih sederhana daripada RDP N64
    • Tekstur tunggal
    • Combiner siklus tunggal
    • Anti-aliasing yang sangat dasar
  • Ada banyak fitur yang sulit diimplementasikan dengan API grafis umum
    • Blending di atas 1.0: perilaku yang diwarisi dari PS1, di mana 0x80 diperlakukan seperti 1.0 dan nilai hingga 0xff bisa direpresentasikan
    • Destination alpha test: destination alpha bisa dipakai seperti stencil semu
    • Blending kondisional: blending dapat dimatikan secara kondisional berdasarkan alpha
    • Koreksi alpha: sebelum alpha ditulis, MSB bisa di-OR agar dipaksa mendekati 1
    • Pembuangan parsial pada alpha test: dimungkinkan membuang hanya warna tetapi tetap mempertahankan penulisan depth
    • AA1: berbasis coverage-to-alpha dan terkait dengan kontrol penulisan depth per piksel
    • Z fixed-point 32-bit: dukungan D32_UINT secara teknis ada, tetapi belum ditemukan kasus penggunaan nyata
  • Tanpa programmable blending, GPU desktop immediate-mode memerlukan ROV atau barrier per piksel, yang sangat menurunkan performa
  • Implementasi komputasi menghindari batasan ini dengan membuat tile-based deferred renderer (TBDR) sendiri

Aturan raster, antrean verteks, dan tata letak memori

  • Primitive GS disediakan secara relatif normal dalam clip space
    • VU1 melakukan transformasi dan clipping, lalu mengekspor berbagai atribut verteks ke GS
  • Koordinat dan atribut memakai format khas GS
    • X/Y: fixed-point unsigned 12.4
    • Z: uint 24-bit atau 32-bit
    • FOG: uint 8-bit
    • RGBA: nilai 8-bit untuk pencahayaan per verteks
    • STQ: koordinat tekstur dengan koreksi perspektif
    • UV: koordinat tidak ternormalisasi fixed-point 12.4 tanpa koreksi perspektif
  • Aturan raster lebih dekat ke gaya D3D9
    • Segitiga memakai aturan raster top-left seperti GPU modern
    • Pusat piksel berada di koordinat integer seperti di D3D9
    • Garis memakai algoritme Bresenham sehingga sulit di-upscale, dan harus didekati sebagai rect atau parallelogram
    • Titik di-snap ke piksel terdekat
    • Sprite adalah quad sederhana dengan dua koordinat
  • Antrean verteks GS mirip immediate mode OpenGL 1.0
    • RGBA, STQ, dan beberapa register disetel lebih dulu, lalu penulisan register XYZ membentuk “kick” verteks
    • TRIANGLE_FAN juga didukung
  • Koordinat piksel PS2 ditata per halaman
    • Satu halaman berukuran 8KiB
    • Halaman dibagi menjadi 32 blok
    • Untuk RGBA 32-bit, satu halaman berukuran 64×32 piksel, dan 32 blok 8×8 di-swizzle dalam urutan Z-order
  • Saat merender ke warna 24-bit atau depth 24-bit, 8 bit atas yang tersisa bisa dipakai untuk tekstur
    • Format 8H, 4HL, 4HH berguna untuk palet 8-bit dan 4-bit

Tekstur, CLUT, TEXFLUSH

  • Teksturing GS punya campuran elemen yang mirip API modern dan yang sangat khas
    • Pusat texel berada di half-pixel seperti API modern
    • Presisi sub-texel tampaknya 4-bit, bukan 8-bit
    • Filter bilinear adalah bilinear biasa, bukan struktur khusus seperti filter 3-point di N64
  • Mode addressing khusus meningkatkan tingkat kesulitan implementasi
    • REGION_CLAMP dapat menerapkan clamp ke area arbitrer di dalam atlas tekstur
    • REGION_REPEAT lebih sulit lagi karena dapat menerapkan operasi bit seperti (u & MASK) | FIX untuk setiap koordinat
  • Mipmapping menghitung LOD dari log2 faktor Q yang diinterpolasi dan koefisien skala, bukan dari turunan
    • Pada implementasi komputasi, ini menjadi keunggulan besar karena tidak bergantung pada turunan
    • Metode ini tidak dapat mendukung fitur seperti anisotropic filtering
  • CLUT adalah cache 1KiB yang menyimpan palet saat ini
    • Agar bisa dipakai, perlu ada penyalinan eksplisit dari VRAM ke cache CLUT
    • Untuk warna 32-bit, cache ini dapat menampung satu palet 256 warna
    • Untuk 16bpp, dapat menampung 32 palet masing-masing 16 warna
  • TEXFLUSH adalah perintah eksplisit yang lebih mirip sinkronisasi dan invalidasi cache tekstur
    • Pada awal implementasi, TEXFLUSH sempat ingin dijadikan dasar pelacakan hazard, tetapi akhirnya harus diabaikan
    • Masalahnya, game bisa lupa memanggil TEXFLUSH atau justru memanggilnya terlalu sering
  • Implementasi akhirnya memilih pendekatan minimal caching
    • Diasumsikan seolah tidak ada cache, lalu hazard dilacak secara langsung
    • Untuk loop umpan balik, dipertimbangkan pengecualian khusus
    • GSdx tampaknya juga bergerak ke arah yang sama

Pipeline rendering komputasi Vulkan

  • Pipeline implementasinya dibangun dengan asumsi sinkronisasi di antara setiap tahap
    • Menyinkronkan salinan VRAM di CPU dengan GPU
    • Melakukan unggah VRAM atau copy local-to-local
    • Memperbarui cache CLUT dari VRAM
    • Melakukan unswizzle VRAM ke VkImage agar bisa disampling langsung
    • Melakukan rendering
    • Menyinkronkan kembali salinan VRAM GPU ke CPU
  • Pola kerja game pada umumnya cocok dengan pipeline ini
    • Mengunggah tekstur ke VRAM
    • Mengunggah palet ke VRAM
    • Memperbarui cache CLUT
    • Menggambar dengan tekstur
    • Jika perlu, melakukan unswizzle dari VRAM ke VkImage
    • Menyusun batch primitive menjadi render pass
  • Jika tidak ada hazard yang mengarah ke belakang, batch dan sinkronisasi bisa ditunda
    • Untuk mendapatkan performa dalam renderer seperti ini, mempertahankan batch sangat penting
  • Kasus hazard utama ditangani secara terpisah
    • Copy lagi ke VRAM yang sudah ditulis copy lain
    • Copy ke VRAM yang sebelumnya dibaca oleh tekstur yang disampling atau CLUT
    • Menyampel area yang dirender sebagai tekstur
    • Copy ke VRAM yang sedang dirender

Pelacakan halaman dan cache tekstur

  • Bagian tersulit dalam emulasi GS adalah menangani hazard read-after-write dan write-after-write di VRAM
  • VRAM 4MiB mula-mula dibagi berdasarkan halaman
    • Halaman adalah satuan framebuffer dan depth buffer, sehingga menjadi dasar pelacakan yang paling bermakna
  • Status yang dilacak per halaman adalah sebagai berikut
    • pending frame buffer write
    • pending frame buffer read
  • Karena tekstur dan copy VRAM punya alignment 256 byte, dipakai bitmask u32 untuk 32 blok
    • VRAM copy write
    • VRAM copy read
    • pending read ke cache CLUT atau VkImage
    • blok yang tertimpa oleh jenis write apa pun
  • Saat merender ke warna 24-bit sambil menyampel 8 bit atas sebagai tekstur, hazard bisa jadi tidak terjadi
    • Untuk ini, write mask framebuffer dan read mask tekstur dilacak secara terpisah
  • Setiap halaman memiliki daftar VkImage yang terhubung
    • Jika tekstur halaman menjadi tidak valid, image akan dihancurkan dan harus di-unswizzle ulang dari VRAM
    • Satu tekstur bisa membentang di beberapa halaman, dan jika salah satunya tertimpa, seluruh tekstur menjadi tidak valid
  • Pelacakan yang sederhana dan terlalu konservatif tidak akan bekerja pada game PS2
    • Pelacakan tingkat blok 256 byte dan pertimbangan write/read mask sangat penting
  • Tekstur POT dan tidak digunakannya REGION_CLAMP bisa memunculkan false positive
    • Misalnya, jika render target 512×448 disetel sebagai tekstur 512×512, area yang tidak dipakai bisa tampak seperti hazard
    • Implementasi memakai solusi yang mengabaikan potensi hazard di “red zone” tersebut

Penjadwalan CLUT dan unswizzle tekstur

  • Untuk membatch unggah tekstur, unggah CLUT juga harus dibatch bersamaan
  • Implementasinya memiliki snapshot ring buffer berisi 1024 salinan CLUT
    • Satu workgroup menelusuri pembaruan dan menulisnya ke SSBO
    • Ini mirip pembaruan TMEM pada RDP N64, tetapi pembaruan CLUT jauh lebih sederhana
  • Di Vulkan, VkImage baru dialokasikan, lalu disubalokasikan dari VkDeviceMemory dan di-unswizzle dengan compute shader
  • Vulkan specialization constants dipakai untuk mengkhususkan format tekstur dan logika swizzle
  • Perilaku khusus REGION_REPEAT juga ditangani di tahap unswizzle
    • Dengan begitu, ubershader tidak terlalu perlu lagi menangani kasus ini dengan filter bilinear manual
  • Render target juga bolak-balik ke tekstur melalui VRAM SSBO
    • Upaya untuk langsung meneruskan render target sebagai tekstur dinilai terlalu penuh bug dan pengecualian

Setup segitiga, binning, ubershader

  • paraLLEl-GS adalah renderer berbasis tile seperti paraLLEl-RDP
  • Setup segitiga dilakukan sebelum binning, dengan input yang dibagi ke tiga array
    • Posisi
    • Atribut per verteks
    • Atribut per primitive
  • Rasterizer-nya berbasis barycentric dan sangat dipengaruhi oleh tulisan Fabian Giesen tentang graphics pipeline serta metode rasterisasi paralel yang dijelaskan dalam makalah Pineda 1988
  • GS PS2 yang sebenarnya adalah DDA, yaitu scanline rasterizer, tetapi karena deskripsi akurat-bit untuk DDA GS tidak diketahui, pendekatan barycentric dipilih
  • Untuk mengimplementasikan wide line dan sprite, parallelogram juga didukung
  • inv_area dihitung dengan RCP fixed-point kustom
    • RCP GPU standar dihindari karena konsistensinya rendah antar implementasi dan akurasinya sekitar 22,5 bit
    • RCP kustom menargetkan akurasi sekitar 24,0 bit
  • Binning biasanya memakai blok 32×32 piksel
    • Jumlah primitive maksimum per render pass adalah 64k karena indeks u16
    • Render pass utama yang diamati umumnya berada di kisaran 10k–30k primitive
  • GS PS2 punya fillrate tinggi dan kompleksitas per piksel yang rendah, sehingga ubershader murni dimungkinkan
    • Tidak seperti N64, bindless bisa dimanfaatkan sehingga kompleksitas teksturing juga berkurang
  • Ubershader memakai early Z, deferred on-tile shading, dan lazy pixel shading
    • Shading sebenarnya hanya dilakukan saat piksel bergantung pada hasil sebelumnya
    • Alpha test, color write mask, alpha blending, dan sejenisnya menciptakan ketergantungan seperti itu
    • Warna framebuffer akhir dan depth ditulis ke SSBO untuk mengurangi penggunaan bandwidth GPU

Supersampling dan mitigasi artefak upscaling

  • Rendering sampel tunggal saja tidak cukup memaksimalkan kegunaan renderer ini
  • Sebagai contoh, pada 8x SSAA, 10 versi VRAM disimpan di GPU
    • 1 VRAM single-sample
    • 1 nilai referensi untuk VRAM single-sample
    • 8 supersample
  • Saat rendering, jika VRAM single-sample dan referensinya cocok, versi supersample dimuat
    • Ini penting dalam rendering inkremental
  • Saat tile selesai, resolve multisample dilakukan dengan operasi subgroup terklaster, lalu salinan supersample dan single-sample ditulis
  • Supersampling memberi lebih sedikit jaggies daripada upscaling sederhana, dan membuat kesan resolusi elemen 3D serta elemen UI lebih konsisten
  • Primitive sprite harus selalu dirender pada single-rate
    • Ini umumnya elemen UI atau sejenisnya; jika di-upscale, ia bisa menyampel di luar rect yang dimaksud atau menjadi terlalu buram karena filter bilinear
  • Karena banyak UI digambar sebagai segitiga biasa, beberapa primitive datar menurunkan interpolasi atribut ke koordinat single-pixel
    • Bahkan jika memakai perspective, bila semua verteks punya Q yang sama dan Z juga sama, primitive itu diasumsikan sebagai primitive UI datar
    • False positive bisa terjadi, tetapi pada game yang diuji cara ini bekerja cukup baik

Hasil per game dan kasus yang sulit

  • Di Tales of the Abyss, upscaling backend Vulkan PCSX2 menunjukkan ketidakselarasan bloom pada kaca dan pola persegi
    • Pada 8x SSAA milik paraLLEl-GS, masalah khas upscaling buruk tidak terlalu terlihat
    • Screenshot tersebut juga menerapkan upscaling pascaproses FSR1
  • UI Final Fantasy X memperlihatkan masalah upscaling saat membandingkan native resolution dengan upscale 4x
    • Trik MSAA snap efektif untuk menghindari artefak
    • Prinsip utamanya adalah jangan meng-upscale UI melebihi nearest-neighbor integer scale
  • MGS2 adalah contoh di PCSX2 yang menuntut akurasi blending tinggi
    • Di jalur programmable blending PCSX2, barrier dimasukkan per primitive sehingga performa turun drastis
    • paraLLEl-GS secara struktural selalu berjalan dengan akurasi blending 100%, dan pada RX 7600 adegan 16x SSAA ditunjukkan berjalan pada 25W dan utilisasi GPU 17%
  • Valkyrie Profile 2 memiliki kasus yang menyampel alpha pikselnya sendiri sebagai indeks palet
    • paraLLEl-GS mendeteksi hal ini, mengubah indeks tekstur menjadi nilai khusus, lalu merujuk warna framebuffer yang masih ada di register
    • Dengan optimasi ini, barrier render pass turun dari lebih dari 500 menjadi 18
  • Efek camo di intro MGS2 menyampel framebuffer sebagai tekstur, tetapi memakai koordinat tumpang tindih yang penyelarasan pikselnya tidak cocok
    • PCSX2 tampaknya juga tidak menambahkan barrier di sini, dan paraLLEl-GS menanganinya dengan cara yang sama
  • Shadow of the Colossus berfungsi sebagai stress test yang berat
    • Pada akurasi blending maksimum PCSX2, GPU bisa turun sampai 24 FPS hanya dengan upscale 2x di intro
    • paraLLEl-GS masih punya performa yang baik bahkan pada 8x SSAA, meski adegan itu tetap berat
    • Dalam kasus ini, bottleneck ada di pemrosesan geometri CPU, bukan GPU

Status saat ini dan langkah berikutnya

  • Saat ini, cara praktis untuk pengujian adalah memakai GS dump
  • Ada hack-patch untuk memungkinkan dump raw GS trace dari PCSX2
  • Pengujian real-time sederhana lewat mkfifo juga dimungkinkan
  • Agar berguna bagi pengguna akhir, integrasi ke emulator dalam bentuk apa pun tetap diperlukan
  • Karena pustaka PS2 sangat besar, kemungkinan masih ada banyak bug tersembunyi
  • Karena sifatnya sebagai standalone library, ada juga potensi penggunaan seperti API rendering gaya lama

1 komentar

 
GN⁺ 2024-09-03
Komentar Hacker News
  • Saya merasa harus mencari cukup lama untuk menemukan kapan singkatan GS dijabarkan di artikel ini. Isinya menarik, tapi bagian itu membuat saya agak tertinggal

    • Itu singkatan dari Graphics Synthesizer, nama yang diberikan Sony untuk “GPU” PS2
  • “Berdoalah semoga ada programmable blending” — sejak pertama kali belajar pixel shader di awal 2000-an, saya terus berharap ada programmable blending lewat “blending shader”
    Sebagai catatan, saya juga menginginkan programmable texture decoding lewat “texture shader”, yang akan berguna untuk format/kompresi tekstur kustom, komposit tekstur, dan sebagainya
    Entah bagaimana, GPU mendapatkan ray tracing lebih dulu daripada programmable blending; yang pertama terasa seperti mimpi di malam musim panas, sedangkan yang kedua terasa hanya seperti mengubah satu blok fungsi tetap lagi menjadi blok yang dapat diprogram. Saya masih menunggu texture shader

    • GPU mobile yang mewarisi lini PowerVR punya fitur seperti itu
      https://medium.com/pocket-gems/programmable-blending-on-ios-...
      https://developer.apple.com/videos/play/tech-talks/605
    • Terakhir kali saya menyentuhnya, PowerVR GPU mobile punya programmable blending, dan sebenarnya itu satu-satunya cara blending yang ada. Di PS Vita, mengganti blend state butuh sekitar 1 ms, jadi hasilnya tidak terlalu bagus
    • Saya rasa VK_EXT_fragment_shader_interlock semacam programmable blending juga. Begitu pula Raster-order-views di sisi DirectX
      Ada contoh bagus yang memanfaatkannya: https://vulkan.org/user/pages/09.events/vulkanised-2024/vulk...
    • Sekarang ada mesh shader, work graphs, CUDA, bahkan shader C++ umum. OTOY sekarang menangani seluruh rendering dengan compute
    • Sebagian besar itu tampaknya bisa diemulasikan di Vulkan atau DX12, tapi saya kurang tahu soal API lain. Yang saya penasaran adalah kasus penggunaan nyatanya
      Saya rasa sampai taraf tertentu itu bisa diimplementasikan, tetapi tanpa use case yang meyakinkan, sulit membenarkan pekerjaan implementasinya
  • Bagian favorit saya dari GS adalah skala struktur bus-nya yang tidak masuk akal. Lebarnya total 2560 bit, dan pembagian cache-nya juga cerdas
    Dalam beberapa hal PS3 terasa seperti langkah mundur, terutama blending

  • Saya penasaran bagaimana pendekatan ini dibandingkan dengan ubershader milik Dolphin

    • Pada dasarnya hampir tidak sebanding. Ubershader Dolphin melakukan satu hal: meniru fixed-function blending/texturing dengan perangkat keras modern yang fleksibel
      Sebenarnya, bahkan saat Dolphin memperkenalkannya pun itu sudah merupakan teknik lama. Proyek ini adalah renderer penuh termasuk rasterizer, dan seperti terlihat di artikel, juga mencakup ubershader untuk blending
      Shader tidak menggambar segitiga; shader dipanggil untuk setiap titik di dalam segitiga, menerima beberapa input, lalu menentukan warna titik itu. Ini agak mirip seperti membandingkan emulator CPU penuh dengan implementasi yang hanya menjalankan instruksi ADD/MUL
  • Saya penasaran apa maksud top-left raster