CD Code Digest Hub /GTA V · v2

grcore:设备抽象、渲染上下文、Effect 绑定、纹理/RT/状态块与 GPU 同步

103 分钟阅读 20 个章节 2 张图

rage/base/src/grcore 是 RAGE 图形栈的最底层:它把 D3D11(PC 与 Xbox One/Durango)、GNM(PS4/Orbis)、GCM(PS3)、D3D9(Xbox 360/Xenon、32 位 PC 工具)等原生 API 包装成一组以 grc 为前缀的平台无关类型,供 grmodel/rmcore(见 models-drawables-and-shaders.md)以及游戏渲染管线(见 ../11-rendering/render-pipeline-and-phases.md)使用。本页解释:

  • 编译期后端选择与“一个类、多份平台实现”的组织方式;
  • grcDevice 的初始化、帧生命周期(BeginFrame/EndFrame)与 present/flip;
  • 渲染线程 TLS 上下文、多渲染线程(MTR)、命令缓冲段、GPU fence 与帧延迟节流;
  • grcEffect/grcInstanceData/grcCBuffer 的着色器参数模型、technique group 与绑定流程;
  • 状态块、纹理与纹理工厂、渲染目标、顶点声明/缓冲、grcViewport、grcImage/DDS;
  • 显存/资源分配与平台差异。

背景与相关页面#

术语:本页“平台”指编译目标;“后端”指 config.h 选出的图形 API 宏组合。“MTR”是源码里的 Multiple Render Threads。

相关源码文件#

文件作用
grcore/config.h、config_switches.h、effect_config.h、effect_mrt_config.h、allocscope.h、gfxcontextcontrol.h后端宏、着色器/实例化开关、MTR 线程数、alloc scope/上下文控制开关
grcore/device.hgrcDevice 全部接口(约 3100 行),fence、MSAA/EQAA、绘制、上下文
grcore/device_d3d11.cpp(PC+Durango)、device_gnm.cpp(Orbis)、device_gcm.cpp(PS3)、device_d3d.cpp(Xenon/D3D9 PC)、device_win32.cpp(PC 窗口)、device_ogl.cpp、device_psp2.cpp各后端实现
grcore/gfxcontext_gnm.*、gfxcontext_durango.*、fencepool.h、wrapper_d3d.*、wrapper_gnm.*、wrapper_gcm.*命令缓冲段/上下文、fence 池、原生 API 包装
grcore/effect.h、effect_load.cpp、effect_render.cpp、effect_update.cpp、effect_resource.cpp、effect_pcd3d11.cpp、effect_durango.cpp、effect_gnm.cpp、effect_gcm.cpp、effect_d3d.cppEffect 系统
grcore/stateblock.h/.cpp光栅/深度模板/混合/采样器状态块
grcore/texture.h/.cpp、texturepc.*、texture_d3d11.*、texturefactory_d3d11.*、texture_durango.*、texture_gnm.*、texturefactory_gnm.*、texturegcm.*、texturexenon.*、texturereference.*、texturestring.*纹理、纹理工厂、纹理引用
grcore/rendertarget_d3d11.*、rendertarget_durango.*、rendertarget_gnm.*平台渲染目标(含 Durango ESRAM)
grcore/fvf.h、fvfchannels.h、vertexdecl.h、vertexbuffer*.{h,cpp}、indexbuffer*、buffer_d3d11.*、instancebuffer.*顶点格式/声明/缓冲、实例缓冲
grcore/viewport.h/.cpp、cullstatus.h视口、矩阵与视锥裁剪
grcore/image.h/.cpp、image_jpeg.cpp、image_png.cpp、dds.hCPU 侧图像与文件格式
grcore/resourcecache.h/.cppPC 显存资源缓存/延迟释放
grcore/grcsetup.cpp、setup.hgrcSetup:样例/工具程序用的“设备+帧”外壳
grcore/im.h、im_*.cpp、debugdraw.*、font.*、quads.*、fastquad.*立即模式绘制、调试绘制、字体
grcore/grcorespu*.{h,cpp}、edge_*、edgegeomspu*、instancerendererspu*PS3 SPU 命令生成与 Edge 作业(见 models-drawables-and-shaders.md)
grcore/gnmx/1.6、gnmx/1.7随源码携带的 Sony Gnmx 辅助库(第三方,未展开)

上表中的文件名相对于 src/dev_ng/rage/base/src/;正文引用一律显示相对源码根的完整路径。

1. 后端矩阵:编译期选择,而非运行期多态#

config.h 用一组互斥宏选择后端;没有 DX12、Prospero(PS5)或 Scarlett(Xbox Series)分支——在 grcore 内检索 RSG_PROSPERO|RSG_SCARLETT|D3D12 无匹配。

平台宏__D3D__D3D9__D3D11__D3D11_1__GCM__GNM__OPENGL证据
__PS30000100src/dev_ng/rage/base/src/grcore/config.h:12-19
__OSX0000001src/dev_ng/rage/base/src/grcore/config.h:20-27
__PSP2全 0(仅桩实现)src/dev_ng/rage/base/src/grcore/config.h:28-35
__XENON0x900100000src/dev_ng/rage/base/src/grcore/config.h:36-43
RSG_ORBIS0000010(另 ENABLE_LCUE 0)src/dev_ng/rage/base/src/grcore/config.h:44-52
RSG_DURANGO0xB00011000src/dev_ng/rage/base/src/grcore/config.h:53-60
__WIN32PC 64 位游戏0xB00010000src/dev_ng/rage/base/src/grcore/config.h:61-66
__WIN32PC 32 位 / __TOOL / __RESOURCECOMPILER0x900100000src/dev_ng/rage/base/src/grcore/config.h:67-75

要点:

  • Durango 与 PC 共享 D3D11 代码:device_d3d11.cpp 只以 #if (__D3D11) 包裹 src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:11,内部再以 RSG_DURANGO/RSG_PC 分叉;Durango 额外启用“mono driver”(__D3D11_MONO_DRIVER)src/dev_ng/rage/base/src/grcore/config.h:140-142。
  • D3D_ONLY/D3D11_ONLY/GCM_ONLY/GNM_ONLY/D3D11_OR_ORBIS_ONLY 等宏在声明处裁剪平台专有参数 src/dev_ng/rage/base/src/grcore/config.h:78-124。
  • 着色器侧 __SHADERMODEL 30/40/50 反推 __D3D9/__D3D11;实例化 technique(RAGE_INSTANCED_TECH)、几何着色器实例化阴影(GS_INSTANCED_SHADOWS)、曲面细分(RAGE_SUPPORT_TESSELLATION_TECHNIQUES)、反转投影(SUPPORT_INVERTED_PROJECTION)在 PC D3D11/Durango/Orbis 上启用,修改后需重编着色器 src/dev_ng/rage/base/src/grcore/config_switches.h:5-65。
  • 每个后端文件用顶层条件编译自我屏蔽,例如 device_gnm.cpp 的 #if __GNM(第 8 行)、device_gcm.cpp 的 #if __GCM(第 8 行)、device_d3d.cpp 的 #if __XENON/#if __WIN32PC(第 37/47 行)、device_psp2.cpp 的 #if __PSP2(第 6 行)。

原生句柄类型同样按后端 typedef:D3D11 下 grcDeviceHandle=ID3D11Device(1)、grcContextHandle=ID3D11DeviceContext(1)、grcContextCommandList=ID3D11CommandList;Durango 下上下文是 grcGfxContextD3dWrapper,命令列表是 grcGfxContextCommandList;Orbis 下上下文与命令列表都是 rage::grcGfxContext src/dev_ng/rage/base/src/grcore/effect.h:283-300、src/dev_ng/rage/base/src/grcore/device.h:246-251。

2. grcDevice:静态单例式设备接口#

