Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
7608c31
feat: add headless rendering sample output
FlyAndNotDown Aug 9, 2026
ca909f2
feat: hide name component in editor details
FlyAndNotDown Aug 9, 2026
eacfabf
fix: share immutable shader bytecode storage
FlyAndNotDown Aug 17, 2026
89b0f25
fix: make RHI modules own instance lifetime
FlyAndNotDown Aug 17, 2026
fc145fd
fix: make buffer texture copy pitches explicit
FlyAndNotDown Aug 17, 2026
4917cd0
fix: support advanced raster shader stages
FlyAndNotDown Aug 17, 2026
0a24ba4
fix: initialize buffer creation defaults
FlyAndNotDown Aug 17, 2026
a97c7ce
fix: use signed base vertex for indexed draws
FlyAndNotDown Aug 17, 2026
664a8c8
fix: handle depth stencil copy planes
FlyAndNotDown Aug 17, 2026
637dfd1
feat: expose advanced shader features
FlyAndNotDown Aug 17, 2026
d662b8b
fix: implement primitive strip restart state
FlyAndNotDown Aug 17, 2026
ed2f7a3
fix: validate Vulkan present queue support
FlyAndNotDown Aug 17, 2026
36aad00
feat: add SwiftShader Conan recipe
FlyAndNotDown Aug 18, 2026
625baac
feat: support software GPU selection
FlyAndNotDown Aug 18, 2026
e706e8d
test: add rendering image regression coverage
FlyAndNotDown Aug 18, 2026
b49eabf
test: organize rendering diagnostics by case
FlyAndNotDown Aug 18, 2026
3170e60
fix: stabilize rendering sample tests
FlyAndNotDown Aug 18, 2026
2f079e0
fix: stabilize SSAO shader calculations
FlyAndNotDown Aug 19, 2026
87b4582
fix: stabilize base texture rendering test
FlyAndNotDown Aug 26, 2026
234bf5f
fix: export engine initialization parameters
FlyAndNotDown Aug 26, 2026
fd7702f
fix: resolve runtime libraries from binary directory
FlyAndNotDown Aug 27, 2026
111715c
fix: prevent worker thread flush deadlock
FlyAndNotDown Aug 27, 2026
2997214
docs: add SwiftShader to third party list
FlyAndNotDown Aug 27, 2026
b0a41eb
fix: register SwiftShader driver in Windows CI
FlyAndNotDown Aug 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,34 @@ jobs:
python ThirdParty/ConanRecipes/build_recipes.py --export-only

- name: Configure CMake
run: cmake -B ${{github.workspace}}/build -G=Ninja -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
run: cmake -B ${{github.workspace}}/build -G=Ninja -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCI=ON

- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j ${{env.MAKE_THREAD_NUM}}

# Elevated Windows processes ignore VK_DRIVER_FILES, so register SwiftShader in the loader's trusted driver list.
- name: Register SwiftShader Vulkan Driver
if: runner.os == 'Windows'
shell: pwsh
run: |
$manifest = (Resolve-Path "${{github.workspace}}/build/Dist/Explosion/Binaries/vk_swiftshader_icd.json").Path
$driverRegistry = 'HKLM:\SOFTWARE\Khronos\Vulkan\Drivers'
New-Item -Path $driverRegistry -Force | Out-Null
New-ItemProperty -Path $driverRegistry -Name $manifest -PropertyType DWord -Value 0 -Force | Out-Null

- name: Test
working-directory: ${{github.workspace}}/build
run: ctest -C ${{env.BUILD_TYPE}} --extra-verbose

- name: Upload Rendering Test Artifacts
if: failure()
uses: actions/upload-artifact@v4
with:
name: rendering-test-${{runner.os}}-${{runner.arch}}
path: ${{github.workspace}}/build/Test/Generated/RenderingSample
if-no-files-found: warn
retention-days: 14

- name: Install
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --target install -j ${{env.MAKE_THREAD_NUM}}

