可可西

D3D11延迟上下文(Deferred Context)

DirectX 11引入了延迟上下文(Deferred Context) 和命令列表(Command List)机制,允许在多线程CPU环境中并行录制渲染命令

Deferred Context是辅助线程使用的上下文,仅用来录制Command List,没有提交功能,其录制的Command List要通过Immediate Context(渲染线程上下文)提交到GPU端执行

 

image

 

D3D11 Deferred Context多线程录制示例

初始化和游戏主循环(main.cpp)

// =============================================================================
// main.cpp
// 程序入口:创建 Win32 窗口、初始化 D3D11 设备与渲染器,运行消息/渲染循环。
//
// 本示例演示 D3D11 "多 Deferred Context 多线程录制 + Immediate Context 回放":
//   - 主线程创建所有资源(设备、交换链、几何、着色器、状态、常量缓冲)
//   - 每帧由多个工作线程各自用一个 Deferred Context 并行录制绘制命令
//   - 每个线程 FinishCommandList 生成 ID3D11CommandList
//   - 主线程在 Immediate Context 上按顺序 ExecuteCommandList 回放后 Present
// =============================================================================
#include <windows.h>
#include <chrono>
#include <string>
#include "DeviceResources.h"
#include "DeferredRenderer.h"

// ---- 可调参数:线程(Deferred Context)数量与立方体数量 ----
static const UINT kThreadCount = 4;   // Deferred Context / 工作线程数量
static const UINT kCubeCount   = 8;   // 场景立方体数量(平均分配到各线程)

static const wchar_t* kWindowClass = L"D3D11DeferredContextWndClass";
static const wchar_t* kWindowTitle = L"D3D11 多 Deferred Context 多线程录制示例";

namespace
{
    DeviceResources  g_device;
    DeferredRenderer g_renderer;
    bool             g_initialized = false;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_SIZE:
        if (g_initialized && wParam != SIZE_MINIMIZED)
        {
            UINT w = LOWORD(lParam);
            UINT h = HIWORD(lParam);
            g_device.Resize(w, h);
        }
        return 0;

    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;

    case WM_KEYDOWN:
        if (wParam == VK_ESCAPE) { PostQuitMessage(0); }
        return 0;
    }
    return DefWindowProc(hwnd, msg, wParam, lParam);
}

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR, int nCmdShow)
{
    // ---- 注册并创建窗口 ----
    WNDCLASSEX wc = {};
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wc.lpszClassName = kWindowClass;
    RegisterClassEx(&wc);

    const UINT width = 1280;
    const UINT height = 720;

    RECT rc = { 0, 0, static_cast<LONG>(width), static_cast<LONG>(height) };
    AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);

    HWND hwnd = CreateWindow(
        kWindowClass, kWindowTitle, WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT,
        rc.right - rc.left, rc.bottom - rc.top,
        nullptr, nullptr, hInstance, nullptr);

    if (!hwnd)
    {
        MessageBox(nullptr, L"窗口创建失败", L"错误", MB_ICONERROR);
        return -1;
    }

    // ---- 初始化 D3D11 与渲染器 ----
    try
    {
        g_device.Initialize(hwnd, width, height);
        g_renderer.Initialize(&g_device, kThreadCount, kCubeCount);
        g_initialized = true;
    }
    catch (const std::exception& e)
    {
        std::string msg = e.what();
        MessageBoxA(nullptr, msg.c_str(), "初始化失败", MB_ICONERROR);
        return -1;
    }

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    // ---- 主循环:消息处理 + 每帧渲染 ----
    using clock = std::chrono::high_resolution_clock;
    auto prev = clock::now();

    MSG msg = {};
    while (msg.message != WM_QUIT)
    {
        if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else
        {
            auto now = clock::now();
            float dt = std::chrono::duration<float>(now - prev).count();
            prev = now;

            try
            {
                g_renderer.Update(dt);
                g_renderer.RenderFrame();
            }
            catch (const std::exception& e)
            {
                MessageBoxA(nullptr, e.what(), "渲染错误", MB_ICONERROR);
                break;
            }
        }
    }

    return static_cast<int>(msg.wParam);
}

 

 

