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,194 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="DebugEditor|x64">
<Configuration>DebugEditor</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="ReleaseEditor|x64">
<Configuration>ReleaseEditor</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>17.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{d1fb8bbd-6836-431b-a43d-f6ea248b8965}</ProjectGuid>
<RootNamespace>EngineTest</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugEditor|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseEditor|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='DebugEditor|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseEditor|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<PreBuildEventUseInBuild>false</PreBuildEventUseInBuild>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugEditor|x64'">
<PreBuildEventUseInBuild>false</PreBuildEventUseInBuild>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<PreBuildEventUseInBuild>false</PreBuildEventUseInBuild>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseEditor|x64'">
<PreBuildEventUseInBuild>false</PreBuildEventUseInBuild>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(SolutionDir)Engine\Common;$(SolutionDir)Engine</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>$(OutDir)</AdditionalLibraryDirectories>
</Link>
<PreBuildEvent>
<Command>xcopy /Y /D "$(SolutionDir)packages\dxCompiler\dxcompiler.dll" "$(OutDir)"</Command>
</PreBuildEvent>
<PostBuildEvent>
<Command>
</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugEditor|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(SolutionDir)Engine\Common;$(SolutionDir)Engine</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>$(OutDir)</AdditionalLibraryDirectories>
</Link>
<PreBuildEvent>
<Command>xcopy /Y /D "$(SolutionDir)packages\dxCompiler\dxcompiler.dll" "$(OutDir)"</Command>
</PreBuildEvent>
<PostBuildEvent>
<Command>
</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(SolutionDir)Engine\Common;$(SolutionDir)Engine</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>$(OutDir)</AdditionalLibraryDirectories>
</Link>
<PreBuildEvent>
<Command>xcopy /Y /D "$(SolutionDir)packages\dxCompiler\dxcompiler.dll" "$(OutDir)"</Command>
</PreBuildEvent>
<PostBuildEvent>
<Command>
</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseEditor|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(SolutionDir)Engine\Common;$(SolutionDir)Engine</AdditionalIncludeDirectories>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>$(OutDir)</AdditionalLibraryDirectories>
</Link>
<PreBuildEvent>
<Command>xcopy /Y /D "$(SolutionDir)packages\dxCompiler\dxcompiler.dll" "$(OutDir)"</Command>
</PreBuildEvent>
<PostBuildEvent>
<Command>
</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="Main.cpp" />
<ClCompile Include="TestRenderer.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="Test.h" />
<ClInclude Include="TestEntityComponent.h" />
<ClInclude Include="TestRenderer.h" />
<ClInclude Include="TestWindow.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="Main.cpp" />
<ClCompile Include="TestRenderer.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="Test.h" />
<ClInclude Include="TestEntityComponent.h" />
<ClInclude Include="TestWindow.h" />
<ClInclude Include="TestRenderer.h" />
</ItemGroup>
</Project>

84
EngineTest/Lights.cpp Normal file
View File

@@ -0,0 +1,84 @@
/**
* @file Lights.cpp
* @brief 渲染测试场景灯光创建与销毁逻辑。
*/
#include "EngineAPI/GameEntity.h"
#include "EngineAPI/Light.h"
#include "EngineAPI/TransformComponent.h"
#include "Graphics_Beta/Renderer.h"
#include "Test.h"
#if TEST_RENDERER
using namespace XEngine;
game_entity::entity create_one_game_entity(math::v3 position, math::v3 rotation, const char* script_name);
void remove_game_entity(game_entity::entity_id id);
const u64 left_set{ 0 };
const u64 right_set{ 1 };
utl::vector<graphics::light> lights;
constexpr math::v3
rgb_to_color(u8 r, u8 g, u8 b) { return { r / 255.f, g / 255.f, b / 255.f }; }
void
generate_lights()
{
// LEFT_SET
graphics::light_init_info info{};
info.entity_id = create_one_game_entity({}, {}, nullptr).get_id();
info.type = graphics::light::directioinal;
info.light_set_key = left_set;
info.intensity = 1.f;
info.color = rgb_to_color(174, 174, 174);
lights.emplace_back(graphics::create_light(info));
info.entity_id = create_one_game_entity({}, { math::pi * 0.5f,0,0 }, nullptr).get_id();
info.color = rgb_to_color(17, 27, 28);
lights.emplace_back(graphics::create_light(info));
info.entity_id = create_one_game_entity({}, { -math::pi * 0.5f,0,0 }, nullptr).get_id();
info.color = rgb_to_color(63, 47, 38);
lights.emplace_back(graphics::create_light(info));
//// RIGHT_SET
//info.entity_id = create_one_game_entity({}, {}, nullptr).get_id();
//info.type = graphics::light::directioinal;
//info.light_set_key = right_set;
//info.intensity = 1.f;
//info.color = rgb_to_color(174, 174, 174);
//lights.emplace_back(graphics::create_light(info));
//info.entity_id = create_one_game_entity({}, { math::pi * 0.5f,0,0 }, nullptr).get_id();
//info.color = rgb_to_color(17, 27, 28);
//lights.emplace_back(graphics::create_light(info));
//info.entity_id = create_one_game_entity({}, { -math::pi * 0.5f,0,0 }, nullptr).get_id();
//info.color = rgb_to_color(63, 47, 38);
//lights.emplace_back(graphics::create_light(info));
}
void
remove_lights()
{
for (auto& light : lights)
{
const game_entity::entity_id id{ light.entity_id() };
graphics::remove_light(light.get_id(), light.get_light_set_key());
remove_game_entity(id);
}
lights.clear();
}
#endif