Expand Down
32 changes: 25 additions & 7 deletions CMake/Target.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ include(CMakePackageConfigHelpers)

option(BUILD_TEST "Build unit tests" ON)
option(BUILD_BENCHMARK "Build benchmarks" ON)
option(CI "Build for continuous integration" OFF)

set(GENERATED_DIR ${CMAKE_BINARY_DIR}/Generated)
set(GENERATED_API_HEADER_DIR ${GENERATED_DIR}/Api)
Expand Down Expand Up @@ -452,6 +453,27 @@ function(exp_get_runtime_output_dir)
endif ()
endfunction()

function(exp_set_runtime_rpath)
set(options "")
set(singleValueArgs NAME)
set(multiValueArgs "")
cmake_parse_arguments(arg "${options}" "${singleValueArgs}" "${multiValueArgs}" ${ARGN})

if (APPLE)
set(runtime_origin "@executable_path")
elseif (UNIX)
set(runtime_origin "$ORIGIN")
else ()
return()
endif ()

set_target_properties(
${arg_NAME} PROPERTIES
BUILD_RPATH "${runtime_origin}"
INSTALL_RPATH "${runtime_origin}"
)
endfunction()

function(exp_add_executable)
set(options NOT_INSTALL)
set(singleValueArgs NAME FOLDER)
Expand Down Expand Up @@ -491,13 +513,7 @@ function(exp_add_executable)
${arg_NAME} PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${runtime_output_dir}
)
if (APPLE)
set_target_properties(
${arg_NAME} PROPERTIES
BUILD_RPATH "@executable_path"
INSTALL_RPATH "@executable_path"
)
endif ()
exp_set_runtime_rpath(NAME ${arg_NAME})

target_include_directories(
${arg_NAME}
Expand Down Expand Up @@ -618,6 +634,8 @@ function(exp_add_library)
)

if ("${arg_TYPE}" STREQUAL "SHARED")
exp_set_runtime_rpath(NAME ${arg_NAME})

exp_get_runtime_output_dir(OUTPUT dist_dir)
add_custom_command(
TARGET ${arg_NAME} POST_BUILD
Expand Down
1 change: 1 addition & 0 deletions Editor/Include/Editor/EditorApplication.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ namespace Editor {
EditorApplicationMode mode;
std::string rhiType;
bool gpuDebug;
bool softwareGpu;
std::string projectRoot;
};

Expand Down
10 changes: 5 additions & 5 deletions Editor/Include/Editor/Frame/ProjectHubFrame.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@ namespace Editor {
ProjectHubFrame();
~ProjectHubFrame();

void Render(EditorWindow& inWindow, const std::string& inRhiType, bool inGpuDebug);
void Render(EditorWindow& inWindow, const std::string& inRhiType, bool inGpuDebug, bool inSoftwareGpu);

private:
void RenderActionBar(EditorWindow& inWindow, const std::string& inRhiType, bool inGpuDebug);
void RenderRecentProjects(EditorWindow& inWindow, const std::string& inRhiType, bool inGpuDebug);
void RenderCreateProjectPopup(EditorWindow& inWindow, const std::string& inRhiType, bool inGpuDebug);
void RenderActionBar(EditorWindow& inWindow, const std::string& inRhiType, bool inGpuDebug, bool inSoftwareGpu);
void RenderRecentProjects(EditorWindow& inWindow, const std::string& inRhiType, bool inGpuDebug, bool inSoftwareGpu);
void RenderCreateProjectPopup(EditorWindow& inWindow, const std::string& inRhiType, bool inGpuDebug, bool inSoftwareGpu);
CreateProjectResult CreateProject();
void OpenProject(EditorWindow& inWindow, const std::string& inProjectPath, const std::string& inRhiType, bool inGpuDebug);
void OpenProject(EditorWindow& inWindow, const std::string& inProjectPath, const std::string& inRhiType, bool inGpuDebug, bool inSoftwareGpu);
void SaveRecentProjects() const;
void TouchRecentProject(const std::string& inProjectPath);

Expand Down
2 changes: 1 addition & 1 deletion Editor/Src/EditorApplication.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ namespace Editor {

void EditorApplication::RenderProjectHubFrame()
{
projectHubFrame->Render(*window, desc.rhiType, desc.gpuDebug);
projectHubFrame->Render(*window, desc.rhiType, desc.gpuDebug, desc.softwareGpu);
ImGui::Render();
Runtime::EngineHolder::Get().Tick(ImGui::GetIO().DeltaTime);
window->RenderUiOnly(*ImGui::GetDrawData());
Expand Down
4 changes: 3 additions & 1 deletion Editor/Src/EditorWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,9 @@ namespace Editor {
0,
RHI::TextureSubResourceInfo(),
Common::UVec3Consts::zero,
Common::UVec3(static_cast<uint32_t>(width), static_cast<uint32_t>(height), 1)));
Common::UVec3(static_cast<uint32_t>(width), static_cast<uint32_t>(height), 1),
copyFootprint.rowPitch,
copyFootprint.slicePitch));
copyRecorder->ResourceBarrier(RHI::Barrier::Transition(imguiFontTexture.Get(), RHI::TextureState::copyDst, RHI::TextureState::shaderReadOnly));
copyRecorder->EndPass();
}
Expand Down
15 changes: 14 additions & 1 deletion Editor/Src/Frame/EditorFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ namespace Editor::Internal {
: qualifiedName.substr(namespaceSeparator + 2);
}