D3D11核心设备资源管理(DeviceResources.h / DeviceResources.cpp)

#pragma once
// =============================================================================
// DeviceResources.h
// 管理 D3D11 核心设备资源:device、immediate context、swapchain、RTV、DSV、视口。
// 负责设备/交换链创建以及窗口尺寸变化时的后备缓冲重建。
// =============================================================================
#include <d3d11.h>
#include <dxgi1_2.h>
#include "DXHelpers.h"

class DeviceResources
{
public:
    DeviceResources() = default;
    ~DeviceResources() = default;

    // 根据窗口句柄创建设备、交换链与后备缓冲
    void Initialize(HWND hwnd, UINT width, UINT height);

    // 窗口尺寸变化时调用,重建 RTV / DSV
    void Resize(UINT width, UINT height);

    // 呈现当前帧(垂直同步)
    void Present();

    // -------- 访问器 --------
    ID3D11Device*        GetDevice() const           { return mDevice.Get(); }
    ID3D11DeviceContext* GetImmediateContext() const { return mImmediateContext.Get(); }
    ID3D11RenderTargetView* GetRTV() const           { return mRTV.Get(); }
    ID3D11DepthStencilView* GetDSV() const           { return mDSV.Get(); }
    const D3D11_VIEWPORT&   GetViewport() const      { return mViewport; }
    UINT GetWidth() const  { return mWidth; }
    UINT GetHeight() const { return mHeight; }
    float GetAspectRatio() const { return mHeight ? static_cast<float>(mWidth) / mHeight : 1.0f; }

private:
    void CreateBackBufferResources(); // 创建/重建 RTV 与 DSV

    HWND mHwnd = nullptr;
    UINT mWidth = 0;
    UINT mHeight = 0;