85
EngineTest/Main.cpp Normal file
View File

@@ -0,0 +1,85 @@
/**
* @file Main.cpp
* @brief 测试程序入口与主循环。
*/
#include "Test.h"
#pragma comment(lib, "engine.lib")
#if TEST_ENTITY_COMPONENTS
#include "TestEntityComponent.h"
#elif TEST_WINDOW_COMPONENTS
#include "TestWindow.h"
#elif TEST_RENDERER
#include "TestRenderer.h"
#endif
#ifdef _WIN64
#include <Windows.h>
#include <filesystem>
// TODO: duplicate
std::filesystem::path
set_current_directory_to_executable_path()
{
wchar_t path[MAX_PATH]{};
const u32 length{ GetModuleFileName(0, &path[0], MAX_PATH) };
if (!length || GetLastError() == ERROR_INSUFFICIENT_BUFFER) return {};
std::filesystem::path p{ path };
std::filesystem::current_path(p.parent_path());
return std::filesystem::current_path();
}
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
#if _DEBUG
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif
set_current_directory_to_executable_path();
engine_test test{};
if (test.initialize())
{
MSG msg{};
bool is_running{ true };
while (is_running)
{
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
is_running &= (msg.message != WM_QUIT);
}
test.run();
}
}
test.shutdown();
}
#else
int main()
{
#if _DEBUG
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif
engine_test test{};
if (test.initialize())
{
test.run();
}
test.shutdown();
}
#endif

141
EngineTest/RenderItem.cpp Normal file
View File