grcDevice 自述为“Abstraction of a Direct3D device for higher-level code”,所有入口用 grcEntry(即 static)声明 src/dev_ng/rage/base/src/grcore/device.h:598-607。GRCDEVICE_IS_STATIC 在非 PC 为真:此时 GRCDEVICE 是一个全局对象;PC 上则是 *g_pGRCDEVICE(为 InitSingleton 留出间接层),grcPure 被定义为空(注释掉的 = 0)src/dev_ng/rage/base/src/grcore/device.h:431-439、src/dev_ng/rage/base/src/grcore/device.h:3130-3135。因此“多平台”不是虚函数派发,而是同一组静态成员函数在不同 .cpp 中各有一份定义,链接期只会编进当前后端那一份。

2.1 接口分组#

分组代表接口声明
生命周期InitClass、ShutdownClass、PrepareForShutdown、Manage(消息泵)、BeginFrame、EndFramesrc/dev_ng/rage/base/src/grcore/device.h:628-647
Fence/GPU 同步AllocFence、CpuMarkFencePending/Done、GpuMarkFencePending/Done、IsFencePending、CpuWaitOnFence、GpuWaitOnFence、CpuWaitOnGpuIdlesrc/dev_ng/rage/base/src/grcore/device.h:653-806
着色器创建/绑定CreateVertexShader…CreateHullShader、SetVertexShader…、SetVertexShaderConstantF/B/I、SetInputLayoutsrc/dev_ng/rage/base/src/grcore/device.h:886-949
立即模式顶点BeginVertices、BeginIndexedVertices、EndCreateVertices、DrawVertices 等src/dev_ng/rage/base/src/grcore/device.h:1779-1834
绘制/计算SetIndices、SetStreamSource、DrawIndexedPrimitive、DrawPrimitive、DrawInstanced*、Dispatch、RunComputation、DrawIndexedInstancedIndirectsrc/dev_ng/rage/base/src/grcore/device.h:1872-1919
顶点声明CreateVertexDeclaration、SetVertexDeclaration、RecordSetVertexDeclarationsrc/dev_ng/rage/base/src/grcore/device.h:1531-1545
渲染目标/MSAA/EQAA/深度边界SetRenderTarget(s)、MsaaResolve、ResolveMsaaBuffer、SetAACount、SetDepthBounds 等src/dev_ng/rage/base/src/grcore/device.h:1286-1345
多线程上下文GetAndIncContextSequenceNumber、CreateContextForThread、BeginContext、EndContext、ExecuteCommandListsrc/dev_ng/rage/base/src/grcore/device.h:2645-2681
遮挡/条件渲染查询CreateOcclusionQuery、GetOcclusionQueryData、BeginConditionalRendersrc/dev_ng/rage/base/src/grcore/device.h:2469-2505
PC 窗口/设备丢失ChangeDevice、ResizeBuffers、DeviceLost、DeviceRestored、RegisterDeviceLostCallbacks 及大量窗口状态位src/dev_ng/rage/base/src/grcore/device.h:1557-1729、src/dev_ng/rage/base/src/grcore/device.h:1974-1993

2.2 关键常量与容量#

常量值证据
grcmrtColorCountWin32(PC/Durango)8 个颜色 MRT,否则 4src/dev_ng/rage/base/src/grcore/device.h:301-319
MAX_GPUSPC 4,其他 1(PC 用于 SLI/CrossFire 下按 GPU 轮换的资源)src/dev_ng/rage/base/src/grcore/device.h:70-74
MAX_CONSTANT_BUFFER / MAX_RAGE_VERTEXBUFFER_SLOTS / MAX_RESOURCES14 / 16 / 128(后两者 D3D11)src/dev_ng/rage/base/src/grcore/device.h:582-585
BEGIN_VERTICES_MAX_SIZEPS3/PSP2 8122 B;Durango/Orbis 128000 B;其余 BANK 20 MB、非 BANK 4 MB(BEGIN_INDICES_MAX_SIZE 65535)src/dev_ng/rage/base/src/grcore/device.h:1756-1767
MAX_FRAMES_RENDER_THREAD_AHEAD_OF_GPUPC 取 GetMaxQueuedFrames();Xenon 2;其他 1src/dev_ng/rage/base/src/grcore/device.h:276-280
DRAWABLE_STATS__BANK 且 PC/Durango/Orbis;按 LOD/SLOD/道具/植被/人物/载具统计 draw callsrc/dev_ng/rage/base/src/grcore/device.h:528-580
功能开关DEVICE_MSAA(D3D11/Orbis)、DEVICE_EQAA(Orbis/Durango)、DEVICE_GPU_WAIT(PPU/Durango/Orbis)、DEVICE_CLIP_PLANES、DEPTH_BOUNDS_SUPPORT、USE_NV_TXAA、ATI_EXTENSIONS、NV_SUPPORTsrc/dev_ng/rage/base/src/grcore/device.h:30-63、src/dev_ng/rage/base/src/grcore/device.h:153-154

3. 初始化顺序#

RAGE 样例与工具通过 grcSetup 驱动设备:grcSetup::BeginGfx 在非 -nographics 时调用 GRCDEVICE.InitClass(inWindow, topMost),再初始化 grcGpuTimeStamp src/dev_ng/rage/base/src/grcore/grcsetup.cpp:1085-1096;CreateDefaultFactories 创建纹理工厂(-nographics 时为字符串工厂)并调用 grcTextureFactory::InitClass src/dev_ng/rage/base/src/grcore/grcsetup.cpp:764-783。grcSetup::BeginDraw/EndDraw 分别包裹 GRCDEVICE.BeginFrame()/GRCDEVICE.EndFrame() src/dev_ng/rage/base/src/grcore/grcsetup.cpp:1331-1338、src/dev_ng/rage/base/src/grcore/grcsetup.cpp:2360。游戏侧如何组织这些调用(CApp/渲染线程)见 ../01-runtime/threading-and-render-thread.md。

3.1 D3D11(PC/Durango)grcDevice::InitClass#

InitClass 定义于 src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:1085,按以下顺序推进(只列关键步骤):

  1. 确定窗口尺寸(-width/-height、全屏/窗口、显示器最近模式)src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:1116-1262。
  2. 特性级别:列表为 11_1(仅 __D3D11_1)、11_0、10_1、10_0;非 PC 运行时强制只用 11_0;PC 可由 -DX11/-DX10_1/-DX10 参数收窄 src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:1313-1346。PC 通过动态获取的 D3D11CreateDeviceAndSwapChain 入口创建设备与交换链 src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:1308-1310、src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:1375,并把选中的级别映射为 SetDxFeatureLevel/SetDxShaderModel(11_0→SM5.0、10_1→SM4.1…)src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:1416-1440。Durango 用 CreateSwapChainForCoreWindow src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:1622。
  3. grcStateBlock::InitClass() 创建默认状态块 src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:1691。
  4. grcEffect::InitClass() 并加载默认 effect(sm_DefaultEffectName)src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:1744-1746。
  5. grcViewport::InitClass()、Blit_Init()、GRC_Init() src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:1759-1762。GRC_Init 创建查询、Durango 的 MemoryCS 计算着色器,以及帧延迟 fence 数组(PC 上 ms_MaxQueuedFrames 默认等于 GPU 数,可由 -FrameQueueLimit 覆盖)src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:737-766。
  6. Durango 额外初始化命令缓冲段:grcGfxContext::init(0x80, 0x40000)(128 段 × 256 KB),随后为主渲染线程 openContext(false, ...) src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:1852-1855。