    ComPtr<ID3D11Device>           mDevice;
    ComPtr<ID3D11DeviceContext>    mImmediateContext;   // 唯一的立即上下文(用于回放命令列表与 Present)
    ComPtr<IDXGISwapChain1>        mSwapChain;
    ComPtr<ID3D11RenderTargetView> mRTV;
    ComPtr<ID3D11Texture2D>        mDepthStencilBuffer;
    ComPtr<ID3D11DepthStencilView> mDSV;
    D3D11_VIEWPORT                 mViewport = {};
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// =============================================================================
// DeviceResources.cpp
// =============================================================================
#include "DeviceResources.h"

void DeviceResources::Initialize(HWND hwnd, UINT width, UINT height)
{
    mHwnd = hwnd;
    mWidth = width;
    mHeight = height;

    UINT createFlags = 0;
#if defined(_DEBUG)
    // Debug 构建启用调试层,便于捕获 D3D 错误与资源泄漏报告
    createFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

    const D3D_FEATURE_LEVEL featureLevels[] = {
        D3D_FEATURE_LEVEL_11_1,
        D3D_FEATURE_LEVEL_11_0,
    };

    // ---- 创建 D3D11 设备与 Immediate Context ----
    ComPtr<ID3D11Device>        baseDevice;
    ComPtr<ID3D11DeviceContext> baseContext;
    D3D_FEATURE_LEVEL obtainedLevel{};

    HRESULT hr = D3D11CreateDevice(
        nullptr,                    // 默认适配器
        D3D_DRIVER_TYPE_HARDWARE,
        nullptr,
        createFlags,
        featureLevels, ARRAYSIZE(featureLevels),
        D3D11_SDK_VERSION,
        baseDevice.GetAddressOf(),
        &obtainedLevel,
        baseContext.GetAddressOf());

    if (FAILED(hr))
    {
        // 回退到 WARP 软件渲染(无独显环境也能运行示例)
        ThrowIfFailed(D3D11CreateDevice(
            nullptr, D3D_DRIVER_TYPE_WARP, nullptr, createFlags,
            featureLevels, ARRAYSIZE(featureLevels), D3D11_SDK_VERSION,
            baseDevice.GetAddressOf(), &obtainedLevel, baseContext.GetAddressOf()));
    }

    ThrowIfFailed(baseDevice.As(&mDevice));
    ThrowIfFailed(baseContext.As(&mImmediateContext));

    // ---- 通过 DXGI 创建交换链 ----
    ComPtr<IDXGIDevice> dxgiDevice;
    ThrowIfFailed(mDevice.As(&dxgiDevice));
    ComPtr<IDXGIAdapter> adapter;
    ThrowIfFailed(dxgiDevice->GetAdapter(adapter.GetAddressOf()));
    ComPtr<IDXGIFactory2> factory;
    ThrowIfFailed(adapter->GetParent(__uuidof(IDXGIFactory2), reinterpret_cast<void**>(factory.GetAddressOf())));

    DXGI_SWAP_CHAIN_DESC1 scDesc = {};
    scDesc.Width = mWidth;
    scDesc.Height = mHeight;
    scDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    scDesc.SampleDesc.Count = 1;            // 不使用 MSAA,保持示例简单
    scDesc.SampleDesc.Quality = 0;
    scDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    scDesc.BufferCount = 2;
    scDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;

    ThrowIfFailed(factory->CreateSwapChainForHwnd(
        mDevice.Get(), mHwnd, &scDesc, nullptr, nullptr, mSwapChain.GetAddressOf()));

    // 禁用 Alt+Enter 全屏切换,避免示例状态混乱
    factory->MakeWindowAssociation(mHwnd, DXGI_MWA_NO_ALT_ENTER);

    CreateBackBufferResources();
}

void DeviceResources::CreateBackBufferResources()
{
    // 重建前先释放旧的视图
    mRTV.Reset();
    mDSV.Reset();
    mDepthStencilBuffer.Reset();

    // ---- 渲染目标视图(来自交换链后备缓冲)----
    ComPtr<ID3D11Texture2D> backBuffer;
    ThrowIfFailed(mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(backBuffer.GetAddressOf())));
    ThrowIfFailed(mDevice->CreateRenderTargetView(backBuffer.Get(), nullptr, mRTV.GetAddressOf()));

    // ---- 深度模板缓冲与视图 ----
    D3D11_TEXTURE2D_DESC dsDesc = {};
    dsDesc.Width = mWidth;
    dsDesc.Height = mHeight;
    dsDesc.MipLevels = 1;
    dsDesc.ArraySize = 1;
    dsDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
    dsDesc.SampleDesc.Count = 1;
    dsDesc.Usage = D3D11_USAGE_DEFAULT;
    dsDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
    ThrowIfFailed(mDevice->CreateTexture2D(&dsDesc, nullptr, mDepthStencilBuffer.GetAddressOf()));
    ThrowIfFailed(mDevice->CreateDepthStencilView(mDepthStencilBuffer.Get(), nullptr, mDSV.GetAddressOf()));

    // ---- 视口 ----
    mViewport.TopLeftX = 0.0f;
    mViewport.TopLeftY = 0.0f;
    mViewport.Width = static_cast<float>(mWidth);
    mViewport.Height = static_cast<float>(mHeight);
    mViewport.MinDepth = 0.0f;
    mViewport.MaxDepth = 1.0f;
}

void DeviceResources::Resize(UINT width, UINT height)
{
    if (width == 0 || height == 0) return;           // 最小化时忽略
    if (width == mWidth && height == mHeight) return; // 尺寸未变

    mWidth = width;
    mHeight = height;

    // 释放对后备缓冲的引用后才能 ResizeBuffers
    mRTV.Reset();
    mDSV.Reset();
    mDepthStencilBuffer.Reset();

    ThrowIfFailed(mSwapChain->ResizeBuffers(0, mWidth, mHeight, DXGI_FORMAT_UNKNOWN, 0));
    CreateBackBufferResources();
}

void DeviceResources::Present()
{
    // 第二参数 0:允许撕裂关闭,垂直同步呈现
    HRESULT hr = mSwapChain->Present(1, 0);
    ThrowIfFailed(hr);
}

 

 

核心渲染器:管理 N 个 Deferred Context 与对应工作线程,多线程并行录制绘制(DeferredRenderer.h / DeferredRenderer.cpp)

#pragma once
// =============================================================================
// DeferredRenderer.h
// 核心渲染器:管理 N 个 Deferred Context 与对应工作线程,多线程并行录制绘制
// 命令并由 Immediate Context 顺序回放。
// =============================================================================
#include <d3d11.h>
#include <DirectXMath.h>
#include <vector>
#include "DXHelpers.h"
#include "CubeGeometry.h"

class DeviceResources;

// 单个立方体的实例数据
struct CubeInstance
{
    DirectX::XMFLOAT3 worldPosition;   // 世界位置
    float             rotationSpeed;   // 旋转速度
    float             angle;           // 当前角度
};

class DeferredRenderer
{
public:
    // threadCount : Deferred Context / 工作线程数量
    // cubeCount   : 场景中立方体数量(会平均分配到各线程录制)
    void Initialize(DeviceResources* res, UINT threadCount, UINT cubeCount);

    // 主线程:更新动画与相机/常量数据
    void Update(float dt);

    // 多线程录制命令 -> 主线程顺序回放 -> Present
    void RenderFrame();

private:
    void CreateGeometry();
    void CreateShaders();
    void CreatePipelineStates();
    void CreatePerObjectResources();

    // 工作线程函数:使用第 threadIndex 个 Deferred Context 录制其负责的立方体
    void RecordCommands(UINT threadIndex);

    DeviceResources* mRes = nullptr;
    UINT mThreadCount = 0;
    UINT mCubeCount = 0;

    // ---- 共享只读资源(主线程创建,各线程只读绑定)----
    ComPtr<ID3D11Buffer>        mVertexBuffer;
    ComPtr<ID3D11Buffer>        mIndexBuffer;
    ComPtr<ID3D11InputLayout>   mInputLayout;
    ComPtr<ID3D11VertexShader>  mVertexShader;
    ComPtr<ID3D11PixelShader>   mPixelShader;
    ComPtr<ID3D11RasterizerState>   mRasterState;
    ComPtr<ID3D11DepthStencilState> mDepthState;

    // ---- 每个 Deferred Context(每线程独占一个)----
    std::vector<ComPtr<ID3D11DeviceContext>> mDeferredContexts;
    // 每帧录制生成的命令列表
    std::vector<ComPtr<ID3D11CommandList>>   mCommandLists;

    // ---- 每个立方体一份独立常量缓冲,避免多线程 Map 竞争 ----
    std::vector<ComPtr<ID3D11Buffer>> mPerObjectCB;

    // 场景立方体实例
    std::vector<CubeInstance> mCubes;

    // 视图/投影矩阵(主线程更新)
    DirectX::XMFLOAT4X4 mViewProj;
};

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// =============================================================================
// DeferredRenderer.cpp
// 演示 D3D11 多 Deferred Context 多线程录制 + Immediate Context 回放。
// =============================================================================
#include "DeferredRenderer.h"
#include "DeviceResources.h"
#include <thread>
#include <vector>

using namespace DirectX;

// -----------------------------------------------------------------------------
// 内嵌着色器源码(与 Shaders.hlsl 内容一致,运行时编译,免预编译步骤)
// -----------------------------------------------------------------------------
static const char* g_ShaderSource = R"(
cbuffer CBPerObject : register(b0)
{
    float4x4 gWorldViewProj;
};

