نمایش HN: بازیهای PS2 به صورت ایستا در WebAssembly کامپایل شدهاند
آدرس مقاله: https://github.com/snuri00/ps2-web-recomp آدرس نظرات: https://news.ycombinator.com/item?id=50052251 امتیاز: 229 # نظرات: 118
بازی های پلی استیشن 2 در مرورگر بدون شبیه ساز اجرا می شوند. کد ماشین Emotion Engine بازی پیش از موعد به C++ ترجمه شده، در WebAssembly کامپایل شده و در برابر اجرای مجدد سیستم عامل و سخت افزار PS2 که با WebGPU طراحی می شود، پیوند داده شده است.
این برادر psp-web-recomp است و از همین روش پیروی می کند. اولین عنوانی که به این شکل مطرح شد Gran Turismo 4 است که به این دلیل انتخاب شد که یکی از سختترین بازیهای سیستم است: بر هر دو واحد برداری، رفتار دقیق ممیز شناور برای بازپخشها و آزمایشهای مجوز و ترفندهای گرافیکی که تصاویر رندر شده را به صورت پالت میخوانند، متکی است. از راهاندازی تا فیلم افتتاحیه، منوها و مسابقهها، با موسیقی و جلوههای صوتی پخش میشود و در کارت حافظه ذخیرهشده در مرورگر ذخیره میشود.
مسابقه ای با سرعت کامل، 50 فریم در ثانیه برای این نسخه PAL، در کروم روی پردازنده گرافیکی یکپارچه لپ تاپ با وضوح دو برابر PS2 اجرا می شود، به همین ترتیب ضبط بالا انجام شد.
God of War II از همان فیلمنامه ها پیروی کرد. به هیچ کد مخصوص بازی نیاز نداشت، اما دو باگ پیدا کرد که Gran Turismo 4 هرگز آنها را راهاندازی نکرده بود، یکی در کامپایلر و دیگری در زمانبندی واحد برداری، و چند قطعه گمشده از پردازنده ورودی/خروجی PS2. در حال حاضر از منوها از طریق کات سین های خود به مبارزات بازی می کند و 60 فریم در ثانیه را در نبردهای سنگین با وضوح PS2 در کروم نگه می دارد. با رزولوشن دو برابر بیشتر از 48 تا 60 کار می کند که توسط GPU یکپارچه لپ تاپ محدود شده است.
هیچ داده بازی در اینجا گنجانده نشده است. شما یک تصویر دیسکی از یک بازی که مالک آن هستید می آورید و اسکریپت های موجود در این مخزن آن را به یک صفحه وب در دستگاه شما تبدیل می کند.
کامپایل مجدد. eerecomp فایل اجرایی بازی را می خواند، جریان کنترل آن را دنبال می کند تا توابع را پیدا کند و C++ را برای آنها در واحدهای 16 کیلوبایتی کد مهمان منتشر می کند. خروجی ویژگیهای R5900 را حفظ میکند: ثباتهای 128 بیتی و دستورالعملهای چندرسانهای که روی آنها کار میکنند، شکافهای تاخیر انشعاب، واحد ممیز شناور PS2 که هیچ بینهایت یا غیرعادی ندارد و متفاوت از IEEE دور میشود، و واحد برداری 0 که بهعنوان یک پردازنده کمکی استفاده میشود.
پرشهای بین واحدها به تماسهای دم تبدیل میشوند و یک سوئیچ رشته پشته میزبان را باز میکند و در آدرس مهمان از سر گرفته میشود، بنابراین کد تولید شده هرگز به پشته در هر رشته مهمان نیاز ندارد.
یک هسته کوچک PS2. سمت EE سیستم عامل توسط شبیه سازی سطح بالا پاسخ داده می شود: رشته ها، سمافورها، کنترل کننده های وقفه برای کنترلر DMA و قسمت خالی عمودی، آلارم ها و کانال SIF که CPU اصلی را به پردازنده I/O متصل می کند. پردازنده ورودی/خروجی ماژولهای IRX خود بازی، درایور صدا، کد پخش دیسک و درایورهای کنترلکننده و کارت حافظه را که روی دیسک ارسال میشوند، روی مفسر پردازنده R3000 آن بازی اجرا میکند، در حالی که هسته IOP و کتابخانههایی که این ماژولها تماس میگیرند در C++ دوباره پیادهسازی میشوند.
دیسک از طریق درخواست های محدوده HTTP خوانده می شود، بنابراین فقط فایل اجرایی از قبل دانلود می شود.
واحدهای برداری بیشتر کارهای سه بعدی یک بازی PS2 در ریزبرنامه هایی انجام می شود که بازی در واحد برداری 1 آپلود می شود. آنها با تاخیرهای خط لوله MAC، پرچم های وضعیت و کلیپ مدل سازی شده اجرا می شوند، زیرا بازی ها کد خود را در اطراف آنها برنامه ریزی می کنند، و با قوانین شناور PS2 برای مقادیر ویژه: بدون بی نهایت یا NaN ها، بزرگ ترین ها به صفر می رسند و سرازیر می شوند. محاسبات به خودی خود مربوط به میزبان است، بنابراین نتیجه می تواند در بیت آخرش با کنسول متفاوت باشد. کنسول در جایی که میزبان دور می شود کوتاه می شود و ضرب کننده آن دقیق نیست.
ریزبرنامههایی که در طول اجرای آزمایشی مشاهده میشوند، پیش از موعد در SIMD C++ کامپایل میشوند. هر صفحه ای که برای اولین بار ملاقات می کند در محل به یک ماژول WebAssembly ترجمه می شود که برای هر برنامه چند میلی ثانیه طول می کشد.
گرافیک. حافظه ویدیویی 4 مگابایتی Graphics Synthesizer روی GPU، در طرحبندیهای چرخان شده اصلی خود، زندگی میکند، و طراحی توسط سایهزنهای محاسباتی WebGPU انجام میشود: مثلثها در کاشیها قرار میگیرند و با قوانین خود GS برای بافت، پالتها، ترکیب آلفا، عمق و آزمایشهای آلفای مقصد شطرنجی میشوند. از آنجایی که همه چیز در حافظه GS باقی میماند، افکتهایی که به یک بافت تبدیل میشوند یا یک فریمبافر را بهعنوان یک پالت دوباره تفسیر میکنند، همانطور که روی کنسول انجام میدهند، عمل میکنند. وضوح داخلی می تواند یک، دو یا سه برابر وضوح داخلی باشد.
یک شطرنج ساز نرم افزاری از همان GS به عنوان مرجعی که خروجی GPU با آن مقایسه می شود، باقی می ماند.
موضوعات. شبیهسازی در یک Worker اجرا میشود، واحد برداری 1 یک کارگر مخصوص به خود را دارد که به طور موازی روی کنسول اجرا میشود، و دستورات GS از طریق یک بافر حلقه به رشته صفحه میرود که صاحب دستگاه WebGPU است.
صدا و فیلم. SPU2 با 48 صدای ADPCM، پاکتهای ADSR و بازیهای جریان DMA که برای موسیقی استفاده میکند، دوباره پیادهسازی شده است و خروجی آن از طریق حافظه مشترک به AudioWorklet میرود. فیلم ها توسط پیاده سازی IPU، رمزگشای MPEG-2 PS2 رمزگشایی می شوند.
مسابقه اول به صورت بومی با سرعت 3 فریم در ثانیه اجرا شد، با یک شطرنجساز نرمافزار و یک مفسر برای واحد برداری 1. انتقال GS به سایهزن محاسباتی WebGPU و کامپایل کردن برنامههای واحد برداری به صورت بومی به حدود 70 فریم رسید، و بیشتر کارهای باقیمانده در مورد جایی بود که مرورگر زمان خود را صرف کرد تا شبیهسازی.
اولین نسخه رندر GPU حدود 650 ارسال محاسباتی در هر فریم ارائه کرد، زیرا هر بارگذاری بافت و بارگذاری پالت دستهای از مثلثهای اطراف خود را تقسیم میکرد. بالا بردن بارگذاریها جلوتر از مثلثهایی که به آنها وابسته نیستند، با همان خروجی، آن را به حدود 80 رساند.
Gran Turismo 4 همان 3 مگابایت بافت را در هر فریم از مسابقه آپلود می کند. حافظه GS بین فریمها جابجا میشود، بنابراین نمیتوان به سادگی از آپلودها صرفنظر کرد، اما یک حافظه پنهان ۱۶ مگابایتی از محتویات اخیرا آپلود شده در GPU به اکثر آنها اجازه میدهد بدون عبور مجدد از WebAssembly به GPU ارائه شوند، که باعث کاهش دادههای ارسالی در هر فریم در حدود ۶۰ درصد میشود.
در سه برابر رزولوشن اصلی، نرخ فریم به 23 کاهش یافت. نمایه سازی سایه زن نشان داد که تنظیمات هر مثلث پس از اینکه شاخهای که بین رشتهها متفاوت بود، بارگیری میشد که کل تنظیمات را متفاوت میکرد، و آپلودهایی که به قطعات 1 مگابایتی تقسیم میشدند، رشته صفحه کروم را مسدود میکرد. بارگذاری یکنواخت تنظیمات و نوشتن داده های هر فریم به صورت یکپارچه، وضوح سه برابر را به 48 تا 50 فریم در ثانیه در همان GPU یکپارچه رساند.
زمانبندی خود مرورگر نیز مهم بود. تماسهای تودرتوی setTimeout به 4 میلیثانیه بسته میشوند، بنابراین حلقه شبیهسازی اکنون از طریق یک MessageChannel تسلیم میشود، و شبیهسازی را به یک worker منتقل میکند، به طوری که رشته صفحه فقط GPU را تغذیه میکند، مسابقهای را که بین 40 تا 50 فریم در ثانیه با 50 ثابت سرگردان بود، تثبیت کرد.
God of War II از ISO به صفحه ای از طریق port.sh تنها با تغییرات کلی تبدیل شد و هر کدام یک باگ یا شکافی بود که بازی اول به سادگی از آن استفاده نکرده بود.
با سکوت شروع شد درایور صدای بازی، تیک خود را از یکی از تایمرهای سخت افزاری پردازنده I/O هدایت می کند، که هیچ چیز آن را اجرا نکرده بود، بنابراین درایور هرگز نمونه ای را با هم ترکیب نکرد. مقدمه بعد از منوی سختی شامل چند ضلعی های مسطح بزرگ بود، و زمانی که گیم پلی شروع می شد، پس از اینکه لیست رندر آن از انتهای حافظه گذشته بود و دسته های خالی عمودی خود را بازنویسی می کرد، بازی از کار افتاد.
دنبال کردن یک لیست پیوندی خراب با نقاط نظارت سختافزاری منجر به کامپایلر مجدد شد: وقتی یک شاخه آخرین دستورالعمل یک واحد 16 کیلوبایتی بود، مسیری که شاخه را نمیگیرد به جای بعد از آن در واحد بعدی در شکاف تاخیر ادامه مییابد، آن دستورالعمل را دوبار اجرا میکند، یا اجرای دستورالعمل احتمالا یک شاخه باید رد میشد. این کار خرابی را برطرف کرد و بازی به نبرد رسید، همچنان که چند ضلعی های غول پیکر روی بخش هایی از صفحه نمایش وجود داشت. آن ها از مثلث هایی می آمدند که باید از بین می رفتند.
میکروکد God of War II، کلیپها را سه دستورالعمل بعد از CLIP که آنها را تنظیم میکند، میخواند، و مدل واحد برداری آنها را تنها پس از چهار قابل مشاهده میکند، که Gran Turismo 4، که میکروکد آن همیشه بیشتر منتظر میماند، نمیتوانست متوجه آن شود.
در طول مسیر، همچنین به یک مسیر بوت مانند بایوس، یک ماژول بارگیری شده از حافظه به جای دیسک، و حالت زنجیره مقصد DMA اسکرچ پد نیاز داشت.
به git، CMake، Ninja، یک کامپایلر C++20 (کلنگ ترجیح داده میشود و بهطور خودکار برداشت میشود)، Python 3 و 7-Zip اگر تصویر دیسک شما در 0.7z باشد، نیاز دارید. همه چیز روی لینوکس روی x86-64 اجرا شده است. اولین اجرا Emscripten SDK را در tools/emsdk نصب میکند، یا از یک نقطه EMSDK استفاده میکند و wgpu-native را برای runner اصلی دانلود میکند. با --native-only اسکریپت از ساخت مرورگر پرش می شود و هرگز SDK را نصب نمی کند. سپس پنجره اصلی به فایل های توسعه SDL2 نیاز دارد. انتظار می رود حدود 300 مگابایت فضای دیسک در هر بازی برای کد تولید شده و ساخت. خود تصویر دیسک پیوند داده شده است، کپی نشده است.
port.sh یک تصویر دیسک می گیرد (یک ISO، یا یک آرشیو ZIP یا 7z که یکی را نگه می دارد) و کل کار را انجام می دهد. برنامه بوت نامگذاری شده در SYSTEM.CNF دیسک را میخواند، کد دستگاه خود را به C++ ترجمه میکند و سپس بازی را چند بار بدون هد اجرا میکند. تجزیه و تحلیل استاتیک نمی تواند هر تابعی را که یک بازی از طریق اشاره گر به آن می رسد، ببیند، بنابراین هر اجرا آدرس هایی را که نمی تواند پیدا کند، یادداشت می کند، اسکریپت آنها را به عنوان نقاط ورودی اضافه می کند و دوباره ترجمه می کند، تا زمانی که یک اجرا بدون آدرس های جدید انجام شود. همین اجراها، ریزبرنامههای واحد برداری را که بازی آپلود میکند، ضبط میکنند، که زودتر از موعد کامپایل میشوند.
در آخر صفحه را به build/web-mygame و رانر بومی را در build/native-mygame میسازد. God of War II در یک لپ تاپ 8 هسته ای در عرض سه دقیقه و نیم از ISO به صفحه می رود.
مراحل کاوش شروع را فشار می دهند و سپس هر پنج ثانیه یکبار عبور می کنند، که اکثر بازی ها را از طریق صفحه نمایش عنوان آنها می گیرند، و یک کارت حافظه خالی را در شیار 1 به روش صفحه قرار می دهند، بنابراین مسیری را که بازیکن طی می کند دنبال می کنند. --press 900:cross با ورودی های خودتان جایگزین می شود و --frames تعیین می کند که هر اجرا چقدر طول می کشد. فریمهای آخرین اجرا در بازیها/mygame/shots میآیند، که سریعترین راه برای دیدن اینکه یک بازی جدید تا کجا پیش میرود. scripts/build_web.sh mygame فقط صفحه را بازسازی می کند.
انتظارات خود را بر این اساس تنظیم کنید: تا کنون دو بازی مطرح شده است. بازی دیگری به احتمال زیاد در یک فراخوانی سیستمی یا یک تابع کتابخانه IOP که هنوز کسی اجرا نکرده است متوقف می شود، که رانر آن را به عنوان [sys] syscall کنترل نشده یا [iop] فاقد <library> #<n> ثبت می کند، یا از ویژگی GS استفاده می کند که رندر هنوز آن را مدیریت نکرده است.
صفحه به WebGPU برای رندر GPU نیاز دارد. کروم و اج آن را در ویندوز و macOS دارند. در لینوکس به طور پیشفرض در Chrome و Chromium خاموش است: chrome://flags/#enable-unsafe-webgpu و chrome://flags/#enable-vulkan را روشن کنید و مرورگر را مجددا راهاندازی کنید، همانطور که صفحه به شما میگوید وقتی آداپتور قابل استفادهای پیدا نکرد. بدون WebGPU، رندر نرم افزار کار را به دست می گیرد، که برای منوها خوب است و برای سه بعدی بسیار کند است. در لپتاپهای دارای دو پردازنده گرافیکی، گاها نمیتوان یک بوم نقاشی روی GPU مجزا را روی صفحه نمایش داد. صفحه هر دو آداپتور را امتحان می کند و به کپی فریم ها از طریق یک بوم دو بعدی بازمی گردد.
صفحهکلید دکمههای صورت را به Z (متقاطع)، X (دایره)، A (مربع) و S (مثلث)، شانهها به Q، W، 1 و 2، شروع و انتخاب کنید تا Enter و Backspace، D-pad به کلیدهای جهتدار و سمت چپ به I، J، K و L میچسبد. گیمپدها از طریق گیمپد API مرورگر کار میکنند. محرکهای آنالوگ آنها بهعنوان L2 و R2 حساس به فشار DualShock 2 به بازی میرسند و کنترلکنندههای دارای موتور در مرورگرهایی که لرزش را نشان میدهند (Chrome و Edge) هنگامی که بازی درخواست میکند، میلرزند. تلفنها کنترلهای روی صفحه را دریافت میکنند و F تمام صفحه را تغییر میدهد. انتخابگر وضوح صفحه را در اندازه جدید بارگیری مجدد می کند.
رانر بومی، build/native-mygame/ps2run، زمانی که SDL2 نصب شده است، بازی را در یک پنجره اجرا می کند. scripts/port.sh mygame "My Game.iso" --native-only فقط این یکی را می سازد. از طریق wgpu-native روی Vulkan میکشد و صدا را از طریق SDL پخش میکند. صفحهکلید از کلیدهای صفحه برای دکمهها استفاده میکند، کلیدهای پیکان هم بهعنوان پد D و هم بهعنوان چوب چپ، و هر کنترلکننده بازی که SDL تشخیص میدهد نیز کار میکند، شامل کنترلهای خود Steam Deck، با فشار ماشه و لرزش مشابه در مرورگر.
- تمام صفحه در تمام صفحه شروع می شود و F11 به جلو و عقب سوئیچ می کند. PS2_SCALE=2 یا 3 وضوح داخلی را افزایش می دهد. همانند مرورگر، شبیهسازی، واحد برداری 1 و GS هر کدام بر روی رشته خاص خود اجرا میشوند، که God of War II را از 43 به 60 فریم در ثانیه در رزولوشن اصلی روی لپتاپ آزمایشی میبرد. PS2_ONE_THREAD=1 همه چیز را در یک رشته قرار می دهد. فایل کارت حافظه از طرح بندی مشابه کارت های صادر شده صفحه استفاده می کند. در Steam Deck، رانر را می توان به عنوان یک بازی غیر Steam به Steam اضافه کرد، اگرچه هنوز کسی آن را روی یکی امتحان نکرده است.
این صفحه یک کارت حافظه را در شکاف 1 قرار می دهد و آن را برای آدرسی که از آن پخش می کنید در حافظه مرورگر نگه می دارد، بنابراین پیشرفت از بارگذاری مجدد نجات پیدا می کند. بازی ها را فرمت کنید و از آن مانند یک کارت واقعی استفاده کنید. کارت صادرات آن را بهعنوان یک فایل ps2. در طرحبندی که PCSX2 استفاده میکند، دانلود میکند، و کارت Import چنین فایلی را دوباره در شکاف قرار میدهد، بنابراین کارت میتواند بین مرورگرها حرکت کند یا از PCSX2 بیاید. ?nocard با خالی بودن اسلات شروع می شود.
این صفحه از SharedArrayBuffer برای رشتههای خود استفاده میکند، بنابراین باید به صورت جدا شده از مبدا متقابل، با Cross-Origin-Opener-Policy: same-origin و Cross-Origin-Embedder-Policy: require-corp ارائه شود. scripts/serve.py هر دو هدر را ارسال میکند و از درخواستهای Range که پخش دیسک به آنها متکی است، پشتیبانی میکند. هر کسی که بتواند به سرور دسترسی پیدا کند می تواند بازی را بارگذاری کند، بنابراین آن را برای خود نگه دارید.
این مخزن تنها حاوی کد اصلی و کد شخص ثالث تحت مجوز خود است. این شامل هیچ کد یا داده بازی نیست. ++C تولید شده و WebAssembly ساخته شده ترجمه های اجرایی بازی هستند، بنابراین به صاحبان بازی تعلق دارند: آنها را در دستگاه خود نگه دارید و آنها را منتشر نکنید. از تصاویر دیسک بازی هایی که دارید استفاده کنید. Gran Turismo، God of War و PlayStation علائم تجاری Sony Interactive Entertainment هستند. این پروژه به سونی، پلیفونی دیجیتال، استودیو سانتا مونیکا یا هر ناشر بازی وابسته یا تایید نشده است.
جداول VLC رسیور MPEG از Play اقتباس شده است! (بند 2 BSD، اطلاعیه در زمان اجرا/src/ipu/NOTICE). PCSX2، بازی! و هدرهای ps2sdk مرجع بسیاری از سخت افزار و رفتار سیستمی بودند که در اینجا شبیه سازی شده اند. wgpu-native و Dawn WebGPU را به صورت بومی و در مرورگر ارائه می دهند و Emscripten WebAssembly را می سازد.
کد موجود در این مخزن تحت مجوز MIT موجود است، به LICENSE مراجعه کنید. runtime/src/ipu/tables.hpp اطلاعیه BSD 2-clause Play! را حفظ می کند.
متن اصلی (انگلیسی)
Show HN: PS2 games statically recompiled to WebAssembly
Article URL: https://github.com/snuri00/ps2-web-recomp Comments URL: https://news.ycombinator.com/item?id=50052251 Points: 229 # Comments: 118