3.2 GNM(Orbis)grcDevice::InitClass#

  • 命令缓冲环:MTR 构建下 initRing(144 个上下文, 16 个 CUE 环项, 128 段, DCB 段 256 KB, CCB 段 64 KB);“旧式”单线程渲染为 initRing(8, 64, 128, 1 MB, 256 KB)。注释说明 DCB:CCB 经验比约 6:1、DCB 至少 64 KB、两者不得超过 4 MB-4 的间接硬件上限 src/dev_ng/rage/base/src/grcore/device_gnm.cpp:1049-1070。
  • 为“缺失顶点数据”建立隐藏流 4(单顶点颜色缓冲)并对每个渲染线程设置 src/dev_ng/rage/base/src/grcore/device_gnm.cpp:1270-1278。
  • 主上下文 openContext(false, seq);分配 ES→GS、GS→VS 环(各 kGsRingSizeSetup4Mb)、全局资源表与曲面细分因子环 src/dev_ng/rage/base/src/grcore/device_gnm.cpp:1283-1300。
  • grcEffect::InitClass 与默认 effect、MemoryCS 分别在 src/dev_ng/rage/base/src/grcore/device_gnm.cpp:1225、src/dev_ng/rage/base/src/grcore/device_gnm.cpp:1246、src/dev_ng/rage/base/src/grcore/device_gnm.cpp:1264。

4. 帧生命周期#

4.1 D3D11(PC/Durango)#

图中各步骤证据:LockContext、帧延迟等待、sm_Controller 50 ms 超时等待 src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:2661-2691;状态清理、后备缓冲锁定(NO_BACKBUFFER_LOCK 在 Durango 为 1)与 grcEffect::BeginFrame src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:2659、src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:2772-2828;EndFrame 中 effect 收尾、帧计数、流清理、信号量、MSAA resolve src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:2840-2893;写帧 fence、Durango grcGfxContext::endFrame 与 DXGIXPresentArray src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:2944-2978。

边界行为:

  • PC 在设备未创建、暂停、重置中、丢失、尺寸拖拽、释放交换链等状态下 BeginFrame 休眠 50 ms 并返回 false;被遮挡时以 DXGI_PRESENT_TEST 试探并在恢复后 ChangeDevice src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:2693-2770。调用方必须检查返回值。
  • PC 的 vsync 间隔来自窗口 uFrameLock,可被 -frameLimit、BANK 控件或 GetFrameLockOverride() 覆盖 src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:2900-2923。
  • 设计意图(注释):帧延迟 fence 防止 GPU 落后过多——在 Durango 上 GPU 过度落后会读到已被 CPU 复用的内存;代码中多处默认“GPU 不会太落后” src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:371-387。
  • BeginFrame 末尾回收溢出 fence(s_FencePool.CleanUpOverflow())src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:2826。
  • BeginFrame 中存在一个 PORTCULLIS_ENABLE 条件下的完整性检查钩子 src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:2818-2824,EndFrame 之后还有与之配套的状态块处理代码;这些属于反篡改范畴,本页不展开,见 ../23-debug-security/security-overview.md。

4.2 GNM(Orbis)#

  • BeginFrame:自旋(sceKernelUsleep(30))直到 GPU 已完成的帧标签 *s_label 与 sm_FrameCounter 的差不超过 MAX_FRAMES_RENDER_THREAD_AHEAD_OF_GPU(Orbis 为 1),超时报告 GPU 挂起;然后 waitUntilSafeForRendering、状态块复位、设置 DX 裁剪空间、InitSubThread、回收溢出 fence src/dev_ng/rage/base/src/grcore/device_gnm.cpp:1371-1414。
  • EndFrame:MSAA 后备缓冲 resolve 或 FinalizeFastClear;(注释承认 EndFrame 并非真正在帧尾调用,因而手动弹出故障上下文栈);EOP 写入帧号到 s_label;grcGfxContext::submitAndFlip;立即为下一帧 openContext 并重置硬件默认状态与 CU 掩码 src/dev_ng/rage/base/src/grcore/device_gnm.cpp:1417-1460。

5. 渲染线程、上下文与多渲染线程(MTR)#

5.1 线程所有权模型#

  • 当前图形上下文是 TLS:extern __THREAD grcContextHandle *g_grcCurrentContext src/dev_ng/rage/base/src/grcore/device.h:284。CheckThreadOwnership() 就是“本线程的 g_grcCurrentContext 非空” src/dev_ng/rage/base/src/grcore/device.h:2459-2460,grcViewport::GetCurrent/SetCurrent 等用它断言 src/dev_ng/rage/base/src/grcore/viewport.h:526-529。
  • D3D11 的 LockContext/UnlockContext 是引用计数 + 互斥量:首次加锁时把 g_grcCurrentContext 指向主(立即)上下文或其包装器,末次解锁时清空 src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:2595-2626。这表明在 PC 上其他线程(例如加载线程创建资源)可以通过加锁临时“借用”设备上下文(推断:依据是互斥量+引用计数的设计,未逐一核对调用点)。
  • 渲染线程索引 g_RenderThreadIndex 是 __THREAD u8 src/dev_ng/rage/base/src/grcore/effect_render.cpp:40;不开 MTR 的构建里它是常量 0(enum { g_RenderThreadIndex = 0 })src/dev_ng/rage/base/src/grcore/effect.h:311-312。
  • DECLARE_MTR_THREAD 在 PC 上为空、其他平台为 __THREAD src/dev_ng/rage/base/src/grcore/config.h:128-132:大量“当前状态”静态量(当前 technique/pass、活跃状态块、当前视口、当前着色器程序等)由此在主机上按线程隔离,在 PC 上为普通全局。

5.2 MTR 配置#

宏值证据
MULTIPLE_RENDER_THREADSDurango/Orbis 为 4(子渲染线程数);其他平台 0src/dev_ng/rage/base/src/grcore/effect_mrt_config.h:2-11
MULTIPLE_RENDER_THREADS_ALLOW_DRAW_LISTS_ON_RENDER_THREADPC、Durango 为 1,Orbis 为 0(注释:Scaleform 与调试 drawlist 需要在主渲染线程执行)src/dev_ng/rage/base/src/grcore/effect_mrt_config.h:25-26
NUMBER_OF_RENDER_THREADSMTR 时为 MTR + 上一项,否则 1;即 Durango 5、Orbis 4、PC 1src/dev_ng/rage/base/src/grcore/effect_mrt_config.h:28

结论:PC 版的 grcore 不启用子渲染线程(MULTIPLE_RENDER_THREADS == 0),只有一个渲染线程使用 D3D11 立即上下文;CreateContextForThread 中的 D3D11 deferred context 代码在 PC 上被编译但其上层 MTR 路径关闭。游戏层是否另行在 PC 上使用 deferred context 未在本目录内找到证据。

5.3 子线程上下文协议#

步骤D3D11 PCDurangoOrbis
CreateContextForThread(工作线程,一次)CreateDeferredContext(1),可选长生命周期命令列表;为本线程建立动态 VB/IB(立即模式用)grcGfxContext::perThreadInit()空实现
BeginContext(control)(每帧)ResetContext():重置状态缓存、cbuffer、资源视图、立即模式缓冲、裁剪平面同上 + grcGfxContext::openContext(true, seq, control)Assert(!g_grcCurrentContext) 后 openContext(true, seq, control)
EndContextFinishCommandList(false,&list)grcGfxContext::closeContext() 返回 grcGfxContextCommandList*closeContext(false),返回上下文本身并清空 TLS
ExecuteCommandList(拥有设备的线程)ExecuteCommandList(list, -restoreContextState) 后 Releaselist->kick(false)见 src/dev_ng/rage/base/src/grcore/device_gnm.cpp:3312

证据:src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:7551-7716、src/dev_ng/rage/base/src/grcore/device_gnm.cpp:3286-3310。GetAndIncContextSequenceNumber 是简单的静态自增,用于给工作线程的上下文排序 src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:7551-7555。drawlist 如何分发到子渲染线程、谁调用 BeginContext/ExecuteCommandList,见 ../11-rendering/drawlists-and-render-thread-handoff.md。