struct VSInput  { float3 position : POSITION; float3 color : COLOR; };
struct VSOutput { float4 position : SV_POSITION; float3 color : COLOR; };

VSOutput VSMain(VSInput input)
{
    VSOutput o;
    o.position = mul(float4(input.position, 1.0f), gWorldViewProj);
    o.color = input.color;
    return o;
}

float4 PSMain(VSOutput input) : SV_TARGET
{
    return float4(input.color, 1.0f);
}
)";

void DeferredRenderer::Initialize(DeviceResources* res, UINT threadCount, UINT cubeCount)
{
    mRes = res;
    mThreadCount = (threadCount == 0) ? 1 : threadCount;
    mCubeCount = (cubeCount == 0) ? 1 : cubeCount;

    CreateGeometry();
    CreateShaders();
    CreatePipelineStates();
    CreatePerObjectResources();

    ID3D11Device* device = mRes->GetDevice();

    // ---- 为每个工作线程创建一个独立的 Deferred Context ----
    // 注意:D3D11 设备本身支持多线程创建资源;每个 Deferred Context 仅由它所属
    // 的线程使用,互不干扰,从而避免对 Immediate Context 的并发访问。
    mDeferredContexts.resize(mThreadCount);
    mCommandLists.resize(mThreadCount);
    for (UINT i = 0; i < mThreadCount; ++i)
    {
        ThrowIfFailed(device->CreateDeferredContext(0, mDeferredContexts[i].GetAddressOf()));
    }

    // ---- 初始化场景立方体实例(围成一圈分布)----
    mCubes.resize(mCubeCount);
    const float radius = 4.0f;
    for (UINT i = 0; i < mCubeCount; ++i)
    {
        float t = (mCubeCount > 1) ? (XM_2PI * i / mCubeCount) : 0.0f;
        mCubes[i].worldPosition = XMFLOAT3(radius * cosf(t), 0.0f, radius * sinf(t));
        mCubes[i].rotationSpeed = 0.5f + 0.3f * (i % 4);
        mCubes[i].angle = t;
    }
}

void DeferredRenderer::CreateGeometry()
{
    ID3D11Device* device = mRes->GetDevice();

    // 顶点缓冲
    D3D11_BUFFER_DESC vbDesc = {};
    vbDesc.ByteWidth = sizeof(CubeGeometry::kVertices);
    vbDesc.Usage = D3D11_USAGE_IMMUTABLE;
    vbDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
    D3D11_SUBRESOURCE_DATA vbData = {};
    vbData.pSysMem = CubeGeometry::kVertices;
    ThrowIfFailed(device->CreateBuffer(&vbDesc, &vbData, mVertexBuffer.GetAddressOf()));

    // 索引缓冲
    D3D11_BUFFER_DESC ibDesc = {};
    ibDesc.ByteWidth = sizeof(CubeGeometry::kIndices);
    ibDesc.Usage = D3D11_USAGE_IMMUTABLE;
    ibDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
    D3D11_SUBRESOURCE_DATA ibData = {};
    ibData.pSysMem = CubeGeometry::kIndices;
    ThrowIfFailed(device->CreateBuffer(&ibDesc, &ibData, mIndexBuffer.GetAddressOf()));
}

