2 puan yazan GN⁺ 2024-09-03 | 1 yorum | WhatsApp'ta paylaş
  • paraLLEl-GS, PS2'nin GS'ini (Graphics Synthesizer) Vulkan compute ile yeniden oluşturarak, yaklaşık 20 yıldır fiilî standart olan GSdx'in bıraktığı doğruluk ve yükseltme sınırlarını ele alıyor
  • GS, 4 MiB VRAM ve yüksek fill-rate üzerine çalışsa da hedef alfa testi, koşullu blending, 1.0'ı aşan alfa/renk gibi piksel pipeline özellikleri nedeniyle genel grafik API'leriyle eşleştirilmesi zor
  • Uygulama, VRAM'i sayfa ve 256 baytlık blok düzeyinde izliyor; CLUT snapshot'ları, texture unswizzle ve render pass batching'i birleştirerek framebuffer/texture feedback durumlarını işliyor
  • Tales of the Abyss, Final Fantasy X, MGS2, Valkyrie Profile 2, Shadow of the Colossus gibi örneklerde yükseltme, UI, yüksek hassasiyetli blending ve texture feedback sorunları karşılaştırılıyor; 8x ve 16x SSAA sahneleri de ele alınıyor
  • Mevcut doğrulama çoğunlukla GS dump oynatımına dayanıyor; PCSX2 hack patch'i ve mkfifo ile gerçek zamanlı testler olsa da gerçek kullanıcılara ulaşması için emülatör entegrasyonu gerekiyor

paraLLEl-GS'in hedefi ve çıkış noktası

  • paraLLEl-GS, PlayStation 2 GS'ini (Graphics Synthesizer) Vulkan compute ile emüle eden bir proje
  • Aynı yazarın 2020 tarihli çalışması paraLLEl-RDP, N64 RDP'yi Vulkan compute ile uygulamış; Angrylion'u referans alarak bit doğruluğuna yakın sonuçları ve yükseltmeyi hedeflemişti
  • PS2 tarafında GSdx, yaklaşık 20 yıl boyunca fiilen en ileri uygulama olarak kaldı
  • 2014 civarında OpenCL tabanlı bir PS2 GS compute uygulaması denemesi vardı, ancak tamamlanmadı ve bugün upstream depoda da bulunması zor
  • PS2'de compute shader raster kullanma gerekçesi N64'e kıyasla daha zayıf
    • PCSX2'de iyi optimize edilmiş bir software renderer ve görece sağlam grafik tabanlı renderer'lar var
    • Software renderer yükseltmeyi desteklemiyor
    • Grafik tabanlı renderer'lar, özellikle yükseltmede çeşitli bug'lar ve glitch'ler gösteriyor
  • paraLLEl-GS, donanıma karşı bit doğruluğundan çok bariz doğruluk sorunlarından kaçınmaya odaklanıyor
    • GSdx software renderer da donanıma bit düzeyinde doğru bir uygulama gibi görünmediğinden, doğrudan karşılaştırmaya dayalı testler hızla sınıra dayanıyor