5.4 命令缓冲段(Orbis grcGfxContext)#

  • grcGfxContext 继承 sce::Gnmx::GfxContext(ENABLE_LCUE=0 时)或 sce::LCUE::GraphicsContext;每个上下文最多积压 MaxPending = 128 个 DCB/CCB 段,状态 OPEN/CLOSED/CLOSED_AND_KICKED,当前上下文是 static __THREAD sm_Current src/dev_ng/rage/base/src/grcore/gfxcontext_gnm.h:103-145。
  • 接口:initRing/openContext/kickContext/kickContextIfPartialSubmitsAllowed/emergencyPartialSubmit/closeContext/submitAndFlip;注释警告上下文池必须大于单帧最大用量,否则线程时序会导致死锁 src/dev_ng/rage/base/src/grcore/gfxcontext_gnm.h:194-226。
  • 命令缓冲内分配:allocateFromCommandBufferAlignBytes 的生命周期绑定到当前 grcContextAllocScope,用于经资源描述符传给 GPU 的临时数据;为处理环绕,只失效 L1/K$ 与 L2 中的 volatile 行,因此对应 sce::Gnm::Buffer 必须调用 setBufferCachePolicy 标记 volatile src/dev_ng/rage/base/src/grcore/gfxcontext_gnm.h:236-273。embedDataDcb 只用于调试数据,不应被着色器读取 src/dev_ng/rage/base/src/grcore/gfxcontext_gnm.h:274-283。
  • 段内存来自 Onion/WB(CPU 要读链表、GPU 要写段,不能用 Garlic)src/dev_ng/rage/base/src/grcore/gfxcontext_gnm.cpp:799-806。
  • 提交被隔离到专用线程:sce::Gnm::submitCommandBuffers/submitAndFlipCommandBuffers/submitDone 可能长时间阻塞,所以渲染线程只把请求压入 16 项消息队列,由 [RAGE] GPU Submit 线程调用 src/dev_ng/rage/base/src/grcore/gfxcontext_gnm.cpp:703-751、src/dev_ng/rage/base/src/grcore/gfxcontext_gnm.cpp:904。
  • 调试开关:GRCGFXCONTEXT_GNM_BASIC_ERROR_HANDLING(非 MASTER)、GPU 挂起超时 10 s、逐 draw 同步标记、GRCDBG_PUSH/POP 故障上下文栈、GPU 空闲记录 src/dev_ng/rage/base/src/grcore/gfxcontext_gnm.h:23-60。

Durango 的 grcGfxContext 与 ID3D11DeviceContextX 共同组成逻辑上下文(注释明言为避免额外间接调用而不做纯 OO 包装),命令列表最多 128 段 src/dev_ng/rage/base/src/grcore/gfxcontext_durango.h:56-128。grcContextAllocScope(仅 Durango/Orbis)记录作用域内分配了哪些段,最多 256 段 src/dev_ng/rage/base/src/grcore/allocscope.h:11-80;grcGfxContextControl 用于跨线程异步控制上下文(段节流、部分提交)src/dev_ng/rage/base/src/grcore/gfxcontextcontrol.h:10-60。

6. GPU 同步:fence 与 fence 池#

grcFenceHandle 是指向 __grcFenceHandle 的不透明指针 src/dev_ng/rage/base/src/grcore/device.h:212-213。接口语义(注释):AllocFence 默认“已完成”,ALLOC_FENCE_INIT_AS_PENDING 初始化为“待定”;ALLOC_FENCE_ALLOC_SCOPE_LIFETIME 让 fence 生命周期等于当前 alloc scope;AllocFenceAndGpuWrite = 分配为待定 + GPU 写完成;GPU_WRITE_FENCE_AFTER_SHADER_READS/BUFFER_WRITES 是允许更早写入的提示;IS_FENCE_PENDING_HINT_NO_KICK 不触发 kick(误用会死锁);CpuWaitOnGpuIdle 只用于调试 src/dev_ng/rage/base/src/grcore/device.h:653-777。

FencePool(每个后端一个 static FencePool s_FencePool(64),见 src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:597、src/dev_ng/rage/base/src/grcore/device_gnm.cpp:166)以空闲链表管理固定数组;空闲链表耗尽时先回收“GPU 已标记 FREE”的待释放项,再不行就断言并从堆上紧急分配带 FLAG_OVERFLOW 的 fence;溢出项在每帧 CleanUpOverflow 中删除 src/dev_ng/rage/base/src/grcore/fencepool.h:70-200。

后端fence 状态值GpuMarkFenceDone 实现IsFencePendingGpuWaitOnFence
Orbis0xfe578001/2/3(PENDING/DONE/FREE)writeAtEndOfPipe(kEopFlushCbDbCaches,或 AFTER_SHADER_READS 时 kEopCbDbReadsDone)写 DONE读 state,必要时先 kick等待索引缓冲输入时 waitOnAddressAndStallCommandBufferParser(只能 >= 比较,故 DONE > PENDING 被编译期断言),否则 waitOnAddress
Durango同上WriteValueBottomOfPipe 写 DONE(注释:其事件为 CACHE_FLUSH_AND_INV_TS)同上InsertWaitOnMemory(July 2014 XDK 起)
PCMARKED_PENDING/QUERY_ISSUED/DONE/FREE(u16)发出 ID3D11Query 的 End,记录 query 索引轮询 GetData,完成后归还 query 位不支持(DEVICE_GPU_WAIT 关闭);GpuWaitOnPreviousWrites 为空操作,依赖 D3D 自动冒险跟踪

证据:状态定义 src/dev_ng/rage/base/src/grcore/fencepool.h:15-67;Orbis 实现 src/dev_ng/rage/base/src/grcore/device_gnm.cpp:3067-3188;Durango 实现 src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:3100-3220;PC 实现 src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:3283-3380。Durango 的 GpuMarkFencePending 需要额外 scratch 值让 PFP 阻塞,以避免 EOP 写入与后续 wait 的竞态(注释解释了 D3D11.x 不暴露 CP DMA 的限制)src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:3139-3159。CPU 等待在两种主机上都带 10 s 看门狗并调用 reportGpuHang src/dev_ng/rage/base/src/grcore/device_gnm.cpp:3131-3148。

Orbis/Durango 还提供 ShaderMemcpy/ShaderMemset32:用 GPU 着色器拷贝/填充,大小须为 64 字节倍数,不自动同步 src/dev_ng/rage/base/src/grcore/device.h:808-819。

7. Effect 系统:grcEffect / grcProgram / grcInstanceData#

7.1 概念 → 类型 → 职责#

概念类型职责 / 关键字段证据
编译后的 effect 文件grcEffecttechnique 数组、局部参数 m_Locals 与其 cbuffer、各阶段程序数组、m_TechniqueMap[64][RMC_COUNT]、默认实例数据 m_InstanceData、名称哈希src/dev_ng/rage/base/src/grcore/effect.h:2044-3033
technique / passgrcEffect::Technique、grcEffect_Technique_Passpass 记录 VS/PS/(GS/CS/DS/HS) 程序索引与光栅/深度模板/混合状态块句柄、AlphaRef、StencilRef;PC/Durango/Orbis 上 12 字节,否则 8 字节src/dev_ng/rage/base/src/grcore/effect.h:2016-2042、src/dev_ng/rage/base/src/grcore/effect.h:2058-2077
着色器程序grcProgram 及 grcVertexProgram、grcFragmentProgram、grcComputeProgram、grcDomainProgram、grcGeometryProgram、grcHullProgram每个程序记录引用的常量(m_Constants 索引表),Bind 时从实例数据取值src/dev_ng/rage/base/src/grcore/effect.h:777-781、src/dev_ng/rage/base/src/grcore/effect.h:1429-1972
参数描述grcParameter类型、寄存器、Usage 掩码、注解(int/float/string)src/dev_ng/rage/base/src/grcore/effect.h:950-1000
每实例参数值grcInstanceDataEntries(常量在前、纹理在后)、Basis 指向 grcEffect(资源中为哈希)、Material、DrawBucket/DrawBucketMask、IsInstanced、UserFlagssrc/dev_ng/rage/base/src/grcore/effect.h:1199-1392
常量缓冲grcCBufferD3D11/Orbis 下的 cbuffer;主机上每渲染线程一份 backing store 与脏标记src/dev_ng/rage/base/src/grcore/effect.h:521-620
材质库grcMaterialLibrary : pgDictionary<grcInstanceData>.sps 预设材质src/dev_ng/rage/base/src/grcore/effect.h:1132
句柄grcEffectVar、grcEffectGlobalVar、grcEffectTechnique强类型枚举句柄,0 表示无(grcevNONE 等)src/dev_ng/rage/base/src/grcore/effect_typedefs.h:11-13