void DeferredRenderer::CreateShaders()
{
    ID3D11Device* device = mRes->GetDevice();

    ComPtr<ID3DBlob> vsBlob = CompileShaderFromString(g_ShaderSource, "VSMain", "vs_5_0");
    ComPtr<ID3DBlob> psBlob = CompileShaderFromString(g_ShaderSource, "PSMain", "ps_5_0");

    ThrowIfFailed(device->CreateVertexShader(
        vsBlob->GetBufferPointer(), vsBlob->GetBufferSize(), nullptr, mVertexShader.GetAddressOf()));
    ThrowIfFailed(device->CreatePixelShader(
        psBlob->GetBufferPointer(), psBlob->GetBufferSize(), nullptr, mPixelShader.GetAddressOf()));

    // 输入布局
    const D3D11_INPUT_ELEMENT_DESC layout[] =
    {
        { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,  D3D11_INPUT_PER_VERTEX_DATA, 0 },
        { "COLOR",    0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
    };
    ThrowIfFailed(device->CreateInputLayout(
        layout, ARRAYSIZE(layout),
        vsBlob->GetBufferPointer(), vsBlob->GetBufferSize(),
        mInputLayout.GetAddressOf()));
}

void DeferredRenderer::CreatePipelineStates()
{
    ID3D11Device* device = mRes->GetDevice();

    // 光栅化状态:实心填充、背面剔除关闭以保证立方体各面可见
    D3D11_RASTERIZER_DESC rsDesc = {};
    rsDesc.FillMode = D3D11_FILL_SOLID;
    rsDesc.CullMode = D3D11_CULL_NONE;
    rsDesc.FrontCounterClockwise = FALSE;
    rsDesc.DepthClipEnable = TRUE;
    ThrowIfFailed(device->CreateRasterizerState(&rsDesc, mRasterState.GetAddressOf()));

    // 深度模板状态:开启深度测试
    D3D11_DEPTH_STENCIL_DESC dsDesc = {};
    dsDesc.DepthEnable = TRUE;
    dsDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
    dsDesc.DepthFunc = D3D11_COMPARISON_LESS;
    dsDesc.StencilEnable = FALSE;
    ThrowIfFailed(device->CreateDepthStencilState(&dsDesc, mDepthState.GetAddressOf()));
}

void DeferredRenderer::CreatePerObjectResources()
{
    ID3D11Device* device = mRes->GetDevice();

    // 每个立方体一份动态常量缓冲(CPU 可写),避免多线程同时 Map 同一资源
    mPerObjectCB.resize(mCubeCount);
    D3D11_BUFFER_DESC cbDesc = {};
    cbDesc.ByteWidth = sizeof(CBPerObject);
    cbDesc.Usage = D3D11_USAGE_DYNAMIC;
    cbDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
    cbDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
    for (UINT i = 0; i < mCubeCount; ++i)
    {
        ThrowIfFailed(device->CreateBuffer(&cbDesc, nullptr, mPerObjectCB[i].GetAddressOf()));
    }
}

void DeferredRenderer::Update(float dt)
{
    // 推进每个立方体的旋转角度
    for (auto& c : mCubes)
    {
        c.angle += c.rotationSpeed * dt;
    }

    // 计算视图 + 投影矩阵
    XMVECTOR eye = XMVectorSet(0.0f, 6.0f, -10.0f, 1.0f);
    XMVECTOR at  = XMVectorSet(0.0f, 0.0f, 0.0f, 1.0f);
    XMVECTOR up  = XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f);
    XMMATRIX view = XMMatrixLookAtLH(eye, at, up);
    XMMATRIX proj = XMMatrixPerspectiveFovLH(XM_PIDIV4, mRes->GetAspectRatio(), 0.1f, 100.0f);
    XMStoreFloat4x4(&mViewProj, view * proj);
}