@@ -0,0 +1,141 @@
/**
* @file RenderItem.cpp
* @brief 渲染项创建与销毁测试逻辑。
*/
#include "CommonHeader.h"
#include <filesystem>
#include "Content/ContentToEngine.h"
#include "ShaderComponents.h"
#include "Components/Entity.h"
#include "Graphics_Beta/Renderer.h"
#include "../ContentTools/Geometry.h"
#include "Test.h"
#if TEST_RENDERER
using namespace XEngine;
bool read_file(std::filesystem::path, std::unique_ptr<u8[]>&, u64&);
namespace {
id::id_type model_id{ id::invalid_id };
id::id_type vs_id{ id::invalid_id };
id::id_type ps_id{ id::invalid_id };
id::id_type mtl_id{ id::invalid_id };
std::unordered_map<id::id_type, id::id_type> render_item_entity_map;
void
load_model()
{
std::unique_ptr<u8[]> model;
u64 size{ 0 };
read_file("..\\..\\enginetest\\model.model", model, size);
model_id = content::create_resource(model.get(), content::asset_type::mesh);
assert(id::is_valid(model_id));
}
void
load_shaders()
{
shader_file_info info{};
info.file_name = "TestShader.hlsl";
info.function = "TestShaderVS";
info.type = shader_type::vertex;
const char* shader_path{ "..\\..\\enginetest\\" };
std::wstring defines[]{ L"ELEMENTS_TYPE=1", L"ELEMENTS_TYPE=3" };
utl::vector<u32> keys;
keys.emplace_back(tools::elements::element_type::static_normal);
keys.emplace_back(tools::elements::element_type::static_normal_texture);
utl::vector<std::wstring> extra_args{};
utl::vector<std::unique_ptr<u8[]>> vertex_shaders;
utl::vector<u8*> vertex_shaders_pointers;
for (u32 i{ 0 }; i < _countof(defines); ++i)
{
extra_args.clear();
extra_args.emplace_back(L"-D");
extra_args.emplace_back(defines[i]);
vertex_shaders.emplace_back(std::move(compile_shader(info, shader_path, extra_args)));
assert(vertex_shaders.back().get());
vertex_shaders_pointers.emplace_back(vertex_shaders.back().get());
}
extra_args.clear();
info.function = "TestShaderPS";
info.type = shader_type::pixel;
auto pixel_shader = compile_shader(info, shader_path, extra_args);
assert(pixel_shader.get());
vs_id = content::add_shader_group(vertex_shaders_pointers.data(), (u32)vertex_shaders_pointers.size(), keys.data());
const u8* pixel_shaders[]{ pixel_shader.get() };
ps_id = content::add_shader_group(&pixel_shaders[0], 1, &u32_invalid_id);
}
void
create_material()
{
graphics::material_init_info info{};
info.shader_ids[graphics::shader_type::vertex] = vs_id;
info.shader_ids[graphics::shader_type::pixel] = ps_id;
info.type = graphics::material_type::opaque;
mtl_id = content::create_resource(&info, content::asset_type::material);
}
}//namespace
id::id_type
create_render_item(id::id_type entity_id)
{
auto _1 = std::thread{ [] {load_model(); } };
auto _2 = std::thread{ [] {load_shaders(); } };
_1.join();
_2.join();
create_material();
id::id_type materials[]{ mtl_id };
id::id_type item_id{ graphics::add_render_item(entity_id, model_id, _countof(materials), &materials[0]) };
render_item_entity_map[item_id] = entity_id;
return item_id;
}
void
destroy_render_item(id::id_type item_id)
{
if (id::is_valid(item_id))
{
graphics::remove_render_item(item_id);
auto pair = render_item_entity_map.find(item_id);
if (pair != render_item_entity_map.end())
{
game_entity::remove(game_entity::entity_id{ pair->second });
}
}
if (id::is_valid(mtl_id))
{
content::destroy_resource(mtl_id, content::asset_type::material);
}
if (id::is_valid(vs_id))
{
content::remove_shader_group(vs_id);
}
if (id::is_valid(ps_id))
{
content::remove_shader_group(ps_id);
}
if (id::is_valid(model_id))
{
content::destroy_resource(model_id, content::asset_type::mesh);
}
}
#endif

View File

@@ -0,0 +1,326 @@
/**
* @file ShaderComponents.cpp
* @brief 测试工程着色器编译与缓存实现。
*/
#include <fstream>
#include <filesystem>
#include <d3d12shader.h>
#include "CommonHeader.h"
#include "Graphics_Beta\Direct3D12\D3D12Shaders.h"
#include "Graphics_Beta\Direct3D12\D3D12CommonHeaders.h"
#include "Graphics_Beta\Direct3D12\D3D12Core.h"
#include "ShaderComponents.h"
#include "Content/ContentToEngine.h"
#include "Utilities/IOStream.h"
#include <dxcapi.h>
using namespace XEngine;
using namespace XEngine::graphics::d3d12::shaders;
using namespace Microsoft::WRL;
namespace {
constexpr const char* shaders_source_path{ "../../Engine/Graphics_Beta/Direct3D12/Shaders/" };
struct engine_shader_info
{
engine_shader::id id;
shader_file_info info;
};
constexpr engine_shader_info engine_shader_files[]
{
engine_shader::fullscreen_triangle_vs,{"FullScreenTriangle.hlsl", "FullScreenTriangleVS",shader_type::vertex},
engine_shader::fill_color_ps,{"FillColor.hlsl", "FillColorPS", shader_type::pixel},
engine_shader::post_process_ps,{"PostProcess.hlsl", "PostProcessPS", shader_type::pixel},
};
static_assert(_countof(engine_shader_files) == engine_shader::count);
struct dxc_compiled_shader
{
ComPtr<IDxcBlob> byte_code;
ComPtr<IDxcBlobUtf8> disassembly;
DxcShaderHash hash;
};
std::wstring
to_wstring(const char* c)
{
std::string s{ c };
return { s.begin(), s.end() };
}
class shader_compiler
{
public:
shader_compiler()
{
HRESULT hr{ S_OK };
DXCall(hr = DxcCreateInstance(CLSID_DxcCompiler, IID_PPV_ARGS(&_compiler)));
if (FAILED(hr)) return;
DXCall(hr = DxcCreateInstance(CLSID_DxcUtils, IID_PPV_ARGS(&_utils)));
if (FAILED(hr)) return;
DXCall(hr = _utils->CreateDefaultIncludeHandler(&_include_handler));
if (FAILED(hr)) return;
}
DISABLE_COPY_AND_MOVE(shader_compiler);
dxc_compiled_shader compile(shader_file_info info, std::filesystem::path full_path, XEngine::utl::vector<std::wstring>& extra_args)
{
assert(_compiler && _utils && _include_handler);
HRESULT hr{ S_OK };
ComPtr<IDxcBlobEncoding> source_blob{ nullptr };
DXCall(hr = _utils->LoadFile(full_path.c_str(), nullptr, &source_blob));
if (FAILED(hr)) return {};
assert(source_blob && source_blob->GetBufferSize());
OutputDebugStringA("Compiling ");
OutputDebugStringA(info.file_name);
return compile(source_blob.Get(), get_args(info, extra_args));
}
dxc_compiled_shader compile(IDxcBlobEncoding* source_blob, XEngine::utl::vector<std::wstring> compiler_args)
{
DxcBuffer buffer{};
buffer.Encoding = DXC_CP_ACP;
buffer.Ptr = source_blob->GetBufferPointer();
buffer.Size = source_blob->GetBufferSize();
utl::vector<LPCWSTR> args;
for (const auto& arg : compiler_args)
{
args.emplace_back(arg.c_str());
}
HRESULT hr{ S_OK };
ComPtr<IDxcResult> results{ nullptr };
DXCall(hr = _compiler->Compile(&buffer, args.data(), args.size(), _include_handler.Get(), IID_PPV_ARGS(&results)));
if (FAILED(hr)) return {};
ComPtr<IDxcBlobUtf8> errors{ nullptr };
DXCall(hr = results->GetOutput(DXC_OUT_ERRORS, IID_PPV_ARGS(&errors), nullptr));
if (FAILED(hr)) return {};
if (errors && errors->GetStringLength())
{
OutputDebugStringA("\nShader compilation error : \n");
OutputDebugStringA(errors->GetStringPointer());
}
else
{
OutputDebugStringA(" [ Succeeded ] ");
}
OutputDebugStringA("\n");
HRESULT status{ S_OK };
DXCall(hr = results->GetStatus(&status));
if (FAILED(hr) || FAILED(status)) return {};
ComPtr<IDxcBlob> hash{ nullptr };
DXCall(hr = results->GetOutput(DXC_OUT_SHADER_HASH, IID_PPV_ARGS(&hash), nullptr));
if (FAILED(hr)) return {};
DxcShaderHash *const hash_buffer{ (DxcShaderHash *const)hash->GetBufferPointer() };
assert(!(hash_buffer->Flags & DXC_HASHFLAG_INCLUDES_SOURCE));
OutputDebugStringA(" [ Shader Hash: ] ");
for (u32 i{ 0 }; i < _countof(hash_buffer->HashDigest); ++i)
{
char hash_bytes[3]{};
sprintf_s(hash_bytes, "%02x", (u32)hash_buffer->HashDigest[i]);
OutputDebugStringA(hash_bytes);
OutputDebugStringA(" ");
}
OutputDebugStringA("\n");
ComPtr<IDxcBlob> shader{ nullptr };
DXCall(hr = results->GetOutput(DXC_OUT_OBJECT, IID_PPV_ARGS(&shader), nullptr));
if (FAILED(hr)) return {};
buffer.Ptr = shader->GetBufferPointer();
buffer.Size = shader->GetBufferSize();
ComPtr<IDxcResult> disasm_results{ nullptr };
DXCall(hr = _compiler->Disassemble(&buffer, IID_PPV_ARGS(&disasm_results)));
ComPtr<IDxcBlobUtf8> disassembly{ nullptr };
DXCall(hr = disasm_results->GetOutput(DXC_OUT_DISASSEMBLY, IID_PPV_ARGS(&disassembly), nullptr));
dxc_compiled_shader result{ shader.Detach(), disassembly.Detach() };
memcpy(&result.hash.HashDigest[0], &hash_buffer->HashDigest[0], _countof(hash_buffer->HashDigest));
return result;
}
private:
utl::vector<std::wstring>
get_args(const shader_file_info& info, utl::vector<std::wstring>& extra_args)
{
utl::vector<std::wstring> args{};
args.emplace_back(to_wstring(info.file_name));
args.emplace_back(L"-E");
args.emplace_back(to_wstring(info.function));
args.emplace_back(L"-T");
args.emplace_back(to_wstring(_profile_strings[(u32)info.type]));
args.emplace_back(L"-I");
args.emplace_back(to_wstring(shaders_source_path));
args.emplace_back(L"-enable-16bit-types");
args.emplace_back(DXC_ARG_ALL_RESOURCES_BOUND);
#if _DEBUG
args.emplace_back(DXC_ARG_DEBUG);
args.emplace_back(DXC_ARG_SKIP_OPTIMIZATIONS);
#else
args.emplace_back(DXC_ARG_OPTIMIZATION_LEVEL3);
#endif // _DEBUG
args.emplace_back(DXC_ARG_WARNINGS_ARE_ERRORS);
args.emplace_back(L"-Qstrip_reflect");
args.emplace_back(L"-Qstrip_debug");
for (const auto& arg : extra_args)
{
args.emplace_back(arg.c_str());
}
return args;
}
constexpr static const char* _profile_strings[]{ "vs_6_6", "hs_6_6", "ds_6_6", "gs_6_6", "ps_6_6", "cs_6_6", "as_6_6", "ms_6_6" };
static_assert(_countof(_profile_strings) == shader_type::count);
ComPtr<IDxcCompiler3> _compiler{ nullptr };
ComPtr<IDxcUtils> _utils{ nullptr };
ComPtr<IDxcIncludeHandler> _include_handler{ nullptr };
};
decltype(auto)
get_engine_shaders_path()
{
return std::filesystem::absolute(graphics::get_engine_shaders_path(graphics::graphics_platform::direct3d12));
}
bool
compiled_shaders_are_up_to_date()
{
auto engine_shaders_path = get_engine_shaders_path();
if (!std::filesystem::exists(engine_shaders_path))return false;
auto shaders_compilation_time = std::filesystem::last_write_time(engine_shaders_path);
std::filesystem::path full_path{};
for (u32 i{ 0 }; i < engine_shader::count; ++i) {
auto& info = engine_shader_files[i];
full_path = shaders_source_path;
full_path += info.info.file_name;
if (!std::filesystem::exists(full_path)) return false;
auto shader_file_time = std::filesystem::last_write_time(full_path);
if (shader_file_time > shaders_compilation_time)
{
return false;
}
}
return true;
}
bool
save_compiled_shaders(utl::vector<dxc_compiled_shader>& shaders)
{
auto engine_shaders_path = get_engine_shaders_path();
std::filesystem::create_directories(engine_shaders_path.parent_path());
std::ofstream file(engine_shaders_path, std::ios::out | std::ios::binary);
if (!file || !std::filesystem::exists(engine_shaders_path))
{
file.close();
return false;
}
for (const auto& shader : shaders)
{
const D3D12_SHADER_BYTECODE byte_code{ shader.byte_code->GetBufferPointer(), shader.byte_code->GetBufferSize() };
file.write((char*)&byte_code.BytecodeLength, sizeof(byte_code.BytecodeLength));
file.write((char*)&shader.hash.HashDigest[0], _countof(shader.hash.HashDigest));
file.write((char*)byte_code.pShaderBytecode, byte_code.BytecodeLength);
}
file.close();
return true;
}
} // namespace
std::unique_ptr<u8[]>
compile_shader(shader_file_info info, const char* file_path, utl::vector<std::wstring>& extra_args)
{
std::filesystem::path full_path{ file_path };
full_path += info.file_name;
if (!std::filesystem::exists(full_path)) return {};
shader_compiler compiler{};
dxc_compiled_shader compiled_shader{ compiler.compile(info, full_path, extra_args) };
if (compiled_shader.byte_code != nullptr && compiled_shader.byte_code->GetBufferPointer() && compiled_shader.byte_code->GetBufferSize())
{
static_assert(content::compiled_shader::hash_length == _countof(DxcShaderHash::HashDigest));
const u64 buffer_size{ sizeof(u64) + content::compiled_shader::hash_length + compiled_shader.byte_code->GetBufferSize() };
std::unique_ptr<u8[]> buffer{ std::make_unique<u8[]>(buffer_size) };
utl::blob_stream_writer blob{ buffer.get(), buffer_size };
blob.write(compiled_shader.byte_code->GetBufferSize());
blob.write(compiled_shader.hash.HashDigest, content::compiled_shader::hash_length);
blob.write((u8*)compiled_shader.byte_code->GetBufferPointer(), compiled_shader.byte_code->GetBufferSize());
assert(blob.offset() == buffer_size);
return buffer;
}
return {};
}
bool
compile_shaders()
{
if (compiled_shaders_are_up_to_date()) return true;
shader_compiler compiler{};
utl::vector<dxc_compiled_shader> shaders;
std::filesystem::path full_path{};
for (u32 i{ 0 }; i < engine_shader::count; ++i)
{
auto& file = engine_shader_files[i];
full_path = shaders_source_path;
full_path += file.info.file_name;
if (!std::filesystem::exists(full_path)) return false;
utl::vector<std::wstring> extra_args{};
dxc_compiled_shader compiled_shader{ compiler.compile(file.info, full_path, extra_args) };
if (compiled_shader.byte_code != nullptr && compiled_shader.byte_code->GetBufferPointer() && compiled_shader.byte_code->GetBufferSize())
{
shaders.emplace_back(std::move(compiled_shader));
}
else
{
return false;
}
}
return save_compiled_shaders(shaders);
}