VarType 枚举涵盖 float/vector/matrix/texture,PC/Durango/Orbis 额外有 int 向量、结构化缓冲、采样器状态、UAV、ByteAddressBuffer src/dev_ng/rage/base/src/grcore/effect.h:2101-2113。

7.2 容量上限#

项值证据
同时驻留 effect 数 c_MaxEffects512(满则 Quitf(ERR_GFX_EFFECT_1))src/dev_ng/rage/base/src/grcore/effect.h:2056、src/dev_ng/rage/base/src/grcore/effect_load.cpp:2404-2408
全局参数 c_MaxGlobalsXenon/PPU 196,其余 256src/dev_ng/rage/base/src/grcore/effect.h:781
technique group 数 MAX_TECHNIQUE_GROUPS64(注释:须为 4 的倍数且非 8 的倍数以满足对齐)src/dev_ng/rage/base/src/grcore/effect.h:2804
每阶段 cbuffer/UAV/采样器/纹理槽Orbis: kSlotCountConstantBuffer-1/kSlotCountRwResource/16/42;Durango: 14/16/16/42;PC D3D11: 14/4/16/42;其他 1/0/16/16src/dev_ng/rage/base/src/grcore/effect.h:751-771
effect 容器64 位或 PC 175 KB,否则 32 KBsrc/dev_ng/rage/base/src/grcore/config.h:126
grcInstanceData 大小32/48 字节(非工具),工具 36/56src/dev_ng/rage/base/src/grcore/effect.h:1386-1392

7.3 加载:从名字到 grcEffect#

  1. grcEffect::Create(name) 先 LookupEffect(atStringHash(name)) 去重,再新建并 Init,成功后 AssignToSlot src/dev_ng/rage/base/src/grcore/effect_load.cpp:2396-2420。CreateWithFallback 失败时用第一个预加载的 effect 兜底,或在禁用 fallback 时 Quitf src/dev_ng/rage/base/src/grcore/effect_load.cpp:2423-2436。
  2. Preload(dir, "preload") 读取 preload.list,逐行创建 src/dev_ng/rage/base/src/grcore/effect_load.cpp:2443-2460。游戏通过 grmShaderFactory::PreloadShaders("common:/shaders")(MSAA 时再加 preloadMS)间接调用,并在之后(断言构建中)禁止再加载 effect src/dev_ng/game/shaders/ShaderLib.cpp:265-275。
  3. GetFullName 拼出 <name>.fxc(PS3 为 .cgx),并插入平台目录:Xenon fxl_final、PS3 psn、Orbis orbis、Durango durango、PC D3D11 依立体/画质/SM 选择 win32_nvstereo、win32_40_lq、win32_50、win32_40,D3D9 win32_30;__FINAL 默认加 _final 后缀(-useFinalShaders 可覆盖)src/dev_ng/rage/base/src/grcore/effect_load.cpp:2635-2642、src/dev_ng/rage/base/src/grcore/effect_load.cpp:2727-2768。
  4. Init 校验魔数 rgxe/rgxf,不匹配直接 Quitf(ERR_GFX_INVALID_SHADER, "...recompile your shaders!") src/dev_ng/rage/base/src/grcore/effect_load.cpp:2872-2882;断言构建中若 sm_AllowShaderLoading 已关闭则报错(名字含 debug 的除外)src/dev_ng/rage/base/src/grcore/effect_load.cpp:2852-2858。
  5. Load 的文件布局(注释即格式规范):属性 → VS → PS → GS(含流输出描述)→ HS → DS → CS → 全局参数 → 局部参数 → technique(含 pass 与状态)src/dev_ng/rage/base/src/grcore/effect_load.cpp:3487-3560。全局参数按名称哈希合并进静态 sm_Globals(跨 effect 共享):重复者合并 Usage 位;PC/Durango/Orbis 上同一全局若属于不同 cbuffer 则 FatalAssert;D3D9/360 上寄存器地址变化会报 “probably need recompile”;新全局在数组未满时追加src/dev_ng/rage/base/src/grcore/effect_load.cpp:3794-3843。最后 ConnectParametersToPasses() 并按已注册的 technique group 哈希填 m_TechniqueMap src/dev_ng/rage/base/src/grcore/effect_load.cpp:4144、src/dev_ng/rage/base/src/grcore/effect_load.cpp:4203-4210。

7.4 Technique group 与 draw type#

eDrawType 包含 RMC_DRAW、RMC_DRAWSKINNED,以及(按开关)曲面细分与实例化变体,实例化变体必须排在末尾 src/dev_ng/rage/base/src/grcore/effect.h:2806-2823。RegisterTechniqueGroup(name) 为每个 draw type 生成 technique 名:default 组无前缀,其余为 <name>_draw、<name>_drawskinned、<name>_drawtessellated、<name>_drawinstanced… 并保存其哈希 src/dev_ng/rage/base/src/grcore/effect_load.cpp:4304-4360。注释强调:必须在任何着色器加载之前注册,之后注册的组对已加载的 effect 无效(映射表在 Load 中一次性填写)src/dev_ng/rage/base/src/grcore/effect.h:2825-2839。于是“选择 technique”退化为一次 m_TechniqueMap[group][type] 数组查表(GetDrawTechnique)。grmShader 如何选 group 见 models-drawables-and-shaders.md。

7.5 绘制时的绑定流程#

证据:BeginDraw 设置 sm_CurrentTechnique,多 pass 时强制 sm_RestoreState src/dev_ng/rage/base/src/grcore/effect.h:2314-2338;BeginPass/EndPass(PS3 GRCORE_ON_SPU>1 时改为写 SPU 命令)src/dev_ng/rage/base/src/grcore/effect_render.cpp:608-669;Pass::Bind 比较 RS_Active/DSS_Active/BS_Active 后设置状态块,PC 上 stencil ref 只覆盖 GetStencilRefMask() 选中的位,Orbis 还根据是否有 GS/DS 设置活动着色器阶段 src/dev_ng/rage/base/src/grcore/effect_render.cpp:706-818;grcInstanceData::Bind 在 D3D11/Orbis 上把带 SPECIAL_GLOBAL_TYPE_MASK 的条目解释为“全局纹理”,其余从 Data() 取局部值 src/dev_ng/rage/base/src/grcore/effect_render.cpp:206-330。注意 Orbis 的 cp/dp/gp/hp.Bind 在 LCUE 下被跳过(注释:绑定空计算程序会让 LCUE 崩溃),实际 ENABLE_LCUE=0 src/dev_ng/rage/base/src/grcore/effect_render.cpp:800-805。

7.6 全局变量与 cbuffer#

  • SetGlobalVar(handle, value) 系列最终走 SetGlobalFloatCommon:录制命令缓冲时(g_grcCommandBuffer)或句柄为 0 时静默忽略;D3D11/Orbis 上只要 Usage 包含任一程序阶段就写入 SetGlobalInternal<grcProgram>;DEV Win32 下会比较 C++ 类型与着色器声明类型并报错 src/dev_ng/rage/base/src/grcore/effect_update.cpp:862-926。
  • grcCBuffer 在 MTR 构建中每线程一份数据指针(GetDataPtrForThread(g_RenderThreadIndex)),PC 上则通过 LockDirect/UnlockDirect(即 BeginUpdate/EndUpdate)直接 Map src/dev_ng/rage/base/src/grcore/effect.h:558-620。
  • grcInstanceData::Data() 在 MTR 构建中返回 Entries + TotalSize * g_RenderThreadIndex,即每个材质实例为每个渲染线程各保留一份参数副本(FLAG_EXTRA_DATA 表示已扩展)src/dev_ng/rage/base/src/grcore/effect.h:1340-1364。这表明在主机上不同子渲染线程可并行为同一 shader 写不同的局部变量值而不互相覆盖(推断:依据是数据布局,未逐一核对所有写入点)。

