找回密码
 立即注册
首页 业界区 业界 [dx12显示图片] ImGui Learn Data Day 3

[dx12显示图片] ImGui Learn Data Day 3

滕佩杉 6 小时前
调试心得:相对路径陷阱与编译期/运行期的区别,以及如何显示图片
1.png

问题背景

最近我遇到了一个非常深刻的教训:imgui代码始终无法加载 graph/Alice.png,这让我一度怀疑是 DirectX 12 环境配置的问题。
就在刚刚,我终于发现了原因。
我的文件结构如下:

  • 项目根目录:rt/
  • 源代码位置:rt/imguiest/main.cpp
  • 资源位置:rt/imguiest/graph/
然而,我的 Visual Studio 项目配置的工作目录(Working Directory) 是以 rt 为根目录开始的。问题正出在这里。
代码分析

我在代码中尝试这样加载图片:
  1. // 调用修复后的加载函数
  2. bool ret = LoadTextureFromFile("graph/Alice.png", &my_texture_resource, &my_image_width, &my_image_height);
复制代码
这段代码试图以相对路径寻找 graph 文件夹,但始终失败。
原因在于:程序运行时,相对路径是从工作目录 rt 开始计算的。程序试图寻找 rt/graph/Alice.png,但实际上文件位于 rt/imguiest/graph/Alice.png,因此无法找到。
核心疑问

这里产生了一个困惑:我的头文件也放在 rt/imguiest 中,为什么编译器能找到头文件,程序运行时却找不到图片资源?
深度解析:编译时 vs 运行时