PS2 GS neden zorlu

  • GS, 2000 yılı itibarıyla teorik olarak saniyede 1 milyardan fazla piksel işleyebilen fill-rate ve bant genişliğine sahip bir aygıttı
  • VRAM 4 MiB ile küçük, ancak birden çok DMA motoru üzerinden sürekli stream edilecek şekilde tasarlanmıştı
  • Piksel pipeline'ının kendisi bazı açılardan N64 RDP'den daha basittir
    • Tek texture
    • Tek cycle combiner
    • Çok temel anti-aliasing
  • Genel grafik API'leriyle uygulanması zor birçok özellik var
    • 1.0'ı aşan blending: PS1'den gelen davranışla 0x80, 1.0 gibi ele alınır ve 0xff'e kadar temsil edilebilir
    • Hedef alfa testi: Hedef alfa, stencil benzeri şekilde kullanılabilir
    • Koşullu blending: Alfaya bağlı olarak blending koşullu biçimde kapatılabilir
    • Alfa düzeltme: Alfa yazılmadan önce MSB OR'lanarak zorla 1'e yakın hale getirilebilir
    • Alfa testinde kısmi discard: Yalnızca rengi atıp depth yazımını korumak gibi davranışlar mümkündür
    • AA1: coverage-to-alpha yaklaşımıdır ve piksel başına depth yazımı kontrolüyle iç içedir
    • 32 bit fixed-point Z: D32_UINT desteği teknik olarak vardır, ancak gerçek kullanım örneği henüz görülmemiştir
  • Programlanabilir blending yoksa immediate-mode masaüstü GPU'larda ROV veya piksel başına bariyer gerekir; bu da performansı ciddi biçimde kötüleştirir
  • Compute uygulaması, bu kısıtları aşmak için kendi tile tabanlı deferred renderer'ını (TBDR) oluşturuyor

Raster kuralları, vertex kuyruğu ve bellek düzeni

  • GS primitive'leri clip space içinde görece sıradan şekilde sağlanır
    • VU1 dönüşüm ve clipping yapar, GS'e çeşitli vertex nitelikleri gönderir
  • Koordinatlar ve nitelikler GS'e özgü biçimlere sahiptir
    • X/Y: 12.4 unsigned fixed-point
    • Z: 24 bit veya 32 bit uint
    • FOG: 8 bit uint
    • RGBA: vertex başına ışıklandırma için 8 bit değerler
    • STQ: perspektif düzeltmeli texture koordinatları
    • UV: perspektif düzeltmesiz 12.4 fixed-point normalize edilmemiş koordinatlar
  • Raster kuralları D3D9 stiline yakındır
    • Üçgenler, modern GPU'lardaki gibi top-left raster kuralını kullanır
    • Piksel merkezi D3D9'daki gibi tam sayı koordinatlardadır
    • Çizgiler Bresenham algoritmasını kullanır; bu nedenle yükseltme zordur ve rect ya da parallelogram ile yaklaştırılmaları gerekir
    • Noktalar en yakın piksele snap edilir
    • Sprite'lar iki koordinatlı basit quad'lardır
  • GS vertex kuyruğu OpenGL 1.0'ın immediate mode'una benzer
    • RGBA, STQ ve çeşitli register'lar ayarlanır; XYZ register yazımı vertex “kick”ini oluşturur
    • TRIANGLE_FAN de desteklenir
  • PS2 piksel koordinatları sayfa birimiyle düzenlenir
    • Bir sayfa 8 KiB'dir
    • Sayfa 32 bloğa ayrılır
    • 32 bit RGBA için sayfa 64×32 pikseldir ve 32 adet 8×8 blok Z-order ile swizzle edilir
  • 24 bit renge veya 24 bit depth'e render ederken kalan üst 8 bite texture konabilir
    • 8H, 4HL, 4HH biçimleri 8 bit ve 4 bit paletler için kullanışlıdır

Texture, CLUT, TEXFLUSH

  • GS texture işlemleri modern API'lere benzeyen yönlerle sıra dışı yönleri bir arada barındırır
    • Texel merkezi modern API'lerdeki gibi half-pixel konumundadır
    • Sub-texel hassasiyeti 8 bit değil, 4 bit gibi görünür
    • Bilinear filter standart bilinear'dır; N64'ün 3-point filter'ı gibi özel bir yapı değildir
  • Özel adresleme modları uygulama zorluğunu artırır
    • REGION_CLAMP, texture atlas içindeki keyfi bir bölgeye clamp uygulanmasını sağlayabilir
    • REGION_REPEAT, her koordinata (u & MASK) | FIX gibi bit işlemleri uygulayabildiği için uygulanması daha zordur
  • Mipmapping, türevler yerine interpolate edilmiş Q factor'ün log2'si ve ölçek katsayısıyla LOD hesaplar
    • Compute uygulamasında türevlere bağlı olmaması büyük avantajdır
    • Bu yöntem anisotropic filtering gibi özellikleri destekleyemez
  • CLUT, geçerli paleti tutan 1 KiB cache'tir
    • Kullanılabilmesi için VRAM'den CLUT cache'e açık bir kopyalama yapılmalıdır
    • 32 bit renk için 256 renkli tek bir paleti tutabilir
    • 16 bpp için 16 renkli 32 paleti tutabilir
  • TEXFLUSH, texture cache senkronizasyonu ve invalidation'a yakın açık bir komuttur
    • Uygulamanın başlarında TEXFLUSH'ın hazard takibi için temel alınması düşünülmüştü, ancak sonunda göz ardı edilmesi gerekti
    • Oyunların TEXFLUSH'ı unutması veya gereğinden fazla çağırması sorundu
  • Nihai uygulama minimal caching yaklaşımını seçti
    • Cache yokmuş gibi davranıp hazard'ları doğrudan izler
    • Feedback loop'lar için ayrı istisna işlemeyi değerlendirir
    • GSdx de aynı yönde görünüyor

Vulkan compute rendering pipeline'ı

  • Uygulama pipeline'ı, her aşama arasında senkronizasyon varsayımıyla kurulmuştur
    • CPU'daki VRAM kopyasını GPU ile senkronize etme
    • VRAM upload veya local-to-local copy gerçekleştirme
    • VRAM'den CLUT cache'i güncelleme
    • VRAM'i doğrudan sample edilebilir hale getirmek için VkImage'a unswizzle etme
    • Rendering gerçekleştirme
    • GPU VRAM kopyasını yeniden CPU'ya senkronize etme
  • Tipik oyun davranışı bu pipeline ile iyi örtüşür
    • Texture'ı VRAM'e upload etme
    • Paleti VRAM'e upload etme
    • CLUT cache'i güncelleme
    • Texture ile draw etme
    • Gerekirse VRAM'den VkImage'a unswizzle etme
    • Primitive batch'ini render pass olarak oluşturma
  • Geriye dönük hazard yoksa batching ve senkronizasyon geciktirilebilir
    • Bu tür bir renderer'da performans almak için batch'i korumak önemlidir
  • Başlıca hazard durumları ayrıca ele alınır
    • Copy'nin yazdığı VRAM'e yeniden copy
    • Sample edilmiş texture veya CLUT tarafından okunmuş VRAM'e copy
    • Render edilmiş bölgeyi texture olarak sample etme
    • Render edilmiş VRAM'e copy

Sayfa takibi ve texture cache

  • GS emülasyonunda en zor kısım, VRAM'deki read-after-write ve write-after-write hazard'larını ele almaktır
  • 4 MiB VRAM önce sayfa birimlerine ayrılır
    • Sayfa, framebuffer ve depth buffer birimi olduğundan en anlamlı takip ölçütüdür
  • Sayfa düzeyinde izlenen durumlar şunlardır
    • pending frame buffer write
    • pending frame buffer read
  • Texture ve VRAM copy, 256 bayt hizalamaya sahip olduğundan 32 blok için u32 bit maskesi kullanılır
    • VRAM copy write
    • VRAM copy read
    • CLUT cache veya VkImage'a pending read
    • Herhangi bir write tarafından üzerine yazılmış blok
  • 24 bit renge render ederken üst 8 biti texture olarak sample etme durumunda hazard olmayabilir
    • Bunun için framebuffer write mask ve texture read mask ayrı takip edilir
  • Her sayfa bağlantılı VkImage listesini tutar
    • Sayfa texture'ı invalidated olduğunda image yok edilir ve VRAM'den yeniden unswizzle edilmesi gerekir
    • Tek bir texture birden çok sayfaya yayılabilir; bunlardan yalnızca biri bile üzerine yazılsa texture invalidated olur
  • Basit ve muhafazakâr takip PS2 oyunlarında işe yaramaz
    • 256 bayt blok düzeyinde takip ile write/read mask'lerin hesaba katılması önemlidir
  • POT texture'lar ve REGION_CLAMP kullanılmaması nedeniyle false positive oluşabilir
    • Örneğin 512×448 render target, 512×512 texture olarak ayarlanırsa kullanılmayan alan hazard gibi görünebilir
    • Uygulama, bu “red zone”daki potansiyel hazard'ı göz ardı eden bir workaround kullanır

CLUT batching ve texture unswizzle

  • Texture upload'larını batch'lemek için CLUT upload'larını da birlikte batch'lemek gerekir
  • Uygulama, CLUT'un 1024 kopyasını snapshot ring buffer olarak tutar
    • Bir workgroup güncellemeleri sırayla dolaşır ve SSBO'ya yazar
    • N64 RDP'deki TMEM güncellemelerine benzer, ancak CLUT güncellemeleri çok daha basittir
  • Vulkan'da yeni bir VkImage allocate edilir, VkDeviceMemory'den sub-allocate edilir ve compute shader ile unswizzle edilir
  • Texture formatını ve swizzle logic'i özelleştirmek için Vulkan specialization constants kullanılır
  • REGION_REPEAT'in özel davranışı da unswizzle aşamasında işlenir
    • Sonrasında ubershader'ın bu durumu dikkate alarak manuel bilinear filtering yapma ihtiyacı azalır
  • Render target'lar da VRAM SSBO üzerinden texture'a gidip gelir
    • Render target'ı doğrudan texture olarak forward etme denemesi, çok fazla bug ve istisna nedeniyle uygun görülmemiştir

Üçgen setup, binning, ubershader

  • paraLLEl-GS, paraLLEl-RDP gibi tile tabanlı renderer'dır
  • Binning öncesinde üçgen setup yapılır; input üç diziye ayrılır
    • Pozisyon
    • Vertex başına nitelikler
    • Primitive başına nitelikler
  • Rasterizer barycentric tabanlıdır ve Fabian Giesen'in grafik pipeline yazılarından ve Pineda'nın 1988 makalesinde açıklanan paralel rasterization yönteminden büyük ölçüde etkilenmiştir
  • Gerçek PS2 GS, DDA yani scanline rasterizer'dır; ancak GS DDA'nın bit düzeyinde doğru açıklaması bilinmediğinden barycentric yöntem kullanılır
  • Wide line ve sprite uygulaması için parallelogram da desteklenir
  • inv_area, özel fixed-point RCP ile hesaplanır
    • Standart GPU RCP, uygulamalar arasında tutarlılığı düşük ve yaklaşık 22.5 bit hassasiyetli olduğundan bundan kaçınılır
    • Özel RCP yaklaşık 24.0 bit hassasiyet hedefler
  • Binning genellikle 32×32 piksellik bloklar kullanır
    • Render pass başına maksimum primitive sayısı u16 indeks nedeniyle 64k'dir
    • Gözlemlenen başlıca render pass'lar genellikle 10k~30k primitive aralığındaydı
  • PS2 GS yüksek fill-rate'e ve piksel başına düşük karmaşıklığa sahip olduğundan saf ubershader mümkündür
    • N64'ün aksine bindless'tan yararlanılabildiği için texture işlemlerinin karmaşıklığı da azalır
  • Ubershader early Z, deferred on-tile shading ve lazy pixel shading kullanır
    • Piksel ancak önceki sonuca bağımlı olduğunda gerçekten shading yapılır
    • Alpha test, color write mask, alpha blending gibi durumlar bu bağımlılıkları oluşturur
    • Nihai framebuffer rengi ve depth, SSBO'ya yazılır; böylece GPU bant genişliği kullanımı azalır