static bool IsComponentVisibleInDetails(const Mirror::Class& inClass)
{
return !inClass.HasMeta(Runtime::MetaPresets::editorHide);
}

static std::string EntityDisplayName(const Runtime::ECRegistry& inRegistry, Runtime::Entity inEntity)
{
const auto* name = inRegistry.Find<Runtime::Name>(inEntity);
Expand Down Expand Up @@ -69,7 +74,9 @@ namespace Editor {
.log = true
}
{
std::erase_if(componentClasses, [](Runtime::CompClass clazz) -> bool { return !clazz->HasMeta("comp"); });
std::erase_if(componentClasses, [](Runtime::CompClass clazz) -> bool {
return !clazz->HasMeta("comp") || !Internal::IsComponentVisibleInDetails(*clazz);
});
std::ranges::sort(componentClasses, [](Runtime::CompClass lhs, Runtime::CompClass rhs) -> bool {
return lhs->GetName() < rhs->GetName();
});
Expand Down Expand Up @@ -253,6 +260,9 @@ namespace Editor {

Runtime::CompClass componentToRemove = nullptr;
inRegistry.CompEach(selectedEntity, [&](Runtime::CompClass compClass) -> void {
if (!Internal::IsComponentVisibleInDetails(*compClass)) {
return;
}
ImGui::PushID(compClass->GetName().c_str());
const std::string componentName = Internal::ComponentDisplayName(*compClass);
const std::string componentLabel = Widgets::Label(Icons::Tabler::boxMultiple, componentName);
Expand All @@ -277,6 +287,9 @@ namespace Editor {
ImGui::PopID();
});
inRegistry.TagEach(selectedEntity, [&](Runtime::TagClass tagClass) -> void {
if (!Internal::IsComponentVisibleInDetails(*tagClass)) {
return;
}
ImGui::PushID(tagClass->GetName().c_str());
const std::string tagName = Internal::ComponentDisplayName(*tagClass);
const std::string tagLabel = Widgets::Label(Icons::Tabler::box, tagName);
Expand Down
31 changes: 16 additions & 15 deletions Editor/Src/Frame/ProjectHubFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,14 @@ namespace Editor::ProjectHub::Internal {
return result;
}

static std::string LaunchCommand(const std::string& inExecutable, const std::string& inProjectPath, const std::string& inRhiType, bool inGpuDebug)
static std::string LaunchCommand(const std::string& inExecutable, const std::string& inProjectPath, const std::string& inRhiType, bool inGpuDebug, bool inSoftwareGpu)
{
const std::string gpuDebugArg = inGpuDebug ? " -gpuDebug" : "";
const std::string softwareGpuArg = inSoftwareGpu ? " -softwareGpu" : "";
#if PLATFORM_WINDOWS
return std::format("start \"\" \"{}\" -project \"{}\" -rhi {}{}", inExecutable, inProjectPath, inRhiType, gpuDebugArg);
return std::format("start \"\" \"{}\" -project \"{}\" -rhi {}{}{}", inExecutable, inProjectPath, inRhiType, gpuDebugArg, softwareGpuArg);
#else
return std::format("\"{}\" -project \"{}\" -rhi {}{} &", inExecutable, inProjectPath, inRhiType, gpuDebugArg);
return std::format("\"{}\" -project \"{}\" -rhi {}{}{} &", inExecutable, inProjectPath, inRhiType, gpuDebugArg, softwareGpuArg);
#endif
}
}
Expand Down Expand Up @@ -126,7 +127,7 @@ namespace Editor {
SaveRecentProjects();
}

void ProjectHubFrame::Render(EditorWindow& inWindow, const std::string& inRhiType, bool inGpuDebug)
void ProjectHubFrame::Render(EditorWindow& inWindow, const std::string& inRhiType, bool inGpuDebug, bool inSoftwareGpu)
{
const ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(viewport->WorkPos);
Expand All @@ -143,21 +144,21 @@ namespace Editor {
| ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::PopStyleVar(2);

RenderActionBar(inWindow, inRhiType, inGpuDebug);
RenderActionBar(inWindow, inRhiType, inGpuDebug, inSoftwareGpu);
ImGui::Dummy(ImVec2(0.0f, 22.0f));
RenderRecentProjects(inWindow, inRhiType, inGpuDebug);
RenderCreateProjectPopup(inWindow, inRhiType, inGpuDebug);
RenderRecentProjects(inWindow, inRhiType, inGpuDebug, inSoftwareGpu);
RenderCreateProjectPopup(inWindow, inRhiType, inGpuDebug, inSoftwareGpu);
ImGui::End();
}

void ProjectHubFrame::RenderActionBar(EditorWindow& inWindow, const std::string& inRhiType, bool inGpuDebug)
void ProjectHubFrame::RenderActionBar(EditorWindow& inWindow, const std::string& inRhiType, bool inGpuDebug, bool inSoftwareGpu)
{
const float spacing = ImGui::GetStyle().ItemSpacing.x;
const float buttonWidth = (ImGui::GetContentRegionAvail().x - spacing) * 0.5f;
const std::string openLabel = Widgets::Label(Icons::Tabler::folderOpen, "Open");
if (Widgets::PrimaryButton(openLabel.c_str(), ImVec2(buttonWidth, ProjectHub::Internal::actionButtonHeight))) {
if (const auto selectedDirectory = PlatformUtils::SelectDirectory("Open Explosion Project")) {
OpenProject(inWindow, *selectedDirectory, inRhiType, inGpuDebug);
OpenProject(inWindow, *selectedDirectory, inRhiType, inGpuDebug, inSoftwareGpu);
}
}

Expand All @@ -169,7 +170,7 @@ namespace Editor {
}
}

void ProjectHubFrame::RenderRecentProjects(EditorWindow& inWindow, const std::string& inRhiType, bool inGpuDebug)
void ProjectHubFrame::RenderRecentProjects(EditorWindow& inWindow, const std::string& inRhiType, bool inGpuDebug, bool inSoftwareGpu)
{
const std::string recentProjectsLabel = Widgets::Label(Icons::Tabler::folder, "Recent projects");
ImGui::TextUnformatted(recentProjectsLabel.c_str());
Expand Down Expand Up @@ -209,11 +210,11 @@ namespace Editor {
ImGui::EndChild();

if (!projectToOpen.empty()) {
OpenProject(inWindow, projectToOpen, inRhiType, inGpuDebug);
OpenProject(inWindow, projectToOpen, inRhiType, inGpuDebug, inSoftwareGpu);
}
}

void ProjectHubFrame::RenderCreateProjectPopup(EditorWindow& inWindow, const std::string& inRhiType, bool inGpuDebug)
void ProjectHubFrame::RenderCreateProjectPopup(EditorWindow& inWindow, const std::string& inRhiType, bool inGpuDebug, bool inSoftwareGpu)
{
const ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(viewport->GetCenter(), ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
Expand Down Expand Up @@ -288,7 +289,7 @@ namespace Editor {
if (result.success) {
statusMessage.clear();
ImGui::CloseCurrentPopup();
OpenProject(inWindow, result.projectPath, inRhiType, inGpuDebug);
OpenProject(inWindow, result.projectPath, inRhiType, inGpuDebug, inSoftwareGpu);
} else {
statusMessage = result.error;
}
Expand Down Expand Up @@ -327,7 +328,7 @@ namespace Editor {
return { .success = true, .error = {}, .projectPath = projectDir.String() };
}

void ProjectHubFrame::OpenProject(EditorWindow& inWindow, const std::string& inProjectPath, const std::string& inRhiType, bool inGpuDebug)
void ProjectHubFrame::OpenProject(EditorWindow& inWindow, const std::string& inProjectPath, const std::string& inRhiType, bool inGpuDebug, bool inSoftwareGpu)
{
const Common::Path projectDir(inProjectPath);
if (!projectDir.Exists() || !projectDir.IsDirectory()) {
Expand All @@ -338,7 +339,7 @@ namespace Editor {
TouchRecentProject(inProjectPath);
SaveRecentProjects();

const std::string command = ProjectHub::Internal::LaunchCommand(Core::Paths::ExecutablePath().String(), inProjectPath, inRhiType, inGpuDebug);
const std::string command = ProjectHub::Internal::LaunchCommand(Core::Paths::ExecutablePath().String(), inProjectPath, inRhiType, inGpuDebug, inSoftwareGpu);
std::ignore = std::system(command.c_str());
inWindow.RequestClose();
}
Expand Down
6 changes: 6 additions & 0 deletions Editor/Src/Main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ static Core::CmdlineArgValue<bool> caGpuDebug(
"gpuDebug", "-gpuDebug", false,
"enable GPU validation layers");

static Core::CmdlineArgValue<bool> caSoftwareGpu(
"softwareGpu", "-softwareGpu", false,
"prefer a software GPU, falling back to hardware when unavailable");

static Editor::EditorApplicationMode GetAppMode()
{
return caProjectRoot.GetValue().empty()
Expand All @@ -35,6 +39,7 @@ static void InitializeEngine()
params.gameRoot = caProjectRoot.GetValue();
params.rhiType = caRhiType.GetValue();
params.gpuDebug = caGpuDebug.GetValue();
params.useSoftwareGpu = caSoftwareGpu.GetValue();
Runtime::EngineHolder::Load("Editor", params);
}

Expand All @@ -47,6 +52,7 @@ int main(int argc, char* argv[])
.mode = GetAppMode(),
.rhiType = caRhiType.GetValue(),
.gpuDebug = caGpuDebug.GetValue(),
.softwareGpu = caSoftwareGpu.GetValue(),
.projectRoot = caProjectRoot.GetValue()
};
int result = 0;
Expand Down
2 changes: 0 additions & 2 deletions Engine/Source/Common/Include/Common/Concurrent.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,8 @@ namespace Common {

private:
bool stop;
bool flush;
std::mutex mutex;
std::condition_variable taskCondition;
std::condition_variable flushCondition;
NamedThread thread;
std::queue<std::function<void()>> tasks;
};
Expand Down
13 changes: 13 additions & 0 deletions Engine/Source/Common/Include/Common/Process.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#pragma once

#include <cstdint>
#include <optional>
#include <string>
#include <vector>

namespace Common {
class Process {
public:
static std::optional<int32_t> Run(const std::string& inExecutablePath, const std::vector<std::string>& inArguments);
};
}
Loading
Loading