这个问题的本质在于编程中两个完全不同的阶段:编译时(Compile-time)运行时(Runtime)
1. 找头文件 (#include) —— 编译器在工作


  • 阶段:编译时。
  • 原理:当你编写 #include "my_header.h" 时,这是给预处理器看的。C++ 标准规定,使用双引号 "" 引用头文件时,优先在当前源文件所在的目录查找
  • 结论:编译器知道你的 main.cpp 位于 rt/imguiest/,所以它会默认从这个文件夹开始寻找旁边的头文件。这是为了方便开发者引用同目录下的模块。
2. 找图片 (LoadTexture) —— 程序在工作


  • 阶段:运行时。
  • 原理:当你运行编译好的程序(.exe)时,它已经脱离了源码文件,只认“当前工作目录 (Current Working Directory)”
  • IDE 的行为

    • Visual Studio 将代码编译成 .exe。
    • VS 指挥 Windows 运行该程序,并指定项目文件夹(.vcxproj 所在目录,即你的 rt/)作为程序的工作目录

  • 为什么不以 main.cpp 为基准?
    一个项目可能包含数百个分布在不同文件夹下的 .cpp 文件。如果以源文件位置为基准,资源路径管理将变得极其混乱。因此,IDE 统一规定:运行时所有相对路径的起点,默认都是项目根目录。
ImGui显示图片

要在 ImGui 中显示一张图片,本质上是一个 “搬运数据”“建立索引” 的过程。我们可以把它分为四个核心步骤:

  • CPU 阶段:把图片文件读到内存(RAM)。
  • GPU 搬运阶段:把内存里的数据搬运到显存(VRAM)。
  • 描述符阶段(最关键):给这张图片办一张“身份证”(SRV)。
  • 渲染阶段:把“身份证”交给 ImGui。
要在 ImGui 中显示一张图片,本质上需要涉及计算机图形学的底层知识。但这门学科体系庞大,对于初学者来说,想要为了显示一张图而去系统学习图形学,时间成本过高(之后我会专门开一期文章来探讨这部分理论)。
鉴于 DX12 涉及的概念(如内存管理、描述符堆等)对于新手来说过于晦涩,我选择借助 AI 辅助生成了具体的实现代码。
然而,即便有了代码,跑通它也绝非易事。过程中我遇到了大量环境配置和依赖库的问题。本文将详细记录我遇到的“坑”以及解决方案,并在文末附上完整的可用代码。
第一步:确认系统与显卡支持的 DirectX 版本

首先,你需要确认你的硬件和系统是否支持 DX12。

  • 按下 Win + R 键,输入 dxdiag 并回车,打开 DirectX 诊断工具。
  • 在“系统”或“显示”选项卡下,查看“DirectX 版本”或“功能级别”。

    • 注:Windows 10 及以上系统通常默认同时支持 DX11 和 DX12。

⚠️ 特别注意:DirectX 11 和 DirectX 12 在架构上有本质的区别,它们不仅仅是版本号的不同,底层逻辑完全不同。
第二步:解决头文件缺失问题 (Windows SDK)

ImGui 官方提供的示例通常可以直接运行,但如果你编译时报错提示找不到文件(例如 d3d11.h 或 d3d12.h),这通常不是代码问题,而是开发环境缺失

  • 错误原因:你的 Visual Studio 安装时未勾选相应的 Windows SDK
  • 错误做法:不要去网上随便下载一个 .h 文件放到项目里,这会导致更多兼容性问题。
  • 正确做法:打开 Visual Studio Installer,修改安装配置,确保勾选了 "Windows 10 SDK" (或 Win11 SDK) 以及 "C++ 桌面开发" 工作负载。
第三步:处理 DX12 特有的 d3dx12.h

⚠️ 特别注意:DX12 开发中经常用到一个辅助头文件 d3dx12.h。
这个文件不包含在标准的 Windows SDK 中,它是一个微软官方提供的辅助库(Helper Library)。
如果你发现代码中缺少这个文件:

  • 你需要手动下载该文件(通常可以在 Microsoft 的 GitHub 仓库 DirectX-Headers 中找到)。
  • 或者在使用 DirectXTex 等库时,它们通常也会包含这个辅助文件。
  • 下载后,将其放入你的项目目录,并在解决方案资源管理器中添加现有项将其包含进去。
第四步:链接库文件配置

在 C++ 中使用 DirectX,除了头文件,还需要链接对应的 .lib 库文件。为了省去在项目属性页面手动配置链接器的麻烦,我们可以直接在代码中使用 #pragma comment 指令:
  1. // 自动链接库文件,省去手动配置库依赖的繁琐步骤
  2. #pragma comment(lib, "d3d12.lib")
  3. #pragma comment(lib, "dxgi.lib")
  4. #pragma comment(lib, "d3dcompiler.lib")
  5. // 如果使用了 GUID 相关功能,可能还需要链接 dxguid
  6. #pragma comment(lib, "dxguid.lib")
复制代码
第五步:常见报错与排查思路

由于 DX12 和 DX11 接口差异巨大,千万不要尝试通过简单地将变量名中的 11 改为 12 来移植代码,这绝对是行不通的。
如果你在编译时遇到大量“未定义标识符”或宏定义不存在的错误,请按以下顺序排查:

  • 头文件未包含:检查 .h 文件是否不仅存在于文件夹中,还必须在 Visual Studio 的解决方案资源管理器中被添加到了项目里。
  • 拼写错误:检查是否拼写错误,或者使用了旧版/新版 API 的名称。
  • SDK 版本不匹配:如果提示 SDK 版本不存在或不兼容:

    • 右键点击项目解决方案 -> 选择 “重定解决方案目标” (Retarget Solution)
    • 在弹出的窗口中选择你当前已安装的 Windows SDK 版本,点击确定。

我的终于能跑的代码:
  1. #include "imgui/imgui.h"
  2. #include "imgui/imgui_impl_win32.h"
  3. #include "imgui/imgui_impl_dx12.h"
  4. #include <d3d12.h>
  5. #include <dxgi1_4.h>
  6. #include <tchar.h>
  7. #include <iostream>
  8. #include <fstream>
  9. // 引入 d3dx12.h (确保文件在你的工程目录下)
  10. #include "imgui/d3dx12.h"
  11. // 自动链接库文件,省去配置项目属性的麻烦
  12. #pragma comment(lib, "d3d12.lib")
  13. #pragma comment(lib, "dxgi.lib")
  14. #pragma comment(lib, "d3dcompiler.lib")
  15. #pragma comment(lib, "dxguid.lib")
  16. #define STB_IMAGE_IMPLEMENTATION
  17. #include "imgui/stb_image.h"
  18. // 全局变量
  19. static ID3D12Device* g_pd3dDevice = nullptr;
  20. static ID3D12DescriptorHeap* g_pd3dRtvHeap = nullptr;
  21. static ID3D12DescriptorHeap* g_pd3dSrvHeap = nullptr; // SRV 堆
  22. static ID3D12CommandQueue* g_pd3dCommandQueue = nullptr;
  23. static ID3D12GraphicsCommandList* g_pd3dCommandList = nullptr;
  24. static ID3D12CommandAllocator* g_pd3dCommandAllocator = nullptr; // 必须有分配器
  25. static IDXGISwapChain3* g_pSwapChain = nullptr;
  26. static ID3D12Resource* g_mainRenderTargetResource[2] = {}; // 双缓冲
  27. static ID3D12Fence* g_fence = nullptr;
  28. static HANDLE g_fenceEvent = nullptr;
  29. static UINT64 g_fenceLastSignaledValue = 0;
  30. static int const NUM_BACK_BUFFERS = 2;
  31. // 辅助函数声明
  32. bool CreateDeviceD3D(HWND hWnd);
  33. void CleanupDeviceD3D();
  34. void CreateRenderTarget();
  35. void CleanupRenderTarget();
  36. void WaitForLastSubmittedFrame();
  37. LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
  38. // --- 修复后的纹理加载函数 ---
  39. // 这里的 ID3D12Resource** out_resource 是用来存纹理内存的
  40. bool LoadTextureFromFile(const char* filename, ID3D12Resource** out_resource, int* out_width, int* out_height)
  41. {
  42.     // 1. 读取图片
  43.     int image_width = 0;
  44.     int image_height = 0;
  45.     unsigned char* image_data = stbi_load(filename, &image_width, &image_height, NULL, 4);
  46.     if (image_data == NULL) return false;
  47.     // 2. 创建纹理资源描述
  48.     D3D12_RESOURCE_DESC textureDesc = {};
  49.     textureDesc.MipLevels = 1;
  50.     textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
  51.     textureDesc.Width = image_width;
  52.     textureDesc.Height = image_height;
  53.     textureDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
  54.     textureDesc.DepthOrArraySize = 1;
  55.     textureDesc.SampleDesc.Count = 1;
  56.     textureDesc.SampleDesc.Quality = 0;
  57.     textureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
  58.     // 3. 创建纹理资源 (默认堆)
  59.     CD3DX12_HEAP_PROPERTIES defaultHeapProps(D3D12_HEAP_TYPE_DEFAULT);
  60.     g_pd3dDevice->CreateCommittedResource(
  61.         &defaultHeapProps,
  62.         D3D12_HEAP_FLAG_NONE,
  63.         &textureDesc,
  64.         D3D12_RESOURCE_STATE_COPY_DEST,
  65.         nullptr,
  66.         IID_PPV_ARGS(out_resource));
  67.     // 4. 创建上传堆 (临时用于传数据)
  68.     UINT64 uploadBufferSize = GetRequiredIntermediateSize(*out_resource, 0, 1);
  69.     ID3D12Resource* uploadHeap = nullptr;
  70.     CD3DX12_HEAP_PROPERTIES uploadHeapProps(D3D12_HEAP_TYPE_UPLOAD);
  71.     CD3DX12_RESOURCE_DESC bufferDesc = CD3DX12_RESOURCE_DESC::Buffer(uploadBufferSize);
  72.     g_pd3dDevice->CreateCommittedResource(
  73.         &uploadHeapProps,
  74.         D3D12_HEAP_FLAG_NONE,
  75.         &bufferDesc,
  76.         D3D12_RESOURCE_STATE_GENERIC_READ,
  77.         nullptr,
  78.         IID_PPV_ARGS(&uploadHeap));
  79.     // 5. 录制上传命令
  80.     D3D12_SUBRESOURCE_DATA textureData = {};
  81.     textureData.pData = image_data;
  82.     textureData.RowPitch = image_width * 4;
  83.     textureData.SlicePitch = textureData.RowPitch * image_height;
  84.     // 重置命令列表以录制上传操作
  85.     g_pd3dCommandAllocator->Reset();
  86.     g_pd3dCommandList->Reset(g_pd3dCommandAllocator, nullptr);
  87.     UpdateSubresources(g_pd3dCommandList, *out_resource, uploadHeap, 0, 0, 1, &textureData);
  88.     // 资源屏障:把状态从 复制目标 改为 像素着色器资源
  89.     CD3DX12_RESOURCE_BARRIER barrier = CD3DX12_RESOURCE_BARRIER::Transition(*out_resource, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
  90.     g_pd3dCommandList->ResourceBarrier(1, &barrier);
  91.     // 执行并等待完成 (简单粗暴的方式,防止 uploadHeap 被提前释放)
  92.     g_pd3dCommandList->Close();
  93.     ID3D12CommandList* ppCommandLists[] = { g_pd3dCommandList };
  94.     g_pd3dCommandQueue->ExecuteCommandLists(1, ppCommandLists);
  95.     WaitForLastSubmittedFrame(); // 等待 GPU 搞定
  96.     // 清理临时内存
  97.     uploadHeap->Release();
  98.     stbi_image_free(image_data);
  99.     *out_width = image_width;
  100.     *out_height = image_height;
  101.     return true;
  102. }
  103. bool CreateDeviceD3D(HWND hWnd)
  104. {
  105.     // 1. 创建 DXGI 工厂
  106.     IDXGIFactory4* dxgiFactory = nullptr;
  107.     if (CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory)) != S_OK) return false;
  108.     // 2. 创建设备
  109.     if (D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&g_pd3dDevice)) != S_OK) return false;
  110.     // 3. 创建命令队列
  111.     D3D12_COMMAND_QUEUE_DESC queueDesc = {};
  112.     queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
  113.     queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
  114.     if (g_pd3dDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&g_pd3dCommandQueue)) != S_OK) return false;
  115.     // 4. 创建交换链
  116.     DXGI_SWAP_CHAIN_DESC1 sd = {};
  117.     sd.BufferCount = NUM_BACK_BUFFERS;
  118.     sd.Width = 0; sd.Height = 0;
  119.     sd.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
  120.     sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
  121.     sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
  122.     sd.SampleDesc.Count = 1;
  123.     IDXGISwapChain1* swapChain1 = nullptr;
  124.     dxgiFactory->CreateSwapChainForHwnd(g_pd3dCommandQueue, hWnd, &sd, nullptr, nullptr, &swapChain1);
  125.     swapChain1->QueryInterface(IID_PPV_ARGS(&g_pSwapChain));
  126.     swapChain1->Release();
  127.     dxgiFactory->Release();
  128.     // 5. 创建 RTV 描述符堆 (Render Target)
  129.     D3D12_DESCRIPTOR_HEAP_DESC rtvDesc = {};
  130.     rtvDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
  131.     rtvDesc.NumDescriptors = NUM_BACK_BUFFERS;
  132.     g_pd3dDevice->CreateDescriptorHeap(&rtvDesc, IID_PPV_ARGS(&g_pd3dRtvHeap));
  133.     // 6. 创建 SRV 描述符堆 (Shader Resource - 存图片的)
  134.     D3D12_DESCRIPTOR_HEAP_DESC srvDesc = {};
  135.     srvDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
  136.     srvDesc.NumDescriptors = 64; // 预留 64 个位置
  137.     srvDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
  138.     g_pd3dDevice->CreateDescriptorHeap(&srvDesc, IID_PPV_ARGS(&g_pd3dSrvHeap));
  139.     // 7. 创建命令分配器和命令列表
  140.     g_pd3dDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&g_pd3dCommandAllocator));
  141.     g_pd3dDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, g_pd3dCommandAllocator, nullptr, IID_PPV_ARGS(&g_pd3dCommandList));
  142.     g_pd3dCommandList->Close(); // 初始化后先关闭
  143.     // 8. 创建同步围栏
  144.     g_pd3dDevice->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&g_fence));
  145.     g_fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
  146.     CreateRenderTarget();
  147.     return true;
  148. }
  149. void CleanupDeviceD3D()
  150. {
  151.     CleanupRenderTarget();
  152.     if (g_pSwapChain) { g_pSwapChain->SetFullscreenState(false, nullptr); g_pSwapChain->Release(); g_pSwapChain = nullptr; }
  153.     if (g_pd3dCommandQueue) { g_pd3dCommandQueue->Release(); g_pd3dCommandQueue = nullptr; }
  154.     if (g_pd3dCommandList) { g_pd3dCommandList->Release(); g_pd3dCommandList = nullptr; }
  155.     if (g_pd3dCommandAllocator) { g_pd3dCommandAllocator->Release(); g_pd3dCommandAllocator = nullptr; }
  156.     if (g_pd3dRtvHeap) { g_pd3dRtvHeap->Release(); g_pd3dRtvHeap = nullptr; }
  157.     if (g_pd3dSrvHeap) { g_pd3dSrvHeap->Release(); g_pd3dSrvHeap = nullptr; }
  158.     if (g_fence) { g_fence->Release(); g_fence = nullptr; }
  159.     if (g_fenceEvent) { CloseHandle(g_fenceEvent); g_fenceEvent = nullptr; }
  160.     if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = nullptr; }
  161. }
  162. void CreateRenderTarget()
  163. {
  164.     UINT rtvDescriptorSize = g_pd3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
  165.     D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = g_pd3dRtvHeap->GetCPUDescriptorHandleForHeapStart();
  166.     for (UINT i = 0; i < NUM_BACK_BUFFERS; i++) {
  167.         g_pSwapChain->GetBuffer(i, IID_PPV_ARGS(&g_mainRenderTargetResource[i]));
  168.         g_pd3dDevice->CreateRenderTargetView(g_mainRenderTargetResource[i], nullptr, rtvHandle);
  169.         rtvHandle.ptr += rtvDescriptorSize;
  170.     }
  171. }
  172. void CleanupRenderTarget()
  173. {
  174.     WaitForLastSubmittedFrame();
  175.     for (UINT i = 0; i < NUM_BACK_BUFFERS; i++)
  176.         if (g_mainRenderTargetResource[i]) { g_mainRenderTargetResource[i]->Release(); g_mainRenderTargetResource[i] = nullptr; }
  177. }
  178. void WaitForLastSubmittedFrame()
  179. {
  180.     UINT64 fenceValue = g_fenceLastSignaledValue + 1;
  181.     g_pd3dCommandQueue->Signal(g_fence, fenceValue);
  182.     g_fenceLastSignaledValue = fenceValue;
  183.     if (g_fence->GetCompletedValue() < fenceValue) {
  184.         g_fence->SetEventOnCompletion(fenceValue, g_fenceEvent);
  185.         WaitForSingleObject(g_fenceEvent, INFINITE);
  186.     }
  187. }
  188. extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
  189. LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  190. {
  191.     if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam)) return true;
  192.     switch (msg) {
  193.     case WM_SIZE:
  194.         if (g_pd3dDevice != nullptr && wParam != SIZE_MINIMIZED) {
  195.             CleanupRenderTarget();
  196.             g_pSwapChain->ResizeBuffers(0, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam), DXGI_FORMAT_UNKNOWN, 0);
  197.             CreateRenderTarget();
  198.         }
  199.         return 0;
  200.     case WM_DESTROY:
  201.         ::PostQuitMessage(0);
  202.         return 0;
  203.     }
  204.     return ::DefWindowProcW(hWnd, msg, wParam, lParam);
  205. }
  206. struct UI
  207. {
  208.     bool ShowImage = 0;
  209. }ui;
  210. // 主函数 (Entry Point)
  211. // 换成这个 Windows 专用入口
  212. int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
  213. {
  214.     // 创建窗口类
  215.     WNDCLASSEXW wc = { sizeof(wc), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(nullptr), nullptr, nullptr, nullptr, nullptr, L"ImGuiDX12", nullptr };
  216.     ::RegisterClassExW(&wc);
  217.     HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"ImGui + DX12", WS_OVERLAPPEDWINDOW, 100, 100, 1280, 720, nullptr, nullptr, wc.hInstance, nullptr);
  218.     // 初始化 DX12
  219.     if (!CreateDeviceD3D(hwnd)) {
  220.         CleanupDeviceD3D();
  221.         ::UnregisterClassW(wc.lpszClassName, wc.hInstance);
  222.         return 1;
  223.     }
  224.     ::ShowWindow(hwnd, SW_SHOWDEFAULT);
  225.     ::UpdateWindow(hwnd);
  226.     // 初始化 ImGui
  227.     IMGUI_CHECKVERSION();
  228.     ImGui::CreateContext();
  229.     ImGuiIO& io = ImGui::GetIO(); (void)io;
  230.     ImGui::StyleColorsDark();
  231.     ImGui_ImplWin32_Init(hwnd);
  232.     // 【重要】初始化 DX12 后端,传入 SRV 堆
  233.     ImGui_ImplDX12_Init(g_pd3dDevice, NUM_BACK_BUFFERS, DXGI_FORMAT_R8G8B8A8_UNORM,
  234.         g_pd3dSrvHeap,
  235.         g_pd3dSrvHeap->GetCPUDescriptorHandleForHeapStart(),
  236.         g_pd3dSrvHeap->GetGPUDescriptorHandleForHeapStart()
  237.     );
  238.     // --- 加载图片 ---
  239.     ID3D12Resource* my_texture_resource = nullptr; // 存放图片的显存资源
  240.     int my_image_width = 0;
  241.     int my_image_height = 0;
  242.     // 调用修复后的加载函数
  243.     bool ret = LoadTextureFromFile("graph/Alice.png", &my_texture_resource, &my_image_width, &my_image_height);
  244.     if (!ret) {
  245.         printf("Failed to load texture!\n");
  246.     }
  247.     // --- 为图片创建 SRV (描述符) ---
  248.     // 1. 计算偏移:ImGui 用了第0个,我们用第1个
  249.     UINT handle_increment = g_pd3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
  250.     D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle = g_pd3dSrvHeap->GetCPUDescriptorHandleForHeapStart();
  251.     cpuHandle.ptr += handle_increment; // 偏移 1
  252.     D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = g_pd3dSrvHeap->GetGPUDescriptorHandleForHeapStart();
  253.     gpuHandle.ptr += handle_increment; // 偏移 1
  254.     if (my_texture_resource != nullptr) {
  255.         D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
  256.         srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
  257.         srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
  258.         srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
  259.         srvDesc.Texture2D.MipLevels = 1;
  260.         g_pd3dDevice->CreateShaderResourceView(my_texture_resource, &srvDesc, cpuHandle);
  261.     }
  262.     // 主循环
  263.     bool done = false;
  264.     while (!done) {
  265.         MSG msg;
  266.         while (::PeekMessage(&msg, nullptr, 0U, 0U, PM_REMOVE)) {
  267.             ::TranslateMessage(&msg);
  268.             ::DispatchMessage(&msg);
  269.             if (msg.message == WM_QUIT) done = true;
  270.         }
  271.         if (done) break;
  272.         ImGui_ImplDX12_NewFrame();
  273.         ImGui_ImplWin32_NewFrame();
  274.         ImGui::NewFrame();
  275.         // 渲染 UI
  276.         if (ImGui::Begin(u8"IMGUI", nullptr, ImGuiWindowFlags_HorizontalScrollbar)) {
  277.             ImGui::Checkbox("show image?", &ui.ShowImage);
  278.             if (ui.ShowImage)
  279.             {
  280.                 if (my_texture_resource) {
  281.                     // 传入 GPU 句柄进行显示
  282.                     ImGui::Image((ImTextureID)gpuHandle.ptr, ImVec2((float)my_image_width, (float)my_image_height));
  283.                 }
  284.                 else {
  285.                     ImGui::Text("Texture not loaded");
  286.                 }
  287.             }
  288.         }
  289.         ImGui::End();
  290.         ImGui::Render();
  291.         // DX12 渲染流程
  292.         UINT backBufferIdx = g_pSwapChain->GetCurrentBackBufferIndex();
  293.         // 1. 重置分配器和命令列表
  294.         g_pd3dCommandAllocator->Reset();
  295.         g_pd3dCommandList->Reset(g_pd3dCommandAllocator, nullptr);
  296.         // 2. 资源屏障:Present -> RenderTarget
  297.         D3D12_RESOURCE_BARRIER barrier = {};
  298.         barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
  299.         barrier.Transition.pResource = g_mainRenderTargetResource[backBufferIdx];
  300.         barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
  301.         barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
  302.         g_pd3dCommandList->ResourceBarrier(1, &barrier);
  303.         // 3. 设置 SRV 堆 (重要!否则 ImGui 没法画图)
  304.         ID3D12DescriptorHeap* ppHeaps[] = { g_pd3dSrvHeap };
  305.         g_pd3dCommandList->SetDescriptorHeaps(1, ppHeaps);
  306.         // 4. 清屏和设置渲染目标
  307.         D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = g_pd3dRtvHeap->GetCPUDescriptorHandleForHeapStart();
  308.         rtvHandle.ptr += backBufferIdx * g_pd3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
  309.         g_pd3dCommandList->OMSetRenderTargets(1, &rtvHandle, FALSE, nullptr);
  310.         const float clear_color[4] = { 0.1f, 0.1f, 0.1f, 1.0f };
  311.         g_pd3dCommandList->ClearRenderTargetView(rtvHandle, clear_color, 0, nullptr);
  312.         // 5. 绘制 ImGui
  313.         ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), g_pd3dCommandList);
  314.         // 6. 资源屏障:RenderTarget -> Present
  315.         barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
  316.         barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
  317.         g_pd3dCommandList->ResourceBarrier(1, &barrier);
  318.         // 7. 关闭并执行
  319.         g_pd3dCommandList->Close();
  320.         ID3D12CommandList* ppCommandLists[] = { g_pd3dCommandList };
  321.         g_pd3dCommandQueue->ExecuteCommandLists(1, ppCommandLists);
  322.         // 8. 呈现
  323.         g_pSwapChain->Present(1, 0);
  324.         WaitForLastSubmittedFrame(); // 简单同步
  325.     }
  326.     WaitForLastSubmittedFrame();
  327.     // 清理
  328.     ImGui_ImplDX12_Shutdown();
  329.     ImGui_ImplWin32_Shutdown();
  330.     ImGui::DestroyContext();
  331.     CleanupDeviceD3D();
  332.     ::DestroyWindow(hwnd);
  333.     ::UnregisterClassW(wc.lpszClassName, wc.hInstance);
  334.     return 0;
  335. }
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册