Supersampling ve yükseltme artefact'larının azaltılması

  • Yalnızca tek sample rendering, bu renderer'ın kullanışlılığı için yeterli değildir
  • Örneğin 8x SSAA'da GPU'da VRAM'in 10 sürümü tutulur
    • Tek sample VRAM 1 adet
    • Tek sample VRAM'in reference değeri 1 adet
    • 8 adet supersample
  • Rendering sırasında tek sample VRAM ve reference eşleşirse supersample sürümü yüklenir
    • Artımlı rendering'de önemlidir
  • Tile tamamlandığında clustered subgroup işlemleriyle multisample resolve yapılır ve supersample ile tek sample kopyaları yazılır
  • Supersampling, basit yükseltmeye göre daha az tırtıklıdır ve 3D öğelerle UI öğelerinin çözünürlük hissini daha tutarlı eşleştirir
  • Sprite primitive'leri her zaman single-rate render edilmelidir
    • Bunlar çoğunlukla UI veya benzeri öğelerdir; yükseltilirse amaçlanan rect dışını sample edebilir veya bilinear filtering nedeniyle aşırı bulanıklaşabilir
  • Pek çok UI normal üçgenlerle çizildiğinden, bazı flat primitive'lerde nitelik interpolasyonu single-pixel koordinatlara düşürülür
    • Perspective kullanılsa bile tüm vertex'lerin Q'su ve Z'si aynıysa flat UI primitive olarak tahmin edilir
    • False positive olabilir, ancak test edilen oyunlarda yeterince iyi çalışmıştır

Oyun bazlı sonuçlar ve zor örnekler

  • Tales of the Abyss'te PCSX2 Vulkan backend yükseltmesinde Yuri'nin bloom hizalama uyumsuzluğu ve kare desen görülür
    • paraLLEl-GS'in 8x SSAA'sında kötü yükseltmenin tipik sorunları pek görünmez
    • İlgili ekran görüntüsünde FSR1 post-process upscaling de uygulanmıştır
  • Final Fantasy X'in UI'ı, native resolution ile 4x upscale karşılaştırmasında yükseltme sorunlarını gösterir
    • MSAA snap trick, artefact'lardan kaçınmada etkilidir
    • Temel ilke, UI'ı nearest neighbor tam sayı ölçeğinden öteye yükseltmemektir
  • MGS2, PCSX2'de yüksek blending accuracy gerektiren örneklere sahiptir
    • PCSX2, programmable blending yolunda primitive başına bariyer eklediği için performans ciddi şekilde düşer
    • paraLLEl-GS her zaman %100 blend accuracy ile çalışacak yapıdadır; RX 7600'de 16x SSAA sahnesi 25 W ve %17 GPU utilization ile sunulmuştur
  • Valkyrie Profile 2'de kendi pikselinin alfasını palet indeksi olarak sample eden bir örnek vardır
    • paraLLEl-GS bunu algılayıp texture indeksini özel bir değere dönüştürür ve in-register framebuffer color'a başvurur
    • Bu optimizasyonla render pass barrier sayısı 500'ün üzerinden 18'e düşer
  • MGS2 intro'sundaki camo efekti, framebuffer'ı texture olarak sample eder ancak piksel hizalaması tutmayan örtüşen koordinatlar kullanır
    • PCSX2 de burada bariyer eklemiyor gibi görünür; paraLLEl-GS de aynı şekilde işler
  • Shadow of the Colossus güçlü bir stres testi işlevi görür
    • PCSX2 maksimum blend accuracy'de intro sırasında 2x upscale ile bile GPU 24 FPS'ye kadar düşer
    • paraLLEl-GS 8x SSAA'da bile iyi performans gösterir, ancak ilgili sahnede yük artar
    • Bu durumda darboğaz GPU'dan çok CPU'nun geometry processing'indedir

Mevcut durum ve sonraki adımlar

  • Mevcut testlerin pratik yöntemi GS dump kullanmaktır
  • PCSX2'de raw GS trace dump alınmasını sağlayan bir hack-patch vardır
  • mkfifo üzerinden kaba gerçek zamanlı test de mümkündür
  • Son kullanıcılar için yararlı hale gelmesi, bir şekilde emülatör entegrasyonu gerektirir
  • PS2 kütüphanesi çok büyük olduğundan hâlâ birçok bug'ın gizli kalmış olma olasılığı yüksektir
  • Standalone library niteliği nedeniyle, eski tarz bir rendering API'si gibi kullanılabilecek potansiyel kullanım örnekleri de vardır