View File

@@ -0,0 +1,35 @@
/**
* @file ShaderComponents.h
* @brief 测试工程着色器编译接口定义。
*/
#pragma once
#include "CommonHeader.h"
#pragma comment(lib, "dxcompiler.lib")
struct shader_type {
enum type :u32 {
vertex = 0,
hull,
domain,
geometry,
pixel,
compute,
amplification,
mesh,
count
};
};
struct shader_file_info
{
const char* file_name;
const char* function;
shader_type::type type;
};
std::unique_ptr<u8[]> compile_shader(shader_file_info info, const char* file_path, XEngine::utl::vector<std::wstring>& extra_args);
bool compile_shaders();

72
EngineTest/Test.h Normal file
View File

@@ -0,0 +1,72 @@
/**
* @file Test.h
* @brief 测试基类与计时工具定义。
*/
#pragma once
#include <thread>
#include <chrono>
#include <string>
#include <iostream>
#include "Id.h"
#define TEST_ENTITY_COMPONENTS 0
#define TEST_WINDOW_COMPONENTS 0
#define TEST_RENDERER 1
class Test
{
public:
virtual bool initialize() = 0;
virtual void run() = 0;
virtual bool shutdown() = 0;
};
#if _WIN64
#include <Windows.h>
class time_it
{
public:
using clock = std::chrono::high_resolution_clock;
using time_stamp = std::chrono::steady_clock::time_point;
constexpr float dt_avg() const { return _dt_avg * 1e-3f; }
void begin()
{
_start = clock::now();
}
void end()
{
auto dt = clock::now() - _start;
_msg_avg += ((float)std::chrono::duration_cast<std::chrono::milliseconds>(dt).count() - _msg_avg) / (float)_counter;
++_counter;
_dt_avg = _msg_avg;
if (std::chrono::duration_cast<std::chrono::seconds>(clock::now() - _seconds).count() >= 1)
{
OutputDebugStringA("Avg. frame (ms): ");
OutputDebugStringA(std::to_string(_msg_avg).c_str());
OutputDebugStringA((" " + std::to_string(_counter)).c_str());
OutputDebugStringA(" fps");
OutputDebugStringA("\n");
_msg_avg = 0.f;
_counter = 1;
_seconds = clock::now();
}
}
private:
float _dt_avg{ 16.7f };
float _msg_avg{ 0.f };
int _counter{ 1 };
time_stamp _start;
time_stamp _seconds{ clock::now() };
};
#endif

View File

@@ -0,0 +1,98 @@
/**
* @file TestEntityComponent.h
* @brief 实体与组件系统测试用例定义。
*/
#pragma once
#include "Test.h"
#include "..\Engine\Components\Entity.h"
#include "..\Engine\Components\Transform.h"
#include <iostream>
#include <ctime>
using namespace XEngine;
class engine_test : public Test
{
public:
bool initialize() override
{
srand((u32)time(nullptr));
return true;
}
void run() override
{
//do {
for (u32 i{ 0 }; i < 10; ++i)
{
create_random();
remove_random();
_num_entities = (u32)_entities.size();
}
print_results();
//} while (getchar() != 'q');
}
bool shutdown() override
{
return true;
}
private:
utl::vector<game_entity::entity> _entities;
u32 _added{ 0 };
u32 _removed{ 0 };
u32 _num_entities{ 0 };
void create_random()
{
u32 count = rand() % 20;
if (_entities.empty()) count = 10;
transform::init_info transform_info{};
game_entity::entity_info entity_info
{
&transform_info,
nullptr,
};
while (count > 0)
{
++_added;
game_entity::entity entity{ game_entity::create(entity_info) };
assert(entity.is_valid() && id::is_valid(entity.get_id()));
_entities.push_back(entity);
assert(game_entity::is_alive(entity.get_id()));
--count;
}
}
void print_results()
{
std::cout << "Entities created:" << _added << std::endl;
std::cout << "Entities deleted:" << _removed << std::endl;
}
void remove_random()
{
u32 count = rand() % 20;
if (_entities.size() < 1000)return;
while (count > 0)
{
++_removed;
const u32 index{ (u32)rand() % (u32)_entities.size() };
auto entity = _entities[index];
assert(entity.is_valid() && id::is_valid(entity.get_id()));
if (entity.is_valid())
{
game_entity::remove(entity.get_id());
_entities.erase(_entities.begin() + index);
assert(!game_entity::is_alive(entity.get_id()));
}
--count;
}
}
};

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

