1
0

optimize dx11 engine

This commit is contained in:
2026-01-05 16:51:58 +08:00
parent 7110becf66
commit 52916db08f
10 changed files with 152 additions and 117 deletions

View File

@@ -1,5 +1,4 @@
#include <basalt_export.hpp>
#include <directx_util.hpp>
#include <engine.hpp>
#include <windows.h>
#include <d3d11.h>
@@ -14,6 +13,91 @@ using Microsoft::WRL::ComPtr;
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;
@@ -61,8 +145,7 @@ Vertex CubeVertices[] = {
{1, -1, 1},
{-1, -1, -1},
{1, -1, 1},
{1, -1, -1}
};
{1, -1, -1}};
// 简单顶点着色器(只传位置)
const char* g_VS = R"(
@@ -80,7 +163,6 @@ const char* g_PS = R"(
using ::Basalt::Shared::Engine::EngineConfig;
using ::Basalt::Shared::Engine::IEngine;
namespace DxUtil = ::Basalt::Shared::DirectX;
class DirectX11Engine : public IEngine {
public:
@@ -88,28 +170,38 @@ public:
virtual ~DirectX11Engine() {}
private:
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; ///< 深度状态
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; ///< 深度数据
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));
@@ -133,7 +225,7 @@ public:
sd.SampleDesc.Count = 1;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.BufferCount = 1;
sd.OutputWindow = DxUtil::CreateRenderWindow(this->config.width, this->config.height, this->config.title); // 创建窗口
sd.OutputWindow = window;
sd.Windowed = TRUE;
ComPtr<IDXGIDevice> dxgi_device;
@@ -228,13 +320,11 @@ public:
//frameCount = 0;
}
virtual void Tick() override {
IEngine::Tick();
virtual bool Tick() override {
if (IEngine::Tick()) return true;
MSG msg;
GetMessage(&msg, nullptr, 0, 0);
TranslateMessage(&msg);
DispatchMessage(&msg);
// Event loop
if (EventLoop()) return true;
// 清屏
float clear_color[] = {0.8f, 0.1f, 0.1f, 1.0f};
@@ -274,6 +364,8 @@ public:
DXCHK(swap_chain->Present(0, 0));
//frameCount++;
return false;
}
virtual void Shutdown() override {
@@ -283,6 +375,10 @@ public:
// CloseHandle(hPipe);
//}
CoUninitialize();
if (!this->config.headless) {
CloseWindow(window);
DestroyRenderWindow(window);
}
}
};

View File

@@ -1,5 +1,4 @@
#include "dll_loader.hpp"
#include <basalt_char.hpp>
#include <stdexcept>
#include <filesystem>
@@ -9,16 +8,18 @@
#include <unistd.h>
#endif
using ::Basalt::Shared::Char::BSStringView;
namespace Basalt::Presenter {
static std::filesystem::path get_executable() {
#if defined(BASALT_OS_WINDOWS)
wchar_t buffer[MAX_PATH];
DWORD hr = GetModuleFileNameW(NULL, buffer, MAX_PATH);
if (hr == 0) {
throw std::runtime_error("Failed to get executable path");
} else {
if (hr > 0 && hr < MAX_PATH) {
return std::filesystem::path(buffer);
} else {
throw std::runtime_error("Failed to get executable path");
}
#else
char buffer[PATH_MAX];
@@ -32,7 +33,7 @@ namespace Basalt::Presenter {
#endif
}
DllLoader::DllLoader(DllKind kind, const std::basic_string_view<BSCHAR> filename) {
DllLoader::DllLoader(DllKind kind, const BSStringView& filename) {
// Build DLL full path
auto dll_path = get_executable().parent_path();
dll_path /= BSTEXT("plugin");

View File

@@ -25,7 +25,7 @@ namespace Basalt::Presenter {
#endif
public:
DllLoader(DllKind kind, const std::basic_string_view<BSCHAR> filename);
DllLoader(DllKind kind, const Shared::Char::BSStringView& filename);
~DllLoader();
private:

View File

@@ -8,12 +8,14 @@ namespace Shared = ::Basalt::Shared;
int main(int argc, char* argv[]) {
auto engine_dll = Presenter::DllLoader(Presenter::DllKind::Engine, BSTEXT("BasaltDirectX11Engine"));
auto* engine = engine_dll.CreateInstance<Shared::Engine::IEngine>();
Shared::Engine::EngineConfig engine_config{.is_headless = false, .title = BSTEXT("Fuck You"), .width = 800, .height = 600};
Shared::Engine::EngineConfig engine_config{.headless = false, .title = BSTEXT("Fuck You"), .width = 800, .height = 600};
engine->Startup(std::move(engine_config));
while (true) {
engine->Tick();
if (engine->Tick()) break;
}
engine->Shutdown();
engine_dll.DestroyInstance(engine);
}

View File

@@ -5,7 +5,7 @@ PRIVATE
pipe_operator.cpp
engine.cpp
deliver.cpp
directx_util.cpp
)
target_sources(BasaltShared
PUBLIC
@@ -17,7 +17,6 @@ FILES
pipe_operator.hpp
engine.hpp
deliver.hpp
directx_util.hpp
)
target_include_directories(BasaltShared
PUBLIC

View File

@@ -1,4 +1,6 @@
#include <cwchar>
#include <string>
#include <string_view>
#if defined(BASALT_OS_WINDOWS)
#define BSCHAR wchar_t
@@ -8,3 +10,10 @@
#define BSCHAR char
#define BSTEXT(x) x
#endif
namespace Basalt::Shared::Char {
using BSString = std::basic_string<BSCHAR>;
using BSStringView = std::basic_string_view<BSCHAR>;
}; // namespace Basalt::Shared::Char

View File

@@ -1,67 +0,0 @@
#include "directx_util.hpp"
#include <stdexcept>
#include <string>
namespace Basalt::Shared::DirectX {
// Window procedure for the render window
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
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,
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");
}
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
return hwnd;
}
}

View File

@@ -1,10 +0,0 @@
#pragma once
#include <cinttypes>
#include <string_view>
#include <windows.h>
namespace Basalt::Shared::DirectX {
HWND CreateRenderWindow(std::uint32_t width, std::uint32_t height, const std::wstring_view& title);
}

View File

@@ -17,8 +17,9 @@ namespace Basalt::Shared::Engine {
this->status = EngineStatus::Running;
}
void IEngine::Tick() {
bool IEngine::Tick() {
if (this->status != EngineStatus::Running) throw std::runtime_error("unexpected engine status");
return false;
}
void IEngine::Shutdown() {

View File

@@ -15,7 +15,7 @@ namespace Basalt::Shared::Engine {
};
struct EngineConfig {
bool is_headless; ///< Whether enable headless mode (No Window created).
bool headless; ///< Whether enable headless mode (No Window created).
std::basic_string<BSCHAR> title; ///< Window title.
std::uint32_t width; ///< Window width.
std::uint32_t height; ///< Window height.
@@ -34,7 +34,11 @@ namespace Basalt::Shared::Engine {
public:
virtual void Startup(EngineConfig&& config);
virtual void Tick();
/**
* @brief
* @return True for active exit.
*/
virtual bool Tick();
virtual void Shutdown();
protected: