grcore:设备抽象、渲染上下文、Effect 绑定、纹理/RT/状态块与 GPU 同步
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; - 显存/资源分配与平台差异。
背景与相关页面#
- 线程模型、渲染线程与主线程交接:../01-runtime/threading-and-render-thread.md;drawlist 与子渲染线程执行:../11-rendering/drawlists-and-render-thread-handoff.md。
- 资源(
pgBase/datResource放置与重定位):../02-rage-core/paging-and-resources.md。纹理字典、drawable 资源通过它被流式载入。 - 内存堆与
MEMTYPE_RESOURCE_*:../01-runtime/memory-and-pools.md。 - 游戏着色器源码与 technique group 注册:../11-rendering/shaders-and-materials.md。
- 计时条/PIX 标记(
grprofile):postfx-shadows-and-occlusion.md。
术语:本页“平台”指编译目标;“后端”指 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.h | grcDevice 全部接口(约 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.cpp | Effect 系统 |
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.h | CPU 侧图像与文件格式 |
grcore/resourcecache.h/.cpp | PC 显存资源缓存/延迟释放 |
grcore/grcsetup.cpp、setup.h | grcSetup:样例/工具程序用的“设备+帧”外壳 |
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 | 证据 |
|---|---|---|---|---|---|---|---|---|
__PS3 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | src/dev_ng/rage/base/src/grcore/config.h:12-19 |
__OSX | 0 | 0 | 0 | 0 | 0 | 0 | 1 | src/dev_ng/rage/base/src/grcore/config.h:20-27 |
__PSP2 | 全 0(仅桩实现) | src/dev_ng/rage/base/src/grcore/config.h:28-35 | ||||||
__XENON | 0x900 | 1 | 0 | 0 | 0 | 0 | 0 | src/dev_ng/rage/base/src/grcore/config.h:36-43 |
RSG_ORBIS | 0 | 0 | 0 | 0 | 0 | 1 | 0(另 ENABLE_LCUE 0) | src/dev_ng/rage/base/src/grcore/config.h:44-52 |
RSG_DURANGO | 0xB00 | 0 | 1 | 1 | 0 | 0 | 0 | src/dev_ng/rage/base/src/grcore/config.h:53-60 |
__WIN32PC 64 位游戏 | 0xB00 | 0 | 1 | 0 | 0 | 0 | 0 | src/dev_ng/rage/base/src/grcore/config.h:61-66 |
__WIN32PC 32 位 / __TOOL / __RESOURCECOMPILER | 0x900 | 1 | 0 | 0 | 0 | 0 | 0 | src/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。- 着色器侧
__SHADERMODEL30/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、EndFrame | src/dev_ng/rage/base/src/grcore/device.h:628-647 |
| Fence/GPU 同步 | AllocFence、CpuMarkFencePending/Done、GpuMarkFencePending/Done、IsFencePending、CpuWaitOnFence、GpuWaitOnFence、CpuWaitOnGpuIdle | src/dev_ng/rage/base/src/grcore/device.h:653-806 |
| 着色器创建/绑定 | CreateVertexShader…CreateHullShader、SetVertexShader…、SetVertexShaderConstantF/B/I、SetInputLayout | src/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、DrawIndexedInstancedIndirect | src/dev_ng/rage/base/src/grcore/device.h:1872-1919 |
| 顶点声明 | CreateVertexDeclaration、SetVertexDeclaration、RecordSetVertexDeclaration | src/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、ExecuteCommandList | src/dev_ng/rage/base/src/grcore/device.h:2645-2681 |
| 遮挡/条件渲染查询 | CreateOcclusionQuery、GetOcclusionQueryData、BeginConditionalRender | src/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 关键常量与容量#
| 常量 | 值 | 证据 |
|---|---|---|
grcmrtColorCount | Win32(PC/Durango)8 个颜色 MRT,否则 4 | src/dev_ng/rage/base/src/grcore/device.h:301-319 |
MAX_GPUS | PC 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_RESOURCES | 14 / 16 / 128(后两者 D3D11) | src/dev_ng/rage/base/src/grcore/device.h:582-585 |
BEGIN_VERTICES_MAX_SIZE | PS3/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_GPU | PC 取 GetMaxQueuedFrames();Xenon 2;其他 1 | src/dev_ng/rage/base/src/grcore/device.h:276-280 |
DRAWABLE_STATS | __BANK 且 PC/Durango/Orbis;按 LOD/SLOD/道具/植被/人物/载具统计 draw call | src/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_SUPPORT | src/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,按以下顺序推进(只列关键步骤):
- 确定窗口尺寸(
-width/-height、全屏/窗口、显示器最近模式)src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:1116-1262。 - 特性级别:列表为 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 用CreateSwapChainForCoreWindowsrc/dev_ng/rage/base/src/grcore/device_d3d11.cpp:1622。 grcStateBlock::InitClass()创建默认状态块src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:1691。grcEffect::InitClass()并加载默认 effect(sm_DefaultEffectName)src/dev_ng/rage/base/src/grcore/device_d3d11.cpp:1744-1746。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。- 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试探并在恢复后ChangeDevicesrc/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、回收溢出 fencesrc/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_grcCurrentContextsrc/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 u8src/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 上为空、其他平台为__THREADsrc/dev_ng/rage/base/src/grcore/config.h:128-132:大量“当前状态”静态量(当前 technique/pass、活跃状态块、当前视口、当前着色器程序等)由此在主机上按线程隔离,在 PC 上为普通全局。
5.2 MTR 配置#
| 宏 | 值 | 证据 |
|---|---|---|
MULTIPLE_RENDER_THREADS | Durango/Orbis 为 4(子渲染线程数);其他平台 0 | src/dev_ng/rage/base/src/grcore/effect_mrt_config.h:2-11 |
MULTIPLE_RENDER_THREADS_ALLOW_DRAW_LISTS_ON_RENDER_THREAD | PC、Durango 为 1,Orbis 为 0(注释:Scaleform 与调试 drawlist 需要在主渲染线程执行) | src/dev_ng/rage/base/src/grcore/effect_mrt_config.h:25-26 |
NUMBER_OF_RENDER_THREADS | MTR 时为 MTR + 上一项,否则 1;即 Durango 5、Orbis 4、PC 1 | src/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 PC | Durango | Orbis |
|---|---|---|---|
CreateContextForThread(工作线程,一次) | CreateDeferredContext(1),可选长生命周期命令列表;为本线程建立动态 VB/IB(立即模式用) | grcGfxContext::perThreadInit() | 空实现 |
BeginContext(control)(每帧) | ResetContext():重置状态缓存、cbuffer、资源视图、立即模式缓冲、裁剪平面 | 同上 + grcGfxContext::openContext(true, seq, control) | Assert(!g_grcCurrentContext) 后 openContext(true, seq, control) |
EndContext | FinishCommandList(false,&list) | grcGfxContext::closeContext() 返回 grcGfxContextCommandList* | closeContext(false),返回上下文本身并清空 TLS |
ExecuteCommandList(拥有设备的线程) | ExecuteCommandList(list, -restoreContextState) 后 Release | list->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_Currentsrc/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标记 volatilesrc/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 实现 | IsFencePending | GpuWaitOnFence |
|---|---|---|---|---|
| Orbis | 0xfe578001/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 起) |
| PC | MARKED_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 文件 | grcEffect | technique 数组、局部参数 m_Locals 与其 cbuffer、各阶段程序数组、m_TechniqueMap[64][RMC_COUNT]、默认实例数据 m_InstanceData、名称哈希 | src/dev_ng/rage/base/src/grcore/effect.h:2044-3033 |
| technique / pass | grcEffect::Technique、grcEffect_Technique_Pass | pass 记录 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 |
| 每实例参数值 | grcInstanceData | Entries(常量在前、纹理在后)、Basis 指向 grcEffect(资源中为哈希)、Material、DrawBucket/DrawBucketMask、IsInstanced、UserFlags | src/dev_ng/rage/base/src/grcore/effect.h:1199-1392 |
| 常量缓冲 | grcCBuffer | D3D11/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_MaxEffects | 512(满则 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_MaxGlobals | Xenon/PPU 196,其余 256 | src/dev_ng/rage/base/src/grcore/effect.h:781 |
technique group 数 MAX_TECHNIQUE_GROUPS | 64(注释:须为 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/16 | src/dev_ng/rage/base/src/grcore/effect.h:751-771 |
| effect 容器 | 64 位或 PC 175 KB,否则 32 KB | src/dev_ng/rage/base/src/grcore/config.h:126 |
grcInstanceData 大小 | 32/48 字节(非工具),工具 36/56 | src/dev_ng/rage/base/src/grcore/effect.h:1386-1392 |
7.3 加载:从名字到 grcEffect#
grcEffect::Create(name)先LookupEffect(atStringHash(name))去重,再新建并Init,成功后AssignToSlotsrc/dev_ng/rage/base/src/grcore/effect_load.cpp:2396-2420。CreateWithFallback失败时用第一个预加载的 effect 兜底,或在禁用 fallback 时Quitfsrc/dev_ng/rage/base/src/grcore/effect_load.cpp:2423-2436。Preload(dir, "preload")读取preload.list,逐行创建src/dev_ng/rage/base/src/grcore/effect_load.cpp:2443-2460。游戏通过grmShaderFactory::PreloadShaders("common:/shaders")(MSAA 时再加preloadMS)间接调用,并在之后(断言构建中)禁止再加载 effectsrc/dev_ng/game/shaders/ShaderLib.cpp:265-275。GetFullName拼出<name>.fxc(PS3 为.cgx),并插入平台目录:Xenonfxl_final、PS3psn、Orbisorbis、Durangodurango、PC D3D11 依立体/画质/SM 选择win32_nvstereo、win32_40_lq、win32_50、win32_40,D3D9win32_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。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。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_TechniqueMapsrc/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)直接 Mapsrc/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_SAMPLERS16);描述结构与 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_STATEBLOCKsrc/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 ...");引用计数上限 65535src/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_THREADsrc/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 上这些变量被宏重定向到pSpuGcmStatesrc/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 / Durango | grcTextureFactoryDX9 / 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 |
| Orbis | grcTextureFactoryGNM | src/dev_ng/rage/base/src/grcore/texturefactory_gnm.cpp:107 |
| PS3 | grcTextureFactoryGCM(PS3 在 InitClass 中提前创建以拿到前后缓冲) | src/dev_ng/rage/base/src/grcore/texturegcm.cpp:788、src/dev_ng/rage/base/src/grcore/device_gcm.cpp:1299 |
| Xenon | grcTextureFactoryXenon | src/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 须实现GenerateMipmapssrc/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 为grcRenderTargetMSAAsrc/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/Outsrc/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.CreateVertexDeclarationsrc/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为动态缓冲选用grcsNoOverwritesrc/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/SetWindowsrc/dev_ng/rage/base/src/grcore/viewport.cpp:130-161。RegenerateDevice把 World/WorldView/WorldViewProj/ViewInverse 四个 4×4 矩阵(256 字节)上传:主机SetDataIndirect,PCBeginUpdate/EndUpdate,PS3 发 SPU 命令(附带 view/proj/视锥平面,供 SPU 重算复合矩阵),D3D9/Xenon 直接设常量寄存器src/dev_ng/rage/base/src/grcore/viewport.cpp:187-209。这与shaderlib/rage_common.fxh的rage_matricescbuffer 对应(见 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/VOLUMEsrc/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 / Durango | RESOURCE_MANAGED/UNMANAGED/SYSTEMMEM 池常量,D3D 运行时对齐 4096、AMD 大对齐 65536 | src/dev_ng/rage/base/src/grcore/device.h:82-88 |
| Orbis | allocateVideoPrivateMemory(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_xsrc/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、SetShaderGPRAllocationsrc/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_IMPLEMENTED | Durango/Orbis/PS3/PC 且非 FINAL、非工具 | GRCDBG_PUSH/POP GPU 调试标记 | src/dev_ng/rage/base/src/grcore/effect_config.h:37-44 |
EFFECT_PRESERVE_STRINGS | PPU PROFILE、BANK、PC、Durango、Orbis | 保留 technique/变量名字符串 | src/dev_ng/rage/base/src/grcore/effect.h:65 |
CHECK_GPU_DATA | DEV 且 PC/Durango/Orbis | 检查写入常量的浮点合法性 | src/dev_ng/rage/base/src/grcore/effect.h:92 |
EFFECT_TRACK_*、TRACK_CONSTANT_BUFFER_CHANGES | ASSERT/BANK | 纹理子类型/MSAA/比较采样器误用检测、cbuffer 改动统计 | src/dev_ng/rage/base/src/grcore/effect.h:39-73 |
DEVICE_EKG | BANK 且 D3D11 | D3D 调用计数/计时(DEVICE_EKG_START 等) | src/dev_ng/rage/base/src/grcore/wrapper_d3d.h:76 |
DEVICE_USE_D3D_WRAPPER | PC 非工具 | 用 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。
约束与边界行为(汇总)#
- 所有 GPU 调用都要求当前线程持有上下文(
g_grcCurrentContext != NULL);PC 由LockContext引用计数互斥提供,主机由openContext/BeginContext提供。 - technique group 必须先于 effect 加载注册;之后注册的组对已加载 effect 静默无效。
- 状态块不得在同帧内被销毁;创建应在初始化或渲染线程;容量超限即
Quitf。 - effect 文件魔数/版本不匹配会直接退出;资源中的实例数据与 effect 变量不匹配时会尝试重建(见 models-drawables-and-shaders.md)。
- fence 池耗尽会断言并紧急堆分配;
IS_FENCE_PENDING_HINT_NO_KICK误用可死锁;PC 不支持GpuWaitOnFence。 - 帧节流:主机最多领先 GPU 1 帧(Xenon 2),PC 默认等于 GPU 数;GNM/Durango 的 CPU 等待有 10 s 挂起看门狗。
- GNM 上下文池必须大于一帧中同时使用的上下文数,否则可能死锁(源码注释,B*2031872)。
- 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的淘汰策略与预算参数未展开。