7.7 Draw bucket 掩码#

DrawBucketMask 低 8 位是“基础桶”位,次 8 位是“子桶”位;BUCKETMASK_GENERATE(b) 产生 0xff00 | (1<<b);BUCKETMASK_MATCH(shader, passed) 要求两部分都有交集;位 15 是曲面细分位;BUCKETMASK_MODELMATCH 用模型的掩码排除子桶 src/dev_ng/rage/base/src/grcore/effect.h:95-107。上层(rmcLodGroup、grmModel)就是用这些宏过滤“本次渲染阶段要画的几何”,详见 models-drawables-and-shaders.md。

8. 状态块(grcStateBlock)#

  • 四类状态:深度模板、光栅、混合(最多 MAX_RAGE_RENDERTARGET_COUNT 个 RT 混合描述:PC/Durango/Orbis 8,其他 4)、采样器(MAX_RAGE_SAMPLERS 16);描述结构与 DX10 二进制兼容 src/dev_ng/rage/base/src/grcore/stateblock.h:28-38、src/dev_ng/rage/base/src/grcore/stateblock.h:57-231。句柄 0 为 INVALID_STATEBLOCK src/dev_ng/rage/base/src/grcore/stateblock.h:18。
  • 语义(注释):Create*State 对相同描述去重并增加引用计数;Destroy*State 递减;在 GPU 可能仍在使用的同一帧内销毁属未定义行为;设计上不支持多线程创建,应在初始化或渲染线程创建 src/dev_ng/rage/base/src/grcore/stateblock.h:326-363。
  • 实现:grcStateBlockStore<..., maxSize> 用数据哈希 + memcmp 在已用链表中查重,未命中则从空闲链表取槽并(D3D11)创建原生对象;满则 Quitf(ERR_GFX_STATE, "Raise grcStateBlockStore size ...");引用计数上限 65535 src/dev_ng/rage/base/src/grcore/stateblock.cpp:402-464。
  • 容量:深度模板 144;光栅 PC 128/其他 80;混合 244;采样器 PC 80/其他 64 src/dev_ng/rage/base/src/grcore/stateblock.cpp:552-563。
  • 默认块在 InitClass 中创建:DSS_Default(CMP_LESS)、DSS_LessEqual、DSS_TestOnly*、DSS_IgnoreDepth、DSS_ForceDepth、RS_Default、RS_NoBackfaceCull、非 FINAL 的 RS_WireFrame 等 src/dev_ng/rage/base/src/grcore/stateblock.cpp:565-600;兼容旧 grcState 的混合集合句柄(BS_Normal/Add/Subtract/Min/Max/...)src/dev_ng/rage/base/src/grcore/stateblock.h:316-324。
  • 懒提交:LAZY_STATEBLOCKS(Win32 或 Orbis)时 Set*State 只记 Stateblock_Dirty,真正下发在 Flush()→DoFlush();活跃/上一个/已刷新句柄都是 DECLARE_MTR_THREAD src/dev_ng/rage/base/src/grcore/stateblock.h:20、src/dev_ng/rage/base/src/grcore/stateblock.h:451-482。D3D11 每次 DrawIndexedPrimitive 先 grcStateBlock::Flush() src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:6601-6605。PS3 SPU 上这些变量被宏重定向到 pSpuGcmState src/dev_ng/rage/base/src/grcore/stateblock.h:424-449。

9. 纹理与纹理工厂#

9.1 grcTexture#

grcTexture : pgBase 是可资源化、引用计数的抽象纹理:纯虚 GetWidth/GetHeight/GetDepth/GetMipMapCount/GetArraySize/GetBitsPerPixel,可选 MSAA/sRGB/有符号通道掩码,GetReference() 支持“引用外部纹理”src/dev_ng/rage/base/src/grcore/texture.h:126-200。引用计数是 mutable u16,Release 归零即 delete src/dev_ng/rage/base/src/grcore/texture.h:381-398。资源布局在各平台一致:开头是一个 24 字节的 grcCellGcmTextureWrapper(注释称本应同时容纳 360 数据),随后是名字、引用计数、类型/转换标志、层数、缓存的原生对象指针 m_CachedTexturePtr、物理尺寸与模板类型、句柄索引 src/dev_ng/rage/base/src/grcore/texture.h:409-419。RORC_VERSION = 13 参与资源版本校验 src/dev_ng/rage/base/src/grcore/texture.h:134。grcInstanceData::Entry 中的纹理在开启碎片整理时存放 grcTextureHandleUnionable(pgHandle),以便纹理被搬移后仍可解析 src/dev_ng/rage/base/src/grcore/effect.h:1326-1330。

9.2 工厂与平台实现#

grcTextureFactory 是可替换的单例(GetInstance/SetInstance/PushInstance/PopInstance),负责从文件/grcImage/原始内存创建纹理、创建渲染目标、锁定 RT/MRT、提供前后缓冲、PlaceTexture(资源放置)src/dev_ng/rage/base/src/grcore/texture.h:1143-1666。TextureCreateParams 区分内存类型(SYSTEM 动态写/VIDEO/STAGING 读回)、线性/平铺、普通/RT/引用/字典引用、MSAA src/dev_ng/rage/base/src/grcore/texture.h:1147-1153。

平台CreatePagedTextureFactory 实例证据
PC D3D9 / PC D3D11 / DurangogrcTextureFactoryDX9 / grcTextureFactoryDX11(Durango 版直接继承 grcTextureFactory,PC 版继承 grcTextureFactoryPC)src/dev_ng/rage/base/src/grcore/texturepc.cpp:50-64、src/dev_ng/rage/base/src/grcore/texturefactory_d3d11.h:25-27
OrbisgrcTextureFactoryGNMsrc/dev_ng/rage/base/src/grcore/texturefactory_gnm.cpp:107
PS3grcTextureFactoryGCM(PS3 在 InitClass 中提前创建以拿到前后缓冲)src/dev_ng/rage/base/src/grcore/texturegcm.cpp:788、src/dev_ng/rage/base/src/grcore/device_gcm.cpp:1299
XenongrcTextureFactoryXenonsrc/dev_ng/rage/base/src/grcore/texturexenon.cpp:49
无图形grcTextureFactoryString(-nographics)src/dev_ng/rage/base/src/grcore/texturestring.cpp:21

grcTextureFactory::InitClass 创建若干哨兵纹理:None、Nonresident、“未实现”橙蓝棋盘纹理与 1×1 RT、NoneDepth(R32F 深度 1.0)等 src/dev_ng/rage/base/src/grcore/texture.cpp:274-300。

9.3 纹理按名解析#

LookupTextureReference(name):去扩展名后哈希;"none" 直接返回 grcTexture::None;先查 RegisterTextureReference 登记的全局引用表,再沿当前 pgDictionary<grcTexture> 链(GetCurrent()→GetParent())逐级 LookupLocal src/dev_ng/rage/base/src/grcore/texture.cpp:447-475。grcTextureReference 是资源内引用外部纹理的唯一方式,其所有查询都转发到被引用对象 src/dev_ng/rage/base/src/grcore/texturereference.h:15-60。游戏的 TXD 父子关系与流式加载见 ../05-world/game-streaming-and-scene-streamer.md。

10. 渲染目标#

  • grcRenderTarget : grcTexture:类型 grcrtFrontBuffer/BackBuffer/DepthBuffer/Permanent/CubeMap(=ArrayMap)/ShadowMap/DepthBufferCubeMap/Volume;可从内存池分配(m_PoolID/m_PoolHeap/m_OffsetInPool);PC/Durango/Orbis 须实现 GenerateMipmaps src/dev_ng/rage/base/src/grcore/texture.h:541-649。
  • grcRenderTargetPoolMgr::Init(maxPools=64)、CreatePool(name, params, buffer):多个 RT 共享一块显存池(注释称多数参数只在 PS3 需要)src/dev_ng/rage/base/src/grcore/texture.h:760-790。
  • 平台类:grcRenderTargetDX11(PC)、grcRenderTargetDurango、grcRenderTargetGNM,并被 typedef 为 grcRenderTargetMSAA src/dev_ng/rage/base/src/grcore/device.h:286-297。
  • Durango ESRAM:DYNAMIC_ESRAM(RSG_DURANGO)src/dev_ng/rage/base/src/grcore/texture.h:29;创建参数中的 ESRAMPhase 非零时,RT 内存由 ESRAMManager::Alloc 分配 src/dev_ng/rage/base/src/grcore/rendertarget_durango.cpp:900-911,并提供 ESRAMCopyIn/Out src/dev_ng/rage/base/src/grcore/rendertarget_durango.cpp:1330-1340;还内置针对 1600×900 EQAA 布局的 XG bug 规避表 src/dev_ng/rage/base/src/grcore/rendertarget_durango.cpp:1219-1240。Orbis/Durango 共用的平铺模式枚举见 src/dev_ng/rage/base/src/grcore/orbisdurangoresourcebase.h:144-174。
  • 锁定 RT 由工厂完成:LockRenderTarget/UnlockRenderTarget/LockMRT/UnlockMRT;D3D11 后端(除 Durango 的 NO_BACKBUFFER_LOCK)在 BeginFrame 锁定后备缓冲、在 EndFrame 的 Present 之后解锁 src/dev_ng/rage/base/src/grcore/texture.h:1476-1518、src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:3059-3065。游戏层 RT 规划见 ../11-rendering/render-pipeline-and-phases.md。

11. 顶点格式、声明与缓冲#

  • grcFvf 描述逻辑顶点格式,通道共 18 个(位置、权重、绑定、法线、漫反射、高光、8 组纹理坐标、2 组切线、2 组副法线),另有 PositionHalf4/Float4 标志位 src/dev_ng/rage/base/src/grcore/fvfchannels.h:11-58。
  • grcVertexElement(流、语义类型、通道、大小、格式、实例频率)用于 GRCDEVICE.CreateVertexDeclaration src/dev_ng/rage/base/src/grcore/vertexdecl.h:174-215;D3D11 下 grcVertexDeclaration 是引用计数头 + 变长 d3d11InputElementDesc[](c_MaxStreams = 4),输入布局在绘制前由 SetInputLayout() 按“当前 VS + 声明”解析 src/dev_ng/rage/base/src/grcore/vertexdecl.h:33-55。
  • grcVertexBuffer : datBase 可资源化;标志 kDynamic/kPreallocatedMemory/kReadWrite;字段 stride、锁指针、顶点数、数据(PS3 上是偏移)、grcFvf;LockHelper 为动态缓冲选用 grcsNoOverwrite src/dev_ng/rage/base/src/grcore/vertexbuffer.h:81-239。平台子类 grcVertexBufferD3D/D3D11/GNM 等在同一头文件中声明。
  • D3D11 每次绘制前的 SetUpPriorToDraw:拒绝 trifan/quad/rect;SetInputLayout;按当前渲染线程的流表绑定连续的顶点缓冲;有 Hull 程序时改用 3 控制点 patch 拓扑;SetProgramResources 与裁剪平面解析 src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:6547-6599。RAGE_INSTANCED_TECH 且视口实例化时 DrawIndexedPrimitive 变成 DrawIndexedInstanced(indexCount, VPInstCount)(例如一次绘制写多个阴影级联)src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:6601-6635。
  • 立即模式:BeginVertices 返回环形缓冲中可写的(写合并)内存,失败返回 NULL 且不得调用 EndVertices;总大小受 BEGIN_VERTICES_MAX_SIZE 约束 src/dev_ng/rage/base/src/grcore/device.h:1782-1797。im.h 在其上提供 grcBegin/grcVertex 风格 API 与大量调试图元(球、胶囊、标签…)src/dev_ng/rage/base/src/grcore/im.h:603-668。
  • 实例缓冲 grcInstanceBuffer:固定尺寸(每实例最多 5 个 float4 寄存器)、可在帧间回收,缓冲最多保留 MaxFrames = 3 帧 src/dev_ng/rage/base/src/grcore/instancebuffer.h:47-74、src/dev_ng/rage/base/src/grcore/instancebuffer.h:136-137。

12. grcViewport:矩阵、窗口与视锥裁剪#

  • SetCurrent(vp, regenDevice) 只能由持有 GPU 的线程调用;更新反转投影标志,主机上把矩阵 cbuffer 的 backing store 指向视口的 m_World44,并在需要时 RegenerateScreen/RegenerateDevice/SetWindow src/dev_ng/rage/base/src/grcore/viewport.cpp:130-161。
  • RegenerateDevice 把 World/WorldView/WorldViewProj/ViewInverse 四个 4×4 矩阵(256 字节)上传:主机 SetDataIndirect,PC BeginUpdate/EndUpdate,PS3 发 SPU 命令(附带 view/proj/视锥平面,供 SPU 重算复合矩阵),D3D9/Xenon 直接设常量寄存器 src/dev_ng/rage/base/src/grcore/viewport.cpp:187-209。这与 shaderlib/rage_common.fxh 的 rage_matrices cbuffer 对应(见 models-drawables-and-shaders.md)。
  • 裁剪:IsSphereVisible、GetAABBCullStatus(单/双平面集)、IsPointVisible 等,返回 grcCullStatus { cullOutside, cullClipped, cullInside } src/dev_ng/rage/base/src/grcore/viewport.h:109-226、src/dev_ng/rage/base/src/grcore/cullstatus.h:16-20。
  • 默认屏幕视口每渲染线程一个:sm_ScreenViewport[MTR ? MTR+2 : 1],GetDefaultScreen 依据 sm_Current 与线程索引选择 src/dev_ng/rage/base/src/grcore/viewport.cpp:74、src/dev_ng/rage/base/src/grcore/viewport.h:684-693。实例化视口(最多 MAX_NUM_CBUFFER_INSTANCING)用于多视口一次绘制 src/dev_ng/rage/base/src/grcore/viewport.h:508-523。

13. CPU 侧图像:grcImage、DDS、JPEG/PNG#

  • grcImage 是与平台格式无关的图像容器,Format 覆盖 DXT1/3/5、CTX1、DXT3A/5A、DXN、BC6/BC7、各类 8/16/32 位色、浮点、深度、调色板等,并有 sRGB/LINEAR/SYSMEM 标志位;类型 STANDARD/CUBE(6 层)/DEPTH/VOLUME src/dev_ng/rage/base/src/grcore/image.h:74-154。注释列出新增格式时需同步修改的 D3D9/D3D11/Xenon/GCM 转换点与 ragebuilder 纹理转换 src/dev_ng/rage/base/src/grcore/image.h:80-89。
  • LoadDDS 读 DDS 魔数与 DDSURFACEDESC2,校验结构大小,解析 mip 数、立方体面数等 src/dev_ng/rage/base/src/grcore/image.cpp:1388-1420;DDS 结构定义在 src/dev_ng/rage/base/src/grcore/dds.h:26-71。JPEG 读写(含直接解码到 RGBA8 或 DXT1 表面)由 image_jpeg.cpp 基于 jpeg/ 库实现 src/dev_ng/rage/base/src/grcore/image.h:381-429、src/dev_ng/rage/base/src/grcore/image.h:708-718。截图走 grcDevice::CaptureScreenShot → grcImage → SaveJPEG/SavePNG(见 src/dev_ng/rage/base/src/grcore/device.h:1482-1492)。

14. 显存与资源分配#