void DeferredRenderer::RecordCommands(UINT threadIndex)
{
    ID3D11DeviceContext* ctx = mDeferredContexts[threadIndex].Get();

    // ---- 每个 Deferred Context 起始为默认状态,必须显式设置完整管线 ----
    ID3D11RenderTargetView* rtv = mRes->GetRTV();
    ID3D11DepthStencilView* dsv = mRes->GetDSV();

    // 由 0 号线程负责清屏(命令列表按顺序回放,0 号最先执行)
    if (threadIndex == 0)
    {
        const float clearColor[4] = { 0.05f, 0.06f, 0.10f, 1.0f };
        ctx->ClearRenderTargetView(rtv, clearColor);
        ctx->ClearDepthStencilView(dsv, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
    }

    // 绑定输出合并阶段、视口、光栅/深度状态
    ctx->OMSetRenderTargets(1, &rtv, dsv);
    D3D11_VIEWPORT vp = mRes->GetViewport();
    ctx->RSSetViewports(1, &vp);
    ctx->RSSetState(mRasterState.Get());
    ctx->OMSetDepthStencilState(mDepthState.Get(), 0);

    // 绑定输入装配阶段
    UINT stride = sizeof(Vertex);
    UINT offset = 0;
    ID3D11Buffer* vb = mVertexBuffer.Get();
    ctx->IASetVertexBuffers(0, 1, &vb, &stride, &offset);
    ctx->IASetIndexBuffer(mIndexBuffer.Get(), DXGI_FORMAT_R16_UINT, 0);
    ctx->IASetInputLayout(mInputLayout.Get());
    ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

    // 绑定着色器
    ctx->VSSetShader(mVertexShader.Get(), nullptr, 0);
    ctx->PSSetShader(mPixelShader.Get(), nullptr, 0);

    XMMATRIX viewProj = XMLoadFloat4x4(&mViewProj);

    // ---- 绘制本线程负责的立方体(按 i % threadCount 分配)----
    for (UINT i = threadIndex; i < mCubeCount; i += mThreadCount)
    {
        const CubeInstance& cube = mCubes[i];

        // 计算世界 * 视图 * 投影
        XMMATRIX world =
            XMMatrixRotationRollPitchYaw(cube.angle * 0.7f, cube.angle, 0.0f) *
            XMMatrixTranslation(cube.worldPosition.x, cube.worldPosition.y, cube.worldPosition.z);
        XMMATRIX wvp = world * viewProj;

        // 更新该立方体专属常量缓冲(HLSL 列主序 -> 这里转置)
        D3D11_MAPPED_SUBRESOURCE mapped;
        ThrowIfFailed(ctx->Map(mPerObjectCB[i].Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped));
        CBPerObject* cb = reinterpret_cast<CBPerObject*>(mapped.pData);
        XMStoreFloat4x4(&cb->worldViewProj, XMMatrixTranspose(wvp));
        ctx->Unmap(mPerObjectCB[i].Get(), 0);

        ID3D11Buffer* cbPtr = mPerObjectCB[i].Get();
        ctx->VSSetConstantBuffers(0, 1, &cbPtr);

        ctx->DrawIndexed(CubeGeometry::kIndexCount, 0, 0);
    }

    // ---- 结束录制,生成命令列表 ----
    // 第二参数 FALSE:不恢复 deferred context 状态(每帧重置即可)
    mCommandLists[threadIndex].Reset();
    ThrowIfFailed(ctx->FinishCommandList(FALSE, mCommandLists[threadIndex].GetAddressOf()));
}

void DeferredRenderer::RenderFrame()
{
    // ---- 启动工作线程并行录制 ----
    std::vector<std::thread> workers;
    workers.reserve(mThreadCount);
    for (UINT i = 0; i < mThreadCount; ++i)
    {
        workers.emplace_back([this, i]() { RecordCommands(i); });
    }
    for (auto& t : workers)
    {
        t.join(); // 等待线程执行完成
    }

    // ---- 主线程:在 Immediate Context 上按顺序回放命令列表 ----
    ID3D11DeviceContext* immediate = mRes->GetImmediateContext();
    for (UINT i = 0; i < mThreadCount; ++i)
    {
        if (mCommandLists[i])
        {
            // 第二参数 FALSE:执行后不恢复 immediate context 状态
            immediate->ExecuteCommandList(mCommandLists[i].Get(), FALSE);
            mCommandLists[i].Reset(); // 命令列表用后即弃,避免泄漏
        }
    }

    // 呈现
    mRes->Present();
}

 

 

运行结果

image

 

posted on 2026-06-29 15:58  可可西  阅读(7)  评论(0)    收藏  举报

导航