feat: initial DX12 foundation framework

This commit is contained in:
SpecialX
2026-03-19 18:27:49 +08:00
commit 60f73b525d
70 changed files with 8993 additions and 0 deletions

View File

@@ -0,0 +1,355 @@
/**
* @file TestRenderer.cpp
* @brief 渲染功能综合测试实现。
*/
#include "TestRenderer.h"
#include "Platform/Platform.h"
#include "Platform/PlatformTypes.h"
#include "Graphics_Beta/Renderer.h"
#include "ShaderComponents.h"
#include "Content/ContentToEngine.h"
#include "Graphics_Beta/Direct3D12/D3D12Core.h"
#include "Components/Script.h"
#include "Components/Entity.h"
#include "Components/Transform.h"
#include <filesystem>
#include <fstream>
#if TEST_RENDERER
using namespace XEngine;
class rotator_script;
REGISTER_SCRIPT(rotator_script);
class rotator_script :public script::entity_script
{
public:
constexpr explicit rotator_script(game_entity::entity entity)
: script::entity_script{ entity }{}
void begin_play() override {}
void update(float dt) override
{
_angle += 0.25f * dt * math::two_pi;
if (_angle > math::two_pi) _angle -= math::two_pi;
math::v3a rot{ 0.f, _angle, 0.f };
DirectX::XMVECTOR quat{ DirectX::XMQuaternionRotationRollPitchYawFromVector(DirectX::XMLoadFloat3A(&rot)) };
math::v4 rot_quat{};
DirectX::XMStoreFloat4(&rot_quat, quat);
set_rotation(rot_quat);
}
private:
f32 _angle{ 0.f };
};
// Multithreading test worker spawn code ///////////////////
#define ENABLE_TEST_WORKERS 0
constexpr u32 num_threads{ 8 };
bool wshutdown{ false };
std::thread workers[num_threads];
utl::vector<u8> buffer(1024 * 1024, 0);
void buffer_test_worker()
{
while (!wshutdown)
{
auto* resource = graphics::d3d12::d3dx::create_buffer(buffer.data(), (u32)buffer.size());
graphics::d3d12::core::deferred_release(resource);
}
}
template<class FnPtr, class... Args>
void init_test_workers(FnPtr&& fnPtr, Args&&... args)
{
#if ENABLE_TEST_WORKERS
wshutdown = false;
for (auto& w : workers)
w = std::thread(std::forward<FnPtr>(fnPtr), std::forward<Args>(args)...);
#endif
}
void joint_test_workers()
{
#if ENABLE_TEST_WORKERS
wshutdown = true;
for (auto&w : workers) w.join();
#endif
}
//////////////////////
struct camera_surface {
game_entity::entity entity{};
graphics::camera camera{};
graphics::render_surface surface{};
};
id::id_type item_id{ id::invalid_id };
id::id_type model_id{ id::invalid_id };
camera_surface _surfaces[3]{};
time_it timer{};
bool resized{ false };
bool is_restarting{ false };
void destroy_camera_surface(camera_surface& surface);
game_entity::entity create_one_game_entity(math::v3 position, math::v3 rotation, const char* script_name);
bool test_initialize();
void test_shutdown();
id::id_type create_render_item(id::id_type entity_id);
void destroy_render_item(id::id_type item_id);
void generate_lights();
void remove_lights();
LRESULT win_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
bool toggle_fullscreen{ false };
switch (msg)
{
case WM_DESTROY:
{
bool all_closed{ true };
for (u32 i{ 0 }; i < _countof(_surfaces); ++i)
{
if (_surfaces[i].surface.window.is_valid())
{
if (_surfaces[i].surface.window.is_closed())
{
destroy_camera_surface(_surfaces[i]);
}
else
{
all_closed = false;
}
}
}
if (all_closed && !is_restarting)
{
PostQuitMessage(0);
return 0;
}
}
case WM_SIZE:
resized = (wparam != SIZE_MINIMIZED);
break;
case WM_SYSCHAR:
toggle_fullscreen = (wparam == VK_RETURN && (HIWORD(lparam) & KF_ALTDOWN));
break;
case WM_KEYDOWN:
if (wparam == VK_ESCAPE)
{
PostMessage(hwnd, WM_CLOSE, 0, 0);
return 0;
}
else if (wparam == VK_F11)
{
is_restarting = true;
test_shutdown();
test_initialize();
}
}
if ((resized & (GetAsyncKeyState(VK_LBUTTON) >= 0)) || toggle_fullscreen)
{
platform::window win{ platform::window_id{(id::id_type)GetWindowLongPtr(hwnd, GWLP_USERDATA)} };
for (u32 i{ 0 }; i < _countof(_surfaces); ++i)
{
if (win.get_id() == _surfaces[i].surface.window.get_id())
{
if (toggle_fullscreen)
{
win.set_fullscreen(!win.is_fullscreen());
return 0;
}
else
{
_surfaces[i].surface.surface.resize(win.width(), win.height());
_surfaces[i].camera.aspect_ratio((f32)win.width() / win.height());
resized = false;
}
break;
}
}
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
void
create_camera_surface(camera_surface& surface, platform::window_init_info info)
{
surface.surface.window = platform::create_window(&info);
surface.surface.surface = graphics::create_surface(surface.surface.window);
surface.entity = create_one_game_entity({ 0.0f,0.f,6.0f }, { 0.f, 3.14f, 0.f }, nullptr);
surface.camera = graphics::create_camera(graphics::perspective_camera_init_info{ surface.entity.get_id() });
surface.camera.aspect_ratio((f32)surface.surface.window.width() / surface.surface.window.height());
}
void
destroy_camera_surface(camera_surface& surface)
{
camera_surface temp{ surface };
surface = {};
if (temp.surface.surface.is_valid()) graphics::remove_surface(temp.surface.surface.get_id());
if (temp.surface.window.is_valid()) platform::remove_window(temp.surface.window.get_id());
if (temp.camera.is_valid()) graphics::remove_camera(temp.camera.get_id());
if (temp.entity.is_valid()) game_entity::remove(temp.entity.get_id());
}
game_entity::entity
create_one_game_entity(math::v3 position, math::v3 rotation, const char* script_name)
{
transform::init_info transform_info{};
DirectX::XMVECTOR quat{ DirectX::XMQuaternionRotationRollPitchYawFromVector(DirectX::XMLoadFloat3(&rotation)) };
math::v4a rot_quat;
DirectX::XMStoreFloat4A(&rot_quat, quat);
memcpy(&transform_info.rotation[0], &rot_quat.x, sizeof(transform_info.rotation));
memcpy(&transform_info.position[0], &position.x, sizeof(transform_info.position));
script::init_info script_info{};
if (script_name)
{
script_info.script_creator = script::detail::get_script_creator(script::detail::string_hash()(script_name));
assert(script_info.script_creator);
}
game_entity::entity_info entity_info{};
entity_info.transform = &transform_info;
entity_info.script = &script_info;
game_entity::entity ntt{ game_entity::create(entity_info) };
assert(ntt.is_valid());
return ntt;
}
void
remove_game_entity(game_entity::entity_id id)
{
game_entity::remove(id);
}
bool
read_file(std::filesystem::path path, std::unique_ptr<u8[]>&data, u64& size)
{
if (!std::filesystem::exists(path)) return false;
size = std::filesystem::file_size(path);
assert(size);
if (!size) return false;
data = std::make_unique<u8[]>(size);
std::ifstream file{ path, std::ios::in | std::ios::binary };
if (!file || !file.read((char*)data.get(), size))
{
file.close();
return false;
}
file.close();
return true;
}
bool
test_initialize()
{
while (!compile_shaders())
{
if (MessageBox(nullptr, L"Failed to compile engine shaders.", L"sahder Compilation Error", MB_RETRYCANCEL) != IDRETRY)
return false;
}
if (!graphics::initialize(graphics::graphics_platform::direct3d12))
return false;
platform::window_init_info info[]
{
{&win_proc, nullptr, L"Test Window 1", 200, 100,400,400},
{&win_proc, nullptr, L"Test Window 2", 700, 100,400,400},
{&win_proc, nullptr, L"Test Window 3", 1200,100,400,400},/*
{&win_proc, nullptr, L"Test Window 4", 200, 600,400,400},
{&win_proc, nullptr, L"Test Window 5", 700, 600,400,400},
{&win_proc, nullptr, L"Test Window 6", 1200,600,400,400},*/
};
static_assert(_countof(info) == _countof(_surfaces));
for (u32 i{ 0 }; i < _countof(_surfaces); ++i)
create_camera_surface(_surfaces[i], info[i]);
//load test model
std::unique_ptr<u8[]> model;
u64 size{ 0 };
if (!read_file("..\\..\\enginetest\\model.model", model, size)) return false;
model_id = content::create_resource(model.get(), content::asset_type::mesh);
if (!id::is_valid(model_id))return false;
init_test_workers(buffer_test_worker);
item_id = create_render_item(create_one_game_entity({ }, { }, nullptr).get_id());
generate_lights();
is_restarting = false;
return true;
}
void
test_shutdown()
{
remove_lights();
destroy_render_item(item_id);
joint_test_workers();
if (id::is_valid(model_id))
{
content::destroy_resource(model_id, content::asset_type::mesh);
}
for (u32 i{ 0 }; i < _countof(_surfaces); ++i)
destroy_camera_surface(_surfaces[i]);
graphics::shutdown();
}
bool
engine_test::initialize()
{
return test_initialize();
}
void
engine_test::run()
{
timer.begin();
std::this_thread::sleep_for(std::chrono::milliseconds(10));
script::update(timer.dt_avg());
for (u32 i{ 0 }; i < _countof(_surfaces); ++i)
{
if (_surfaces[i].surface.surface.is_valid())
{
f32 threshold{ 10 };
graphics::frame_info info{};
info.render_item_ids = &item_id;
info.render_item_count = 1;
info.thresholds = &threshold;
info.light_set_key = 0;
info.average_frame_time = timer.dt_avg();
info.camera_id = _surfaces[i].camera.get_id();
_surfaces[i].surface.surface.render(info);
}
}
timer.end();
}
bool
engine_test::shutdown()
{
test_shutdown();
return true;
}
#endif