平台机制证据
PC D3D11(非工具)USE_RESOURCE_CACHE:grcResourceCache 追踪 MT_Video/MT_System 总量,SAFE_RELEASE_RESOURCE 调用 grcResourceCache::Release,把资源排入缓存/延迟删除队列(推断目的:避免 GPU 仍在使用时立即释放);RESOURCE_CACHE_MANAGING 开、备份管理与复用关闭src/dev_ng/rage/base/src/grcore/resourcecache.h:14-94、src/dev_ng/rage/base/src/grcore/resourcecache.h:273-309
PC / DurangoRESOURCE_MANAGED/UNMANAGED/SYSTEMMEM 池常量,D3D 运行时对齐 4096、AMD 大对齐 65536src/dev_ng/rage/base/src/grcore/device.h:82-88
OrbisallocateVideoPrivateMemory(Garlic)与 allocateVideoSharedMemory(Onion)在 wrapper_gnm 中实现;命令段、环缓冲、全局资源表从中分配src/dev_ng/rage/base/src/grcore/device_gnm.cpp:1286-1297、src/dev_ng/rage/base/src/grcore/gfxcontext_gnm.cpp:781-806
Durango命令段 128×256 KB;ESRAM 由 ESRAMManager 管理;DEVICE_USE_FAST_TEXTURES(mono driver 下快速纹理绑定)src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:1853、src/dev_ng/rage/base/src/grcore/device.h:31

资源级(pgBase/datResource)内存与 MEMTYPE_RESOURCE_PHYSICAL/VIRTUAL 的划分见 ../01-runtime/memory-and-pools.md 与 ../02-rage-core/paging-and-resources.md;resourcecache.h 中 PC 的 RESOURCE_DEALLOCATE 会按指针归属判断走虚拟堆还是物理堆 src/dev_ng/rage/base/src/grcore/resourcecache.h:110-114。

15. 遗留主机后端(PS3 / Xbox 360)#

GTA V 源码仍完整保留上代主机后端,读代码时会频繁遇到:

  • PS3 SPU 命令 FIFO:SPU_GCM_FIFO = 3,GRCORE_ON_SPU = __PPU ? 3 : 0(等级 1=FIFO 与 Edge 作业,2=grcEffect,3=rmcDrawable 也放到 SPU);USE_EDGE 在 PS3 恒开;USE_PACKED_GCMTEX 在未开 PGHANDLE_REF_COUNT 时启用;非 DEV 构建启用片段微码分支剥离 src/dev_ng/rage/base/src/grcore/effect_config.h:15-35。SPU_COMMAND(x, ...) 在 GCM 上下文里分配一条 spuCmd_x src/dev_ng/rage/base/src/grcore/grcorespu.h:1127,BeginPass、SetGlobalVar、RegenerateDevice、rmcDrawable::Draw 都有对应 SPU 分支。Edge 剔除配置(模型/三角形裁剪平面数)见 src/dev_ng/rage/base/src/grcore/grcorespu.h:20-36。
  • Xbox 360:EDRAM 保护宏(ENABLE_EDRAM_PROTECTION,BANK/DEV)src/dev_ng/rage/base/src/grcore/device.h:254-274、谓词平铺渲染 BeginTiledRendering/EndTiledRendering、SaveBackBuffer、SetShaderGPRAllocation src/dev_ng/rage/base/src/grcore/device.h:1093-1158。
  • API_QUADS_SUPPORT 只在 Xenon/PS3 为真(其他平台 quad 需 6 顶点模拟)src/dev_ng/rage/base/src/grcore/device.h:67-68。

16. 构建配置与调试设施#

开关条件作用证据
GRCDBG_IMPLEMENTEDDurango/Orbis/PS3/PC 且非 FINAL、非工具GRCDBG_PUSH/POP GPU 调试标记src/dev_ng/rage/base/src/grcore/effect_config.h:37-44
EFFECT_PRESERVE_STRINGSPPU PROFILE、BANK、PC、Durango、Orbis保留 technique/变量名字符串src/dev_ng/rage/base/src/grcore/effect.h:65
CHECK_GPU_DATADEV 且 PC/Durango/Orbis检查写入常量的浮点合法性src/dev_ng/rage/base/src/grcore/effect.h:92
EFFECT_TRACK_*、TRACK_CONSTANT_BUFFER_CHANGESASSERT/BANK纹理子类型/MSAA/比较采样器误用检测、cbuffer 改动统计src/dev_ng/rage/base/src/grcore/effect.h:39-73
DEVICE_EKGBANK 且 D3D11D3D 调用计数/计时(DEVICE_EKG_START 等)src/dev_ng/rage/base/src/grcore/wrapper_d3d.h:76
DEVICE_USE_D3D_WRAPPERPC 非工具用 RageDirect3DDeviceContext11 包装上下文,带状态缓存(__D3D11_USE_STATE_CACHING)与冗余资源/cbuffer 绑定消除src/dev_ng/rage/base/src/grcore/device.h:30、src/dev_ng/rage/base/src/grcore/wrapper_d3d.h:846-853、src/dev_ng/rage/base/src/grcore/wrapper_d3d.h:1923-1992
GRCGFXCONTEXT_GNM_*、GRCGFXCONTEXT_DURANGO_*非 MASTER/非 PROFILE 等GPU 挂起检测、逐 draw 标记、检查点src/dev_ng/rage/base/src/grcore/gfxcontext_gnm.h:34-60、src/dev_ng/rage/base/src/grcore/gfxcontext_durango.h:20-47

GPU 计时:grcGpuTimeStamp(查询池,每帧 UpdateAllTimers)与 grcGpuTimer(Start/Stop/GetIdleTimeMs)src/dev_ng/rage/base/src/grcore/gputimer.h:28-115;grcSetup::BeginGfx 中可选接入 NVIDIA PerfKit / AMD GPUPerfAPI(-usePerfAPI)src/dev_ng/rage/base/src/grcore/grcsetup.cpp:1026-1083。

约束与边界行为(汇总)#

  1. 所有 GPU 调用都要求当前线程持有上下文(g_grcCurrentContext != NULL);PC 由 LockContext 引用计数互斥提供,主机由 openContext/BeginContext 提供。
  2. technique group 必须先于 effect 加载注册;之后注册的组对已加载 effect 静默无效。
  3. 状态块不得在同帧内被销毁;创建应在初始化或渲染线程;容量超限即 Quitf。
  4. effect 文件魔数/版本不匹配会直接退出;资源中的实例数据与 effect 变量不匹配时会尝试重建(见 models-drawables-and-shaders.md)。
  5. fence 池耗尽会断言并紧急堆分配;IS_FENCE_PENDING_HINT_NO_KICK 误用可死锁;PC 不支持 GpuWaitOnFence。
  6. 帧节流:主机最多领先 GPU 1 帧(Xenon 2),PC 默认等于 GPU 数;GNM/Durango 的 CPU 等待有 10 s 挂起看门狗。
  7. GNM 上下文池必须大于一帧中同时使用的上下文数,否则可能死锁(源码注释,B*2031872)。
  8. PC 设备丢失/窗口变化期间 BeginFrame 返回 false,上层必须跳过本帧渲染。

未决问题 / 证据不足#

  • PC 上游戏是否在 grcore 之外另用 D3D11 deferred context 实现并行录制:grcore 自身 MULTIPLE_RENDER_THREADS 为 0,但未全面检索游戏层;交由 ../11-rendering/drawlists-and-render-thread-handoff.md 核实。
  • 未逐行阅读 effect_pcd3d11.cpp/effect_durango.cpp/effect_gnm.cpp 的程序资源绑定细节(EFFECT_CACHE_PROGRAMRESOURCES 指纹缓存、Durango fast SRV 路径),也未展开 gnmx/ 第三方库。
  • device_gcm.cpp(PS3)与 device_d3d.cpp(Xenon)只做了结构性确认,未逐流程验证。
  • grcResourceCache 的淘汰策略与预算参数未展开。