154
EngineTest/TestShader.hlsl Normal file
View File

@@ -0,0 +1,154 @@
//#include "../Engine/Graphics/Direct3D12/Shaders/Common.hlsli"
#include "D:\AllWX\AllC\XEngine\Engine\Graphics\Direct3D12\Shaders\Common.hlsli"
struct VertexOut
{
float4 HomogeneousPosition : SV_Position;
float3 WorldPosition : POSITIONT;
float3 WorldNormal : NORMAL;
float3 WorldTangent : TANGENT;
float2 uv : TEXTURE;
};
struct PixelOut
{
float4 Color : SV_TARGET0;
};
#define ElementsTypeStaticNormal 0x01
#define ElementsTypeStaticNormalTexture 0x03
#define ElementsTypeStaticColor 0x04
#define ElementsTypeSkeletal 0x08
#define ElementsTypeSkeletalColor ElementsTypeSkeletal | ElementsTypeStaticColor
#define ElementsTypeSkeletalNormal ElementsTypeSkeletal | ElementsTypeStaticNormal
#define ElementsTypeSkeletalNormalColor ElementsTypeSkeletalNormal | ElementsTypeSkeletalColor
#define ElementsTypeSkeletalNormalTexture ElementsTypeSkeletal | ElementsTypeStaticNormalTexture
#define ElementsTypeSkeletalNormalTextureColor ElementsTypeSkeletalNormalTexture | ElementsTypeStaticColor
struct VertexElement
{
#if ELEMENTS_TYPE == ElementsTypeStaticNormal
uint ColorTSign;
uint16_t2 Normal;
#elif ELEMENTS_TYPE == ElementsTypeStaticNormalTexture
uint ColorTSign;
uint16_t2 Normal;
uint16_t2 Tangent;
float2 UV;
#elif ELEMENTS_TYPE == ElementsTypeStaticColor
#elif ELEMENTS_TYPE == ElementsTypeSkeletal
#elif ELEMENTS_TYPE == ElementsTypeSkeletalColor
#elif ELEMENTS_TYPE == ElementsTypeSkeletalNormal
#elif ELEMENTS_TYPE == ElementsTypeSkeletalNormalColor
#elif ELEMENTS_TYPE == ElementsTypeSkeletalNormalTexture
#elif ELEMENTS_TYPE == ElementsTypeSkeletalNormalTextureColor
#endif
};
const static float InvIntervals = 2.f / ((1 << 16) - 1);
ConstantBuffer<GlobalShaderData> GlobalData : register(b0, space0);
ConstantBuffer<PerObjectData> PerObjectBuffer : register(b1, space0);
StructuredBuffer<float3> VertexPositions : register(t0, space0);
StructuredBuffer<VertexElement> Elements : register(t1, space0);
StructuredBuffer<DirectionalLightParameters> DirectionLights : register(t3, space0);
VertexOut TestShaderVS(in uint VertexIdx : SV_VertexID)
{
VertexOut vsOut;
float4x4 IdentityMatrix =
{
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
float4 position = float4(VertexPositions[VertexIdx], 1.f);
float4 worldPosition = mul(PerObjectBuffer.World, position);
#if ELEMENTS_TYPE == ElementsTypeStaticNormal
VertexElement element = Elements[VertexIdx];
float2 nXY = element.Normal * InvIntervals - 1.f;
uint signs = (element.ColorTSign >> 24) & 0xff;
float nSign = float(signs & 0x02) - 1;
float3 normal = float3(nXY.x, nXY.y, sqrt(saturate(1.f - dot(normal.xy, normal.xy))) * nSign);
vsOut.HomogeneousPosition = mul(PerObjectBuffer.WorldViewProjection, position);
vsOut.WorldPosition = worldPosition.xyz;
vsOut.WorldNormal = 0;
vsOut.WorldTangent = 0.f;
vsOut.uv = 0.f;
#elif ELEMENTS_TYPE == ElementsTypeStaticNormalTexture
VertexElement element = Elements[VertexIdx];
float2 nXY = element.Normal * InvIntervals - 1.f;
uint signs = (element.ColorTSign >> 24) & 0xff;
float nSign = float(signs & 0x02) - 1;
float3 normal = float3(nXY.x, nXY.y, sqrt(saturate(1.f - dot(normal.xy, normal.xy))) * nSign);
vsOut.HomogeneousPosition = mul(PerObjectBuffer.WorldViewProjection, position);
vsOut.WorldPosition = worldPosition.xyz;
vsOut.WorldNormal = mul(float4(normal, 0.f), PerObjectBuffer.InvWorld).xyz;
vsOut.WorldNormal = mul(PerObjectBuffer.World, float4(normal, 0.f)).xyz;
//vsOut.WorldNormal = sqrt(saturate(1.f - dot(normal.xy, normal.xy))) * nSign;
vsOut.WorldTangent = 0.f;
vsOut.uv = 0.f;
#else
#undef ELEMENTS_TYPE
vsOut.HomogeneousPosition = mul(PerObjectBuffer.WorldViewProjection, position);
vsOut.WorldPosition = worldPosition.xyz;
vsOut.WorldNormal = 0.f;
vsOut.WorldTangent = 0.f;
vsOut.uv = 0.f;
#endif
return vsOut;
}
[earlydepthstencil]
PixelOut TestShaderPS(in VertexOut psIn)
{
PixelOut psOut;
psOut.Color = float4(psIn.WorldNormal, 1);
float4x4 IdentityMatrix =
{
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
float3 normal = normalize(psIn.WorldNormal);
float3 viewDir = normalize(GlobalData.CameraPosition - psIn.WorldPosition);
float3 color = 0;
for (uint i = 0; i < GlobalData.NumIdrectionalLights; ++i)
{
DirectionalLightParameters light = DirectionLights[i];
float3 lr = normalize(light.Direction);
float3 lightDirection = mul(IdentityMatrix, float4(lr, 0)).xyz;
//float3 lightDirection = normalize(float3(0, 1, 0));
float diffuse = max(dot(normal, -lightDirection), 0.f);
float3 h = (viewDir + lightDirection);
float specular = pow(max(dot(normal, h), 0.f), 16) * 0.5f;
//float3 lightColor = float3(1, 1, 1);
float3 lightColor = light.Color * light.Intensity;
//color = dot(normal, -lr);
color = normal;
}
float3 ambient = 10 / 255.f;
psOut.Color = saturate(float4(color + ambient, 1.f));
return psOut;
}

78
EngineTest/TestWindow.h Normal file
View File

@@ -0,0 +1,78 @@
/**
* @file TestWindow.h
* @brief 窗口系统测试用例实现。
*/
#pragma once
#include "Test.h"
#include "..\Platform\Platform.h"
#include "..\Platform\PlatformTypes.h"
using namespace XEngine;
platform::window _windows[4];
LRESULT win_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch (msg)
{
case WM_DESTROY:
{
bool all_closed{ true };
for (u32 i{ 0 }; i < _countof(_windows); ++i)
{
if (!_windows[i].is_closed())
{
all_closed = false;
}
}
if (all_closed)
{
PostQuitMessage(0);
return 0;
}
}
case WM_SYSCHAR:
if (wparam == VK_RETURN && (HIWORD(lparam) & KF_ALTDOWN))
{
platform::window win{ platform::window_id{(id::id_type)GetWindowLongPtr(hwnd, GWLP_USERDATA)} };
win.set_fullscreen(!win.is_fullscreen());
return 0;
}
default:
break;
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
class engine_test :public Test
{
public:
bool initialize() override
{
platform::window_init_info info[]
{
{&win_proc, nullptr, L"Test Window 1", 100,100,400,800},
{&win_proc, nullptr, L"Test Window 2", 150,200,400,700},
{&win_proc, nullptr, L"Test Window 3", 200,300,400,600},
{&win_proc, nullptr, L"Test Window 4", 150,400,400,500},
};
static_assert(_countof(info) == _countof(_windows));
for (u32 i{ 0 }; i < _countof(_windows); ++i)
_windows[i] = platform::create_window(&info[i]);
return true;
}
void run() override
{
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
bool shutdown() override
{
for (u32 i{ 0 }; i < _countof(_windows); ++i)
platform::remove_window(_windows[i].get_id());
return true;
}
};

BIN
EngineTest/model.model Normal file

Binary file not shown.