1
0
Files
BasaltMeter/BasaltPresenter/Plugins/Engine/DirectX11Engine/main.cpp
2026-01-06 16:27:19 +08:00

391 lines
14 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include <basalt/export_macro.hpp>
#include <basalt/kernel.hpp>
#include <windows.h>
#include <d3d11.h>
#include <d3dcompiler.h>
#include <wrl/client.h>
#include <vector>
#include <iostream>
using Microsoft::WRL::ComPtr;
#define DXCHK(condition) \
if (FAILED(condition)) { \
throw std::runtime_error("bad DirectX calling"); \
}
#pragma region Win32 Window Creation
// Window procedure for the render window
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_CLOSE:
PostQuitMessage(0);
return 0;
default:
break;
}
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
// Create a render window for DirectX
static HWND CreateRenderWindow(std::uint32_t width, std::uint32_t height, const std::wstring_view& title) {
static bool g_CLSREG = false;
constexpr wchar_t class_name[] = L"DirectXRenderWindowClass";
std::wstring c_title(title);
if (!g_CLSREG) {
WNDCLASSEXW wc = {0};
wc.cbSize = sizeof(WNDCLASSEXW);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.hInstance = GetModuleHandleW(nullptr);
wc.hCursor = LoadCursorW(nullptr, IDC_ARROW);
wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
wc.lpszClassName = class_name;
if (!RegisterClassExW(&wc)) {
throw std::runtime_error("Failed to register window class");
}
g_CLSREG = true;
}
// Calculate window size including borders
RECT rect = {0, 0, static_cast<LONG>(width), static_cast<LONG>(height)};
AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);
HWND hwnd = CreateWindowExW(0,
class_name,
c_title.c_str(),
WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME ^ WS_MAXIMIZEBOX,
CW_USEDEFAULT,
CW_USEDEFAULT,
rect.right - rect.left,
rect.bottom - rect.top,
nullptr,
nullptr,
GetModuleHandleW(nullptr),
nullptr);
if (!hwnd) {
throw std::runtime_error("Failed to create render window");
}
return hwnd;
}
static void DestroyRenderWindow(HWND hwnd) {
DestroyWindow(hwnd);
}
/**
* @brief Non-block event loop.
* @return True for indicate active exit.
*/
static bool EventLoop() {
MSG msg;
if (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE) == 0) {
// No msg
return false;
} else {
if (msg.message == WM_QUIT) {
return true;
} else {
TranslateMessage(&msg);
DispatchMessageW(&msg);
return false;
}
}
}
#pragma endregion
// 立方体顶点(位置)
struct Vertex {
float x, y, z;
};
// 立方体顶点
Vertex CubeVertices[] = {
// 前面
{-1, -1, -1},
{1, -1, -1},
{1, 1, -1},
{-1, -1, -1},
{1, 1, -1},
{-1, 1, -1},
// 后面
{-1, -1, 1},
{-1, 1, 1},
{1, 1, 1},
{-1, -1, 1},
{1, 1, 1},
{1, -1, 1},
// 左面
{-1, -1, -1},
{-1, -1, 1},
{-1, 1, 1},
{-1, -1, -1},
{-1, 1, 1},
{-1, 1, -1},
// 右面
{1, -1, -1},
{1, 1, -1},
{1, 1, 1},
{1, -1, -1},
{1, 1, 1},
{1, -1, 1},
// 顶面
{-1, 1, -1},
{1, 1, -1},
{1, 1, 1},
{-1, 1, -1},
{1, 1, 1},
{-1, 1, 1},
// 底面
{-1, -1, -1},
{-1, -1, 1},
{1, -1, 1},
{-1, -1, -1},
{1, -1, 1},
{1, -1, -1}};
// 简单顶点着色器(只传位置)
const char* g_VS = R"(
float4 main(float3 pos : POSITION) : SV_POSITION {
return float4(pos, 1.0f);
}
)";
// 简单像素着色器(返回固定颜色)
const char* g_PS = R"(
float4 main() : SV_TARGET {
return float4(0.2f, 0.4f, 0.8f, 1.0f);
}
)";
using ::Basalt::Shared::Kernel::EngineConfig;
using ::Basalt::Shared::Kernel::IEngine;
class DirectX11Engine : public IEngine {
public:
DirectX11Engine() : IEngine() {}
virtual ~DirectX11Engine() {}
private:
HWND window; ///< Win32窗口
ComPtr<ID3D11Device> device; ///< 设备
ComPtr<ID3D11DeviceContext> context; ///< 上下文
ComPtr<IDXGISwapChain> swap_chain; ///< 交换链
ComPtr<ID3D11RenderTargetView> rtv; ///< 渲染目标视图
ComPtr<ID3D11Texture2D> back_buffer; ///< 后缓冲
ComPtr<ID3D11Texture2D> depth_buffer; ///< 深度缓冲
ComPtr<ID3D11DepthStencilView> dsv; ///< 深度模板视图
ComPtr<ID3D11Texture2D> depth_staging; ///< 用于CPU读取的深度暂存纹理
ComPtr<ID3D11VertexShader> vs; ///< 顶点着色器
ComPtr<ID3D11PixelShader> ps; ///< 像素着色器
ComPtr<ID3DBlob> vs_blob; ///< 顶点着色器字节码
ComPtr<ID3DBlob> ps_blob; ///< 像素着色器字节码
ComPtr<ID3D11InputLayout> input_layout; ///< 输入布局
ComPtr<ID3D11Buffer> vertex_buffer; ///< 顶点缓冲
ComPtr<ID3D11DepthStencilState> depth_state; ///< 深度状态
ComPtr<ID3D11RasterizerState> rasterizer_state; ///< 光栅化状态
std::vector<BYTE> depth_data; ///< 深度数据
public:
virtual void Startup(EngineConfig&& config) override {
IEngine::Startup(std::move(config));
// 创建Win32窗口并显示
if (this->config.headless) {
window = NULL;
} else {
window = CreateRenderWindow(this->config.width, this->config.height, this->config.title);
ShowWindow(window, SW_SHOW);
UpdateWindow(window);
}
// 初始化 COM
DXCHK(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED));
// 创建设备和上下文
DXCHK(D3D11CreateDevice(nullptr,
D3D_DRIVER_TYPE_HARDWARE,
nullptr,
D3D11_CREATE_DEVICE_DEBUG,
nullptr,
0,
D3D11_SDK_VERSION,
&device,
nullptr,
&context));
// 创建交换链(窗口可选,这里仅用于渲染上下文)
DXGI_SWAP_CHAIN_DESC sd = {};
sd.BufferDesc.Width = this->config.width;
sd.BufferDesc.Height = this->config.height;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.SampleDesc.Count = 1;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.BufferCount = 1;
sd.OutputWindow = window;
sd.Windowed = TRUE;
ComPtr<IDXGIDevice> dxgi_device;
DXCHK(device.As(&dxgi_device));
ComPtr<IDXGIAdapter> adapter;
DXCHK(dxgi_device->GetAdapter(&adapter));
ComPtr<IDXGIFactory> factory;
DXCHK(adapter->GetParent(IID_PPV_ARGS(&factory)));
DXCHK(factory->CreateSwapChain(device.Get(), &sd, &swap_chain));
// 创建渲染目标
DXCHK(swap_chain->GetBuffer(0, IID_PPV_ARGS(&back_buffer)));
DXCHK(device->CreateRenderTargetView(back_buffer.Get(), nullptr, &rtv));
// 创建深度缓冲D32_FLOAT
D3D11_TEXTURE2D_DESC depth_desc = {};
depth_desc.Width = this->config.width;
depth_desc.Height = this->config.height;
depth_desc.MipLevels = 1;
depth_desc.ArraySize = 1;
depth_desc.Format = DXGI_FORMAT_D32_FLOAT;
depth_desc.SampleDesc.Count = 1;
depth_desc.Usage = D3D11_USAGE_DEFAULT;
depth_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
DXCHK(device->CreateTexture2D(&depth_desc, nullptr, &depth_buffer));
DXCHK(device->CreateDepthStencilView(depth_buffer.Get(), nullptr, &dsv));
// 创建 staging texture 用于 CPU 读取深度R32_FLOAT
D3D11_TEXTURE2D_DESC staging_desc = depth_desc;
staging_desc.Format = DXGI_FORMAT_R32_FLOAT; // 注意DSV 用 D32_FLOATstaging 用 R32_FLOAT
staging_desc.BindFlags = 0;
staging_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
staging_desc.Usage = D3D11_USAGE_STAGING;
DXCHK(device->CreateTexture2D(&staging_desc, nullptr, &depth_staging));
// 编译并创建着色器
DXCHK(D3DCompile(g_VS, static_cast<UINT>(strlen(g_VS)), nullptr, nullptr, nullptr, "main", "vs_4_0", 0, 0, &vs_blob, nullptr));
DXCHK(D3DCompile(g_PS, static_cast<UINT>(strlen(g_PS)), nullptr, nullptr, nullptr, "main", "ps_4_0", 0, 0, &ps_blob, nullptr));
DXCHK(device->CreateVertexShader(vs_blob->GetBufferPointer(), vs_blob->GetBufferSize(), nullptr, &vs));
DXCHK(device->CreatePixelShader(ps_blob->GetBufferPointer(), ps_blob->GetBufferSize(), nullptr, &ps));
// 输入布局
D3D11_INPUT_ELEMENT_DESC layout[] = {{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}};
DXCHK(device->CreateInputLayout(layout, 1, vs_blob->GetBufferPointer(), vs_blob->GetBufferSize(), &input_layout));
// 顶点缓冲
D3D11_BUFFER_DESC vb_desc = {};
vb_desc.ByteWidth = sizeof(CubeVertices);
vb_desc.Usage = D3D11_USAGE_DEFAULT;
vb_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
D3D11_SUBRESOURCE_DATA vb_data = {CubeVertices};
DXCHK(device->CreateBuffer(&vb_desc, &vb_data, &vertex_buffer));
// 深度测试启用
D3D11_DEPTH_STENCIL_DESC ds_desc = {};
ds_desc.DepthEnable = TRUE;
ds_desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
ds_desc.DepthFunc = D3D11_COMPARISON_LESS;
DXCHK(device->CreateDepthStencilState(&ds_desc, &depth_state));
// 设置光栅化模式
D3D11_RASTERIZER_DESC rasterizer_desc;
ZeroMemory(&rasterizer_desc, sizeof(D3D11_RASTERIZER_DESC));
rasterizer_desc.FillMode = D3D11_FILL_SOLID;
rasterizer_desc.CullMode = D3D11_CULL_NONE;
DXCHK(device->CreateRasterizerState(&rasterizer_desc, &rasterizer_state));
// 视口
D3D11_VIEWPORT vp;
ZeroMemory(&vp, sizeof(D3D11_VIEWPORT));
vp.TopLeftX = 0;
vp.TopLeftY = 0;
vp.Width = (float) this->config.width;
vp.Height = (float) this->config.height;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
context->RSSetViewports(1, &vp); // RSSetViewports doesn't return HRESULT, so no need to wrap
//// 打开命名管道(需另一进程已创建)
//constexpr const wchar_t* PipeName = L"\\\\.\\pipe\\54da494c-301a-47a9-9d67-4fbebaeda8cf";
//hPipe = CreateFileW(PipeName, GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
//if (hPipe == INVALID_HANDLE_VALUE) {
// std::cerr << "Failed to open named pipe. Ensure another process created it.\n";
// return;
//}
// 缩放深度数据数组到指定大小
depth_data.resize(this->config.width * this->config.height * sizeof(float));
//frameCount = 0;
}
virtual bool Tick() override {
if (IEngine::Tick()) return true;
// Event loop
if (EventLoop()) return true;
// 清屏
float clear_color[] = {0.8f, 0.1f, 0.1f, 1.0f};
context->ClearRenderTargetView(rtv.Get(), clear_color);
context->ClearDepthStencilView(dsv.Get(), D3D11_CLEAR_DEPTH, 1.0f, 0);
// 设置管线
context->OMSetRenderTargets(1, rtv.GetAddressOf(), dsv.Get());
context->OMSetDepthStencilState(depth_state.Get(), 1);
context->RSSetState(rasterizer_state.Get());
context->VSSetShader(vs.Get(), nullptr, 0);
context->PSSetShader(ps.Get(), nullptr, 0);
context->IASetInputLayout(input_layout.Get());
UINT stride = sizeof(Vertex), offset = 0;
context->IASetVertexBuffers(0, 1, vertex_buffer.GetAddressOf(), &stride, &offset);
context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// 绘制立方体
context->Draw(sizeof(CubeVertices) / sizeof(Vertex), 0); // 自动计算顶点数
//// 复制深度缓冲到 staging texture
//context->CopyResource(depth_staging.Get(), depth_buffer.Get());
//// Map 获取数据
//D3D11_MAPPED_SUBRESOURCE mapped;
//DXCHK(context->Map(depth_staging.Get(), 0, D3D11_MAP_READ, 0, &mapped));
//memcpy(depth_data.data(), mapped.pData, this->config.width * this->config.height * sizeof(float));
//context->Unmap(depth_staging.Get(), 0);
//// 写入命名管道
//DWORD written;
//if (!WriteFile(hPipe, depth_data.data(), static_cast<DWORD>(this->config.width * this->config.height * sizeof(float)), &written, nullptr) || written != this->config.width * this->config.height * sizeof(float)) {
// std::cerr << "WriteFile failed or incomplete.\n";
//}
// 呈现(可选)
DXCHK(swap_chain->Present(0, 0));
//frameCount++;
return false;
}
virtual void Shutdown() override {
IEngine::Shutdown();
//if (hPipe != INVALID_HANDLE_VALUE) {
// CloseHandle(hPipe);
//}
CoUninitialize();
if (!this->config.headless) {
CloseWindow(window);
DestroyRenderWindow(window);
}
}
};
BS_EXPORT void* BSCreateInstance() {
return static_cast<IEngine*>(new DirectX11Engine());
}
BS_EXPORT void BSDestroyInstance(void* instance) {
delete dynamic_cast<DirectX11Engine*>(static_cast<IEngine*>(instance));
}