遊戲軟體開發,硬體的架構影響有多大?
PS3早期,一度流傳同一個遊戲,因為ps3的cpu架構太古怪而導致遊戲質量普遍比360差,索尼吸取了這個教訓,這次ps4全面換成了x86架構。
我的困惑是,現在遊戲(或引擎)開發,難道需要直接去執行cpu指令,而不是通過官方提供的sdk進行軟體開發?sdk不能屏蔽這些嗎?
影響很大。硬體的功能及性能直接影響遊戲軟體的開發。
題主談及PS3和Xbox360,本人參與過兩個相關遊戲項目的開發,就簡單介紹一下它們硬體的差異如何影響軟體開發。
PS3的特別之處,是它有一個主要的CPU(稱為PPU),另外有6個可用的輔助處理單元(稱為SPU)。軟體開發難處在於,那些SPU各有256KB本地內存,開發者要把想並行計算的數據從主內存打包發送到SPU,完成工作後再把結果抄回來!SPU還用另一套指令集、編譯程序!
PS3 Cell處理器
而Xbox360則是3核CPU,能正常地訪問512MB主內存(統一內存架構)。雖然不是x86架構,但能直接用正常的多線程方式來做並行。
Xbox360 Xenon處理器
至於SDK,微軟提供Direct3D 9變種的API,移植PC的代碼變得簡單得多。而PS3的圖形API則是非常低階的,顯存的地址分配都要開發者自己實現。另外,遊戲的優化是需要針對個別CPU/GPU去做的,例如用該CPU/SPU的SIMD指令集去編程。
如果同時還要跨平台至Wii,那才是惡夢!因為Wii的"GPU"是固定管道的!Shader Model 1.0都沒有啊!做個材質效果要設置幾十個渲染狀態,像IQ題一樣!想用cube map做反射效果要先把它貼在球體上渲染至frame buffer(render target都沒有!),然後抄到紋理,再把紋理當作sphere map來用啊!!Wii所有內存加起來100MB都沒有,而且有分快主內存、慢主內存、幀緩衝內存、紋理內存啊!!你說怎麼影響軟體開發!!
吐完。
早期的遊戲機主機為了提高性能(主要是圖形性能)往往採用了大量的定製晶元,這就導致給它們編程非常困難。
在早期 PC,畫圖的方法就是寫顯存,在屏幕上畫條線就是把線條覆蓋到的像素對應的顯存給寫成 1,換言之,顯示屏上所有的像素在都有對應的地址。但是遊戲機不同。早期「遊戲機」的顯存並不直接存儲像素,相反它存儲了構成遊戲畫面的數據結構,比如各個圖塊的內容(起始地址)、位置、調色板,高級些的主機還有大小旋轉等。有些像什麼呢?像 HTML+CSS……對,就是 HTML4 時代做無 flash 頁游。
遊戲機和 PC 統一架構是第八代才做到的,當然苗頭是在第六代,這個過程 M¥ 功不可沒……
給你們上一段 FC 的技術資料(經過逆向工程得到),讓你們感受下 1980 年代的十一區為了榨取機器性能都幹了些什麼事情
+--------+
| 4. PPU |
+--------+
A. 概述
-------
鏡像 (也被稱為"shadowing") 是將特殊的地址或抵制範圍通過硬體映射到其他地址的一種處理.
B. 內存映射
-----------
這裡有兩 (2) 片內存映射. 第一部分被稱為 "RAM Memory Map",是一段不算長的指向NES本身
物理RAM的區域。第二部分是 "Programmer Memory Map",是比較長的描述全部NES和他如何被使
用以及如何被操作的內存區域.
RAM Memory Map
+---------+-------+--------------------+
| Address | Size | Description |
+---------+-------+--------------------+
| $0000 | $1000 | Pattern Table #0 |
| $1000 | $1000 | Pattern Table #1 |
| $2000 | $800 | Name Tables |
| $3F00 | $20 | Palettes |
+---------+-------+--------------------+
Programmer Memory Map
+---------+-------+-------+--------------------+
| Address | Size | Flags | Description |
+---------+-------+-------+--------------------+
| $0000 | $1000 | C | Pattern Table #0 |
| $1000 | $1000 | C | Pattern Table #1 |
| $2000 | $3C0 | | Name Table #0 |
| $23C0 | $40 | N | Attribute Table #0 |
| $2400 | $3C0 | N | Name Table #1 |
| $27C0 | $40 | N | Attribute Table #1 |
| $2800 | $3C0 | N | Name Table #2 |
| $2BC0 | $40 | N | Attribute Table #2 |
| $2C00 | $3C0 | N | Name Table #3 |
| $2FC0 | $40 | N | Attribute Table #3 |
| $3000 | $F00 | R | |
| $3F00 | $10 | | Image Palette #1 |
| $3F10 | $10 | | Sprite Palette #1 |
| $3F20 | $E0 | P | |
| $4000 | $C000 | F | |
+---------+-------+-------+--------------------+
C = Possibly CHR-ROM
N = Mirrored (see Subsection G)
P = Mirrored (see Subsection H)
R = Mirror of $2000-2EFF (VRAM)
F = Mirror of $0000-3FFF (VRAM)
C. Name Tables
--------------
NES使用馬賽克矩陣進行圖形顯示; 這樣的格子被叫做 Name Table. 馬賽克是 8x8像素 [pixels].
完整的 Name Table 有 32*30 個馬賽克 (256*240 像素). 緊記: NTSC和PAL單元在顯示上有不同
的刷新率.
Name Tables 之中的馬賽克的資料被保存在 Pattern Table 之中 (continue on).
D. Pattern Tables
-----------------
Pattern Table 包括了 Name Table 所需要的 8x8 的馬賽克. 他保存了NES調色板中所有16色的
4-bit 顏色矩陣的低兩 (2) 位 [bit]. 例如:
VRAM Contents of Colour
Addr Pattern Table Result
------ --------------- --------
$0000: %00010000 = $10 --+ ...1.... Periods are used to
.. %00000000 = $00 | ..2.2... represent colour 0.
.. %01000100 = $44 | .3...3.. Numbers represent
.. %00000000 = $00 +-- Bit 0 2.....2. the actual palette
.. %11111110 = $FE | 1111111. colour #.
.. %00000000 = $00 | 2.....2.
.. %10000010 = $82 | 3.....3.
$0007: %00000000 = $00 --+ ........
$0008: %00000000 = $00 --+
.. %00101000 = $28 |
.. %01000100 = $44 |
.. %10000010 = $82 +-- Bit 1
.. %00000000 = $00 |
.. %10000010 = $82 |
.. %10000010 = $82 |
$000F: %00000000 = $00 --+
上面的 Pattern Table 的結果就是字元 "A",就像右上角的 "Colour Result" 部分顯示的.
E. Attribute Tables
-------------------
Attribute Table 的每一個位元組都描述了顯示器上的一個 4*4 馬賽克組. 有許多種方法來描述
Attribute Table 中一 (1) 個位元組的函數是怎樣的:
* 保存了 32*32 像素格子 中每 16*16 像素 的高兩 (2) 位.
* 保存了 十六 (16) 個 8x8 馬賽克中的 高兩 (2) 位.
* 保存了 四 (4) 個 4*4 像素格子 的高兩 (2) 位.
這樣說確實很亂; 下面的兩個圖表將有所幫助:
+------------+------------+
| Square 0 | Square 1 | #0-F represents an 8x8 tile
| #0 #1 | #4 #5 |
| #2 #3 | #6 #7 | Square [x] represents four (4) 8x8 tiles
+------------+------------+ (i.e. a 16x16 pixel grid)
| Square 2 | Square 3 |
| #8 #9 | #C #D |
| #A #B | #E #F |
+------------+------------+
真正的 attribute byte 格式如下 (與上面的相比較):
Attribute Byte
(Square #)
----------------
33221100
||||||+--- Upper two (2) colour bits for Square 0 (Tiles #0,1,2,3)
||||+----- Upper two (2) colour bits for Square 1 (Tiles #4,5,6,7)
||+------- Upper two (2) colour bits for Square 2 (Tiles #8,9,A,B)
+--------- Upper two (2) colour bits for Square 3 (Tiles #C,D,E,F)
F. 調色板
---------
NES有兩個 16色 "調色板": 圖形調色板和子圖形調色板. 因為他們不儲存物理RGB值,所以
比起一個真正的調色板,它們更像 "查找表格".
寫到 $3F00-3FFF 的 D7-D6 位元組將被忽略.
G. Name Table 鏡像
------------------
需要緊記的一點是在理解NES的時候,有許多鏡像表格. 即使在使用 CHR-ROM-mapped Name Tables
(mapper-specific).
NES本身只有 2048 ($800) 位元組的RAM給 Name Tables. 然而,就像在 Subsection B 中所表現
得那樣 NES 有升至 四 (4) 個 Name Tables 的地址容量.
預設的是許多 carts 伴隨的是 "水平" 和 "垂直" 的鏡像,允許你修改 Name Tables 所指向的NES
PPU RAM 的位置. 這個鏡像表格同時作用於兩 (2) 個Name Tables; 你不可以獨自選擇 Name Tables.
下面的表格將幫助理解NES中遇到的所有鏡像類型. 請注意顯示的地址 (大小為 12-bit) 都是 NES PPU
RAM 的 Name Table 的一部分; 有人為認為這些與VRAM區域中的 "$2xxx" 同義:
Name NT#0 NT#1 NT#2 NT#3 Flags
+--------------------------+------+------+------+------+-------+
| Horizontal | $000 | $000 | $400 | $400 | |
| Vertical | $000 | $400 | $000 | $400 | |
| Four-screen | $000 | $400 | $800 | $C00 | F |
| Single-screen | | | | | S |
| CHR-ROM mirroring | | | | | C |
+--------------------------+------+------+------+------+-------+
F = 依賴於擴展的 2048 ($800) RAM (kept on the cart) 的四屏鏡像,導致四 (4) 個獨立的物理
Name Tables.
S = 擁有映射表 [mapper] 的單屏遊戲,允許你選擇哪個PPU RAM區域來使用 ($000, $400, $800,
$C00); 所有的 NT 都指向同樣的 PPU RAM 地址.
C = 映射表 #68 (Afterburner 2),允許你將 CHR-ROM 鏡像到 NES 的 PPU RAM 區域中 Name Tables
區域. 很自然的這使得 Name Table 成為 ROM 基礎,並且不能對它寫入. 然而,這個特點可以通
過映射表本身進行控制,使得你打開或關閉這個特點.
H. 調色板鏡像
-------------
鏡像發生在圖形調色板和子圖形調色板之間. 任何寫入 $3F00 的數據都被鏡像到 $3F10. 任何寫入 $3F04
的數據都被鏡像到 $3F14,如此. 如此...
在圖形調色板和子圖形調色板的高三 (3) 色 Colour #0 被定義為透明 (實際上那裡儲存的顏色不被顯示).
PPU 使用 $3F00 來定義背景色.
另一個長一些的解釋,如下:
* $0D 被寫入 $3F00 (鏡像到 $3F00)
* $03 被寫入 $3F08 (鏡像到 $3F08)
* $1A 被寫入 $3F18
* $3F08 被讀入 累加器 [accumulator]
PPU 使用 $0D 作為背景色,不論 $3F08 飽含了 $03 的值 (因為所有的調色板中的 colour #0 都被定義為
透明色,它不被顯示). 最後,累加器將保存 $1A 的值,它是 $3F18 的鏡像. 然後 $1A 不被顯示,因為
colour #0 為透明色.
圖形和子圖形調色板都被鏡像到其他的VRAM區域; $3F20-3FFF 分別是他們的鏡像.
被寫入 $3F00-3FFF 的 D7-D6 位元組被忽略.
I. 背景捲軸
-----------
NES能夠獨立於在背景之上的字畫面來捲動背景 (pre-rendered Name Table + Pattern Table + Attribute
Table). 背景能被水平和豎直捲動.
捲軸工作如下:
Horizontal Scrolling Vertical Scrolling
0 512
+-----+-----+ +-----+ 0
| | | | |
| A | B | | A |
| | | | |
+-----+-----+ +-----+
| |
| B |
| |
+-----+ 480
Name Table "A" 是通過在寄存器 $2000 中的 Bits D1-D0 來指定的,"B" 接下來的 Name Table (由於鏡
像,這是動態的). 它在同時使用水平和豎直捲軸的遊戲時不工作.
背景將跨越多個 Name Table,就像這裡展示的:
+---------------+---------------+
| Name Table #2 | Name Table #3 |
| ($2800) | ($2C00) |
+---------------+---------------+
| Name Table #0 | Name Table #1 |
| ($2000) | ($2400) |
+---------------+---------------+
寫到水平捲軸地址為 $2005 的值得範圍是 0 至 256.
寫到垂直捲軸的值是 0-239; 超過 239 的值時不被考慮的 (例如: 248 被認為是 -8).
J. 屏幕和子圖形的層
-------------------
在NES顯示的時候使用的是一種特殊的規則:
FRONT BACK
+----+-----------+----+-----------+-----+
| CI | OBJs 0-63 | BG | OBJs 0-63 | EXT |
+----+-----------+----+-----------+-----+
| SPR-RAM | | SPR-RAM |
| BGPRI==0 | | BGPRI==1 |
+-----------+ +-----------+
CI 的意思是 "Colour Intensity" [顏色亮度], 與 $2001 的 D7-D5 等價. BG 是 背景 [BackGround],EXT
是 擴展埠視頻信號 [EXTension port video signal].
"BGPRI" 描述的是 SPR-RAM 中的 "Background Priority" [背景優先權] bit,在 per-sprite 基礎上 (D5,
Byte 2).
OBJ 數目描述真正的子圖形數目,不是 馬賽克索引值 [Title Index values].
FRONT 被認為是在其他所有層上被看到的 (最後繪製),BACK 被認為是其他所有層之下的 (最先繪製).
K. 子圖形和 SPR-RAM
-------------------
NES支持64個子圖形. 每個子畫面的大小可以是 8x8 或者 8x16 像素. 子畫面數據被保存在 VRAM 的 Partt-
ern Table 區域.
子畫面的特徵,比如 flipping 和 優先權,被保存在 SPR-RAM. SPR-RAM 的格式如下:
+-----------+-----------+-----+------------+
| Sprite #0 | Sprite #1 | ... | Sprite #63 |
+-+------+--+-----------+-----+------------+
| |
+------+----------+--------------------------------------+
+ Byte | Bits | Description |
+------+----------+--------------------------------------+
| 0 | YYYYYYYY | Y Coordinate - 1. Consider the coor- |
| | | dinate the upper-left corner of the |
| | | sprite itself. |
| 1 | IIIIIIII | Tile Index # |
| 2 | vhp000cc | Attributes |
| | | v = Vertical Flip (1=Flip) |
| | | h = Horizontal Flip (1=Flip) |
| | | p = Background Priority |
| | | 0 = In front |
| | | 1 = Behind |
| | | c = Upper two (2) bits of colour |
| 3 | XXXXXXXX | X Coordinate (upper-left corner) |
+------+----------+--------------------------------------+
Tile Index # 被獲得的方法與 Name Table 數據一樣.
大小為 8x16 的子畫面函數有些不同. 一個有偶數 Tile Index # 的 8x16 子畫面使用在 VRAM 中 $2000
的 Pattern Table; 奇數 Tile Index #s 使用 $1000. *注意*: 寄存器 $2000 對 8x16子畫面無效.
所有 64 個子圖形都包括一個內部優先權; 子畫面 #0 的優先權比 #63高 (子畫面 #0 應當被最後繪製,
etc...).
只有八 (8) 個子圖形可以被顯示在同一個掃描線 [scan-line] 上. 每個 SPR-RAM 入口都被檢測來知道他
是不是與其他的子圖形在同一水平線上. 記得,這是在每個掃描線的基礎上被執行的,不是在每個子畫面
的基礎上 (例如,做256次,而不是 256/8 或者 256/16次).
(注意: 在一個真正的NES單元,如果子畫面被關閉了很長一段時間 ($2001的D4是0),SPR-RAM 將被降低. 一
個被建議的觀點是 SPR-RAM 是一個真正的 DRAM,D4 控制這個 DRAM 的刷新周期).
L. 子圖形 #0 點擊標記
---------------------
PPU有能力演算出子圖形 #0的位置,並且把它的發現儲存到 $2002 的 D6. 工作的方式如下:
PPU掃描第一個真正的不透明 "子圖形像素" 和第一個不透明 "背景像素". 背景像素是被Name Table使用中
的馬賽克. 記得colour #0 被定義為透明.
導致 D6 被設置為 *IS* 的像素被繪製.
下面的例子也許有所幫助. 下面是兩個馬賽克. 透明色 (colour #0) 被通過下劃線字元 ("_") 定義. 一個
星號 ("*") 在 D6 被設置時描述.
Sprite BG Result
------ -- ------
__1111__ ________ __1111__
_111111_ _______2 _1111112
11222211 ______21 11222211
112__211 + _____211 = 112__*11 "*" will be drawn as colour #2
112__211 ____2111 112_2211
11222211 ___21111 11222211
_111111_ __211111 _1111111
__1111__ _2111111 _2111111
這同樣適用於那些在 BG 下面 (通過 "Background Priority" SPR-RAM bit),可是上面的例子應當為 "BG+Sprite".
同樣,D6在每個VBlank之後清空 (設置為0).
M. 水平和豎直空白
-----------------
就像其他的控制台,NES有一個更新: 顯示設備重新定位電子槍來顯示可視數據. 最普通的顯示設備是電視機. NTSC
設備每秒鐘刷新60次,PAL每秒50次.
電子槍繪製像素時從左向右: 這種過程導致一 (1) 條水平掃描線被繪製. 在電子槍繪製完成一條完整的掃描線後,電
子槍必須回到顯示設備的左邊,準備好去繪製下一條掃描線. 電子槍回到左邊的過程叫做 水平空白期 [Horizontal
Blank period] (HBlank).
當電子槍完成繪製所有掃描線,它必須回到顯示設備的最上端; 電子槍複位回到顯示設備的最上端的時間叫做 豎直空
白期 [Vertial Blank period] (VBlank).
就像你能從下面的圖表中看到的,電子槍或多或少的工作在 "Z" 字形軌跡直到到達 VBlank,然後過程重複:
+-----------+
+---&>|***********| &<-- Scanline 0
| | ___---~~~ | &<-- HBlank
V |***********| &<-- Scanline 1
B | ___---~~~ | &<-- HBlank
l | ... | ...
a | ... | ...
n |***********| &<-- Scanline 239
k +-----+-----+
| |
+--VBlank--+
NTSC的NES是下面的刷新方式和屏幕格式:
+--------+ 0 ----+
| | |
| | |
| Screen | +-- (0-239) 256x240 on-screen results
| | |
| | |
+--------+ 240 --+
| ?? | +-- (240-242) Unknown
+--------+ 243 --+
| | |
| VBlank | +-- (243-262) VBlank
| | |
+--------+ 262 --+
豎直空白 (VBlank) 標記被包括在 $2002 的 D7中. 它指出PPU是否處於VBlank. 一段程序可以通過讀 $2002 重置D7.
N. $2005/2006 矩陣編碼
----------------------
有關於 $2005 和 $2006 寄存器的詳細信息,參考 Loopy 的 $2005/2006 文檔. 他的文檔提供了的關於這些寄存器
如何工作的完整準確的信息. 聯繫 Loopy 獲得更多信息.
O. PPU 怪癖
-----------
第一次讀VRAM是無效的. 由於這種現象,NES返回 pseudo 緩衝值,而不是期待的線性值. 看下面的例子:
VRAM $2000 contains $AA $BB $CC $DD.
VRAM incrementation value is 1.
The result of execution is printed in the comment field.
LDA #$20
STA $2006
LDA #$00
STA $2006 ; VRAM address now set at $2000
LDA $2007 ; A=?? VRAM Buffer=$AA
LDA $2007 ; A=$AA VRAM Buffer=$BB
LDA $2007 ; A=$BB VRAM Buffer=$CC
LDA #$20
STA $2006
LDA #$00
STA $2006 ; VRAM address now set at $2000
LDA $2007 ; A=$CC VRAM Buffer=$AA
LDA $2007 ; A=$AA VRAM Buffer=$BB
就像演示的,PPU將在第一次讀入執行後傳遞增加他的內部地址. 這 *僅適用* 於 VRAM $0000-3FFF (例如: 調色板信
息和他們各自的鏡像不必忍受這種現象).
P. 注意
-------
PPU 將會在訪問 $2007 後自動增加VRAM地址,加1或者32 (基於 $2000 的 D2).
本代主機的情況請看葉老師的答案,說的很清楚了。
PS3這代機器其實已經很好了,再往上一代的PS2是個遊戲史上數得著的奇葩機器。我們都知道硬體TL是從GEFORCE這代顯卡才開始採用的,而過去的幾何與光源運算全是扔給CPU做的,顯卡只負責光柵化之後的工作填。XBOX和NGC(也就是後來的WII)都跟上了時代的潮流,而PS2仍然沿用的是90年代的思路,一個極為強大的CPU EE+單純的光柵化渲染器GS。於是這主機就出現了一個奇觀:爺爺輩的VOODOO1,第一代GPU GEFORCE1, 第一代可編程GPU GEFORCE3,技術相差10年原理完全不同的顯卡同台競爭,而且遊戲要實現互相移植!
遊戲機原理統一是PS3和XBOX360這代才開始的,過去遊戲機之間硬體原理完全不同。遊戲機的硬體構架之恐怖,最資深的PC程序員在最可怕的噩夢中都不會夢到。
比如SS,這是一台純2D主機,但是要做3D遊戲。於是SEGA公司提供了一個思路:2D遊戲機一般有專門的處理器負責顯示畫面上的角色(活動塊,直譯精靈),它有將方形的圖像單位做旋轉變形的功能,所以就將一個活動塊視為一個多邊形,按照透視關係做相應的變形拉伸,看上去好像是一塊3D材質貼圖。而CPU負責幾何運算,提供活動塊的位置、大小和扭曲量。這和現代3D遊戲的差別,就如同鳥與火箭的原理差別。
再早的機器就更誇張了。
比如MD CD 32X這個機器,它是3台機器並聯,2台是16位匯流排,1台是32位匯流排(而且這台還是雙CPU的),兩邊匯流排不能直接交換數據,一般開發商的思路就是乾脆讓16位CPU計算背景,32位CPU計算前景畫面,兩邊直接輸出成視頻信號再做合成。
有人可能會說,反正都是編程嘛——誰告訴你的?早期遊戲機是沒有處理器的,人家用的是分離式邏輯電路(當時集成電路很罕見),想要改變程序?自己做個板子插門電路吧。
最BT的還是FC主機,遊戲卡裡帶專用晶元是家常便飯,典型的是任天堂出的MMC,控制內存翻頁的。熟悉FC的人可能聽說過MAPPER這個詞,它就是專門用來模擬MMC硬體的函數庫。MAPPER編號是開發模擬器的人給各遊戲廠商專有MMC指定的代號,這個代號我記得有200多個。好了,你要是任天堂,怎麼給200種遊戲機寫驅動?
有興趣的可以看看這個介紹
32X之死——遊戲機硬體增強的歷史 - 未曾發生的遊戲史 - 知乎專欄
這是FC的MMC3晶元
推薦閱讀:
※如何看待VGtime盜取抄襲PSNINE獎盃翻譯、圖片、TIPS?
※有哪些遊戲中時間會隨著遊玩不斷流逝,如果你不幹正事就會導致壞結局的遊戲?
※近年出現的新的遊戲IP中,有哪些可能成為經久不衰的系列遊戲?
TAG:遊戲 | Xbox | 遊戲開發 | PlayStation |