1 yorum

 
GN⁺ 2024-09-03
Hacker News yorumları
  • Bu yazıda GS kısaltmasının ne zaman açılacağını uzun süre aramam gerekecek sandım. İçerik ilginç ama burada biraz geride bırakılmış gibi hissettiriyor

    • Graphics Synthesizer’ın kısaltması; Sony’nin PS2’nin “GPU”suna verdiği ad
  • “Programlanabilir blending olsun diye dua et” denmiş; 2000’lerin başında pixel shader’ları ilk öğrendiğimden beri “blending shader” üzerinden programlanabilir blending’i hep istedim
    Bu arada özel texture formatları/sıkıştırma, texture birleştirme vb. için kullanışlı olacak “texture shader” üzerinden programlanabilir texture decoding de istemiştim
    Bir şekilde GPU’lar programlanabilir blending’den önce ray tracing kazandı; ilki yaz gecesi rüyası gibiydi, ikincisi ise bir başka sabit işlev bloğunu programlanabilir bir bloğa çevirmek gibi gelmişti. Texture shader’ları hâlâ bekliyorum

    • PowerVR soyunu devralan mobil GPU’larda böyle bir özellik var
      https://medium.com/pocket-gems/programmable-blending-on-ios-...
      https://developer.apple.com/videos/play/tech-talks/605
    • En son ellediğimde mobil PowerVR GPU’larda programlanabilir blending vardı; hatta blending yapmanın tek yolu buydu. PS Vita’da blend state’i değiştirmek yaklaşık 1 ms sürüyordu, pek hoş değildi
    • VK_EXT_fragment_shader_interlock bir tür programlanabilir blending sayılmaz mı diye düşünüyorum. DirectX tarafındaki Raster-order-views da öyle
      Bunu kullanan iyi bir örnek var: https://vulkan.org/user/pages/09.events/vulkanised-2024/vulk...
    • Bugünlerde mesh shader’lar, work graphs, CUDA, genel C++ shader’ları da var. OTOY artık render işleminin tamamını compute ile yapıyor
    • Bunların çoğu Vulkan veya DX12’de emüle edilebilir gibi geliyor; diğer API’leri pek bilmiyorum. Ama gerçek kullanım alanlarını merak ediyorum
      Bir dereceye kadar uygulanabilir olduğunu düşünüyorum; ancak ikna edici kullanım örnekleri yoksa uygulama işini gerekçelendirmek zor olur
  • GS’de en sevdiğim kısım veri yolu mimarisinin akıl almaz ölçeğiydi. Toplamda 2560 bit genişliğindeydi ve cache bölümlendirmesi de zekiceydi
    PS3 bazı açılardan geriye gidiş gibi hissettirmişti; özellikle blending konusunda

  • Bu yaklaşımın Dolphin’in ubershader’ı ile nasıl karşılaştırıldığını merak ediyorum

    • Temelde neredeyse karşılaştırılacak şeyler değiller. Dolphin’in ubershader’ı, modern esnek donanımla sabit işlevli blending/texturing’i taklit eden tek bir işi yapıyor
      Aslında Dolphin bunu getirdiğinde bile zaten eski bir teknikti. Bu proje, rasterizer dâhil tam bir renderer ve metinde görüldüğü gibi blending için ubershader da içeriyor
      Shader üçgen çizmek yerine, üçgenin içindeki her nokta için çağrılıyor; birkaç girdi alıp o noktanın rengini belirliyor. Bütün bir CPU emülatörünü yalnızca ADD/MUL komutlarını uygulayan bir şeyle karşılaştırmaya belli belirsiz benziyor
  • top-left raster ne demek merak ediyorum