linux适配

This commit is contained in:
Rowland 2026-06-02 09:19:17 +08:00
parent 93fddb0296
commit f5aa77b71e
311 changed files with 49477 additions and 271 deletions

View File

@ -2,7 +2,6 @@
#include <csignal>
#include <cstdio>
#include <crtdbg.h>
#include <cstdlib>
#include <exception>
#include <filesystem>
@ -11,14 +10,21 @@
#include <new>
#include <vector>
#if defined(_WIN32)
#include <crtdbg.h>
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#else
#include <limits.h>
#include <unistd.h>
#endif
namespace {
std::filesystem::path MetaCoreGetExecutableDirectory() {
#if defined(_WIN32)
std::vector<wchar_t> buffer(MAX_PATH, L'\0');
while (true) {
const DWORD copied = GetModuleFileNameW(nullptr, buffer.data(), static_cast<DWORD>(buffer.size()));
@ -32,6 +38,21 @@ std::filesystem::path MetaCoreGetExecutableDirectory() {
buffer.resize(buffer.size() * 2, L'\0');
}
#else
std::vector<char> buffer(PATH_MAX, '\0');
while (true) {
const ssize_t copied = readlink("/proc/self/exe", buffer.data(), buffer.size());
if (copied < 0) {
return {};
}
if (static_cast<std::size_t>(copied) < buffer.size()) {
return std::filesystem::path(std::string(buffer.data(), static_cast<std::size_t>(copied))).parent_path();
}
buffer.resize(buffer.size() * 2, '\0');
}
#endif
}
std::filesystem::path MetaCoreGetCrashLogPath() {
@ -60,13 +81,19 @@ void MetaCoreWriteCrashLogRaw(const char* message) {
}
FILE* file = nullptr;
const std::filesystem::path crashLogPath = MetaCoreGetCrashLogPath();
#if defined(_WIN32)
if (fopen_s(&file, crashLogPath.string().c_str(), "a") == 0 && file != nullptr) {
#else
file = std::fopen(crashLogPath.string().c_str(), "a");
if (file != nullptr) {
#endif
std::fputs(message, file);
std::fputs("\n", file);
std::fclose(file);
}
}
#if defined(_WIN32)
void MetaCoreInvalidParameterHandler(
const wchar_t* expression,
const wchar_t* functionName,
@ -91,6 +118,7 @@ void MetaCoreInvalidParameterHandler(
void MetaCorePureCallHandler() {
MetaCoreWriteCrashLog("metacore.app: purecall handler triggered");
}
#endif
void MetaCoreSignalHandler(int signalValue) {
MetaCoreWriteCrashLog("metacore.app: signal=" + std::to_string(signalValue));
@ -113,7 +141,7 @@ void MetaCoreTerminateHandler() {
}
void MetaCoreInstallDebugCrashHooks() {
#if defined(_DEBUG)
#if defined(_WIN32) && defined(_DEBUG)
_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);

View File

@ -13,6 +13,8 @@
#include <filesystem>
#include <iostream>
#include <memory>
#include <unordered_map>
#include <vector>
namespace {
@ -40,7 +42,7 @@ MetaCore::MetaCoreSceneView MetaCoreBuildPlayerSceneView() {
"replay-source",
"file_replay",
"Replay Source",
{MetaCore::MetaCoreDataSourceSetting{"file_path", "TestProject/Runtime/RuntimeReplay.mcstream"}},
{MetaCore::MetaCoreDataSourceSetting{"file_path", "Runtime/RuntimeReplay.mcstream"}},
true,
1000
});
@ -83,6 +85,63 @@ MetaCore::MetaCoreSceneView MetaCoreBuildPlayerSceneView() {
return document;
}
[[nodiscard]] std::filesystem::path MetaCoreStripFirstPathComponent(const std::filesystem::path& path) {
std::filesystem::path stripped;
auto iterator = path.begin();
if (iterator == path.end()) {
return stripped;
}
++iterator;
for (; iterator != path.end(); ++iterator) {
stripped /= *iterator;
}
return stripped;
}
void MetaCoreResolveRuntimeSourcePathsForProject(
MetaCore::MetaCoreRuntimeDataSourcesDocument& sourcesDocument,
const std::filesystem::path& projectRoot
) {
for (MetaCore::MetaCoreDataSourceDefinition& source : sourcesDocument.Sources) {
if (source.AdapterType != "file_replay") {
continue;
}
for (MetaCore::MetaCoreDataSourceSetting& setting : source.ConnectionSettings) {
if (setting.Key != "file_path" || setting.Value.empty()) {
continue;
}
const std::filesystem::path configuredPath(setting.Value);
if (configuredPath.is_absolute()) {
continue;
}
std::vector<std::filesystem::path> candidates;
candidates.push_back(projectRoot / configuredPath);
candidates.push_back(projectRoot.parent_path() / configuredPath);
candidates.push_back(projectRoot / "Runtime" / configuredPath.filename());
const std::filesystem::path strippedPath = MetaCoreStripFirstPathComponent(configuredPath);
if (!strippedPath.empty()) {
candidates.push_back(projectRoot / strippedPath);
}
for (const std::filesystem::path& candidate : candidates) {
std::error_code errorCode;
if (!std::filesystem::exists(candidate, errorCode)) {
continue;
}
const std::filesystem::path canonicalCandidate = std::filesystem::weakly_canonical(candidate, errorCode);
setting.Value = errorCode ? candidate.lexically_normal().string() : canonicalCandidate.string();
break;
}
}
}
}
[[nodiscard]] MetaCore::MetaCoreRuntimeBindingsDocument MetaCoreBuildDefaultRuntimeBindingsDocument() {
MetaCore::MetaCoreRuntimeBindingsDocument document;
document.Bindings.push_back(MetaCore::MetaCoreSceneBindingDefinition{
@ -217,8 +276,9 @@ int main(int argc, char* argv[]) {
const auto diagnosticsPath = (projectRoot / runtimeProjectDocument.DiagnosticsPath).lexically_normal();
const auto loadedSourcesDocument = MetaCore::MetaCoreReadRuntimeDataSourcesDocument(sourcesPath, typeRegistry);
const auto loadedBindingsDocument = MetaCore::MetaCoreReadRuntimeBindingsDocument(bindingsPath, typeRegistry);
const auto sourcesDocument = loadedSourcesDocument.value_or(MetaCoreBuildDefaultRuntimeDataSourcesDocument());
const auto bindingsDocument = loadedBindingsDocument.value_or(MetaCoreBuildDefaultRuntimeBindingsDocument());
auto sourcesDocument = loadedSourcesDocument.value_or(MetaCoreBuildDefaultRuntimeDataSourcesDocument());
auto bindingsDocument = loadedBindingsDocument.value_or(MetaCoreBuildDefaultRuntimeBindingsDocument());
MetaCoreResolveRuntimeSourcePathsForProject(sourcesDocument, projectRoot);
if (loadedSourcesDocument.has_value() && loadedBindingsDocument.has_value()) {
std::cout << "MetaCorePlayer: loaded runtime config from binary .mcruntime documents\n";
@ -260,6 +320,7 @@ int main(int argc, char* argv[]) {
runtimeAdapter != nullptr
? runtimeAdapter->GetStatus().State
: MetaCore::MetaCoreRuntimeDataSourceState::Disconnected;
std::unordered_map<std::string, std::string> lastReportedBindingIssueKeys;
std::uint64_t diagnosticsWriteFrame = 0;
while (!window.ShouldClose()) {
@ -294,9 +355,26 @@ int main(int argc, char* argv[]) {
if (diagnostics.HasFaults) {
for (const MetaCore::MetaCoreRuntimeBindingStatus& bindingStatus : diagnostics.BindingStatuses) {
if (!bindingStatus.Healthy || bindingStatus.Stale) {
std::cout << "MetaCorePlayer: binding issue id=" << bindingStatus.BindingId
<< " stale=" << (bindingStatus.Stale ? "true" : "false")
<< " error=" << bindingStatus.LastError << '\n';
const std::string issueKey =
std::string(bindingStatus.Healthy ? "healthy" : "fault") +
"|" +
(bindingStatus.Stale ? "stale" : "fresh") +
"|" +
bindingStatus.LastError;
const auto previousIssue = lastReportedBindingIssueKeys.find(bindingStatus.BindingId);
if (previousIssue == lastReportedBindingIssueKeys.end() ||
previousIssue->second != issueKey) {
std::cout << "MetaCorePlayer: binding issue id=" << bindingStatus.BindingId
<< " stale=" << (bindingStatus.Stale ? "true" : "false")
<< " error=" << bindingStatus.LastError << '\n';
lastReportedBindingIssueKeys[bindingStatus.BindingId] = issueKey;
}
} else {
const auto previousIssue = lastReportedBindingIssueKeys.find(bindingStatus.BindingId);
if (previousIssue != lastReportedBindingIssueKeys.end()) {
std::cout << "MetaCorePlayer: binding recovered id=" << bindingStatus.BindingId << '\n';
lastReportedBindingIssueKeys.erase(previousIssue);
}
}
}
}

View File

@ -16,6 +16,19 @@ if(MSVC)
endif()
option(METACORE_BUILD_TESTS "Build MetaCore tests" ON)
option(METACORE_BUILD_CORE_ONLY "Build only MetaCore core libraries" OFF)
option(METACORE_BUILD_PLATFORM "Build MetaCore platform module when core-only is enabled" OFF)
option(METACORE_BUILD_RUNTIME_DATA "Build MetaCore runtime data module when core-only is enabled" OFF)
set(METACORE_ENABLE_PLATFORM FALSE)
if(NOT METACORE_BUILD_CORE_ONLY OR METACORE_BUILD_PLATFORM)
set(METACORE_ENABLE_PLATFORM TRUE)
endif()
set(METACORE_ENABLE_RUNTIME_DATA FALSE)
if(NOT METACORE_BUILD_CORE_ONLY OR METACORE_BUILD_RUNTIME_DATA)
set(METACORE_ENABLE_RUNTIME_DATA TRUE)
endif()
list(PREPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
@ -26,11 +39,24 @@ endif()
include(MetaCoreFilament)
find_package(glm CONFIG REQUIRED)
find_package(imgui CONFIG REQUIRED)
set(METACORE_IMGUI_USES_PKG_CONFIG FALSE)
if(NOT METACORE_BUILD_CORE_ONLY)
include(MetaCoreFilament)
find_package(imgui CONFIG QUIET)
if(NOT imgui_FOUND)
set(METACORE_IMGUI_USES_PKG_CONFIG TRUE)
find_package(PkgConfig REQUIRED)
pkg_check_modules(IMGUI REQUIRED IMPORTED_TARGET imgui)
add_library(imgui::imgui INTERFACE IMPORTED)
target_link_libraries(imgui::imgui INTERFACE PkgConfig::IMGUI)
target_include_directories(imgui::imgui INTERFACE /usr/include/imgui/backends)
endif()
endif()
if(METACORE_ENABLE_PLATFORM AND NOT WIN32)
find_package(glfw3 CONFIG REQUIRED)
endif()
set(METACORE_COMMON_WARNINGS)
if(MSVC)
@ -39,55 +65,89 @@ else()
set(METACORE_COMMON_WARNINGS -Wall -Wextra -Wpedantic)
endif()
set(METACORE_UI_BLIT_MATERIAL_SOURCE "${CMAKE_SOURCE_DIR}/third_party/filament/libs/filagui/src/materials/uiBlit.mat")
set(METACORE_UI_BLIT_MATERIAL_OUTPUT "${CMAKE_BINARY_DIR}/uiBlit.filamat")
set(METACORE_FILAMENT_MATC "${METACORE_FILAMENT_ROOT}/bin/matc.exe")
if(NOT METACORE_BUILD_CORE_ONLY)
set(METACORE_UI_BLIT_MATERIAL_SOURCE "${CMAKE_SOURCE_DIR}/third_party/filament/libs/filagui/src/materials/uiBlit.mat")
set(METACORE_UI_BLIT_MATERIAL_OUTPUT "${CMAKE_BINARY_DIR}/uiBlit.filamat")
if(WIN32)
set(METACORE_FILAMENT_MATC "${METACORE_FILAMENT_ROOT}/bin/matc.exe")
else()
set(METACORE_FILAMENT_MATC "${METACORE_FILAMENT_ROOT}/bin/matc")
endif()
add_custom_command(
OUTPUT "${METACORE_UI_BLIT_MATERIAL_OUTPUT}"
COMMAND "${METACORE_FILAMENT_MATC}"
--platform desktop
--api opengl
--output "${METACORE_UI_BLIT_MATERIAL_OUTPUT}"
"${METACORE_UI_BLIT_MATERIAL_SOURCE}"
DEPENDS
"${METACORE_UI_BLIT_MATERIAL_SOURCE}"
"${METACORE_FILAMENT_MATC}"
VERBATIM
)
add_custom_target(MetaCoreUiBlitMaterial
DEPENDS "${METACORE_UI_BLIT_MATERIAL_OUTPUT}"
)
function(metacore_stage_ui_blit_material target_name)
add_dependencies(${target_name} MetaCoreUiBlitMaterial)
add_custom_command(TARGET ${target_name} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${METACORE_UI_BLIT_MATERIAL_OUTPUT}"
"$<TARGET_FILE_DIR:${target_name}>/uiBlit.filamat"
VERBATIM
set(METACORE_UI_BLIT_MATERIAL_PREBUILT "")
foreach(candidate IN ITEMS
"${CMAKE_SOURCE_DIR}/build_filament/libs/filagui/generated/material/uiBlit.filamat"
"${METACORE_FILAMENT_ROOT}/bin/uiBlit.filamat"
)
endfunction()
if(EXISTS "${candidate}")
set(METACORE_UI_BLIT_MATERIAL_PREBUILT "${candidate}")
break()
endif()
endforeach()
if(EXISTS "${METACORE_UI_BLIT_MATERIAL_SOURCE}")
add_custom_command(
OUTPUT "${METACORE_UI_BLIT_MATERIAL_OUTPUT}"
COMMAND "${METACORE_FILAMENT_MATC}"
--platform desktop
--api opengl
--output "${METACORE_UI_BLIT_MATERIAL_OUTPUT}"
"${METACORE_UI_BLIT_MATERIAL_SOURCE}"
DEPENDS
"${METACORE_UI_BLIT_MATERIAL_SOURCE}"
"${METACORE_FILAMENT_MATC}"
VERBATIM
)
elseif(METACORE_UI_BLIT_MATERIAL_PREBUILT)
add_custom_command(
OUTPUT "${METACORE_UI_BLIT_MATERIAL_OUTPUT}"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${METACORE_UI_BLIT_MATERIAL_PREBUILT}"
"${METACORE_UI_BLIT_MATERIAL_OUTPUT}"
DEPENDS "${METACORE_UI_BLIT_MATERIAL_PREBUILT}"
VERBATIM
)
else()
message(FATAL_ERROR "Missing uiBlit material source or prebuilt uiBlit.filamat.")
endif()
add_custom_target(MetaCoreUiBlitMaterial
DEPENDS "${METACORE_UI_BLIT_MATERIAL_OUTPUT}"
)
function(metacore_stage_ui_blit_material target_name)
add_dependencies(${target_name} MetaCoreUiBlitMaterial)
add_custom_command(TARGET ${target_name} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${METACORE_UI_BLIT_MATERIAL_OUTPUT}"
"$<TARGET_FILE_DIR:${target_name}>/uiBlit.filamat"
VERBATIM
)
endfunction()
endif()
add_executable(MetaCoreHeaderTool
Tools/MetaCoreHeaderTool/main.cpp
tools/MetaCoreHeaderTool/main.cpp
)
target_compile_options(MetaCoreHeaderTool PRIVATE ${METACORE_COMMON_WARNINGS})
add_executable(MetaCoreRuntimeConfigTool
Tools/MetaCoreRuntimeConfigTool/main.cpp
)
if(METACORE_ENABLE_RUNTIME_DATA)
add_executable(MetaCoreRuntimeConfigTool
tools/MetaCoreRuntimeConfigTool/main.cpp
)
target_compile_options(MetaCoreRuntimeConfigTool PRIVATE ${METACORE_COMMON_WARNINGS})
target_compile_options(MetaCoreRuntimeConfigTool PRIVATE ${METACORE_COMMON_WARNINGS})
add_executable(MetaCoreTcpSenderTool
Tools/MetaCoreTcpSenderTool/main.cpp
)
add_executable(MetaCoreTcpSenderTool
tools/MetaCoreTcpSenderTool/main.cpp
)
target_compile_options(MetaCoreTcpSenderTool PRIVATE ${METACORE_COMMON_WARNINGS})
target_link_libraries(MetaCoreTcpSenderTool PRIVATE ws2_32)
target_compile_options(MetaCoreTcpSenderTool PRIVATE ${METACORE_COMMON_WARNINGS})
if(WIN32)
target_link_libraries(MetaCoreTcpSenderTool PRIVATE ws2_32)
endif()
endif()
function(metacore_generate_reflection module_name function_name output_var)
set(generated_headers ${ARGN})
@ -167,34 +227,51 @@ target_link_libraries(MetaCoreFoundation
target_compile_options(MetaCoreFoundation PRIVATE ${METACORE_COMMON_WARNINGS})
set(METACORE_PLATFORM_HEADERS
Source/MetaCorePlatform/Public/MetaCorePlatform/MetaCoreInput.h
Source/MetaCorePlatform/Public/MetaCorePlatform/MetaCoreWindow.h
)
if(METACORE_ENABLE_PLATFORM)
set(METACORE_PLATFORM_HEADERS
Source/MetaCorePlatform/Public/MetaCorePlatform/MetaCoreInput.h
Source/MetaCorePlatform/Public/MetaCorePlatform/MetaCoreWindow.h
)
set(METACORE_PLATFORM_SOURCES
Source/MetaCorePlatform/Private/MetaCoreInput.cpp
Source/MetaCorePlatform/Private/MetaCoreWindow.cpp
)
set(METACORE_PLATFORM_SOURCES
Source/MetaCorePlatform/Private/MetaCoreInput.cpp
)
add_library(MetaCorePlatform STATIC
${METACORE_PLATFORM_HEADERS}
${METACORE_PLATFORM_SOURCES}
)
if(WIN32)
list(APPEND METACORE_PLATFORM_SOURCES
Source/MetaCorePlatform/Private/MetaCoreWindow.cpp
)
else()
list(APPEND METACORE_PLATFORM_SOURCES
Source/MetaCorePlatform/Private/MetaCoreWindowGlfw.cpp
)
endif()
target_include_directories(MetaCorePlatform
PUBLIC
Source/MetaCorePlatform/Public
)
add_library(MetaCorePlatform STATIC
${METACORE_PLATFORM_HEADERS}
${METACORE_PLATFORM_SOURCES}
)
target_link_libraries(MetaCorePlatform
PUBLIC
MetaCoreFoundation
glm::glm
)
target_include_directories(MetaCorePlatform
PUBLIC
Source/MetaCorePlatform/Public
)
target_compile_options(MetaCorePlatform PRIVATE ${METACORE_COMMON_WARNINGS})
target_compile_definitions(MetaCorePlatform PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX)
target_link_libraries(MetaCorePlatform
PUBLIC
MetaCoreFoundation
glm::glm
)
if(NOT WIN32)
target_link_libraries(MetaCorePlatform PUBLIC glfw)
endif()
target_compile_options(MetaCorePlatform PRIVATE ${METACORE_COMMON_WARNINGS})
if(WIN32)
target_compile_definitions(MetaCorePlatform PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX)
endif()
endif()
set(METACORE_SCENE_HEADERS
Source/MetaCoreScene/Public/MetaCoreScene/MetaCoreComponents.h
@ -241,6 +318,7 @@ target_link_libraries(MetaCoreScene
target_compile_options(MetaCoreScene PRIVATE ${METACORE_COMMON_WARNINGS})
if(NOT METACORE_BUILD_CORE_ONLY)
set(METACORE_RENDER_HEADERS
Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreEditorViewportRenderer.h
Source/MetaCoreRender/Public/MetaCoreRender/MetaCoreFilamentSceneBridge.h
@ -253,6 +331,7 @@ set(METACORE_RENDER_HEADERS
set(METACORE_RENDER_SOURCES
Source/MetaCoreRender/Private/MetaCoreEditorViewportRenderer.cpp
Source/MetaCoreRender/Private/MetaCoreFilamentSceneBridge.cpp
Source/MetaCoreRender/Private/MetaCoreGlibcCompat.cpp
Source/MetaCoreRender/Private/MetaCoreImGuiHelper.cpp
Source/MetaCoreRender/Private/MetaCoreRenderDevice.cpp
Source/MetaCoreRender/Private/MetaCoreSceneRenderSync.cpp
@ -282,7 +361,9 @@ metacore_use_filament(MetaCoreRender)
target_compile_options(MetaCoreRender PRIVATE ${METACORE_COMMON_WARNINGS})
endif()
if(METACORE_ENABLE_RUNTIME_DATA)
set(METACORE_RUNTIME_DATA_HEADERS
Source/MetaCoreRuntimeData/Public/MetaCoreRuntimeData/MetaCoreRuntimeDataDispatcher.h
Source/MetaCoreRuntimeData/Public/MetaCoreRuntimeData/MetaCoreRuntimeDataProject.h
@ -320,10 +401,12 @@ target_link_libraries(MetaCoreRuntimeData
MetaCoreFoundation
MetaCoreScene
glm::glm
PRIVATE
ws2_32
)
if(WIN32)
target_link_libraries(MetaCoreRuntimeData PRIVATE ws2_32)
endif()
target_compile_options(MetaCoreRuntimeData PRIVATE ${METACORE_COMMON_WARNINGS})
target_link_libraries(MetaCoreRuntimeConfigTool
@ -332,7 +415,9 @@ target_link_libraries(MetaCoreRuntimeConfigTool
MetaCoreRuntimeData
MetaCoreScene
)
endif()
if(NOT METACORE_BUILD_CORE_ONLY)
set(METACORE_EDITOR_HEADERS
Source/MetaCoreEditor/Public/MetaCoreEditor/MetaCoreBuiltinModules.h
Source/MetaCoreEditor/Public/MetaCoreEditor/MetaCoreEditorApp.h
@ -368,6 +453,13 @@ set(METACORE_EDITOR_SOURCES
third_party/stb/stb_image_impl.cpp
)
if(NOT WIN32 AND METACORE_IMGUI_USES_PKG_CONFIG)
set(METACORE_IMGUI_GLFW_BACKEND_SOURCE "/usr/share/doc/libimgui-dev/examples/backends/imgui_impl_glfw.cpp")
if(EXISTS "${METACORE_IMGUI_GLFW_BACKEND_SOURCE}")
list(APPEND METACORE_EDITOR_SOURCES "${METACORE_IMGUI_GLFW_BACKEND_SOURCE}")
endif()
endif()
metacore_generate_reflection(
Editor
MetaCoreRegisterEditorGeneratedTypes
@ -406,10 +498,13 @@ target_link_libraries(MetaCoreEditor
)
target_compile_options(MetaCoreEditor PRIVATE ${METACORE_COMMON_WARNINGS})
target_compile_definitions(MetaCoreEditor PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX)
if(WIN32)
target_compile_definitions(MetaCoreEditor PRIVATE WIN32_LEAN_AND_MEAN NOMINMAX)
endif()
add_executable(MetaCoreEditorApp
Apps/MetaCoreEditor/main.cpp
Source/MetaCoreRender/Private/MetaCoreGlibcCompat.cpp
)
target_link_libraries(MetaCoreEditorApp
@ -422,6 +517,7 @@ metacore_stage_ui_blit_material(MetaCoreEditorApp)
add_executable(MetaCorePlayer
Apps/MetaCorePlayer/main.cpp
Source/MetaCoreRender/Private/MetaCoreGlibcCompat.cpp
)
target_link_libraries(MetaCorePlayer
@ -456,6 +552,7 @@ if(METACORE_BUILD_TESTS)
# Filament + ImGui Demo
add_executable(FilamentImGuiDemo
tests/FilamentImGuiDemo.cpp
Source/MetaCoreRender/Private/MetaCoreGlibcCompat.cpp
)
target_link_libraries(FilamentImGuiDemo
PRIVATE
@ -467,3 +564,4 @@ if(METACORE_BUILD_TESTS)
metacore_use_filament(FilamentImGuiDemo)
metacore_stage_ui_blit_material(FilamentImGuiDemo)
endif()
endif()

View File

@ -16,6 +16,7 @@
#include <nlohmann/json.hpp>
#include <imgui.h>
#include <imgui_internal.h>
#include "cgltf.h"
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/glm.hpp>
@ -1718,27 +1719,31 @@ void MetaCoreDrawMeshRendererComponentInspector(MetaCoreEditorContext& editorCon
int materialSlotCount = static_cast<int>(meshRenderer.MaterialAssetGuids.size());
// Size 槽位数量排版
ImGui::Columns(2, "MaterialSizeCol", false);
ImGui::SetColumnWidth(0, 100.0F);
ImGui::Text("Size");
ImGui::NextColumn();
if (ImGui::BeginTable("MaterialSizeTable", 2, ImGuiTableFlags_SizingStretchProp)) {
ImGui::TableSetupColumn("Label", ImGuiTableColumnFlags_WidthFixed, 100.0F);
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextUnformatted("Size");
ImGui::TableNextColumn();
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.0F, 0.0F, 0.0F, 0.0F));
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0F);
ImGui::SetNextItemWidth(-1);
if (ImGui::InputInt("##MaterialSlotCount", &materialSlotCount)) {
materialSlotCount = std::max(materialSlotCount, 0);
MetaCoreTrackComponentInspectorEdit(editorContext, "修改检查器属性", false);
MetaCoreForEachSelectedGameObject(editorContext, [&](MetaCoreGameObject& selectedObject) {
if (!selectedObject.HasComponent<MetaCoreMeshRendererComponent>()) {
return;
}
selectedObject.GetComponent<MetaCoreMeshRendererComponent>().MaterialAssetGuids.resize(static_cast<std::size_t>(materialSlotCount));
});
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.0F, 0.0F, 0.0F, 0.0F));
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0F);
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x > 1.0F ? ImGui::GetContentRegionAvail().x : 1.0F);
if (ImGui::InputInt("##MaterialSlotCount", &materialSlotCount)) {
materialSlotCount = std::max(materialSlotCount, 0);
MetaCoreTrackComponentInspectorEdit(editorContext, "修改检查器属性", false);
MetaCoreForEachSelectedGameObject(editorContext, [&](MetaCoreGameObject& selectedObject) {
if (!selectedObject.HasComponent<MetaCoreMeshRendererComponent>()) {
return;
}
selectedObject.GetComponent<MetaCoreMeshRendererComponent>().MaterialAssetGuids.resize(static_cast<std::size_t>(materialSlotCount));
});
}
ImGui::PopStyleVar();
ImGui::PopStyleColor();
ImGui::EndTable();
}
ImGui::PopStyleVar();
ImGui::PopStyleColor();
ImGui::Columns(1);
// 槽位列表循环
const auto materialChoices = MetaCoreCollectMaterialAssetChoices(editorContext);
@ -1746,14 +1751,23 @@ void MetaCoreDrawMeshRendererComponentInspector(MetaCoreEditorContext& editorCon
MetaCoreAssetGuid materialGuid = meshRenderer.MaterialAssetGuids[materialIndex];
const std::string elementLabel = "Element " + std::to_string(materialIndex);
ImGui::Columns(2, ("MaterialSlotCol_" + std::to_string(materialIndex)).c_str(), false);
ImGui::SetColumnWidth(0, 100.0F);
ImGui::Text("%s", elementLabel.c_str());
ImGui::NextColumn();
const std::string materialSlotTableId = "MaterialSlotTable_" + std::to_string(materialIndex);
if (!ImGui::BeginTable(materialSlotTableId.c_str(), 2, ImGuiTableFlags_SizingStretchProp)) {
continue;
}
ImGui::TableSetupColumn("Label", ImGuiTableColumnFlags_WidthFixed, 100.0F);
ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TextUnformatted(elementLabel.c_str());
ImGui::TableNextColumn();
// 精简槽位扁平外观,右侧留出小圆球材质选择按钮空间
float circleBtnWidth = 18.0F;
float comboWidth = ImGui::GetContentRegionAvail().x - circleBtnWidth - 4.0F;
if (comboWidth < 1.0F) {
comboWidth = 1.0F;
}
std::string materialName = "None (Material)";
if (materialGuid.IsValid()) {
@ -1909,7 +1923,7 @@ void MetaCoreDrawMeshRendererComponentInspector(MetaCoreEditorContext& editorCon
ImGui::EndPopup();
}
ImGui::Columns(1);
ImGui::EndTable();
}
ImGui::TreePop();
}
@ -3573,7 +3587,11 @@ bool MetaCoreBuiltinAssetDatabaseService::OpenProject(const std::filesystem::pat
return false;
}
#if defined(_WIN32)
_putenv_s("METACORE_PROJECT_PATH", projectFilePath.parent_path().string().c_str());
#else
setenv("METACORE_PROJECT_PATH", projectFilePath.parent_path().string().c_str(), 1);
#endif
LoadProjectDescriptor();
const bool refreshed = Refresh();
if (ModuleRegistry_ != nullptr && refreshed && HasProject()) {

View File

@ -1,5 +1,7 @@
#include "MetaCoreBuiltinEditorModule.h"
#if defined(_WIN32)
#include <windows.h>
#endif
#include "MetaCoreEditor/MetaCoreEditorContext.h"
#include "MetaCoreEditor/MetaCoreEditorAssetTypes.h"
@ -17,6 +19,7 @@
#include "MetaCoreFoundation/MetaCoreAssetRegistry.h"
#include <imgui.h>
#include <imgui_internal.h>
#include <algorithm>
#include <array>
@ -55,8 +58,10 @@ static std::array<char, 256> AssetFilterBuffer_{};
static std::filesystem::path PendingRenamePath_{};
static std::array<char, 256> RenameBuffer_{};
static std::array<char, 256> NewFolderBuffer_{};
#if defined(_WIN32)
static HANDLE PlayerProcessHandle_ = NULL;
static HANDLE PlayerStdoutRead_ = NULL;
#endif
[[nodiscard]] MetaCoreTypeRegistry MetaCoreBuildRuntimePanelTypeRegistry() {
MetaCoreTypeRegistry registry;
@ -3489,6 +3494,7 @@ void DrawUiDocumentDetails(MetaCoreEditorContext& editorContext, MetaCoreIAssetD
}
void MetaCoreLaunchPlayer(MetaCoreEditorContext& editorContext, MetaCoreIAssetDatabaseService& assetDatabaseService, MetaCoreIScenePersistenceService& scenePersistenceService) {
#if defined(_WIN32)
if (PlayerProcessHandle_ != NULL) {
TerminateProcess(PlayerProcessHandle_, 0);
CloseHandle(PlayerProcessHandle_);
@ -3567,9 +3573,19 @@ void MetaCoreLaunchPlayer(MetaCoreEditorContext& editorContext, MetaCoreIAssetDa
CloseHandle(pi.hThread);
editorContext.GetLogService().AddEntry(MetaCoreLogLevel::Info, "Editor", "MetaCore Player 已启动:" + cmdLine);
#else
(void)assetDatabaseService;
(void)scenePersistenceService.SaveCurrentScene(editorContext);
editorContext.GetLogService().AddEntry(
MetaCoreLogLevel::Warning,
"Editor",
"Linux 下的 MetaCore Player 启动流程尚未接入"
);
#endif
}
void MetaCorePollPlayerProcess(MetaCoreEditorContext& editorContext) {
#if defined(_WIN32)
if (PlayerProcessHandle_ == NULL) {
return;
}
@ -3614,6 +3630,9 @@ void MetaCorePollPlayerProcess(MetaCoreEditorContext& editorContext) {
PlayerStdoutRead_ = NULL;
}
}
#else
(void)editorContext;
#endif
}
void DrawMaterialAssetDetails(

View File

@ -6,15 +6,21 @@
#include <imgui.h>
#include <imgui_internal.h>
#include <imgui_impl_opengl3.h>
#if defined(_WIN32)
#include <imgui_impl_win32.h>
#else
#include <imgui_impl_glfw.h>
#include <GLFW/glfw3.h>
#endif
#include "ImGuizmo.h"
#if defined(_WIN32)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <shellapi.h>
#endif
#define GLM_ENABLE_EXPERIMENTAL
#include <cstdlib>
@ -33,7 +39,9 @@
#include <unordered_set>
#include <vector>
#if defined(_WIN32)
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
#endif
namespace MetaCore {
@ -51,7 +59,12 @@ void MetaCoreTraceStartup(const char* message) {
return;
}
FILE* file = nullptr;
#if defined(_MSC_VER)
if (fopen_s(&file, "editor.crash.log", "a") == 0 && file != nullptr) {
#else
file = std::fopen("editor.crash.log", "a");
if (file != nullptr) {
#endif
std::fputs(message, file);
std::fputs("\n", file);
std::fclose(file);
@ -467,8 +480,12 @@ void MetaCoreConfigureChineseFont() {
ImGuiIO& io = ImGui::GetIO();
io.Fonts->AddFontDefault();
#if defined(_WIN32)
std::vector<std::filesystem::path> candidatePaths;
if (const char* overrideFontPath = std::getenv("METACORE_IMGUI_FONT"); overrideFontPath != nullptr && overrideFontPath[0] != '\0') {
candidatePaths.emplace_back(overrideFontPath);
}
#if defined(_WIN32)
char* windowsDirectory = nullptr;
std::size_t windowsDirectoryLength = 0;
if (_dupenv_s(&windowsDirectory, &windowsDirectoryLength, "WINDIR") == 0 && windowsDirectory != nullptr) {
@ -481,6 +498,14 @@ void MetaCoreConfigureChineseFont() {
candidatePaths.push_back(fontsPath / "simhei.ttf");
free(windowsDirectory);
}
#else
candidatePaths.emplace_back("/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc");
candidatePaths.emplace_back("/usr/share/fonts/opentype/noto/NotoSansCJK-Medium.ttc");
candidatePaths.emplace_back("/usr/share/fonts/truetype/wqy/wqy-microhei.ttc");
candidatePaths.emplace_back("/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc");
candidatePaths.emplace_back("/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf");
candidatePaths.emplace_back("/usr/share/fonts/truetype/arphic/uming.ttc");
#endif
for (const auto& candidatePath : candidatePaths) {
if (std::filesystem::exists(candidatePath)) {
@ -491,10 +516,11 @@ void MetaCoreConfigureChineseFont() {
candidatePath.string().c_str(),
17.0F,
nullptr,
io.Fonts->GetGlyphRangesChineseFull()
io.Fonts->GetGlyphRangesChineseSimplifiedCommon()
);
if (chineseFont != nullptr) {
io.FontDefault = chineseFont;
MetaCoreTraceStartup(("metacore.ui: loaded CJK font: " + candidatePath.string()).c_str());
break;
}
} catch (const std::bad_alloc&) {
@ -502,7 +528,6 @@ void MetaCoreConfigureChineseFont() {
}
}
}
#endif
}
} // namespace
@ -518,6 +543,14 @@ bool MetaCoreEditorApp::Initialize(int argc, char* argv[]) {
return true;
}
if (std::getenv("METACORE_FILAMENT_BACKEND") == nullptr) {
#if defined(_WIN32)
_putenv_s("METACORE_FILAMENT_BACKEND", "opengl");
#else
setenv("METACORE_FILAMENT_BACKEND", "opengl", 0);
#endif
}
MetaCoreTraceStartup("metacore.app: initialize window");
if (!Window_.Initialize(1600, 900, "MetaCore Editor")) {
MetaCoreTraceStartup("metacore.app: initialize window failed");
@ -525,6 +558,7 @@ bool MetaCoreEditorApp::Initialize(int argc, char* argv[]) {
}
MetaCoreTraceStartup("metacore.app: initialize message handler");
#if defined(_WIN32)
DragAcceptFiles(static_cast<HWND>(Window_.GetNativeWindowHandle()), TRUE);
// 允许低权限的 Windows 资源管理器 (Explorer) 向高权限的编辑器进程发送拖放消息,彻底解决拖拽禁止图标的系统底层限制
@ -578,6 +612,7 @@ bool MetaCoreEditorApp::Initialize(int argc, char* argv[]) {
}
return ImGui_ImplWin32_WndProcHandler(static_cast<HWND>(nativeWindowHandle), message, static_cast<WPARAM>(wparam), static_cast<LPARAM>(lparam)) != 0;
});
#endif
MetaCoreTraceStartup("metacore.app: initialize render device");
if (!RenderDevice_.Initialize(Window_)) {
@ -704,8 +739,11 @@ int MetaCoreEditorApp::Run() {
while (!Window_.ShouldClose()) {
Window_.BeginFrame();
// Filament 接管,不再使用 OpenGL3 后端的 NewFrame
#if defined(_WIN32)
ImGui_ImplWin32_NewFrame();
#else
ImGui_ImplGlfw_NewFrame();
#endif
ImGui::NewFrame();
if (GMetaCoreEnableImGuizmo) {
ImGuizmo::BeginFrame();
@ -766,7 +804,11 @@ bool MetaCoreEditorApp::InitializeImGui() {
MetaCoreApplyEditorStyle();
MetaCoreTraceStartup("metacore.ui: editor style ready");
#if defined(_WIN32)
if (!ImGui_ImplWin32_InitForOpenGL(Window_.GetNativeWindowHandle())) {
#else
if (!ImGui_ImplGlfw_InitForOther(static_cast<GLFWwindow*>(Window_.GetNativeWindowHandle()), true)) {
#endif
MetaCoreTraceStartup("metacore.ui: platform backend init failed");
return false;
}
@ -776,7 +818,11 @@ bool MetaCoreEditorApp::InitializeImGui() {
}
void MetaCoreEditorApp::ShutdownImGui() {
#if defined(_WIN32)
ImGui_ImplWin32_Shutdown();
#else
ImGui_ImplGlfw_Shutdown();
#endif
ImGui::DestroyContext();
}
@ -874,7 +920,9 @@ void MetaCoreEditorApp::DrawEditorFrame() {
if (centralNode != nullptr) {
// MetaCore main viewport keeps a Unity-like Scene / Game mental model.
ImGui::SetNextWindowPos(centralNode->Pos);
ImGui::SetNextWindowSize(ImVec2(centralNode->Size.x, 30.0F));
const float centralWidth = centralNode->Size.x > 1.0F ? centralNode->Size.x : 1.0F;
const float centralHeight = centralNode->Size.y > 1.0F ? centralNode->Size.y : 1.0F;
ImGui::SetNextWindowSize(ImVec2(centralWidth, 30.0F));
ImGui::Begin("ViewportTabs", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);
static int activeTab = 0; // 0: Scene, 1: Game
@ -890,11 +938,11 @@ void MetaCoreEditorApp::DrawEditorFrame() {
const ImVec2 mainViewportPosition = ImGui::GetMainViewport()->Pos;
viewportState.Left = centralNode->Pos.x;
viewportState.Top = centralNode->Pos.y + sceneToolbarHeight + tabHeaderHeight;
viewportState.Width = centralNode->Size.x;
viewportState.Width = centralWidth;
if (viewportState.Width < 1.0F) {
viewportState.Width = 1.0F;
}
viewportState.Height = centralNode->Size.y - sceneToolbarHeight - tabHeaderHeight;
viewportState.Height = centralHeight - sceneToolbarHeight - tabHeaderHeight;
if (viewportState.Height < 1.0F) {
viewportState.Height = 1.0F;
}
@ -903,7 +951,7 @@ void MetaCoreEditorApp::DrawEditorFrame() {
// 1. 准备 GizmoCanvas 窗口属性
ImGui::SetNextWindowPos(ImVec2(centralNode->Pos.x, centralNode->Pos.y + sceneToolbarHeight + tabHeaderHeight));
ImGui::SetNextWindowSize(ImVec2(centralNode->Size.x, centralNode->Size.y - sceneToolbarHeight - tabHeaderHeight));
ImGui::SetNextWindowSize(ImVec2(viewportState.Width, viewportState.Height));
ImGui::SetNextWindowBgAlpha(0.0F);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0F);
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0F);
@ -929,6 +977,12 @@ void MetaCoreEditorApp::DrawEditorFrame() {
// 2. 拿到最精确的屏幕绝对位置和画布大小
ImVec2 viewportPos = ImGui::GetCursorScreenPos();
ImVec2 viewportSize = ImGui::GetContentRegionAvail();
if (viewportSize.x < 1.0F) {
viewportSize.x = 1.0F;
}
if (viewportSize.y < 1.0F) {
viewportSize.y = 1.0F;
}
// 3. 全局统一更新 viewportState
viewportState.Left = viewportPos.x;
@ -1074,4 +1128,3 @@ void MetaCoreEditorApp::EnsureDefaultDockLayout(unsigned int dockSpaceId) {
}
} // namespace MetaCore

View File

@ -18,7 +18,12 @@ namespace MetaCore {
void MetaCoreGltfTrace(const char* message) {
FILE* file = nullptr;
#if defined(_MSC_VER)
if (fopen_s(&file, "editor.crash.log", "a") == 0 && file != nullptr) {
#else
file = std::fopen("editor.crash.log", "a");
if (file != nullptr) {
#endif
std::fputs("[GLTF Import] ", file);
std::fputs(message, file);
std::fputs("\n", file);

View File

@ -1,5 +1,6 @@
#include "MetaCoreFoundation/MetaCorePackage.h"
#include <cstring>
#include <fstream>
namespace MetaCore {

View File

@ -1,6 +1,7 @@
#include "MetaCoreFoundation/MetaCoreProject.h"
#include <array>
#include <cstdlib>
#include <fstream>
#include <regex>
#include <sstream>
@ -81,6 +82,7 @@ std::filesystem::path MetaCoreGetProjectFilePath(const std::filesystem::path& pr
}
std::optional<std::filesystem::path> MetaCoreReadProjectRootFromEnvironment() {
#if defined(_MSC_VER)
char* projectPathRaw = nullptr;
std::size_t projectPathLength = 0;
if (_dupenv_s(&projectPathRaw, &projectPathLength, "METACORE_PROJECT_PATH") != 0 || projectPathRaw == nullptr) {
@ -88,7 +90,15 @@ std::optional<std::filesystem::path> MetaCoreReadProjectRootFromEnvironment() {
}
std::filesystem::path projectPath(projectPathRaw);
free(projectPathRaw);
std::free(projectPathRaw);
#else
const char* projectPathRaw = std::getenv("METACORE_PROJECT_PATH");
if (projectPathRaw == nullptr || projectPathRaw[0] == '\0') {
return std::nullopt;
}
std::filesystem::path projectPath(projectPathRaw);
#endif
if (projectPath.filename() == "MetaCore.project.json") {
projectPath = projectPath.parent_path();

View File

@ -0,0 +1,247 @@
#include "MetaCorePlatform/MetaCoreWindow.h"
#include <GLFW/glfw3.h>
#include <chrono>
#include <cstdio>
#include <exception>
#include <memory>
#include <string>
#include <utility>
namespace MetaCore {
namespace {
void MetaCoreWindowTrace(const char* message) {
if (message == nullptr) {
return;
}
std::printf("%s\n", message);
}
bool MetaCoreIsGlfwKeyDown(GLFWwindow* window, int key) {
return glfwGetKey(window, key) == GLFW_PRESS;
}
bool MetaCoreIsGlfwMouseButtonDown(GLFWwindow* window, int button) {
return glfwGetMouseButton(window, button) == GLFW_PRESS;
}
class MetaCoreGlfwContext {
public:
static bool Retain() {
if (ReferenceCount_ == 0 && glfwInit() != GLFW_TRUE) {
return false;
}
++ReferenceCount_;
return true;
}
static void Release() {
if (ReferenceCount_ == 0) {
return;
}
--ReferenceCount_;
if (ReferenceCount_ == 0) {
glfwTerminate();
}
}
private:
inline static int ReferenceCount_ = 0;
};
} // namespace
class MetaCoreWindow::MetaCoreWindowImpl {
public:
GLFWwindow* NativeHandle = nullptr;
MetaCoreInput Input{};
MetaCoreNativeWindowMessageHandler MessageHandler{};
std::chrono::steady_clock::time_point LastFrameTime = std::chrono::steady_clock::now();
float DeltaSeconds = 1.0F / 60.0F;
bool Initialized = false;
bool CloseRequested = false;
bool OwnsGlfwContext = false;
};
namespace {
MetaCoreWindow::MetaCoreWindowImpl* MetaCoreGetWindowImpl(GLFWwindow* window) {
return static_cast<MetaCoreWindow::MetaCoreWindowImpl*>(glfwGetWindowUserPointer(window));
}
void MetaCoreGlfwWindowCloseCallback(GLFWwindow* window) {
if (auto* owner = MetaCoreGetWindowImpl(window); owner != nullptr) {
owner->CloseRequested = true;
}
}
void MetaCoreGlfwScrollCallback(GLFWwindow* window, double, double yOffset) {
if (auto* owner = MetaCoreGetWindowImpl(window); owner != nullptr) {
owner->Input.AddMouseWheelDelta(static_cast<float>(yOffset));
}
}
} // namespace
MetaCoreWindow::MetaCoreWindow()
: Impl_(std::make_unique<MetaCoreWindowImpl>()) {
}
MetaCoreWindow::~MetaCoreWindow() {
Shutdown();
}
bool MetaCoreWindow::Initialize(int width, int height, const std::string& title) {
try {
if (Impl_->Initialized) {
return true;
}
if (!MetaCoreGlfwContext::Retain()) {
MetaCoreWindowTrace("metacore.window: glfwInit failed");
return false;
}
Impl_->OwnsGlfwContext = true;
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
Impl_->NativeHandle = glfwCreateWindow(width, height, title.c_str(), nullptr, nullptr);
if (Impl_->NativeHandle == nullptr) {
MetaCoreWindowTrace("metacore.window: glfwCreateWindow failed");
Shutdown();
return false;
}
glfwSetWindowUserPointer(Impl_->NativeHandle, Impl_.get());
glfwSetWindowCloseCallback(Impl_->NativeHandle, MetaCoreGlfwWindowCloseCallback);
glfwSetScrollCallback(Impl_->NativeHandle, MetaCoreGlfwScrollCallback);
Impl_->CloseRequested = false;
Impl_->LastFrameTime = std::chrono::steady_clock::now();
Impl_->Initialized = true;
return true;
} catch (const std::bad_alloc&) {
MetaCoreWindowTrace("metacore.window: exception bad_alloc");
Shutdown();
return false;
} catch (const std::exception& exceptionObject) {
MetaCoreWindowTrace(exceptionObject.what());
Shutdown();
return false;
} catch (...) {
MetaCoreWindowTrace("metacore.window: exception unknown");
Shutdown();
return false;
}
}
void MetaCoreWindow::Shutdown() {
if (Impl_->NativeHandle != nullptr) {
glfwSetWindowUserPointer(Impl_->NativeHandle, nullptr);
glfwDestroyWindow(Impl_->NativeHandle);
Impl_->NativeHandle = nullptr;
}
if (Impl_->OwnsGlfwContext) {
MetaCoreGlfwContext::Release();
Impl_->OwnsGlfwContext = false;
}
Impl_->Initialized = false;
Impl_->CloseRequested = false;
}
void MetaCoreWindow::BeginFrame() {
if (!Impl_->Initialized || Impl_->NativeHandle == nullptr) {
return;
}
const auto now = std::chrono::steady_clock::now();
Impl_->DeltaSeconds = std::chrono::duration<float>(now - Impl_->LastFrameTime).count();
if (Impl_->DeltaSeconds <= 0.0F) {
Impl_->DeltaSeconds = 1.0F / 60.0F;
}
Impl_->LastFrameTime = now;
Impl_->Input.BeginFrame();
glfwPollEvents();
GLFWwindow* window = Impl_->NativeHandle;
Impl_->Input.SetKeyState(MetaCoreInputKey::LeftAlt, MetaCoreIsGlfwKeyDown(window, GLFW_KEY_LEFT_ALT));
Impl_->Input.SetKeyState(MetaCoreInputKey::RightAlt, MetaCoreIsGlfwKeyDown(window, GLFW_KEY_RIGHT_ALT));
Impl_->Input.SetKeyState(
MetaCoreInputKey::Control,
MetaCoreIsGlfwKeyDown(window, GLFW_KEY_LEFT_CONTROL) || MetaCoreIsGlfwKeyDown(window, GLFW_KEY_RIGHT_CONTROL)
);
Impl_->Input.SetKeyState(MetaCoreInputKey::Focus, MetaCoreIsGlfwKeyDown(window, GLFW_KEY_F));
Impl_->Input.SetMouseButtonState(MetaCoreMouseButton::Left, MetaCoreIsGlfwMouseButtonDown(window, GLFW_MOUSE_BUTTON_LEFT));
Impl_->Input.SetMouseButtonState(MetaCoreMouseButton::Right, MetaCoreIsGlfwMouseButtonDown(window, GLFW_MOUSE_BUTTON_RIGHT));
Impl_->Input.SetMouseButtonState(MetaCoreMouseButton::Middle, MetaCoreIsGlfwMouseButtonDown(window, GLFW_MOUSE_BUTTON_MIDDLE));
double cursorX = 0.0;
double cursorY = 0.0;
glfwGetCursorPos(window, &cursorX, &cursorY);
Impl_->Input.SetCursorPosition(glm::vec2(static_cast<float>(cursorX), static_cast<float>(cursorY)));
Impl_->CloseRequested = Impl_->CloseRequested || glfwWindowShouldClose(window) == GLFW_TRUE;
}
void MetaCoreWindow::EndFrame() {
}
bool MetaCoreWindow::ShouldClose() const {
return !Impl_->Initialized || Impl_->CloseRequested;
}
void MetaCoreWindow::RequestClose() {
Impl_->CloseRequested = true;
if (Impl_->NativeHandle != nullptr) {
glfwSetWindowShouldClose(Impl_->NativeHandle, GLFW_TRUE);
}
}
void* MetaCoreWindow::GetNativeWindowHandle() const {
return Impl_->NativeHandle;
}
MetaCoreInput& MetaCoreWindow::GetInput() {
return Impl_->Input;
}
const MetaCoreInput& MetaCoreWindow::GetInput() const {
return Impl_->Input;
}
float MetaCoreWindow::GetDeltaSeconds() const {
return Impl_->DeltaSeconds;
}
std::pair<int, int> MetaCoreWindow::GetWindowSize() const {
if (Impl_->NativeHandle == nullptr) {
return {0, 0};
}
int width = 0;
int height = 0;
glfwGetWindowSize(Impl_->NativeHandle, &width, &height);
return {width, height};
}
std::pair<int, int> MetaCoreWindow::GetFramebufferSize() const {
if (Impl_->NativeHandle == nullptr) {
return {0, 0};
}
int width = 0;
int height = 0;
glfwGetFramebufferSize(Impl_->NativeHandle, &width, &height);
return {width, height};
}
void MetaCoreWindow::SetNativeWindowMessageHandler(MetaCoreNativeWindowMessageHandler handler) {
Impl_->MessageHandler = std::move(handler);
}
} // namespace MetaCore

View File

@ -21,13 +21,22 @@ void MetaCoreTrace(const char* message) {
const auto now = std::chrono::system_clock::now();
const auto time = std::chrono::system_clock::to_time_t(now);
struct tm timeInfo {};
#if defined(_MSC_VER)
localtime_s(&timeInfo, &time);
#else
localtime_r(&time, &timeInfo);
#endif
char timeBuffer[32]{};
std::strftime(timeBuffer, sizeof(timeBuffer), "[%H:%M:%S] ", &timeInfo);
FILE* file = nullptr;
#if defined(_MSC_VER)
if (fopen_s(&file, "editor.crash.log", "a") == 0 && file != nullptr) {
#else
file = std::fopen("editor.crash.log", "a");
if (file != nullptr) {
#endif
std::fputs(timeBuffer, file);
std::fputs(message, file);
std::fputs("\n", file);

View File

@ -42,19 +42,64 @@
#include <iostream>
#include <fstream>
#include <algorithm>
#include <cstdlib>
#include <cstdint>
#include <unordered_map>
#include <unordered_set>
#include <map>
#include <string_view>
#include <vector>
#include <functional>
// 引入 Windows 和 OpenGL 头文件以创建纹理
// 引入平台和 OpenGL 头文件以创建纹理
#if defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#include <GL/gl.h>
#if !defined(_WIN32)
#define GLFW_EXPOSE_NATIVE_X11
#include <GLFW/glfw3.h>
#include <GLFW/glfw3native.h>
#endif
namespace MetaCore {
namespace {
void* MetaCoreGetFilamentNativeWindowHandle(MetaCoreWindow& window) {
#if defined(_WIN32)
return window.GetNativeWindowHandle();
#else
auto* glfwWindow = static_cast<GLFWwindow*>(window.GetNativeWindowHandle());
if (glfwWindow == nullptr) {
return nullptr;
}
const Window x11Window = glfwGetX11Window(glfwWindow);
return reinterpret_cast<void*>(static_cast<std::uintptr_t>(x11Window));
#endif
}
filament::Engine::Backend MetaCoreResolveFilamentBackend() {
const char* backend = std::getenv("METACORE_FILAMENT_BACKEND");
if (backend == nullptr) {
return filament::Engine::Backend::DEFAULT;
}
const std::string_view backendName(backend);
if (backendName == "opengl") {
return filament::Engine::Backend::OPENGL;
}
if (backendName == "vulkan") {
return filament::Engine::Backend::VULKAN;
}
return filament::Engine::Backend::DEFAULT;
}
} // namespace
class MetaCoreFilamentSceneBridge::MetaCoreFilamentSceneBridgeImpl {
public:
MetaCoreFilamentSceneBridgeImpl() = default;
@ -72,7 +117,7 @@ public:
std::cout << "FilamentSceneBridge: Initializing..." << std::endl;
// 创建 Filament Engine (不共享上下文,避免闪退)
Engine_ = filament::Engine::create();
Engine_ = filament::Engine::create(MetaCoreResolveFilamentBackend());
if (!Engine_) {
std::cerr << "Failed to create Filament Engine with shared context!" << std::endl;
@ -80,7 +125,7 @@ public:
}
// 创建绑定真实窗口的交换链
SwapChain_ = Engine_->createSwapChain((void*)window.GetNativeWindowHandle());
SwapChain_ = Engine_->createSwapChain(MetaCoreGetFilamentNativeWindowHandle(window));
if (!SwapChain_) {
std::cerr << "Failed to create Filament SwapChain with window handle!" << std::endl;
return false;

View File

@ -0,0 +1,37 @@
#include <features.h>
#if defined(__linux__) && defined(__GLIBC__) && (!defined(__GLIBC_PREREQ) || !__GLIBC_PREREQ(2, 38))
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
extern "C" {
long __isoc23_strtol(const char* text, char** end, int base) {
return std::strtol(text, end, base);
}
unsigned long __isoc23_strtoul(const char* text, char** end, int base) {
return std::strtoul(text, end, base);
}
long long __isoc23_strtoll(const char* text, char** end, int base) {
return std::strtoll(text, end, base);
}
unsigned long long __isoc23_strtoull(const char* text, char** end, int base) {
return std::strtoull(text, end, base);
}
int __isoc23_sscanf(const char* text, const char* format, ...) {
va_list args;
va_start(args, format);
const int result = std::vsscanf(text, format, args);
va_end(args);
return result;
}
}
#endif

View File

@ -4,6 +4,7 @@
#include <unordered_map>
#include <fstream>
#include <iostream>
#include <cstdint>
#include <imgui.h>
@ -38,6 +39,7 @@ MetaCoreImGuiHelper::MetaCoreImGuiHelper(Engine* engine, filament::View* view, c
ImGui::SetCurrentContext(mImGuiContext);
ImGuiIO& io = ImGui::GetIO();
io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures;
// 加载我们编译好的材质
std::ifstream file("uiBlit.filamat", std::ios::binary);
@ -112,12 +114,24 @@ void MetaCoreImGuiHelper::createAtlasTexture(Engine* engine) {
mTexture->setImage(*engine, 0, std::move(pb));
mSampler = TextureSampler(MinFilter::LINEAR, MagFilter::LINEAR);
#if IMGUI_VERSION_NUM >= 19200
if (io.Fonts->TexData != nullptr) {
io.Fonts->TexData->SetTexID(static_cast<ImTextureID>(reinterpret_cast<std::uintptr_t>(mTexture)));
io.Fonts->TexData->BackendUserData = mTexture;
io.Fonts->TexData->SetStatus(ImTextureStatus_OK);
}
#else
io.Fonts->SetTexID(static_cast<ImTextureID>(reinterpret_cast<std::uintptr_t>(mTexture)));
#endif
if (mMaterial2d) {
mMaterial2d->setDefaultParameter("albedo", mTexture, mSampler);
}
}
MetaCoreImGuiHelper::~MetaCoreImGuiHelper() {
ImGui::SetCurrentContext(mImGuiContext);
ImGui::GetIO().BackendFlags &= ~ImGuiBackendFlags_RendererHasTextures;
mEngine->destroy(mScene);
mEngine->destroy(mRenderable);
mEngine->destroyCameraComponent(mCameraEntity);
@ -190,6 +204,13 @@ void MetaCoreImGuiHelper::processImGuiCommands(ImDrawData* commands, const ImGui
int fbheight = (int)(io.DisplaySize.y * io.DisplayFramebufferScale.y);
if (fbwidth == 0 || fbheight == 0)
return;
if (commands->Textures != nullptr) {
for (ImTextureData* textureData : *commands->Textures) {
if (textureData != nullptr && textureData->Status != ImTextureStatus_OK) {
updateTexture(textureData);
}
}
}
commands->ScaleClipRects(io.DisplayFramebufferScale);
createBuffers(commands->CmdListsCount);
@ -343,6 +364,83 @@ void MetaCoreImGuiHelper::populateVertexData(size_t bufferIndex, size_t vbSizeIn
}, /* user = */ nullptr));
}
void MetaCoreImGuiHelper::updateTexture(ImTextureData* textureData) {
if (textureData == nullptr) {
return;
}
if (textureData->Status == ImTextureStatus_WantCreate) {
if (mTexture != nullptr) {
mEngine->destroy(mTexture);
mTexture = nullptr;
}
Texture::PixelBufferDescriptor pb(
textureData->GetPixels(),
static_cast<size_t>(textureData->GetSizeInBytes()),
Texture::Format::RGBA,
Texture::Type::UBYTE);
mTexture = Texture::Builder()
.width(static_cast<uint32_t>(textureData->Width))
.height(static_cast<uint32_t>(textureData->Height))
.levels(static_cast<uint8_t>(1))
.format(Texture::InternalFormat::RGBA8)
.sampler(Texture::Sampler::SAMPLER_2D)
.build(*mEngine);
mTexture->setImage(*mEngine, 0, std::move(pb));
textureData->BackendUserData = mTexture;
textureData->SetTexID(static_cast<ImTextureID>(reinterpret_cast<std::uintptr_t>(mTexture)));
textureData->SetStatus(ImTextureStatus_OK);
return;
}
if (textureData->Status == ImTextureStatus_WantUpdates) {
auto* texture = static_cast<Texture*>(textureData->BackendUserData);
if (texture == nullptr) {
texture = reinterpret_cast<Texture*>(static_cast<std::uintptr_t>(textureData->TexID));
}
if (texture == nullptr) {
textureData->SetStatus(ImTextureStatus_WantCreate);
updateTexture(textureData);
return;
}
for (const ImTextureRect& rect : textureData->Updates) {
Texture::PixelBufferDescriptor pb(
textureData->GetPixels(),
static_cast<size_t>(textureData->GetSizeInBytes()),
Texture::Format::RGBA,
Texture::Type::UBYTE,
1,
static_cast<uint32_t>(rect.x),
static_cast<uint32_t>(rect.y),
static_cast<uint32_t>(textureData->Width));
texture->setImage(
*mEngine,
0,
static_cast<uint32_t>(rect.x),
static_cast<uint32_t>(rect.y),
static_cast<uint32_t>(rect.w),
static_cast<uint32_t>(rect.h),
std::move(pb));
}
textureData->SetStatus(ImTextureStatus_OK);
return;
}
if (textureData->Status == ImTextureStatus_WantDestroy && textureData->UnusedFrames > 0) {
auto* texture = static_cast<Texture*>(textureData->BackendUserData);
if (texture != nullptr && texture != mTexture) {
mEngine->destroy(texture);
}
textureData->BackendUserData = nullptr;
textureData->SetTexID(ImTextureID_Invalid);
textureData->SetStatus(ImTextureStatus_Destroyed);
}
}
void MetaCoreImGuiHelper::syncThreads() {
#if UTILS_HAS_THREADING
if (!mHasSynced) {

View File

@ -18,6 +18,7 @@
struct ImDrawData;
struct ImGuiIO;
struct ImGuiContext;
struct ImTextureData;
namespace MetaCore {
@ -46,6 +47,7 @@ private:
size_t ibSizeInBytes, void* ibData);
void createVertexBuffer(size_t bufferIndex, size_t capacity);
void createIndexBuffer(size_t bufferIndex, size_t capacity);
void updateTexture(ImTextureData* textureData);
void syncThreads();
filament::Engine* mEngine;

View File

@ -83,7 +83,7 @@ void MetaCoreRuntimeDataDispatcher::ApplyUpdates(const std::vector<MetaCoreRunti
void MetaCoreRuntimeDataDispatcher::TickStaleness(std::uint64_t currentTimestamp) {
for (MetaCoreRuntimeBindingStatus& bindingStatus : BindingStatuses_) {
if (bindingStatus.LastAppliedAt == 0) {
if (!AppliedBindingIds_.contains(bindingStatus.BindingId)) {
bindingStatus.Stale = true;
continue;
}
@ -129,6 +129,7 @@ MetaCoreRuntimeDiagnosticsSnapshot MetaCoreRuntimeDataDispatcher::BuildDiagnosti
void MetaCoreRuntimeDataDispatcher::RebuildBindingStatuses() {
BindingStatuses_.clear();
BindingStatusIndexById_.clear();
AppliedBindingIds_.clear();
BindingStatuses_.reserve(BindingDefinitions_.size());
for (const MetaCoreSceneBindingDefinition& bindingDefinition : BindingDefinitions_) {
@ -167,6 +168,7 @@ void MetaCoreRuntimeDataDispatcher::MarkBindingHealthy(const std::string& bindin
status.Stale = false;
status.LastAppliedAt = appliedAt;
status.LastError.clear();
AppliedBindingIds_.insert(bindingId);
}
const MetaCoreDataPointDefinition* MetaCoreRuntimeDataDispatcher::FindDataPointDefinition(const std::string& dataPointId) const {

View File

@ -4,12 +4,14 @@
#include <algorithm>
#include <array>
#include <cctype>
#include <cstdint>
#include <cmath>
#include <filesystem>
#include <fstream>
#include <memory>
#include <sstream>
#if defined(_WIN32)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
@ -18,12 +20,106 @@
#endif
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <cerrno>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#endif
namespace MetaCore {
namespace {
constexpr SOCKET MetaCoreInvalidSocket = INVALID_SOCKET;
using MetaCoreSocketLength =
#if defined(_WIN32)
int;
using MetaCoreSocket = SOCKET;
constexpr MetaCoreSocket MetaCoreInvalidSocket = INVALID_SOCKET;
constexpr int MetaCoreSocketError = SOCKET_ERROR;
#else
socklen_t;
using MetaCoreSocket = int;
constexpr MetaCoreSocket MetaCoreInvalidSocket = -1;
constexpr int MetaCoreSocketError = -1;
#endif
[[nodiscard]] bool MetaCoreInitializeSocketApi(std::string& errorMessage) {
#if defined(_WIN32)
static bool initialized = false;
if (initialized) {
return true;
}
WSADATA wsaData{};
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
errorMessage = "WSAStartup failed";
return false;
}
initialized = true;
#else
(void)errorMessage;
#endif
return true;
}
void MetaCoreCloseSocket(MetaCoreSocket socketHandle) {
if (socketHandle == MetaCoreInvalidSocket) {
return;
}
#if defined(_WIN32)
closesocket(socketHandle);
#else
close(socketHandle);
#endif
}
[[nodiscard]] int MetaCoreGetLastSocketError() {
#if defined(_WIN32)
return WSAGetLastError();
#else
return errno;
#endif
}
[[nodiscard]] bool MetaCoreIsSocketWouldBlock(int socketError) {
#if defined(_WIN32)
return socketError == WSAEWOULDBLOCK;
#else
return socketError == EWOULDBLOCK || socketError == EAGAIN;
#endif
}
[[nodiscard]] bool MetaCoreSetSocketNonBlocking(MetaCoreSocket socketHandle) {
#if defined(_WIN32)
u_long nonBlocking = 1;
return ioctlsocket(socketHandle, FIONBIO, &nonBlocking) == 0;
#else
const int flags = fcntl(socketHandle, F_GETFL, 0);
if (flags == -1) {
return false;
}
return fcntl(socketHandle, F_SETFL, flags | O_NONBLOCK) == 0;
#endif
}
[[nodiscard]] void* MetaCoreSocketToHandle(MetaCoreSocket socketHandle) {
#if defined(_WIN32)
return reinterpret_cast<void*>(socketHandle);
#else
return reinterpret_cast<void*>(static_cast<std::intptr_t>(socketHandle));
#endif
}
[[nodiscard]] MetaCoreSocket MetaCoreHandleToSocket(void* socketHandle) {
#if defined(_WIN32)
return reinterpret_cast<MetaCoreSocket>(socketHandle);
#else
return static_cast<MetaCoreSocket>(reinterpret_cast<std::intptr_t>(socketHandle));
#endif
}
[[nodiscard]] std::string MetaCoreTrim(std::string value) {
const auto isSpace = [](unsigned char ch) {
@ -437,24 +533,13 @@ bool MetaCoreTcpRuntimeDataSourceAdapter::Configure(const MetaCoreDataSourceDefi
return true;
}
bool MetaCoreTcpRuntimeDataSourceAdapter::EnsureWinsockInitialized() {
static bool initialized = false;
if (initialized) {
return true;
}
WSADATA wsaData{};
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
Status_.LastError = "WSAStartup failed";
return false;
}
initialized = true;
return true;
bool MetaCoreTcpRuntimeDataSourceAdapter::EnsureSocketApiInitialized() {
return MetaCoreInitializeSocketApi(Status_.LastError);
}
bool MetaCoreTcpRuntimeDataSourceAdapter::Connect() {
Status_.State = MetaCoreRuntimeDataSourceState::Connecting;
if (!EnsureWinsockInitialized()) {
if (!EnsureSocketApiInitialized()) {
Status_.State = MetaCoreRuntimeDataSourceState::Faulted;
return false;
}
@ -472,7 +557,7 @@ bool MetaCoreTcpRuntimeDataSourceAdapter::Connect() {
return false;
}
SOCKET socketHandle = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
MetaCoreSocket socketHandle = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (socketHandle == MetaCoreInvalidSocket) {
freeaddrinfo(result);
Status_.State = MetaCoreRuntimeDataSourceState::Faulted;
@ -480,8 +565,8 @@ bool MetaCoreTcpRuntimeDataSourceAdapter::Connect() {
return false;
}
if (connect(socketHandle, result->ai_addr, static_cast<int>(result->ai_addrlen)) == SOCKET_ERROR) {
closesocket(socketHandle);
if (connect(socketHandle, result->ai_addr, static_cast<MetaCoreSocketLength>(result->ai_addrlen)) == MetaCoreSocketError) {
MetaCoreCloseSocket(socketHandle);
freeaddrinfo(result);
Status_.State = MetaCoreRuntimeDataSourceState::Faulted;
Status_.LastError = "tcp connect failed";
@ -489,9 +574,14 @@ bool MetaCoreTcpRuntimeDataSourceAdapter::Connect() {
}
freeaddrinfo(result);
u_long nonBlocking = 1;
ioctlsocket(socketHandle, FIONBIO, &nonBlocking);
SocketHandle_ = reinterpret_cast<void*>(socketHandle);
if (!MetaCoreSetSocketNonBlocking(socketHandle)) {
MetaCoreCloseSocket(socketHandle);
Status_.State = MetaCoreRuntimeDataSourceState::Faulted;
Status_.LastError = "tcp non-blocking mode failed";
return false;
}
SocketHandle_ = MetaCoreSocketToHandle(socketHandle);
Status_.State = MetaCoreRuntimeDataSourceState::Connected;
Status_.LastConnectedAt = 1;
Status_.LastError.clear();
@ -500,7 +590,7 @@ bool MetaCoreTcpRuntimeDataSourceAdapter::Connect() {
void MetaCoreTcpRuntimeDataSourceAdapter::Disconnect() {
if (SocketHandle_ != nullptr) {
closesocket(reinterpret_cast<SOCKET>(SocketHandle_));
MetaCoreCloseSocket(MetaCoreHandleToSocket(SocketHandle_));
SocketHandle_ = nullptr;
}
Status_.State = MetaCoreRuntimeDataSourceState::Disconnected;
@ -515,10 +605,10 @@ void MetaCoreTcpRuntimeDataSourceAdapter::Tick(double deltaSeconds) {
return;
}
SOCKET socketHandle = reinterpret_cast<SOCKET>(SocketHandle_);
MetaCoreSocket socketHandle = MetaCoreHandleToSocket(SocketHandle_);
std::array<char, 1024> buffer{};
for (;;) {
const int received = recv(socketHandle, buffer.data(), static_cast<int>(buffer.size()), 0);
const auto received = recv(socketHandle, buffer.data(), static_cast<MetaCoreSocketLength>(buffer.size()), 0);
if (received > 0) {
ReceiveBuffer_.append(buffer.data(), static_cast<std::size_t>(received));
Status_.State = MetaCoreRuntimeDataSourceState::Connected;
@ -531,8 +621,8 @@ void MetaCoreTcpRuntimeDataSourceAdapter::Tick(double deltaSeconds) {
break;
}
const int socketError = WSAGetLastError();
if (socketError == WSAEWOULDBLOCK) {
const int socketError = MetaCoreGetLastSocketError();
if (MetaCoreIsSocketWouldBlock(socketError)) {
break;
}
Status_.State = MetaCoreRuntimeDataSourceState::Faulted;

View File

@ -6,6 +6,7 @@
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace MetaCore {
@ -37,6 +38,7 @@ private:
std::vector<MetaCoreSceneBindingDefinition> BindingDefinitions_{};
std::vector<MetaCoreRuntimeBindingStatus> BindingStatuses_{};
std::unordered_map<std::string, std::size_t> BindingStatusIndexById_{};
std::unordered_set<std::string> AppliedBindingIds_{};
};
} // namespace MetaCore

View File

@ -80,7 +80,7 @@ public:
[[nodiscard]] const MetaCoreRuntimeDataSourceStatus& GetStatus() const override;
private:
[[nodiscard]] bool EnsureWinsockInitialized();
[[nodiscard]] bool EnsureSocketApiInitialized();
[[nodiscard]] bool ParseSocketLine(const std::string& line, MetaCoreRuntimeDataUpdate& update);
MetaCoreDataSourceDefinition SourceDefinition_{};

View File

@ -0,0 +1,8 @@
{
"asset_type": "asset",
"guid": "7e3d10d5-d46f-4622-9615-25ce0abef1e7",
"importer_id": "BinaryImporter",
"package_path": "Assets/Materials/default_pbr.material.json.mcasset",
"source_hash": 12650993429013095480,
"source_path": "Assets/Materials/default_pbr.material.json"
}

Binary file not shown.

View File

@ -0,0 +1,22 @@
{
"asset_type": "model",
"guid": "e85ce6d3-12a9-475a-a224-9da4035ce22a",
"importer_id": "GltfModelImporter",
"package_path": "Assets/Models/Cube.glb.mcasset",
"source_hash": 2668036311376285448,
"source_path": "Assets/Models/Cube.glb",
"sub_assets": [
{
"guid": "9cd50d41-df30-26ff-da7e-e01bb57f7135",
"index": 0,
"name": "Cube",
"type": "mesh"
},
{
"guid": "20a8eca7-ae5b-8ed9-c7a8-b64b59567283",
"index": 0,
"name": "Cube_Material",
"type": "material"
}
]
}

Binary file not shown.

View File

@ -0,0 +1,22 @@
{
"asset_type": "model",
"guid": "75068577-37e3-4a9d-954a-54251c667933",
"importer_id": "GltfModelImporter",
"package_path": "Assets/Models/Plane.glb.mcasset",
"source_hash": 7189019093832532692,
"source_path": "Assets/Models/Plane.glb",
"sub_assets": [
{
"guid": "804bca6d-b9ea-f7af-3f0a-f078d5de0d45",
"index": 0,
"name": "Plane",
"type": "mesh"
},
{
"guid": "6c337003-dc9a-da7c-d685-6d27c673046a",
"index": 0,
"name": "Plane_Material",
"type": "material"
}
]
}

View File

@ -0,0 +1,8 @@
{
"asset_type": "ui_stylesheet",
"guid": "9bd0aa89-1f68-4a4d-b9ed-1fcf1dc6180d",
"importer_id": "UiStylesheetImporter",
"package_path": "Assets/UI/main_menu.rcss.mcasset",
"source_hash": 8787559501102875347,
"source_path": "Assets/UI/main_menu.rcss"
}

View File

@ -0,0 +1,8 @@
{
"asset_type": "ui_document",
"guid": "7fa1231a-4620-479f-8238-03531e78202a",
"importer_id": "UiDocumentImporter",
"package_path": "Assets/UI/main_menu.rml.mcasset",
"source_hash": 11300316873669048992,
"source_path": "Assets/UI/main_menu.rml"
}

Binary file not shown.

View File

@ -0,0 +1,8 @@
{
"asset_type": "scene",
"guid": "83429142-44ec-4b15-bf3e-bac36bc8fc7c",
"importer_id": "SceneImporter",
"package_path": "Scenes/Main.mcscene.json",
"source_hash": 108452126926794326,
"source_path": "Scenes/Main.mcscene.json"
}

View File

@ -0,0 +1,8 @@
{
"asset_type": "scene",
"guid": "0e6c871d-52c4-4af3-ae1b-575b4eede7dc",
"importer_id": "SceneImporter",
"package_path": "Scenes/Main.mcscene",
"source_hash": 15718088956628388019,
"source_path": "Scenes/Main.mcscene"
}

View File

@ -0,0 +1,8 @@
{
"asset_type": "scene",
"guid": "d4f241ff-2970-45f8-a6d7-5a4441980a89",
"importer_id": "SceneImporter",
"package_path": "Scenes/Test.mcscene.json",
"source_hash": 4489916389061150913,
"source_path": "Scenes/Test.mcscene.json"
}

View File

@ -11,37 +11,55 @@ function(metacore_use_filament target_name)
message(STATUS "Linking Filament to ${target_name}")
#
set(_filament_libs
"${METACORE_FILAMENT_LIB_DIR}/filament.lib"
"${METACORE_FILAMENT_LIB_DIR}/backend.lib"
"${METACORE_FILAMENT_LIB_DIR}/utils.lib"
"${METACORE_FILAMENT_LIB_DIR}/filabridge.lib"
"${METACORE_FILAMENT_LIB_DIR}/filaflat.lib"
"${METACORE_FILAMENT_LIB_DIR}/filamat.lib"
"${METACORE_FILAMENT_LIB_DIR}/shaders.lib"
"${METACORE_FILAMENT_LIB_DIR}/bluegl.lib"
"${METACORE_FILAMENT_LIB_DIR}/gltfio.lib"
"${METACORE_FILAMENT_LIB_DIR}/gltfio_core.lib"
"${METACORE_FILAMENT_LIB_DIR}/abseil.lib"
"${METACORE_FILAMENT_LIB_DIR}/perfetto.lib"
"${METACORE_FILAMENT_LIB_DIR}/dracodec.lib"
"${METACORE_FILAMENT_LIB_DIR}/geometry.lib"
"${METACORE_FILAMENT_LIB_DIR}/smol-v.lib"
"${METACORE_FILAMENT_LIB_DIR}/zstd.lib"
opengl32.lib
gdi32.lib
user32.lib
shlwapi.lib
)
if(WIN32)
set(_filament_libs
"${METACORE_FILAMENT_LIB_DIR}/filament.lib"
"${METACORE_FILAMENT_LIB_DIR}/backend.lib"
"${METACORE_FILAMENT_LIB_DIR}/utils.lib"
"${METACORE_FILAMENT_LIB_DIR}/filabridge.lib"
"${METACORE_FILAMENT_LIB_DIR}/filaflat.lib"
"${METACORE_FILAMENT_LIB_DIR}/filamat.lib"
"${METACORE_FILAMENT_LIB_DIR}/shaders.lib"
"${METACORE_FILAMENT_LIB_DIR}/bluegl.lib"
"${METACORE_FILAMENT_LIB_DIR}/gltfio.lib"
"${METACORE_FILAMENT_LIB_DIR}/gltfio_core.lib"
"${METACORE_FILAMENT_LIB_DIR}/abseil.lib"
"${METACORE_FILAMENT_LIB_DIR}/perfetto.lib"
"${METACORE_FILAMENT_LIB_DIR}/dracodec.lib"
"${METACORE_FILAMENT_LIB_DIR}/geometry.lib"
"${METACORE_FILAMENT_LIB_DIR}/smol-v.lib"
"${METACORE_FILAMENT_LIB_DIR}/zstd.lib"
opengl32.lib
gdi32.lib
user32.lib
shlwapi.lib
)
else()
find_package(OpenGL REQUIRED)
find_package(Threads REQUIRED)
file(GLOB _filament_static_libs CONFIGURE_DEPENDS
"${METACORE_FILAMENT_LIB_DIR}/lib*.a"
)
set(_filament_libs
-Wl,--start-group
${_filament_static_libs}
-Wl,--end-group
OpenGL::GL
Threads::Threads
${CMAKE_DL_LIBS}
)
endif()
target_include_directories(${target_name} PRIVATE "${METACORE_FILAMENT_INCLUDE_DIR}")
target_link_libraries(${target_name} PRIVATE ${_filament_libs})
# Filament C++17
target_compile_features(${target_name} PRIVATE cxx_std_17)
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
target_compile_options(${target_name} PRIVATE -fpermissive)
endif()
# MSVC
if(MSVC)
target_compile_definitions(${target_name} PRIVATE
WIN32_LEAN_AND_MEAN

View File

@ -51,6 +51,15 @@ namespace IMGUIZMO_NAMESPACE
// scale a bit so translate axis do not touch when in universal
const float rotationDisplayFactor = 1.2f;
static void AddPolylineCompat(ImDrawList* drawList, const ImVec2* points, int pointsCount, ImU32 color, bool closed, float thickness)
{
#if IMGUI_VERSION_NUM >= 19280
drawList->AddPolyline(points, pointsCount, color, thickness, closed ? ImDrawFlags_Closed : ImDrawFlags_None);
#else
drawList->AddPolyline(points, pointsCount, color, closed, thickness);
#endif
}
static OPERATION operator&(OPERATION lhs, OPERATION rhs)
{
return static_cast<OPERATION>(static_cast<int>(lhs) & static_cast<int>(rhs));
@ -1324,7 +1333,7 @@ namespace IMGUIZMO_NAMESPACE
}
if (!gContext.mbUsing || usingAxis)
{
drawList->AddPolyline(circlePos, circleMul* halfCircleSegmentCount + 1, colors[3 - axis], false, gContext.mStyle.RotationLineThickness);
AddPolylineCompat(drawList, circlePos, circleMul* halfCircleSegmentCount + 1, colors[3 - axis], false, gContext.mStyle.RotationLineThickness);
}
float radiusAxis = sqrtf((ImLengthSqr(worldToPos(gContext.mModel.v.position, gContext.mViewProjection) - circlePos[0])));
@ -1354,7 +1363,7 @@ namespace IMGUIZMO_NAMESPACE
circlePos[i] = worldToPos(pos + gContext.mModel.v.position, gContext.mViewProjection);
}
drawList->AddConvexPolyFilled(circlePos, halfCircleSegmentCount + 1, GetColorU32(ROTATION_USING_FILL));
drawList->AddPolyline(circlePos, halfCircleSegmentCount + 1, GetColorU32(ROTATION_USING_BORDER), true, gContext.mStyle.RotationLineThickness);
AddPolylineCompat(drawList, circlePos, halfCircleSegmentCount + 1, GetColorU32(ROTATION_USING_BORDER), true, gContext.mStyle.RotationLineThickness);
ImVec2 destinationPosOnScreen = circlePos[1];
char tmps[512];
@ -1617,7 +1626,7 @@ namespace IMGUIZMO_NAMESPACE
vec_t cornerWorldPos = (dirPlaneX * quadUV[j * 2] + dirPlaneY * quadUV[j * 2 + 1]) * gContext.mScreenFactor;
screenQuadPts[j] = worldToPos(cornerWorldPos, gContext.mMVP);
}
drawList->AddPolyline(screenQuadPts, 4, GetColorU32(DIRECTION_X + i), true, 1.0f);
AddPolylineCompat(drawList, screenQuadPts, 4, GetColorU32(DIRECTION_X + i), true, 1.0f);
drawList->AddConvexPolyFilled(screenQuadPts, 4, colors[i + 4]);
}
}

View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2023 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,183 @@
# Filament
This package contains several executables and libraries you can use to build applications using
Filament. Latest versions are available on the [project page](https://github.com/google/filament).
## Binaries
- `cmgen`, Image-based lighting asset generator
- `filamesh`, Mesh converter
- `glslminifier`, Tool to minify GLSL shaders
- `gltf_viewer`, glTF 2.0 viewer that lets you explore many features of Filament
- `matc`, Material compiler
- `material_sandbox`, simple mesh viewer that lets you explore material and lighting features
- `matinfo`, Displays information about materials compiled with `matc`
- `mipgen`, Generates a series of miplevels from a source image.
- `normal-blending`, Tool to blend normal maps
- `resgen`, Tool to convert files into binary resources to be embedded at compie time
- `roughness-prefilter`, Pre-filters a roughness map from a normal map to reduce aliasing
- `specular-color`, Computes the specular color of conductors based on spectral data
You can refer to the individual documentation files in `docs/` for more information.
## Libraries
Filament is distributed as a set of static libraries you must link against:
- `backend`, Required, implements all backends
- `bluegl`, Required to render with OpenGL or OpenGL ES
- `bluevk`, Required to render with Vulkan
- `filabridge`, Support library for Filament
- `filaflat`, Support library for Filament
- `filament`, Main Filament library
- `backend`, Filament render backend library
- `ibl`, Image-based lighting support library
- `utils`, Support library for Filament
- `geometry`, Geometry helper library for Filament
- `smol-v`, SPIR-V compression library, used only with Vulkan support
To use Filament from Java you must use the following two libraries instead:
- `filament-java.jar`, Contains Filament's Java classes
- `filament-jni`, Filament's JNI bindings
To link against debug builds of Filament, you must also link against:
- `matdbg`, Support library that adds an interactive web-based debugger to Filament
To use the Vulkan backend on macOS you must install the LunarG SDK, enable "System Global
Components", and reboot your machine.
The easiest way to install those files is to use the macOS
[LunarG Vulkan SDK](https://www.lunarg.com/vulkan-sdk/) installer.
## Linking against Filament
This walkthrough will get you successfully compiling and linking native code
against Filament with minimum dependencies.
To start, download Filament's [latest binary release](https://github.com/google/filament/releases)
and extract into a directory of your choosing. Binary releases are suffixed
with the platform name, for example, `filament-20181009-linux.tgz`.
Create a file, `main.cpp`, in the same directory with the following contents:
```c++
#include <filament/FilamentAPI.h>
#include <filament/Engine.h>
using namespace filament;
int main(int argc, char** argv)
{
Engine *engine = Engine::create();
engine->destroy(&engine);
return 0;
}
```
The directory should look like:
```
|-- README.md
|-- bin
|-- docs
|-- include
|-- lib
|-- main.cpp
```
We'll use a platform-specific Makefile to compile and link `main.cpp` with Filament's libraries.
Copy your platform's Makefile below into a `Makefile` inside the same directory.
### Linux
```make
FILAMENT_LIBS=-lfilament -lbackend -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils -lgeometry -lsmol-v -libl -labseil
CC=clang++
main: main.o
$(CC) -Llib/x86_64/ main.o $(FILAMENT_LIBS) -lpthread -lc++ -ldl -o main
main.o: main.cpp
$(CC) -Iinclude/ -std=c++20 -pthread -c main.cpp
clean:
rm -f main main.o
.PHONY: clean
```
### macOS
```make
FILAMENT_LIBS=-lfilament -lbackend -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils -lgeometry -lsmol-v -libl -labseil
FRAMEWORKS=-framework Cocoa -framework Metal -framework CoreVideo
CC=clang++
ARCH ?= $(shell uname -m)
main: main.o
$(CC) -Llib/$(ARCH)/ main.o $(FILAMENT_LIBS) $(FRAMEWORKS) -o main
main.o: main.cpp
$(CC) -Iinclude/ -std=c++20 -c main.cpp
clean:
rm -f main main.o
.PHONY: clean
```
### Windows
Note that the static libraries distributed for Windows include several
variants: mt, md, mtd, mdd. These correspond to the [run-time library
flags](https://docs.microsoft.com/en-us/cpp/build/reference/md-mt-ld-use-run-time-library?view=vs-2017)
`/MT`, `/MD`, `/MTd`, and `/MDd`, respectively. Here we use the mt variant. For the debug variants,
be sure to also include `matdbg.lib` in `FILAMENT_LIBS`.
When building Filament from source, the `USE_STATIC_CRT` CMake option can be
used to change the run-time library version.
```make
FILAMENT_LIBS=filament.lib backend.lib bluegl.lib bluevk.lib filabridge.lib filaflat.lib \
utils.lib geometry.lib smol-v.lib ibl.lib abseil.lib
CC=cl.exe
main.exe: main.obj
$(CC) main.obj /link /libpath:"lib\\x86_64\\mt\\" $(FILAMENT_LIBS) \
gdi32.lib user32.lib opengl32.lib
main.obj: main.cpp
$(CC) /MT /Iinclude\\ /std:c++20 /c main.cpp
clean:
del main.exe main.obj
.PHONY: clean
```
### Compiling
You should be able to invoke `make` and run the executable successfully:
```
$ make
$ ./main
FEngine (64 bits) created at 0x106471000 (threading is enabled)
```
On Windows, you'll need to open up a Visual Studio Native Tools Command Prompt
and invoke `nmake` instead of `make`.
### Generating C++ documentation
To generate the documentation you must first install `doxygen` and `graphviz`, then run the
following commands:
```shell
cd filament/filament
doxygen docs/doxygen/filament.doxygen
```
Finally simply open `docs/html/index.html` in your web browser.

View File

@ -0,0 +1 @@
Apache License, Version 2.0

View File

@ -0,0 +1,9 @@
( 0.785786807537079, 0.785786807537079, 0.785786807537079); // L00, irradiance, pre-scaled base
( 0.402588516473770, 0.402588516473770, 0.402588516473770); // L1-1, irradiance, pre-scaled base
( 0.460519373416901, 0.460519373416901, 0.460519373416901); // L10, irradiance, pre-scaled base
( 0.084180898964405, 0.084180898964405, 0.084180898964405); // L11, irradiance, pre-scaled base
( 0.058341909199953, 0.058341909199953, 0.058341909199953); // L2-2, irradiance, pre-scaled base
( 0.204982891678810, 0.204982891678810, 0.204982891678810); // L2-1, irradiance, pre-scaled base
( 0.092737942934036, 0.092737942934036, 0.092737942934036); // L20, irradiance, pre-scaled base
(-0.091809459030628, -0.091809459030628, -0.091809459030628); // L21, irradiance, pre-scaled base
(-0.006748970132321, -0.006748970132321, -0.006748970132321); // L22, irradiance, pre-scaled base

View File

@ -0,0 +1,268 @@
# filamesh
`filamesh` converts any mesh file supported by `assimp` (as configured in this source tree) into a
custom binary file format. The goal of this binary file format is to allow test applications to
easily and quickly load meshes.
The source mesh must have at least one set of UV coordinates.
The destination mesh will contain vertex positions, one set of UV coordinates and per-vertex
tangents, bitangents and normals.
The destination mesh is made of a single vertex buffer and a single index buffer. Mesh parts are
identified by an offset and count in the index buffer. Each part can have its own material.
## Usage
```shell
filamesh source_mesh destination_mesh
```
## Format
Note: the UV1 attribute cannot be used in interleaved mode
> **Note**
> If you use the hex editor for macOS called [Hex Fiend](https://hexfiend.com/), you can use the
> template found in `ide/hexfiend/Templates` to inspect filamesh files.
### Header
char[8] : magic identifier "FILAMESH"
uint32 : version number
uint32 : number of parts (sub-meshes or draw calls)
float3 : center of the total bounding box (AABB)
float3 : half extent of the total bounding box (AABB)
uint32 : flags (see below)
uint32 : offset of the position attribute
uint32 : stride of the position attribute
uint32 : offset of the tangents attribute
uint32 : stride of the tangents attribute
uint32 : offset of the color attribute
uint32 : stride of the color attribute
uint32 : offset of the UV0 attribute
uint32 : stride of the UV0 attribute
uint32 : offset of the UV1 attribute (0xffffffff if UV1 is not present)
uint32 : stride of the UV1 attribute (0xffffffff if UV1 is not present)
uint32 : total number of vertices
uint32 : size in bytes occupied by the (compressed) vertices
uint32 : 0 if indices are stored as uint32, 1 if stored as uint16
uint32 : total number of indices
uint32 : size in bytes occupied by the (compressed) indices
The `flags` field contains the following bits:
- Bit 0: Specifies that vertex attributes are interleaved.
- Bit 1: UV's are 16-bit integers normalized into [-1, +1] rather than half-floats.
- Bit 2: Vertex and index data are compressed using zeux/meshoptimizer.
### Vertex data
char* : non-interleaved:
with n = number of vertices
n * half4: XYZ positions, W set to 1.0
n * short4: tangent, bitangent and normal as a quaternion (snorm unsigned short)
n * ubyte4: color
n * half2: UV texture coordinates
n * half2: UV texture coordinates (if UV1 offset and stride != 0xffffffff)
interleaved:
for each vertex:
half4: XYZ position, W set to 1.0
short4: tangent, bitangent and normal as a quaternion (snorm unsigned short)
ubyte4: color
half2: UV texture coordinates
### Index data
char* : each index is a uint32 or uint16 (see header)
### Parts
for each part:
uint32: offset of the first index in the index buffer
uint32: number of indices that compose this part
uint32: min index referenced by this part (glDrawRangeElements)
uint32: max index referenced by this part (glDrawRangeElements)
uint32: material ID (index in list of materials)
float3: center of the part's bounding box (AABB)
float3: half extent of the part's bounding box (AABB)
### Materials
uint32 : number of materials
for each material:
uint32: length in bytes of the material name's string (not counting terminating \0)
char* : name of the material (null terminated)
## Example
```c++
struct Mesh {
utils::Entity renderable;
VertexBuffer* vertexBuffer = nullptr;
IndexBuffer* indexBuffer = nullptr;
};
struct Header {
uint32_t version;
uint32_t parts;
Box aabb;
uint32_t flags;
uint32_t offsetPosition;
uint32_t stridePosition;
uint32_t offsetTangents;
uint32_t strideTangents;
uint32_t offsetColor;
uint32_t strideColor;
uint32_t offsetUV0;
uint32_t strideUV0;
uint32_t offsetUV1;
uint32_t strideUV1;
uint32_t vertexCount;
uint32_t vertexSize;
uint32_t indexType;
uint32_t indexCount;
uint32_t indexSize;
};
struct Vertex {
half4 position;
short4 tangents;
ubyte4 color;
short2 uv0; // either half-float or snorm int16
};
struct Part {
uint32_t offset;
uint32_t indexCount;
uint32_t minIndex;
uint32_t maxIndex;
uint32_t materialID;
Box aabb;
};
static size_t fileSize(int fd) {
size_t filesize;
filesize = (size_t) lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
return filesize;
}
Mesh loadMeshFromFile(filament::Engine* engine, const utils::Path& path,
const std::map<std::string, filament::MaterialInstance*>& materials) {
Mesh mesh;
int fd = open(path.c_str(), O_RDONLY);
size_t size = fileSize(fd);
char* data = (char*) mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
if (data) {
char *p = data;
char magic[9];
memcpy(magic, (const char*) p, sizeof(char) * 8);
magic[8] = '\0';
p += sizeof(char) * 8;
if (!strcmp("FILAMESH", magic)) {
Header* header = (Header*) p;
p += sizeof(Header);
char* vertexData = p;
p += header->vertexSize;
char* indices = p;
p += header->indexSize;
Part* parts = (Part*) p;
p += header->parts * sizeof(Part);
uint32_t materialCount = (uint32_t) *p;
p += sizeof(uint32_t);
std::vector<std::string> partsMaterial;
partsMaterial.resize(materialCount);
for (size_t i = 0; i < materialCount; i++) {
uint32_t nameLength = (uint32_t) *p;
p += sizeof(uint32_t);
partsMaterial[i] = p;
p += nameLength + 1; // null terminated
}
mesh.indexBuffer = IndexBuffer::Builder()
.indexCount(header->indexCount)
.bufferType(header->indexType ? IndexBuffer::IndexType::USHORT
: IndexBuffer::IndexType::UINT)
.build(*engine);
mesh.indexBuffer->setBuffer(*engine,
IndexBuffer::BufferDescriptor(indices, header->indexSize));
const uint32_t FLAG_SNORM16_UV = 0x2;
VertexBuffer::AttributeType::HALF2 uvType = VertexBuffer::AttributeType::HALF2;
if (header->flags & FLAG_SNORM16_UV) {
uvType = VertexBuffer::AttributeType::SHORT2;
}
bool uvNormalized = header->flags & FLAG_SNORM16_UV;
VertexBuffer::Builder vbb;
vbb.vertexCount(header->vertexCount)
.bufferCount(1)
.normalized(VertexAttribute::TANGENTS)
.normalized(VertexAttribute::COLOR)
.attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::HALF4,
header->offsetPosition, uint8_t(header->stridePosition))
.attribute(VertexAttribute::TANGENTS, 0, VertexBuffer::AttributeType::SHORT4,
header->offsetTangents, uint8_t(header->strideTangents))
.attribute(VertexAttribute::COLOR, 0, VertexBuffer::AttributeType::UBYTE4,
header->offsetColor, uint8_t(header->strideColor))
.attribute(VertexAttribute::UV0, 0, uvType,
header->offsetUV0, uint8_t(header->strideUV0))
.normalized(VertexAttribute::UV0, uvNormalized);
}
if (header->offsetUV1 != std::numeric_limits<uint32_t>::max() &&
header->strideUV1 != std::numeric_limits<uint32_t>::max()) {
vbb
.attribute(VertexAttribute::UV1, 0, uvType,
header->offsetUV1, uint8_t(header->strideUV1))
.normalized(VertexAttribute::UV1, uvNormalized);
}
mesh.vertexBuffer = vbb.build(*engine);
VertexBuffer::BufferDescriptor buffer(vertexData, header->vertexSize);
mesh.vertexBuffer->setBufferAt(*engine, 0, std::move(buffer));
RenderableManager::Builder builder(header->parts);
builder.boundingBox(header->aabb);
for (size_t i = 0; i < header->parts; i++) {
builder.geometry(i, RenderableManager::PrimitiveType::TRIANGLES,
mesh.vertexBuffer, mesh.indexBuffer, parts[i].offset,
parts[i].minIndex, parts[i].maxIndex, parts[i].indexCount);
auto m = materials.find(partsMaterial[i]);
if (m != materials.end()) {
builder.material(i, m->second);
} else {
builder.material(i, materials.at("DefaultMaterial"));
}
}
mesh.renderable = utils::EntityManager::get().create();
builder.build(*engine, mesh.renderable);
}
Fence::waitAndDestroy(engine->createFence());
munmap(data, size);
}
close(fd);
return mesh;
}
```

View File

@ -0,0 +1,10 @@
# matinfo
`matinfo` lists the content of a compiled material as output by `matc`. This tool is meant to be
used for debug purpose only.
## Usage
```shell
matinfo [options] <material file>
```

View File

@ -0,0 +1,11 @@
# mipgen
`mipgen` generates mipmaps for an image down to the 1x1 level.
## Usage
```shell
mipgen [options] <input_file> <output_pattern>
```
Run `mipgen --help` for more information about available options.

View File

@ -0,0 +1,6 @@
# Normal Blending
`normal_blending` is a simple tool that can be used to combine two normal maps in a single texture.
This tool uses the blending technique called _Reoriented Normal Mapping_ which offers mathematically
correct results (as opposed to common techniques such as linear or overlay blending).

View File

@ -0,0 +1,5 @@
# Roughness Prefilter
`roughness_prefilter` is a simple tool that can be used to generate a pre-filtered roughness map
from a normal map. The input roughness can either be a constant or a roughness map. The output can
be used to reduce shading aliasing.

View File

@ -0,0 +1,31 @@
# specular-color
`specular-color` computes the base color of conductors based on spectral data.
The base color is output in linear and sRGB formats. The base color is the reflectance
at normal incidence (0°) and is often noted f0.
`specular-color` can also compute the perceived color of a conductor at another angle,
set to ~82° by default. This value is particularly useful when used in combination with
the Lazanyi-Schlick model to better approximate the behavior of metallic surfaces at
grazing angles. See Hoffman 2019, "Fresnel Equations Considered Harmful".
## Usage
```shell
specular-color <spectral data file>
```
The spectral data files can be obtained from
[Refractive Index](https://refractiveindex.info/?shelf=3d&book=metals&page=brass).
For instance, to compute the base color of gold:
```shell
specular-color data/gold.txt
```
To set the second angle, use `-a` to specify the angle in degrees:
```shell
specular-color -a 75 data/gold.txt
```

View File

@ -0,0 +1,40 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_PRIVATE_ACQUIREDIMAGE_H
#define TNT_FILAMENT_BACKEND_PRIVATE_ACQUIREDIMAGE_H
#include <backend/DriverEnums.h>
#include <math/mat3.h>
namespace filament::backend {
class CallbackHandler;
// This lightweight POD allows us to bundle the state required to process an ACQUIRED stream.
// Since these types of external images need to be moved around and queued up, an encapsulation is
// very useful.
struct AcquiredImage {
void* image = nullptr;
backend::StreamCallback callback = nullptr;
void* userData = nullptr;
CallbackHandler* handler = nullptr;
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_PRIVATE_ACQUIREDIMAGE_H

View File

@ -0,0 +1,229 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_BACKEND_BUFFERDESCRIPTOR_H
#define TNT_FILAMENT_BACKEND_BUFFERDESCRIPTOR_H
#include <utils/compiler.h>
#include <utility>
#include <stddef.h>
namespace utils::io {
class ostream;
} // namespace utils::io
namespace filament::backend {
class CallbackHandler;
/**
* A CPU memory-buffer descriptor, typically used to transfer data from the CPU to the GPU.
*
* A BufferDescriptor owns the memory buffer it references, therefore BufferDescriptor cannot
* be copied, but can be moved.
*
* BufferDescriptor releases ownership of the memory-buffer when it's destroyed.
*/
class UTILS_PUBLIC BufferDescriptor {
public:
/**
* Callback used to destroy the buffer data.
* Guarantees:
* Called on the main filament thread.
*
* Limitations:
* Must be lightweight.
* Must not call filament APIs.
*/
using Callback = void(*)(void* buffer, size_t size, void* user);
//! creates an empty descriptor
BufferDescriptor() noexcept = default;
//! calls the callback to advertise BufferDescriptor no-longer owns the buffer
~BufferDescriptor() noexcept {
if (mCallback) {
mCallback(buffer, size, mUser);
}
}
BufferDescriptor(const BufferDescriptor& rhs) = delete;
BufferDescriptor& operator=(const BufferDescriptor& rhs) = delete;
BufferDescriptor(BufferDescriptor&& rhs) noexcept
: buffer(rhs.buffer), size(rhs.size),
mCallback(rhs.mCallback), mUser(rhs.mUser), mHandler(rhs.mHandler) {
rhs.buffer = nullptr;
rhs.mCallback = nullptr;
}
BufferDescriptor& operator=(BufferDescriptor&& rhs) noexcept {
if (this != &rhs) {
buffer = rhs.buffer;
size = rhs.size;
mCallback = rhs.mCallback;
mUser = rhs.mUser;
mHandler = rhs.mHandler;
rhs.buffer = nullptr;
rhs.mCallback = nullptr;
}
return *this;
}
/**
* Creates a BufferDescriptor that references a CPU memory-buffer
* @param buffer Memory address of the CPU buffer to reference
* @param size Size of the CPU buffer in bytes
* @param callback A callback used to release the CPU buffer from this BufferDescriptor
* @param user An opaque user pointer passed to the callback function when it's called
*/
BufferDescriptor(void const* buffer, size_t const size,
Callback const callback = nullptr, void* user = nullptr) noexcept
: buffer(const_cast<void*>(buffer)), size(size), mCallback(callback), mUser(user) {
}
/**
* Creates a BufferDescriptor that references a CPU memory-buffer
* @param buffer Memory address of the CPU buffer to reference
* @param size Size of the CPU buffer in bytes
* @param handler A custom handler for the callback
* @param callback A callback used to release the CPU buffer from this BufferDescriptor
* @param user An opaque user pointer passed to the callback function when it's called
*/
BufferDescriptor(void const* buffer, size_t const size,
CallbackHandler* handler, Callback const callback, void* user = nullptr) noexcept
: buffer(const_cast<void*>(buffer)), size(size),
mCallback(callback), mUser(user), mHandler(handler) {
}
// --------------------------------------------------------------------------------------------
/**
* Helper to create a BufferDescriptor that uses a KNOWN method pointer w/ object passed
* by pointer as the callback. e.g.:
* auto bd = BufferDescriptor::make<Foo, &Foo::method>(buffer, size, foo);
*
* @param buffer Memory address of the CPU buffer to reference
* @param size Size of the CPU buffer in bytes
* @param data A pointer to the data
* @param handler Handler to use to dispatch the callback, or nullptr for the default handler
* @return A new BufferDescriptor
*/
template<typename T, void(T::*method)(void const*, size_t)>
static BufferDescriptor make(void const* buffer, size_t size, T* data,
CallbackHandler* handler = nullptr) noexcept {
return {
buffer, size,
handler, [](void* b, size_t s, void* u) {
(static_cast<T*>(u)->*method)(b, s);
}, data
};
}
/**
* Helper to create a BufferDescriptor that uses a functor as the callback.
*
* Caveats:
* - DO NOT CALL setCallback() when using this helper.
* - This make a heap allocation
*
* @param buffer Memory address of the CPU buffer to reference
* @param size Size of the CPU buffer in bytes
* @param functor functor of type f(void const* buffer, size_t size)
* @param handler Handler to use to dispatch the callback, or nullptr for the default handler
* @return a new BufferDescriptor
*/
template<typename T>
static BufferDescriptor make(void const* buffer, size_t size, T&& functor,
CallbackHandler* handler = nullptr) noexcept {
return {
buffer, size,
handler, [](void* b, size_t s, void* u) {
T* const that = static_cast<T*>(u);
that->operator()(b, s);
delete that;
},
new T(std::forward<T>(functor))
};
}
// --------------------------------------------------------------------------------------------
/**
* Set or replace the release callback function
* @param callback The new callback function
* @param user An opaque user pointer passed to the callbeck function when it's called
*/
void setCallback(Callback const callback, void* user = nullptr) noexcept {
this->mCallback = callback;
this->mUser = user;
this->mHandler = nullptr;
}
/**
* Set or replace the release callback function
* @param handler The Handler to use to dispatch the callback
* @param callback The new callback function
* @param user An opaque user pointer passed to the callbeck function when it's called
*/
void setCallback(CallbackHandler* handler, Callback const callback, void* user = nullptr) noexcept {
mCallback = callback;
mUser = user;
mHandler = handler;
}
//! Returns whether a release callback is set
bool hasCallback() const noexcept { return mCallback != nullptr; }
//! Returns the currently set release callback function
Callback getCallback() const noexcept {
return mCallback;
}
//! Returns the handler for this callback or nullptr if the default handler is to be used.
CallbackHandler* getHandler() const noexcept {
return mHandler;
}
//! Returns the user opaque pointer associated to this BufferDescriptor
void* getUser() const noexcept {
return mUser;
}
//! CPU memory-buffer virtual address
void* buffer = nullptr;
//! CPU memory-buffer size in bytes
size_t size = 0;
private:
// callback when the buffer is consumed.
Callback mCallback = nullptr;
void* mUser = nullptr;
CallbackHandler* mHandler = nullptr;
};
} // namespace filament::backend
#if !defined(NDEBUG)
utils::io::ostream& operator<<(utils::io::ostream& out, const filament::backend::BufferDescriptor& b);
#endif
#endif // TNT_FILAMENT_BACKEND_BUFFERDESCRIPTOR_H

View File

@ -0,0 +1,84 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_CALLBACKHANDLER_H
#define TNT_FILAMENT_BACKEND_CALLBACKHANDLER_H
#ifdef __clang__
#pragma clang diagnostic push
// Disable the weak-vtables warning because we need the destructor to be inlined in the headers. It
// is not optimal (for build), but we have clients that compile their libraries (filament) and app
// in different rtti settings.
#pragma clang diagnostic ignored "-Wweak-vtables"
#endif
namespace filament::backend {
/**
* A generic interface to dispatch callbacks.
*
* All APIs that take a callback as argument also take a
* CallbackHandler* which is used to dispatch the
* callback: CallbackHandler::post() method is called from a service thread as soon
* as possible (this will NEVER be the main thread), CallbackHandler::post()
* is responsible for scheduling the callback onto the thread the
* user desires.
*
* This is intended to make callbacks interoperate with
* the platform/OS's own messaging system.
*
* CallbackHandler* can always be nullptr in which case the default handler is used. The
* default handler always dispatches callbacks on filament's main thread opportunistically.
*
* Life time:
* ---------
*
* Filament make no attempts to manage the life time of the CallbackHandler* and never takes
* ownership.
* In particular, this means that the CallbackHandler instance must stay valid until all
* pending callbacks are been dispatched.
*
* Similarly, when shutting down filament, care must be taken to ensure that all pending callbacks
* that might access filament's state have been dispatched. Filament can no longer ensure this
* because callback execution is the responsibility of the CallbackHandler, which is external to
* filament.
* Typically, the concrete CallbackHandler would have a mechanism to drain and/or wait for all
* callbacks to be processed.
*
*/
class CallbackHandler {
public:
using Callback = void(*)(void* user);
/**
* Schedules the callback to be called onto the appropriate thread.
* Typically this will be the application's main thead.
*
* Must be thread-safe.
*/
virtual void post(void* user, Callback callback) = 0;
protected:
virtual ~CallbackHandler() = default;
};
} // namespace filament::backend
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#endif // TNT_FILAMENT_BACKEND_CALLBACKHANDLER_H

View File

@ -0,0 +1,109 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_COMMANDSTREAMVECTOR_H
#define TNT_FILAMENT_BACKEND_COMMANDSTREAMVECTOR_H
#include <backend/DriverApiForward.h>
#include <initializer_list>
#include <memory>
#include <stddef.h>
#include <stdint.h>
namespace utils::io {
class ostream;
} // namespace utils::io
namespace filament::backend {
void* allocateFromCommandStream(DriverApi& driver, size_t size, size_t alignment) noexcept;
class DescriptorSetOffsetArray {
public:
using value_type = uint32_t;
using reference = value_type&;
using const_reference = value_type const&;
using size_type = uint32_t;
using difference_type = int32_t;
using pointer = value_type*;
using const_pointer = value_type const*;
using iterator = pointer;
using const_iterator = const_pointer;
DescriptorSetOffsetArray() noexcept = default;
~DescriptorSetOffsetArray() noexcept = default;
DescriptorSetOffsetArray(size_type size, DriverApi& driver) noexcept {
mOffsets = (value_type *)allocateFromCommandStream(driver,
size * sizeof(value_type), alignof(value_type));
std::uninitialized_fill_n(mOffsets, size, 0);
}
DescriptorSetOffsetArray(std::initializer_list<uint32_t> list, DriverApi& driver) noexcept {
mOffsets = (value_type *)allocateFromCommandStream(driver,
list.size() * sizeof(value_type), alignof(value_type));
std::uninitialized_copy(list.begin(), list.end(), mOffsets);
}
DescriptorSetOffsetArray(DescriptorSetOffsetArray const&) = delete;
DescriptorSetOffsetArray& operator=(DescriptorSetOffsetArray const&) = delete;
DescriptorSetOffsetArray(DescriptorSetOffsetArray&& rhs) noexcept
: mOffsets(rhs.mOffsets) {
rhs.mOffsets = nullptr;
}
DescriptorSetOffsetArray& operator=(DescriptorSetOffsetArray&& rhs) noexcept {
if (this != &rhs) {
mOffsets = rhs.mOffsets;
rhs.mOffsets = nullptr;
}
return *this;
}
bool empty() const noexcept { return mOffsets == nullptr; }
value_type* data() noexcept { return mOffsets; }
const value_type* data() const noexcept { return mOffsets; }
reference operator[](size_type n) noexcept {
return *(data() + n);
}
const_reference operator[](size_type n) const noexcept {
return *(data() + n);
}
void clear() noexcept {
mOffsets = nullptr;
}
private:
value_type *mOffsets = nullptr;
};
} // namespace filament::backend
#if !defined(NDEBUG)
utils::io::ostream& operator<<(utils::io::ostream& out, const filament::backend::DescriptorSetOffsetArray& rhs);
#endif
#endif //TNT_FILAMENT_BACKEND_COMMANDSTREAMVECTOR_H

View File

@ -0,0 +1,28 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_PRIVATE_DRIVERAPIFORWARD_H
#define TNT_FILAMENT_BACKEND_PRIVATE_DRIVERAPIFORWARD_H
namespace filament::backend {
class CommandStream;
using DriverApi = CommandStream;
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_PRIVATE_DRIVERAPIFORWARD_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,167 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_HANDLE_H
#define TNT_FILAMENT_BACKEND_HANDLE_H
#include <utils/debug.h>
#include <type_traits> // FIXME: STL headers are not allowed in public headers
#include <utility>
#include <stdint.h>
namespace utils::io {
class ostream;
} // namespace utils::io
namespace filament::backend {
struct HwBufferObject;
struct HwFence;
struct HwIndexBuffer;
struct HwProgram;
struct HwRenderPrimitive;
struct HwRenderTarget;
struct HwStream;
struct HwSwapChain;
struct HwSync;
struct HwTexture;
struct HwTimerQuery;
struct HwVertexBufferInfo;
struct HwVertexBuffer;
struct HwDescriptorSetLayout;
struct HwDescriptorSet;
struct HwMemoryMappedBuffer;
/*
* A handle to a backend resource. HandleBase is for internal use only.
* HandleBase *must* be a trivial for the purposes of calls, that is, it cannot have user-defined
* copy or move constructors.
*/
//! \privatesection
class HandleBase {
public:
using HandleId = uint32_t;
static constexpr const HandleId nullid = HandleId{ UINT32_MAX };
constexpr HandleBase() noexcept: object(nullid) {}
// whether this Handle is initialized
explicit operator bool() const noexcept { return object != nullid; }
// clear the handle, this doesn't free associated resources
void clear() noexcept { object = nullid; }
// get this handle's handleId
HandleId getId() const noexcept { return object; }
// initialize a handle, for internal use only.
explicit HandleBase(HandleId id) noexcept : object(id) {
assert_invariant(object != nullid); // usually means an uninitialized handle is used
}
protected:
HandleBase(HandleBase const& rhs) noexcept = default;
HandleBase& operator=(HandleBase const& rhs) noexcept = default;
HandleBase(HandleBase&& rhs) noexcept
: object(rhs.object) {
rhs.object = nullid;
}
HandleBase& operator=(HandleBase&& rhs) noexcept {
if (this != &rhs) {
object = rhs.object;
rhs.object = nullid;
}
return *this;
}
private:
HandleId object;
};
/**
* Type-safe handle to backend resources
* @tparam T Type of the resource
*/
template<typename T>
struct Handle : public HandleBase {
Handle() noexcept = default;
Handle(Handle const& rhs) noexcept = default;
Handle(Handle&& rhs) noexcept = default;
// Explicitly redefine copy/move assignment operators rather than just using default here.
// Because it doesn't make a call to the parent's method automatically during the std::move
// function call(https://en.cppreference.com/w/cpp/algorithm/move) in certain compilers like
// NDK 25.1.8937393 and below (see b/371980551)
Handle& operator=(Handle const& rhs) noexcept {
HandleBase::operator=(rhs);
return *this;
}
Handle& operator=(Handle&& rhs) noexcept {
HandleBase::operator=(std::move(rhs));
return *this;
}
explicit Handle(HandleId id) noexcept : HandleBase(id) { }
// compare handles of the same type
bool operator==(const Handle& rhs) const noexcept { return getId() == rhs.getId(); }
bool operator!=(const Handle& rhs) const noexcept { return getId() != rhs.getId(); }
bool operator<(const Handle& rhs) const noexcept { return getId() < rhs.getId(); }
bool operator<=(const Handle& rhs) const noexcept { return getId() <= rhs.getId(); }
bool operator>(const Handle& rhs) const noexcept { return getId() > rhs.getId(); }
bool operator>=(const Handle& rhs) const noexcept { return getId() >= rhs.getId(); }
// type-safe Handle cast
template<typename B, typename = std::enable_if_t<std::is_base_of_v<T, B>> >
Handle(Handle<B> const& base) noexcept : HandleBase(base) { } // NOLINT(hicpp-explicit-conversions,google-explicit-constructor)
private:
#if !defined(NDEBUG)
template <typename U>
friend utils::io::ostream& operator<<(utils::io::ostream& out, const Handle<U>& h) noexcept;
#endif
};
// Types used by the command stream
// (we use this renaming because the macro-system doesn't deal well with "<" and ">")
using BufferObjectHandle = Handle<HwBufferObject>;
using FenceHandle = Handle<HwFence>;
using IndexBufferHandle = Handle<HwIndexBuffer>;
using ProgramHandle = Handle<HwProgram>;
using RenderPrimitiveHandle = Handle<HwRenderPrimitive>;
using RenderTargetHandle = Handle<HwRenderTarget>;
using StreamHandle = Handle<HwStream>;
using SwapChainHandle = Handle<HwSwapChain>;
using SyncHandle = Handle<HwSync>;
using TextureHandle = Handle<HwTexture>;
using TimerQueryHandle = Handle<HwTimerQuery>;
using VertexBufferHandle = Handle<HwVertexBuffer>;
using VertexBufferInfoHandle = Handle<HwVertexBufferInfo>;
using DescriptorSetLayoutHandle = Handle<HwDescriptorSetLayout>;
using DescriptorSetHandle = Handle<HwDescriptorSet>;
using MemoryMappedBufferHandle = Handle<HwMemoryMappedBuffer>;
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_HANDLE_H

View File

@ -0,0 +1,57 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_PIPELINESTATE_H
#define TNT_FILAMENT_BACKEND_PIPELINESTATE_H
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <array>
#include <stdint.h>
namespace utils::io {
class ostream;
} // namespace utils::io
namespace filament::backend {
//! \privatesection
struct PipelineLayout {
using SetLayout = std::array<Handle<HwDescriptorSetLayout>, MAX_DESCRIPTOR_SET_COUNT>;
SetLayout setLayout; // 16
};
struct PipelineState {
Handle<HwProgram> program; // 4
Handle<HwVertexBufferInfo> vertexBufferInfo; // 4
PipelineLayout pipelineLayout; // 16
RasterState rasterState; // 4
StencilState stencilState; // 12
PolygonOffset polygonOffset; // 8
PrimitiveType primitiveType = PrimitiveType::TRIANGLES; // 1
uint8_t padding[3] = {}; // 3
};
} // namespace filament::backend
#if !defined(NDEBUG)
utils::io::ostream& operator<<(utils::io::ostream& out, const filament::backend::PipelineState& ps);
#endif
#endif //TNT_FILAMENT_BACKEND_PIPELINESTATE_H

View File

@ -0,0 +1,332 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_BACKEND_PIXELBUFFERDESCRIPTOR_H
#define TNT_FILAMENT_BACKEND_PIXELBUFFERDESCRIPTOR_H
#include <backend/BufferDescriptor.h>
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <stddef.h>
#include <stdint.h>
namespace utils::io {
class ostream;
} // namespace utils::io
namespace filament::backend {
/**
* A descriptor to an image in main memory, typically used to transfer image data from the CPU
* to the GPU.
*
* A PixelBufferDescriptor owns the memory buffer it references, therefore PixelBufferDescriptor
* cannot be copied, but can be moved.
*
* PixelBufferDescriptor releases ownership of the memory-buffer when it's destroyed.
*/
class UTILS_PUBLIC PixelBufferDescriptor : public BufferDescriptor {
public:
using PixelDataFormat = backend::PixelDataFormat;
using PixelDataType = backend::PixelDataType;
PixelBufferDescriptor() = default;
/**
* Creates a new PixelBufferDescriptor referencing an image in main memory
*
* @param buffer Virtual address of the buffer containing the image
* @param size Size in bytes of the buffer containing the image
* @param format Format of the image pixels
* @param type Type of the image pixels
* @param alignment Alignment in bytes of pixel rows
* @param left Left coordinate in pixels
* @param top Top coordinate in pixels
* @param stride Stride of a row in pixels
* @param handler Handler to dispatch the callback or nullptr for the default handler
* @param callback A callback used to release the CPU buffer
* @param user An opaque user pointer passed to the callback function when it's called
*/
PixelBufferDescriptor(void const* buffer, size_t size,
PixelDataFormat format, PixelDataType type, uint8_t alignment,
uint32_t left, uint32_t top, uint32_t stride,
CallbackHandler* handler, Callback callback, void* user = nullptr) noexcept
: BufferDescriptor(buffer, size, handler, callback, user),
left(left), top(top), stride(stride),
format(format), type(type), alignment(alignment) {
}
PixelBufferDescriptor(void const* buffer, size_t size,
PixelDataFormat format, PixelDataType type, uint8_t alignment = 1,
uint32_t left = 0, uint32_t top = 0, uint32_t stride = 0,
Callback callback = nullptr, void* user = nullptr) noexcept
: BufferDescriptor(buffer, size, callback, user),
left(left), top(top), stride(stride),
format(format), type(type), alignment(alignment) {
}
/**
* Creates a new PixelBufferDescriptor referencing an image in main memory
*
* @param buffer Virtual address of the buffer containing the image
* @param size Size in bytes of the buffer containing the image
* @param format Format of the image pixels
* @param type Type of the image pixels
* @param handler Handler to dispatch the callback or nullptr for the default handler
* @param callback A callback used to release the CPU buffer
* @param user An opaque user pointer passed to the callback function when it's called
*/
PixelBufferDescriptor(void const* buffer, size_t size,
PixelDataFormat format, PixelDataType type,
CallbackHandler* handler, Callback callback, void* user = nullptr) noexcept
: BufferDescriptor(buffer, size, handler, callback, user),
stride(0), format(format), type(type), alignment(1) {
}
PixelBufferDescriptor(void const* buffer, size_t size,
PixelDataFormat format, PixelDataType type,
Callback callback, void* user = nullptr) noexcept
: BufferDescriptor(buffer, size, callback, user),
stride(0), format(format), type(type), alignment(1) {
}
/**
* Creates a new PixelBufferDescriptor referencing a compressed image in main memory
*
* @param buffer Virtual address of the buffer containing the image
* @param size Size in bytes of the buffer containing the image
* @param format Compressed format of the image
* @param imageSize Compressed size of the image
* @param handler Handler to dispatch the callback or nullptr for the default handler
* @param callback A callback used to release the CPU buffer
* @param user An opaque user pointer passed to the callback function when it's called
*/
PixelBufferDescriptor(void const* buffer, size_t size,
backend::CompressedPixelDataType format, uint32_t imageSize,
CallbackHandler* handler, Callback callback, void* user = nullptr) noexcept
: BufferDescriptor(buffer, size, handler, callback, user),
imageSize(imageSize), compressedFormat(format), type(PixelDataType::COMPRESSED),
alignment(1) {
}
PixelBufferDescriptor(void const* buffer, size_t size,
backend::CompressedPixelDataType format, uint32_t imageSize,
Callback callback, void* user = nullptr) noexcept
: BufferDescriptor(buffer, size, callback, user),
imageSize(imageSize), compressedFormat(format), type(PixelDataType::COMPRESSED),
alignment(1) {
}
// --------------------------------------------------------------------------------------------
template<typename T, void(T::*method)(void const*, size_t)>
static PixelBufferDescriptor make(void const* buffer, size_t size,
PixelDataFormat format, PixelDataType type, uint8_t alignment,
uint32_t left, uint32_t top, uint32_t stride, T* data,
CallbackHandler* handler = nullptr) noexcept {
return { buffer, size, format, type, alignment, left, top, stride,
handler, [](void* b, size_t s, void* u) {
(static_cast<T*>(u)->*method)(b, s); }, data };
}
template<typename T, void(T::*method)(void const*, size_t)>
static PixelBufferDescriptor make(void const* buffer, size_t size,
PixelDataFormat format, PixelDataType type, T* data,
CallbackHandler* handler = nullptr) noexcept {
return { buffer, size, format, type, handler, [](void* b, size_t s, void* u) {
(static_cast<T*>(u)->*method)(b, s); }, data };
}
template<typename T, void(T::*method)(void const*, size_t)>
static PixelBufferDescriptor make(void const* buffer, size_t size,
backend::CompressedPixelDataType format, uint32_t imageSize, T* data,
CallbackHandler* handler = nullptr) noexcept {
return { buffer, size, format, imageSize, handler, [](void* b, size_t s, void* u) {
(static_cast<T*>(u)->*method)(b, s); }, data
};
}
template<typename T>
static PixelBufferDescriptor make(void const* buffer, size_t size,
PixelDataFormat format, PixelDataType type, uint8_t alignment,
uint32_t left, uint32_t top, uint32_t stride, T&& functor,
CallbackHandler* handler = nullptr) noexcept {
return { buffer, size, format, type, alignment, left, top, stride,
handler, [](void* b, size_t s, void* u) {
T* const that = static_cast<T*>(u);
that->operator()(b, s);
delete that;
}, new T(std::forward<T>(functor))
};
}
template<typename T>
static PixelBufferDescriptor make(void const* buffer, size_t size,
PixelDataFormat format, PixelDataType type, T&& functor,
CallbackHandler* handler = nullptr) noexcept {
return { buffer, size, format, type,
handler, [](void* b, size_t s, void* u) {
T* const that = static_cast<T*>(u);
that->operator()(b, s);
delete that;
}, new T(std::forward<T>(functor))
};
}
template<typename T>
static PixelBufferDescriptor make(void const* buffer, size_t size,
backend::CompressedPixelDataType format, uint32_t imageSize, T&& functor,
CallbackHandler* handler = nullptr) noexcept {
return { buffer, size, format, imageSize,
handler, [](void* b, size_t s, void* u) {
T* const that = static_cast<T*>(u);
that->operator()(b, s);
delete that;
}, new T(std::forward<T>(functor))
};
}
/**
* Computes the size in bytes for a pixel of given dimensions and format
*
* @param format Format of the image pixels
* @param type Type of the image pixels
* @return The size of the specified pixel in bytes
*/
static constexpr size_t computePixelSize(PixelDataFormat format, PixelDataType type) noexcept {
if (type == PixelDataType::COMPRESSED) {
return 0;
}
size_t n = 0;
switch (format) {
case PixelDataFormat::R:
case PixelDataFormat::R_INTEGER:
case PixelDataFormat::DEPTH_COMPONENT:
case PixelDataFormat::ALPHA:
n = 1;
break;
case PixelDataFormat::RG:
case PixelDataFormat::RG_INTEGER:
case PixelDataFormat::DEPTH_STENCIL:
n = 2;
break;
case PixelDataFormat::RGB:
case PixelDataFormat::RGB_INTEGER:
n = 3;
break;
case PixelDataFormat::UNUSED:// shouldn't happen (used to be rgbm)
case PixelDataFormat::RGBA:
case PixelDataFormat::RGBA_INTEGER:
n = 4;
break;
}
size_t bpp = n;
switch (type) {
case PixelDataType::COMPRESSED:// Impossible -- to squash the IDE warnings
case PixelDataType::UBYTE:
case PixelDataType::BYTE:
// nothing to do
break;
case PixelDataType::USHORT:
case PixelDataType::SHORT:
case PixelDataType::HALF:
bpp *= 2;
break;
case PixelDataType::UINT:
case PixelDataType::INT:
case PixelDataType::FLOAT:
bpp *= 4;
break;
case PixelDataType::UINT_10F_11F_11F_REV:
// Special case, format must be RGB and uses 4 bytes
assert_invariant(format == PixelDataFormat::RGB);
bpp = 4;
break;
case PixelDataType::UINT_2_10_10_10_REV:
// Special case, format must be RGBA and uses 4 bytes
assert_invariant(format == PixelDataFormat::RGBA);
bpp = 4;
break;
case PixelDataType::USHORT_565:
// Special case, format must be RGB and uses 2 bytes
assert_invariant(format == PixelDataFormat::RGB);
bpp = 2;
break;
}
return bpp;
}
// --------------------------------------------------------------------------------------------
/**
* Computes the size in bytes needed to fit an image of given dimensions and format
*
* @param format Format of the image pixels
* @param type Type of the image pixels
* @param stride Stride of a row in pixels
* @param height Height of the image in rows
* @param alignment Alignment in bytes of pixel rows
* @return The buffer size needed to fit this image in bytes
*/
static constexpr size_t computeDataSize(PixelDataFormat format, PixelDataType type,
size_t stride, size_t height, size_t alignment) noexcept {
assert_invariant(alignment);
size_t bpp = computePixelSize(format, type);
size_t const bpr = bpp * stride;
size_t const bprAligned = (bpr + (alignment - 1)) & (~alignment + 1);
return bprAligned * height;
}
//! left coordinate in pixels
uint32_t left = 0;
//! top coordinate in pixels
uint32_t top = 0;
union {
struct {
//! stride in pixels
uint32_t stride;
//! Pixel data format
PixelDataFormat format;
};
struct {
//! compressed image size
uint32_t imageSize;
//! compressed image format
backend::CompressedPixelDataType compressedFormat;
};
};
//! pixel data type
PixelDataType type : 4;
//! row alignment in bytes
uint8_t alignment : 4;
};
} // namespace backend::filament
#if !defined(NDEBUG)
utils::io::ostream& operator<<(utils::io::ostream& out, const filament::backend::PixelBufferDescriptor& b);
#endif
#endif // TNT_FILAMENT_BACKEND_PIXELBUFFERDESCRIPTOR_H

View File

@ -0,0 +1,645 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_BACKEND_PLATFORM_H
#define TNT_FILAMENT_BACKEND_PLATFORM_H
#include <utils/CString.h>
#include <utils/compiler.h>
#include <utils/Invocable.h>
#include <utils/Mutex.h>
#include <stddef.h>
#include <stdint.h>
#include <atomic>
#include <memory>
#include <mutex>
namespace utils {
class FeatureFlagManager;
}
namespace filament::backend {
class CallbackHandler;
class Driver;
/**
* Platform is an interface that abstracts how the backend (also referred to as Driver) is
* created. The backend provides several common Platform concrete implementations, which are
* selected automatically. It is possible however to provide a custom Platform when creating
* the filament Engine.
*/
class UTILS_PUBLIC Platform {
public:
struct SwapChain {};
struct Fence {};
struct Stream {};
struct Sync {};
using SyncCallback = void(*)(Sync* UTILS_NONNULL sync, void* UTILS_NULLABLE userData);
class ExternalImageHandle;
class ExternalImage {
friend class ExternalImageHandle;
std::atomic_uint32_t mRefCount{0};
protected:
virtual ~ExternalImage() noexcept;
};
class ExternalImageHandle {
ExternalImage* UTILS_NULLABLE mTarget = nullptr;
static void incref(ExternalImage* UTILS_NULLABLE p) noexcept;
static void decref(ExternalImage* UTILS_NULLABLE p) noexcept;
public:
ExternalImageHandle() noexcept;
~ExternalImageHandle() noexcept;
explicit ExternalImageHandle(ExternalImage* UTILS_NULLABLE p) noexcept;
ExternalImageHandle(ExternalImageHandle const& rhs) noexcept;
ExternalImageHandle(ExternalImageHandle&& rhs) noexcept;
ExternalImageHandle& operator=(ExternalImageHandle const& rhs) noexcept;
ExternalImageHandle& operator=(ExternalImageHandle&& rhs) noexcept;
bool operator==(const ExternalImageHandle& rhs) const noexcept {
return mTarget == rhs.mTarget;
}
explicit operator bool() const noexcept { return mTarget != nullptr; }
ExternalImage* UTILS_NULLABLE get() noexcept { return mTarget; }
ExternalImage const* UTILS_NULLABLE get() const noexcept { return mTarget; }
ExternalImage* UTILS_NULLABLE operator->() noexcept { return mTarget; }
ExternalImage const* UTILS_NULLABLE operator->() const noexcept { return mTarget; }
ExternalImage& operator*() noexcept { return *mTarget; }
ExternalImage const& operator*() const noexcept { return *mTarget; }
void clear() noexcept;
void reset(ExternalImage* UTILS_NULLABLE p) noexcept;
private:
friend utils::io::ostream& operator<<(utils::io::ostream& out,
ExternalImageHandle const& handle);
};
using ExternalImageHandleRef = ExternalImageHandle const&;
struct CompositorTiming {
/** duration in nanosecond since epoch of std::steady_clock */
using time_point_ns = int64_t;
/** duration in nanosecond on the std::steady_clock */
using duration_ns = int64_t;
static constexpr time_point_ns INVALID = -1; //!< value not supported
/**
* The time delta [ns] between subsequent composition events.
*/
duration_ns compositeInterval;
/**
* The timestamp [ns] since epoch of the next time the compositor will begin composition.
* This is effectively the deadline for when the compositor must receive a newly queued
* frame.
*/
time_point_ns compositeDeadline;
/**
* The time delta [ns] between the start of composition and the expected present time of
* that composition. This can be used to estimate the latency of the actual present time.
*/
duration_ns compositeToPresentLatency;
/**
* Expected latency [ns] of frame presentation relative to vsync.
*/
duration_ns expectedPresentLatency;
};
struct FrameTimestamps {
/** duration in nanosecond since epoch of std::steady_clock */
using time_point_ns = int64_t;
static constexpr time_point_ns INVALID = -1; //!< value not supported
static constexpr time_point_ns PENDING = -2; //!< value not yet available
/**
* The time the application requested this frame be presented.
* If the application does not request a presentation time explicitly,
* this will correspond to buffer's queue time.
*/
time_point_ns requestedPresentTime;
/**
* The time when all the application's rendering to the surface was completed.
*/
time_point_ns acquireTime;
/**
* The time when the compositor selected this frame as the one to use for the next
* composition. This is the earliest indication that the frame was submitted in time.
*/
time_point_ns latchTime;
/**
* The first time at which the compositor began preparing composition for this frame.
* Zero if composition was handled by the display and the compositor didn't do any
* rendering.
*/
time_point_ns firstCompositionStartTime;
/**
* The last time at which the compositor began preparing composition for this frame, for
* frames composited more than once. Zero if composition was handled by the display and the
* compositor didn't do any rendering.
*/
time_point_ns lastCompositionStartTime;
/**
* The time at which the compositor's rendering work for this frame finished. This will be
* INVALID if composition was handled by the display and the compositor didn't do any
* rendering.
*/
time_point_ns gpuCompositionDoneTime;
/**
* The time at which this frame started to scan out to the physical display.
*/
time_point_ns displayPresentTime;
/**
* The time when the buffer became available for reuse as a buffer the client can target
* without blocking. This is generally the point when all read commands of the buffer have
* been submitted, but not necessarily completed.
*/
time_point_ns dequeueReadyTime;
/**
* The time at which all reads for the purpose of display/composition were completed for
* this frame.
*/
time_point_ns releaseTime;
};
/**
* The type of technique for stereoscopic rendering. (Note that the materials used will need to
* be compatible with the chosen technique.)
*/
enum class StereoscopicType : uint8_t {
/**
* No stereoscopic rendering
*/
NONE,
/**
* Stereoscopic rendering is performed using instanced rendering technique.
*/
INSTANCED,
/**
* Stereoscopic rendering is performed using the multiview feature from the graphics
* backend.
*/
MULTIVIEW,
};
/**
* Types of device/driver information that can be queried from the platform.
*/
enum class DeviceInfoType {
OPENGL_RENDERER, //!< glGetString(GL_RENDERER)
OPENGL_VENDOR, //!< glGetString(GL_VENDOR)
OPENGL_VERSION, //!< glGetString(GL_VERSION)
VULKAN_DEVICE_NAME, //!< VkPhysicalDeviceProperties::deviceName
VULKAN_DRIVER_NAME, //!< VkPhysicalDeviceDriverProperties::driverName
VULKAN_DRIVER_INFO, //!< VkPhysicalDeviceDriverProperties::driverInfo
};
/**
* This controls the priority level for GPU work scheduling, which helps prioritize the
* submitted GPU work and enables preemption.
*/
enum class GpuContextPriority : uint8_t {
/**
* Backend default GPU context priority (typically MEDIUM)
*/
DEFAULT,
/**
* For non-interactive, deferrable workloads. This should not interfere with standard
* applications.
*/
LOW,
/**
* The default priority level for standard applications.
*/
MEDIUM,
/**
* For high-priority, latency-sensitive workloads that are more important than standard
* applications.
*/
HIGH,
/**
* The highest priority, intended for system-critical, real-time applications where missing
* deadlines is unacceptable (e.g., VR/AR compositors or other system-critical tasks).
*/
REALTIME,
};
/**
* Defines how asynchronous operations are handled by the engine.
*/
enum class AsynchronousMode : uint8_t {
/**
* Asynchronous operations are disabled. This is the default.
*/
NONE,
/**
* Attempts to use a dedicated worker thread for asynchronous tasks. If threading is not
* supported by the platform, it automatically falls back to using an amortization strategy.
*/
THREAD_PREFERRED,
/**
* Uses an amortization strategy, processing a small number of asynchronous tasks during
* each engine update cycle.
*/
AMORTIZATION,
};
struct DriverConfig {
/**
* Reference to the system's FeatureFlagManager. Can be nullptr.
*/
utils::FeatureFlagManager const * UTILS_NULLABLE featureFlagManager = nullptr;
/**
* Size of handle arena in bytes. Setting to 0 indicates default value is to be used.
* Driver clamps to valid values.
*/
size_t handleArenaSize = 0;
size_t metalUploadBufferSizeBytes = 512 * 1024;
/**
* Set to `true` to forcibly disable parallel shader compilation in the backend.
* Currently only honored by the GL and Metal backends, and the Vulkan backend
* when some experimental features are enabled.
*/
bool disableParallelShaderCompile = false;
/**
* Set to `true` to forcibly disable amortized shader compilation in the backend.
* Currently only honored by the GL backend.
*/
bool disableAmortizedShaderCompile = true;
/**
* Disable backend handles use-after-free checks.
*/
bool disableHandleUseAfterFreeCheck = false;
/**
* Disable backend handles tags for heap allocated (fallback) handles
*/
bool disableHeapHandleTags = false;
/**
* Force GLES2 context if supported, or pretend the context is ES2. Only meaningful on
* GLES 3.x backends.
*/
bool forceGLES2Context = false;
/**
* Sets the technique for stereoscopic rendering.
*/
StereoscopicType stereoscopicType = StereoscopicType::NONE;
/**
* The number of eyes to render when stereoscopic rendering is enabled. Supported values are
* between 1 and Engine::getMaxStereoscopicEyes() (inclusive).
*/
uint8_t stereoscopicEyeCount = 2;
/**
* Assert the native window associated to a SwapChain is valid when calling makeCurrent().
* This is only supported for:
* - PlatformEGLAndroid
*/
bool assertNativeWindowIsValid = false;
/**
* The action to take if a Drawable cannot be acquired. If true, the
* frame is aborted instead of panic. This is only supported for:
* - PlatformMetal
*/
bool metalDisablePanicOnDrawableFailure = false;
/**
* GPU context priority level. Controls GPU work scheduling and preemption.
* This is only supported for:
* - PlatformEGL
*/
GpuContextPriority gpuContextPriority = GpuContextPriority::DEFAULT;
/**
* Enables asynchronous pipeline cache preloading, if supported on this device.
* This is only supported for:
* - VulkanPlatform
* When the following device extensions are available:
* - VK_KHR_dynamic_rendering
* - VK_EXT_vertex_input_dynamic_state
* Should be enabled only for devices where it has been shown this is effective.
*/
bool vulkanEnableAsyncPipelineCachePrewarming = false;
/**
* Bypass the staging buffer because the device is of Unified Memory Architecture.
* This is only supported for:
* - VulkanPlatform
*/
bool vulkanEnableStagingBufferBypass = false;
/**
* Asynchronous mode for the engine. Defines how asynchronous operations are handled.
*/
AsynchronousMode asynchronousMode = AsynchronousMode::NONE;
};
Platform() noexcept;
virtual ~Platform() noexcept;
/**
* Queries the underlying OS version.
* @return The OS version.
*/
virtual int getOSVersion() const noexcept = 0;
/**
* Queries device/driver information of the graphics API.
* @param infoType the type of information to query.
* @param driver a pointer to the current driver.
* @return a CString containing the requested information.
*/
virtual utils::CString getDeviceInfo(DeviceInfoType infoType,
Driver* UTILS_NULLABLE driver) const noexcept = 0;
/**
* Creates and initializes the low-level API (e.g. an OpenGL context or Vulkan instance),
* then creates the concrete Driver.
* The caller takes ownership of the returned Driver* and must destroy it with delete.
*
* @param sharedContext an optional shared context. This is not meaningful with all graphic
* APIs and platforms.
* For EGL platforms, this is an EGLContext.
*
* @param driverConfig specifies driver initialization parameters
*
* @return nullptr on failure, or a pointer to the newly created driver.
*/
virtual Driver* UTILS_NULLABLE createDriver(void* UTILS_NULLABLE sharedContext,
const DriverConfig& driverConfig) = 0;
/**
* Processes the platform's event queue when called from its primary event-handling thread.
*
* Internally, Filament might need to call this when waiting on a fence. It is only implemented
* on platforms that need it, such as macOS + OpenGL. Returns false if this is not the main
* thread, or if the platform does not need to perform any special processing.
*/
virtual bool pumpEvents() noexcept;
// --------------------------------------------------------------------------------------------
// Swapchain timing APIs
/**
* Whether this platform supports compositor timing querying.
*
* @return true if this Platform supports compositor timings, false otherwise [default]
* @see queryCompositorTiming()
* @see setPresentFrameId()
* @see queryFrameTimestamps()
*/
virtual bool isCompositorTimingSupported() const noexcept;
/**
* If compositor timing is supported, fills the provided CompositorTiming structure
* with timing information form the compositor the swapchain's native window is using.
* The swapchain'snative window must be valid (i.e. not a headless swapchain).
* @param swapchain to query the compositor timing from
* @return true on success, false otherwise (e.g. if not supported)
* @see isCompositorTimingSupported()
*/
virtual bool queryCompositorTiming(SwapChain const* UTILS_NONNULL swapchain,
CompositorTiming* UTILS_NONNULL outCompositorTiming) const noexcept;
/**
* Associate a generic frameId which must be monotonically increasing (albeit not strictly) with
* the next frame to be presented on the specified swapchain.
*
* This must be called from the backend thread.
*
* @param swapchain
* @param frameId
* @return true on success, false otherwise
* @see isCompositorTimingSupported()
* @see queryFrameTimestamps()
*/
virtual bool setPresentFrameId(SwapChain const* UTILS_NONNULL swapchain,
uint64_t frameId) noexcept;
/**
* If compositor timing is supported, fills the provided FrameTimestamps structure
* with timing information of a given frame, identified by the frame id, of the specified
* swapchain. The system only keeps a limited history of frames timings.
*
* This API is thread safe and can be called from any thread.
*
* @param swapchain swapchain to query the timestamps of
* @param frameId frame we're interested it
* @param outFrameTimestamps output structure receiving the timestamps
* @return true if successful, false otherwise
* @see isCompositorTimingSupported()
* @see setPresentFrameId()
*/
virtual bool queryFrameTimestamps(SwapChain const* UTILS_NONNULL swapchain,
uint64_t frameId, FrameTimestamps* UTILS_NONNULL outFrameTimestamps) const noexcept;
// --------------------------------------------------------------------------------------------
// Caching APIs
/**
* InsertBlobFunc is an Invocable to an application-provided function that a
* backend implementation may use to insert a key/value pair into the
* cache.
*/
using InsertBlobFunc = utils::Invocable<
void(const void* UTILS_NONNULL key, size_t keySize,
const void* UTILS_NONNULL value, size_t valueSize)>;
/*
* RetrieveBlobFunc is an Invocable to an application-provided function that a
* backend implementation may use to retrieve a cached value from the
* cache.
*/
using RetrieveBlobFunc = utils::Invocable<
size_t(const void* UTILS_NONNULL key, size_t keySize,
void* UTILS_NONNULL value, size_t valueSize)>;
/**
* Sets the callback functions that the backend can use to interact with caching functionality
* provided by the application.
*
* Cache functions may only be specified once during the lifetime of a
* Platform. The <insert> and <retrieve> Invocables may be called at any time and
* from any thread from the time at which setBlobFunc is called until the time that Platform
* is destroyed. Concurrent calls to these functions from different threads is also allowed.
* Either function can be null.
*
* @param insertBlob an Invocable that inserts a new value into the cache and associates
* it with the given key
* @param retrieveBlob an Invocable that retrieves from the cache the value associated with a
* given key
*/
void setBlobFunc(InsertBlobFunc&& insertBlob, RetrieveBlobFunc&& retrieveBlob) noexcept;
/**
* @return true if insertBlob is valid.
*/
bool hasInsertBlobFunc() const noexcept;
/**
* @return true if retrieveBlob is valid.
*/
bool hasRetrieveBlobFunc() const noexcept;
/**
* @return true if either of insertBlob or retrieveBlob are valid.
*/
bool hasBlobFunc() const noexcept {
return hasInsertBlobFunc() || hasRetrieveBlobFunc();
}
/**
* To insert a new binary value into the cache and associate it with a given
* key, the backend implementation can call the application-provided callback
* function insertBlob.
*
* No guarantees are made as to whether a given key/value pair is present in
* the cache after the set call. If a different value has been associated
* with the given key in the past then it is undefined which value, if any, is
* associated with the key after the set call. Note that while there are no
* guarantees, the cache implementation should attempt to cache the most
* recently set value for a given key.
*
* @param key pointer to the beginning of the key data that is to be inserted
* @param keySize specifies the size in byte of the data pointed to by <key>
* @param value pointer to the beginning of the value data that is to be inserted
* @param valueSize specifies the size in byte of the data pointed to by <value>
*/
void insertBlob(const void* UTILS_NONNULL key, size_t keySize,
const void* UTILS_NONNULL value, size_t valueSize);
/**
* To retrieve the binary value associated with a given key from the cache, a
* the backend implementation can call the application-provided callback
* function retrieveBlob.
*
* If the cache contains a value for the given key and its size in bytes is
* less than or equal to <valueSize> then the value is written to the memory
* pointed to by <value>. Otherwise nothing is written to the memory pointed
* to by <value>.
*
* @param key pointer to the beginning of the key
* @param keySize specifies the size in bytes of the binary key pointed to by <key>
* @param value pointer to a buffer to receive the cached binary data, if it exists
* @param valueSize specifies the size in bytes of the memory pointed to by <value>
* @return If the cache contains a value associated with the given key then the
* size of that binary value in bytes is returned. Otherwise 0 is returned.
*/
size_t retrieveBlob(const void* UTILS_NONNULL key, size_t keySize,
void* UTILS_NONNULL value, size_t valueSize);
// --------------------------------------------------------------------------------------------
// Debugging APIs
using DebugUpdateStatFunc = utils::Invocable<void(const char* UTILS_NONNULL key,
uint64_t intValue, utils::CString stringValue)>;
/**
* Sets the callback function that the backend can use to update backend-specific statistics
* to aid with debugging. This callback can be called on either the Filament main thread or
* the Filament driver thread.
*
* The callback signature is (key, intValue, stringValue). Note that for any given call,
* only one of the value parameters (intValue or stringValue) will be meaningful, depending on
* the specific key.
*
* IMPORTANT_NOTE: because the callback can be called on the driver thread, only quick,
* non-blocking work should be done inside it. Furthermore, no graphics API calls (such as GL
* calls) should be made, which could interfere with Filament's driver state. Lastly, the
* callback implementation must be synchronized (thread-safe) since it can be called from
* either thread.
*
* @param debugUpdateStat an Invocable that updates debug statistics
*/
void setDebugUpdateStatFunc(DebugUpdateStatFunc&& debugUpdateStat) noexcept;
/**
* @return true if debugUpdateStat is valid.
*/
bool hasDebugUpdateStatFunc() const noexcept;
/**
* To track backend-specific statistics, the backend implementation can call the
* application-provided callback function debugUpdateStatFunc to associate or update a value
* with a given key. It is possible for this function to be called multiple times with the
* same key, in which case newer values should overwrite older values.
*
* This function can be called on either the Filament main thread or the Filament driver thread.
*
* @param key a null-terminated C-string with the key of the debug statistic
* @param intValue the updated integer value of key (the string value passed to the
* callback will be empty)
*/
void debugUpdateStat(const char* UTILS_NONNULL key, uint64_t intValue);
/**
* To track backend-specific statistics, the backend implementation can call the
* application-provided callback function debugUpdateStatFunc to associate or update a value
* with a given key. It is possible for this function to be called multiple times with the
* same key, in which case newer values should overwrite older values.
*
* This function can be called on either the Filament main thread or the Filament driver thread.
*
* @param key a null-terminated C-string with the key of the debug statistic
* @param stringValue the updated string value of key (the integer value passed to the
* callback will be 0)
*/
void debugUpdateStat(const char* UTILS_NONNULL key, utils::CString stringValue);
private:
std::shared_ptr<InsertBlobFunc> mInsertBlob;
std::shared_ptr<RetrieveBlobFunc> mRetrieveBlob;
std::shared_ptr<DebugUpdateStatFunc> mDebugUpdateStat;
mutable utils::Mutex mMutex;
};
} // namespace filament
#endif // TNT_FILAMENT_BACKEND_PLATFORM_H

View File

@ -0,0 +1,102 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_BACKEND_PRESENTCALLABLE
#define TNT_FILAMENT_BACKEND_PRESENTCALLABLE
#include <utils/compiler.h>
namespace filament::backend {
/**
* A PresentCallable is a callable object that, when called, schedules a frame for presentation on
* a SwapChain.
*
* Typically, Filament's backend is responsible for scheduling a frame's presentation. However,
* there are certain cases where the application might want to control when a frame is scheduled for
* presentation.
*
* For example, on iOS, UIKit elements can be synchronized to 3D content by scheduling a present
* within a CATransaction:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* void myFrameScheduledCallback(PresentCallable presentCallable, void* user) {
* [CATransaction begin];
* // Update other UI elements...
* presentCallable();
* [CATransaction commit];
* }
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* To obtain a PresentCallable, set a SwapChain::FrameScheduledCallback on a SwapChain with the
* SwapChain::setFrameScheduledCallback method. The callback is called with a PresentCallable object
* and optional user data:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* swapChain->setFrameScheduledCallback(nullptr, myFrameScheduledCallback);
* if (renderer->beginFrame(swapChain)) {
* renderer->render(view);
* renderer->endFrame();
* }
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* @remark The PresentCallable mechanism for user-controlled presentation is only supported by
* Filament's Metal backend. On other backends, the FrameScheduledCallback is still invoked, but the
* PresentCallable passed to it is a no-op and calling it has no effect.
*
* When using the Metal backend, applications *must* call each PresentCallable they receive. Each
* PresentCallable represents a frame that is waiting to be presented, and failing to call it
* will result in a memory leak. To "cancel" the presentation of a frame, pass false to the
* PresentCallable, which will cancel the presentation of the frame and release associated memory.
*
* @see Renderer, SwapChain::setFrameScheduledCallback
*/
class UTILS_PUBLIC PresentCallable {
public:
using PresentFn = void(*)(bool presentFrame, void* user);
static void noopPresent(bool, void*) {}
PresentCallable(PresentFn fn, void* user) noexcept;
~PresentCallable() noexcept = default;
PresentCallable(const PresentCallable& rhs) = default;
PresentCallable& operator=(const PresentCallable& rhs) = default;
/**
* Call this PresentCallable, scheduling the associated frame for presentation. Pass false for
* presentFrame to effectively "cancel" the presentation of the frame.
*
* @param presentFrame if false, will not present the frame but releases associated memory
*/
void operator()(bool presentFrame = true) noexcept;
private:
PresentFn mPresentFn;
void* mUser = nullptr;
};
/**
* @deprecated, FrameFinishedCallback has been renamed to SwapChain::FrameScheduledCallback.
*/
using FrameFinishedCallback UTILS_DEPRECATED = void(*)(PresentCallable callable, void* user);
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_PRESENTCALLABLE

View File

@ -0,0 +1,213 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_PRIVATE_PROGRAM_H
#define TNT_FILAMENT_BACKEND_PRIVATE_PROGRAM_H
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Invocable.h>
#include <utils/Slice.h>
#include <backend/DriverEnums.h>
#include <array>
#include <tuple>
#include <utility>
#include <variant>
#include <stddef.h>
#include <stdint.h>
namespace utils::io {
class ostream;
} // namespace utils::io
namespace filament::backend {
class Program {
public:
static constexpr size_t SHADER_TYPE_COUNT = 3;
static constexpr size_t UNIFORM_BINDING_COUNT = CONFIG_UNIFORM_BINDING_COUNT;
static constexpr size_t SAMPLER_BINDING_COUNT = CONFIG_SAMPLER_BINDING_COUNT;
struct Descriptor {
utils::CString name;
DescriptorType type;
descriptor_binding_t binding;
};
struct DescriptorSetLayoutBinding {
descriptor_set_t set;
DescriptorSetLayout layout;
};
using SpecializationConstant = std::variant<int32_t, float, bool>;
using DescriptorSetLayoutArray = utils::FixedCapacityVector<DescriptorSetLayoutBinding>;
struct Uniform { // For ES2 support
utils::CString name; // full qualified name of the uniform field
uint16_t offset; // offset in 'uint32_t' into the uniform buffer
uint8_t size; // >1 for arrays
UniformType type; // uniform type
};
using DescriptorBindingsInfo = utils::FixedCapacityVector<Descriptor>;
using DescriptorSetInfo = std::array<DescriptorBindingsInfo, MAX_DESCRIPTOR_SET_COUNT>;
using SpecializationConstantsInfo = utils::FixedCapacityVector<SpecializationConstant>;
using ShaderBlob = utils::FixedCapacityVector<uint8_t>;
using ShaderSource = std::array<ShaderBlob, SHADER_TYPE_COUNT>;
using AttributesInfo = utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>>;
using UniformInfo = utils::FixedCapacityVector<Uniform>;
using BindingUniformsInfo = utils::FixedCapacityVector<
std::tuple<uint8_t, utils::CString, Program::UniformInfo>>;
Program() noexcept;
Program(const Program& rhs) = delete;
Program& operator=(const Program& rhs) = delete;
Program(Program&& rhs) noexcept;
Program& operator=(Program&& rhs) noexcept;
~Program() noexcept;
Program& priorityQueue(CompilerPriorityQueue priorityQueue) noexcept;
// sets the material name and variant for diagnostic purposes only
Program& diagnostics(utils::CString const& name,
utils::Invocable<utils::io::ostream&(utils::CString const& name,
utils::io::ostream& out)>&& logger);
// Sets one of the program's shader (e.g. vertex, fragment)
// string-based shaders are null terminated, consequently the size parameter must include the
// null terminating character.
Program& shader(ShaderStage shader, void const* data, size_t size);
// Sets the language of the shader sources provided with shader() (defaults to ESSL3)
Program& shaderLanguage(ShaderLanguage shaderLanguage);
// Descriptor binding (set, binding, type -> shader name) info
Program& descriptorBindings(backend::descriptor_set_t set,
DescriptorBindingsInfo descriptorBindings) noexcept;
Program& specializationConstants(SpecializationConstantsInfo specConstants) noexcept;
struct PushConstant {
utils::CString name;
ConstantType type;
};
Program& pushConstants(ShaderStage stage,
utils::FixedCapacityVector<PushConstant> constants) noexcept;
Program& cacheId(uint64_t cacheId) noexcept;
Program& multiview(bool multiview) noexcept;
// For ES2 support only...
Program& uniforms(uint32_t index, utils::CString name, UniformInfo uniforms);
Program& attributes(AttributesInfo attributes) noexcept;
//
// Getters for program construction...
//
ShaderSource const& getShadersSource() const noexcept { return mShadersSource; }
ShaderSource& getShadersSource() noexcept { return mShadersSource; }
utils::CString const& getName() const noexcept { return mName; }
utils::CString& getName() noexcept { return mName; }
auto const& getShaderLanguage() const { return mShaderLanguage; }
uint64_t getCacheId() const noexcept { return mCacheId; }
bool isMultiview() const noexcept { return mMultiview; }
CompilerPriorityQueue getPriorityQueue() const noexcept { return mPriorityQueue; }
SpecializationConstantsInfo const& getSpecializationConstants() const noexcept {
return mSpecializationConstants;
}
DescriptorSetInfo& getDescriptorBindings() noexcept {
return mDescriptorBindings;
}
inline Program& descriptorLayout(backend::descriptor_set_t set,
DescriptorSetLayout descriptorLayout) noexcept {
mDescriptorLayouts.push_back({
.set = set,
.layout = std::move(descriptorLayout),
});
return *this;
}
const DescriptorSetLayoutArray& getDescriptorSetLayouts() const noexcept {
return mDescriptorLayouts;
}
utils::FixedCapacityVector<PushConstant> const& getPushConstants(
ShaderStage stage) const noexcept {
return mPushConstants[static_cast<uint8_t>(stage)];
}
utils::FixedCapacityVector<PushConstant>& getPushConstants(ShaderStage stage) noexcept {
return mPushConstants[static_cast<uint8_t>(stage)];
}
auto const& getBindingUniformInfo() const { return mBindingUniformsInfo; }
auto& getBindingUniformInfo() { return mBindingUniformsInfo; }
auto const& getAttributes() const { return mAttributes; }
auto& getAttributes() { return mAttributes; }
private:
friend utils::io::ostream& operator<<(utils::io::ostream& out, const Program& builder);
ShaderSource mShadersSource;
ShaderLanguage mShaderLanguage = ShaderLanguage::ESSL3;
utils::CString mName;
uint64_t mCacheId{};
CompilerPriorityQueue mPriorityQueue = CompilerPriorityQueue::HIGH;
utils::Invocable<utils::io::ostream&(utils::CString const& name, utils::io::ostream& out)>
mLogger;
SpecializationConstantsInfo mSpecializationConstants;
std::array<utils::FixedCapacityVector<PushConstant>, SHADER_TYPE_COUNT> mPushConstants;
DescriptorSetInfo mDescriptorBindings;
// Descriptions for descriptor set layouts that may be used for this Program, which
// can be useful for attempting to compile the pipeline ahead of time.
DescriptorSetLayoutArray mDescriptorLayouts =
DescriptorSetLayoutArray::with_capacity(MAX_DESCRIPTOR_SET_COUNT);
// For ES2 support only
AttributesInfo mAttributes;
BindingUniformsInfo mBindingUniformsInfo;
// Indicates the current engine was initialized with multiview stereo, and the variant for this
// program contains STE flag. This will be referred later for the OpenGL shader compiler to
// determine whether shader code replacement for the num_views should be performed.
// This variable could be promoted as a more generic variable later if other similar needs occur.
bool mMultiview = false;
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_PRIVATE_PROGRAM_H

View File

@ -0,0 +1,4 @@
# include/backend Headers
Headers in `include/backend/` are fully public, in particular they can be included in filament's
public headers.

View File

@ -0,0 +1,36 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_BACKEND_SAMPLERDESCRIPTOR_H
#define TNT_FILAMENT_BACKEND_SAMPLERDESCRIPTOR_H
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <utils/compiler.h>
namespace filament::backend {
struct UTILS_PUBLIC SamplerDescriptor {
Handle<HwTexture> t;
SamplerParams s{};
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_SAMPLERDESCRIPTOR_H

View File

@ -0,0 +1,118 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_TARGETBUFFERINFO_H
#define TNT_FILAMENT_BACKEND_TARGETBUFFERINFO_H
#include <backend/Handle.h>
#include <utility>
#include <stddef.h>
#include <stdint.h>
namespace utils::io {
class ostream;
} // namespace utils::io
namespace filament::backend {
//! \privatesection
struct TargetBufferInfo {
// note: the parameters of this constructor are not in the order of this structure's fields
TargetBufferInfo(Handle<HwTexture> handle, uint8_t const level, uint16_t const layer) noexcept
: handle(std::move(handle)), level(level), layer(layer) {
}
TargetBufferInfo(Handle<HwTexture> handle, uint8_t const level) noexcept
: handle(handle), level(level) {
}
TargetBufferInfo(Handle<HwTexture> handle) noexcept // NOLINT(*-explicit-constructor)
: handle(handle) {
}
TargetBufferInfo() noexcept = default;
// texture to be used as render target
Handle<HwTexture> handle;
// level to be used
uint8_t level = 0;
// - For cubemap textures, this indicates the face of the cubemap. See TextureCubemapFace for
// the face->layer mapping)
// - For 2d array, cubemap array, and 3d textures, this indicates an index of a single layer of
// them.
// - For multiview textures (i.e., layerCount for the RenderTarget is greater than 1), this
// indicates a starting layer index of the current 2d array texture for multiview.
uint16_t layer = 0;
};
class MRT {
public:
static constexpr uint8_t MIN_SUPPORTED_RENDER_TARGET_COUNT = 4u;
// When updating this, make sure to also take care of RenderTarget.java
static constexpr uint8_t MAX_SUPPORTED_RENDER_TARGET_COUNT = 8u;
private:
TargetBufferInfo mInfos[MAX_SUPPORTED_RENDER_TARGET_COUNT];
public:
TargetBufferInfo const& operator[](size_t const i) const noexcept {
return mInfos[i];
}
TargetBufferInfo& operator[](size_t const i) noexcept {
return mInfos[i];
}
MRT() noexcept = default;
MRT(TargetBufferInfo const& color) noexcept // NOLINT(hicpp-explicit-conversions, *-explicit-constructor)
: mInfos{ color } {
}
MRT(TargetBufferInfo const& color0, TargetBufferInfo const& color1) noexcept
: mInfos{ color0, color1 } {
}
MRT(TargetBufferInfo const& color0, TargetBufferInfo const& color1,
TargetBufferInfo const& color2) noexcept
: mInfos{ color0, color1, color2 } {
}
MRT(TargetBufferInfo const& color0, TargetBufferInfo const& color1,
TargetBufferInfo const& color2, TargetBufferInfo const& color3) noexcept
: mInfos{ color0, color1, color2, color3 } {
}
// this is here for backward compatibility
MRT(Handle<HwTexture> handle, uint8_t level, uint16_t layer) noexcept
: mInfos{{ handle, level, layer }} {
}
};
} // namespace filament::backend
#if !defined(NDEBUG)
utils::io::ostream& operator<<(utils::io::ostream& out, const filament::backend::TargetBufferInfo& tbi);
utils::io::ostream& operator<<(utils::io::ostream& out, const filament::backend::MRT& mrt);
#endif
#endif //TNT_FILAMENT_BACKEND_TARGETBUFFERINFO_H

View File

@ -0,0 +1,79 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FILAMENT_BACKEND_ANDROIDNDK_H
#define FILAMENT_BACKEND_ANDROIDNDK_H
#include <android/native_window.h>
#include <android/hardware_buffer.h>
#define FILAMENT_REQUIRES_API(x) __attribute__((__availability__(android,introduced=x)))
#define FILAMENT_USE_DLSYM(api) (__ANDROID_API__ < (api))
namespace filament::backend {
class AndroidNdk {
public:
AndroidNdk();
#if FILAMENT_USE_DLSYM(26)
static void AHardwareBuffer_acquire(
AHardwareBuffer* buffer) FILAMENT_REQUIRES_API(26) {
ndk.AHardwareBuffer_acquire(buffer);
}
static void AHardwareBuffer_release(
AHardwareBuffer* buffer) FILAMENT_REQUIRES_API(26) {
ndk.AHardwareBuffer_release(buffer);
}
static void AHardwareBuffer_describe(
AHardwareBuffer const* buffer, AHardwareBuffer_Desc* desc) FILAMENT_REQUIRES_API(26) {
ndk.AHardwareBuffer_describe(buffer, desc);
}
#else
static void AHardwareBuffer_acquire(
AHardwareBuffer* buffer) FILAMENT_REQUIRES_API(26) {
::AHardwareBuffer_acquire(buffer);
}
static void AHardwareBuffer_release(
AHardwareBuffer* buffer) FILAMENT_REQUIRES_API(26) {
::AHardwareBuffer_release(buffer);
}
static void AHardwareBuffer_describe(
AHardwareBuffer const* buffer, AHardwareBuffer_Desc* desc) FILAMENT_REQUIRES_API(26) {
::AHardwareBuffer_describe(buffer, desc);
}
#endif
private:
#if FILAMENT_USE_DLSYM(26)
static struct Ndk {
void (*AHardwareBuffer_acquire)(AHardwareBuffer*);
void (*AHardwareBuffer_release)(AHardwareBuffer*);
void (*AHardwareBuffer_describe)(AHardwareBuffer const*, AHardwareBuffer_Desc*);
} ndk;
#endif
};
} // filament::backend
#endif //FILAMENT_BACKEND_ANDROIDNDK_H

View File

@ -0,0 +1,462 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_PRIVATE_OPENGLPLATFORM_H
#define TNT_FILAMENT_BACKEND_PRIVATE_OPENGLPLATFORM_H
#include <backend/AcquiredImage.h>
#include <backend/DriverEnums.h>
#include <backend/Platform.h>
#include <utils/compiler.h>
#include <utils/Invocable.h>
#include <utils/CString.h>
#include <stddef.h>
#include <stdint.h>
#include <math/mat3.h>
namespace filament::backend {
class Driver;
/**
* A Platform interface that creates an OpenGL backend.
*
* WARNING: None of the methods below are allowed to change the GL state and must restore it
* upon return.
*
*/
class OpenGLPlatform : public Platform {
protected:
/*
* Derived classes can use this to instantiate the default OpenGLDriver backend.
* This is typically called from your implementation of createDriver()
*/
static Driver* UTILS_NULLABLE createDefaultDriver(OpenGLPlatform* UTILS_NONNULL platform,
void* UTILS_NULLABLE sharedContext, const DriverConfig& driverConfig);
~OpenGLPlatform() noexcept override;
utils::CString getDeviceInfo(DeviceInfoType infoType,
Driver* UTILS_NULLABLE driver) const noexcept override;
public:
struct ExternalTexture {
unsigned int target; // GLenum target
unsigned int id; // GLuint id
};
/**
* Return the OpenGL vendor string of the specified Driver instance.
* @return The GL_VENDOR string
*/
static utils::CString getVendorString(Driver const* UTILS_NONNULL driver);
/**
* Return the OpenGL vendor string of the specified Driver instance
* @return The GL_RENDERER string
*/
static utils::CString getRendererString(Driver const* UTILS_NONNULL driver);
/**
* Return the OpenGL version string of the specified Driver instance.
* @return The GL_VERSION string
*/
static utils::CString getVersionString(Driver const* UTILS_NONNULL driver);
/**
* Called by the driver to destroy the OpenGL context. This should clean up any windows
* or buffers from initialization. This is for instance where `eglDestroyContext` would be
* called.
*/
virtual void terminate() noexcept = 0;
/**
* Return whether createSwapChain supports the SWAP_CHAIN_CONFIG_SRGB_COLORSPACE flag.
* The default implementation returns false.
*
* @return true if SWAP_CHAIN_CONFIG_SRGB_COLORSPACE is supported, false otherwise.
*/
virtual bool isSRGBSwapChainSupported() const noexcept;
/**
* Return whether createSwapChain supports the SWAP_CHAIN_CONFIG_MSAA_*_SAMPLES flag.
* The default implementation returns false.
*
* @param samples The number of samples
* @return true if SWAP_CHAIN_CONFIG_MSAA_*_SAMPLES is supported, false otherwise.
*/
virtual bool isMSAASwapChainSupported(uint32_t samples) const noexcept;
/**
* Return whether protected contexts are supported by this backend.
* If protected context are supported, the SWAP_CHAIN_CONFIG_PROTECTED_CONTENT flag can be
* used when creating a SwapChain.
* The default implementation returns false.
*/
virtual bool isProtectedContextSupported() const noexcept;
/**
* Called by the driver to create a SwapChain for this driver.
*
* @param nativeWindow a token representing the native window. See concrete implementation
* for details.
* @param flags extra flags used by the implementation, see filament::SwapChain
* @return The driver's SwapChain object.
*
*/
virtual SwapChain* UTILS_NULLABLE createSwapChain(
void* UTILS_NULLABLE nativeWindow, uint64_t flags) = 0;
/**
* Called by the driver create a headless SwapChain.
*
* @param width width of the buffer
* @param height height of the buffer
* @param flags extra flags used by the implementation, see filament::SwapChain
* @return The driver's SwapChain object.
*
* TODO: we need a more generic way of passing construction parameters
* A void* might be enough.
*/
virtual SwapChain* UTILS_NULLABLE createSwapChain(
uint32_t width, uint32_t height, uint64_t flags) = 0;
/**
* Called by the driver to destroys the SwapChain
* @param swapChain SwapChain to be destroyed.
*/
virtual void destroySwapChain(SwapChain* UTILS_NONNULL swapChain) noexcept = 0;
/**
* Returns the set of buffers that must be preserved up to the call to commit().
* The default value is TargetBufferFlags::NONE.
* The color buffer is always preserved, however ancillary buffers, such as the depth buffer
* are generally discarded. The preserve flags can be used to make sure those ancillary
* buffers are preserved until the call to commit.
*
* @param swapChain
* @return buffer that must be preserved
* @see commit()
*/
virtual TargetBufferFlags getPreservedFlags(SwapChain* UTILS_NONNULL swapChain) noexcept;
/**
* Returns true if the swapchain is protected
*/
virtual bool isSwapChainProtected(Platform::SwapChain* UTILS_NONNULL swapChain) noexcept;
/**
* Called by the driver to establish the default FBO. The default implementation returns 0.
*
* This method can be called either on the regular or protected OpenGL contexts and can return
* a different or identical name, since these names exist in different namespaces.
*
* @return a GLuint casted to a uint32_t that is an OpenGL framebuffer object.
*/
virtual uint32_t getDefaultFramebufferObject() noexcept;
/**
* Called by the backend when a frame starts.
* @param steady_clock_ns vsync time point on the monotonic clock
* @param refreshIntervalNs refresh interval in nanosecond
* @param frameId a frame id
*/
virtual void beginFrame(
int64_t monotonic_clock_ns,
int64_t refreshIntervalNs,
uint32_t frameId) noexcept;
/**
* Called by the backend when a frame ends.
* @param frameId the frame id used in beginFrame
*/
virtual void endFrame(
uint32_t frameId) noexcept;
/**
* Type of contexts available
*/
enum class ContextType {
NONE, //!< No current context
UNPROTECTED, //!< current context is unprotected
PROTECTED //!< current context supports protected content
};
/**
* Returns the type of the context currently in use. This value is updated by makeCurrent()
* and therefore can be cached between calls. ContextType::PROTECTED can only be returned
* if isProtectedContextSupported() is true.
* @return ContextType
*/
virtual ContextType getCurrentContextType() const noexcept;
/**
* Binds the requested context to the current thread and drawSwapChain to the default FBO
* returned by getDefaultFramebufferObject().
*
* @param type type of context to bind to the current thread.
* @param drawSwapChain SwapChain to draw to. It must be bound to the default FBO.
* @param readSwapChain SwapChain to read from (for operation like `glBlitFramebuffer`)
* @return true on success, false on error.
*/
virtual bool makeCurrent(ContextType type,
SwapChain* UTILS_NONNULL drawSwapChain,
SwapChain* UTILS_NONNULL readSwapChain) = 0;
/**
* Called by the driver to make the OpenGL context active on the calling thread and bind
* the drawSwapChain to the default FBO returned by getDefaultFramebufferObject().
* The context used is either the default context or the protected context. When a context
* change is necessary, the preContextChange and postContextChange callbacks are called,
* before and after the context change respectively. postContextChange is given the index
* of the new context (0 for default and 1 for protected).
* The default implementation just calls makeCurrent(getCurrentContextType(), SwapChain*, SwapChain*).
*
* @param drawSwapChain SwapChain to draw to. It must be bound to the default FBO.
* @param readSwapChain SwapChain to read from (for operation like `glBlitFramebuffer`)
* @param preContextChange called before the context changes
* @param postContextChange called after the context changes
*/
virtual void makeCurrent(
SwapChain* UTILS_NONNULL drawSwapChain,
SwapChain* UTILS_NONNULL readSwapChain,
utils::Invocable<void()> preContextChange,
utils::Invocable<void(size_t index)> postContextChange);
/**
* Called by the backend just before calling commit()
* @see commit()
*/
virtual void preCommit() noexcept;
/**
* Called by the driver once the current frame finishes drawing. Typically, this should present
* the drawSwapChain. This is for example where `eglMakeCurrent()` would be called.
* @param swapChain the SwapChain to present.
*/
virtual void commit(SwapChain* UTILS_NONNULL swapChain) noexcept = 0;
/**
* Set the time the next committed buffer should be presented to the user at.
*
* @param presentationTimeInNanosecond time in the future in nanosecond. The clock used depends
* on the concrete platform implementation.
*/
virtual void setPresentationTime(int64_t presentationTimeInNanosecond) noexcept;
// --------------------------------------------------------------------------------------------
// Fence support
/**
* Can this implementation create a Fence.
* @return true if supported, false otherwise. The default implementation returns false.
*/
virtual bool canCreateFence() noexcept;
/**
* Creates a Fence (e.g. eglCreateSyncKHR). This must be implemented if `canCreateFence`
* returns true. Fences are used for frame pacing.
*
* @return A Fence object. The default implementation returns nullptr.
*/
virtual Fence* UTILS_NULLABLE createFence() noexcept;
/**
* Destroys a Fence object. The default implementation does nothing.
*
* @param fence Fence to destroy.
*/
virtual void destroyFence(Fence* UTILS_NONNULL fence) noexcept;
/**
* Waits on a Fence.
*
* @param fence Fence to wait on.
* @param timeout Timeout.
* @return Whether the fence signaled or timed out. See backend::FenceStatus.
* The default implementation always return backend::FenceStatus::ERROR.
*/
virtual backend::FenceStatus waitFence(Fence* UTILS_NONNULL fence, uint64_t timeout) noexcept;
// --------------------------------------------------------------------------------------------
// Sync support
/**
* Creates a Sync. These can be used for frame synchronization externally
* (certain platform implementations can be exported to handles that can
* be used in other processes).
*
* @return A Sync object.
*/
virtual Platform::Sync* UTILS_NONNULL createSync() noexcept;
/**
* Destroys a sync. If called with a sync not created by this platform
* object, this will lead to undefined behavior.
*
* @param sync The sync to destroy, that was created by this platform
* instance.
*/
virtual void destroySync(Platform::Sync* UTILS_NONNULL sync) noexcept;
// --------------------------------------------------------------------------------------------
// Streaming support
/**
* Creates a Stream from a native Stream.
*
* WARNING: This is called synchronously from the application thread (NOT the Driver thread)
*
* @param nativeStream The native stream, this parameter depends on the concrete implementation.
* @return A new Stream object.
*/
virtual Stream* UTILS_NULLABLE createStream(void* UTILS_NULLABLE nativeStream) noexcept;
/**
* Destroys a Stream.
* @param stream Stream to destroy.
*/
virtual void destroyStream(Stream* UTILS_NONNULL stream) noexcept;
/**
* The specified stream takes ownership of the texture (tname) object
* Once attached, the texture is automatically updated with the Stream's content, which
* could be a video stream for instance.
*
* @param stream Stream to take ownership of the texture
* @param tname GL texture id to "bind" to the Stream.
*/
virtual void attach(Stream* UTILS_NONNULL stream, intptr_t tname) noexcept;
/**
* Destroys the texture associated to the stream
* @param stream Stream to detach from its texture
*/
virtual void detach(Stream* UTILS_NONNULL stream) noexcept;
/**
* Updates the content of the texture attached to the stream.
* @param stream Stream to update
* @param timestamp Output parameter: Timestamp of the image bound to the texture.
*/
virtual void updateTexImage(Stream* UTILS_NONNULL stream,
int64_t* UTILS_NONNULL timestamp) noexcept;
/**
* Returns the transform matrix of the texture attached to the stream.
* @param stream Stream to get the transform matrix from
* @param uvTransform Output parameter: Transform matrix of the image bound to the texture. Returns identity if not supported.
*/
virtual math::mat3f getTransformMatrix(Stream* UTILS_NONNULL stream) noexcept;
// --------------------------------------------------------------------------------------------
// External Image support
/**
* Creates an external texture handle. External textures don't have any parameters because
* these are undefined until setExternalImage() is called.
* @return a pointer to an ExternalTexture structure filled with valid token. However, the
* implementation could just return { 0, GL_TEXTURE_2D } at this point. The actual
* values can be delayed until setExternalImage.
*/
virtual ExternalTexture* UTILS_NULLABLE createExternalImageTexture() noexcept;
/**
* Destroys an external texture handle and associated data.
* @param texture a pointer to the handle to destroy.
*/
virtual void destroyExternalImageTexture(ExternalTexture* UTILS_NONNULL texture) noexcept;
// called on the application thread to allow Filament to take ownership of the image
/**
* Takes ownership of the externalImage. The externalImage parameter depends on the Platform's
* concrete implementation. Ownership is released when destroyExternalImageTexture() is called.
*
* WARNING: This is called synchronously from the application thread (NOT the Driver thread)
*
* @param externalImage A token representing the platform's external image.
* @see destroyExternalImage
* @{
*/
virtual void retainExternalImage(void* UTILS_NONNULL externalImage) noexcept;
virtual void retainExternalImage(ExternalImageHandleRef externalImage) noexcept;
/** @}*/
/**
* Called to bind the platform-specific externalImage to an ExternalTexture.
* ExternalTexture::id is guaranteed to be bound when this method is called and ExternalTexture
* is updated with new values for id/target if necessary.
*
* WARNING: this method is not allowed to change the bound texture, or must restore the previous
* binding upon return. This is to avoid a problem with a backend doing state caching.
*
* @param externalImage The platform-specific external image.
* @param texture an in/out pointer to ExternalTexture, id and target can be updated if necessary.
* @return true on success, false on error.
* @{
*/
virtual bool setExternalImage(void* UTILS_NONNULL externalImage,
ExternalTexture* UTILS_NONNULL texture) noexcept;
virtual bool setExternalImage(ExternalImageHandleRef externalImage,
ExternalTexture* UTILS_NONNULL texture) noexcept;
/** @}*/
/**
* The method allows platforms to convert a user-supplied external image object into a new type
* (e.g. HardwareBuffer => EGLImage). The default implementation returns source.
* @param source Image to transform.
* @return Transformed image.
*/
virtual AcquiredImage transformAcquiredImage(AcquiredImage source) noexcept;
// --------------------------------------------------------------------------------------------
/**
* Returns true if additional OpenGL contexts can be created. Default: false.
* @return true if additional OpenGL contexts can be created.
* @see createContext
*/
virtual bool isExtraContextSupported() const noexcept;
/**
* Creates an OpenGL context with the same configuration as the main context and makes it
* current to the current thread. Must not be called from the main driver thread.
* createContext() is only supported if isExtraContextSupported() returns true.
* These additional contexts will be automatically terminated in terminate.
*
* @param shared whether the new context is shared with the main context.
* @see isExtraContextSupported()
* @see terminate()
*/
virtual void createContext(bool shared);
/**
* Detach and destroy the current context if any and releases all resources associated to
* this thread. This must be called from the same thread where createContext() was called.
*/
virtual void releaseContext() noexcept;
};
} // namespace filament
#endif // TNT_FILAMENT_BACKEND_PRIVATE_OPENGLPLATFORM_H

View File

@ -0,0 +1,76 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_COCOA_GL_H
#define TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_COCOA_GL_H
#include <backend/platforms/OpenGLPlatform.h>
#include <stdint.h>
namespace filament::backend {
struct PlatformCocoaGLImpl;
/**
* A concrete implementation of OpenGLPlatform that supports macOS's Cocoa.
*/
class PlatformCocoaGL : public OpenGLPlatform {
public:
PlatformCocoaGL();
~PlatformCocoaGL() noexcept override;
ExternalImageHandle UTILS_PUBLIC createExternalImage(void* cvPixelBuffer) noexcept;
protected:
// --------------------------------------------------------------------------------------------
// Platform Interface
Driver* createDriver(void* sharedContext,
const DriverConfig& driverConfig) override;
// Currently returns 0
int getOSVersion() const noexcept override;
bool pumpEvents() noexcept override;
// --------------------------------------------------------------------------------------------
// OpenGLPlatform Interface
bool isExtraContextSupported() const noexcept override;
void createContext(bool shared) override;
void terminate() noexcept override;
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) override;
void commit(SwapChain* swapChain) noexcept override;
ExternalTexture* createExternalImageTexture() noexcept override;
void destroyExternalImageTexture(ExternalTexture* texture) noexcept override;
void retainExternalImage(void* externalImage) noexcept override;
bool setExternalImage(void* externalImage, ExternalTexture* texture) noexcept override;
void retainExternalImage(ExternalImageHandleRef externalImage) noexcept override;
bool setExternalImage(ExternalImageHandleRef externalImage, ExternalTexture* texture) noexcept override;
private:
PlatformCocoaGLImpl* pImpl = nullptr;
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_COCOA_GL_H

View File

@ -0,0 +1,76 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_COCOA_TOUCH_GL_H
#define TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_COCOA_TOUCH_GL_H
#include <stdint.h>
#include <backend/platforms/OpenGLPlatform.h>
#include <backend/DriverEnums.h>
namespace filament::backend {
struct PlatformCocoaTouchGLImpl;
class PlatformCocoaTouchGL : public OpenGLPlatform {
public:
PlatformCocoaTouchGL();
~PlatformCocoaTouchGL() noexcept override;
ExternalImageHandle UTILS_PUBLIC createExternalImage(void* cvPixelBuffer) noexcept;
// --------------------------------------------------------------------------------------------
// Platform Interface
Driver* createDriver(void* sharedGLContext,
const DriverConfig& driverConfig) override;
int getOSVersion() const noexcept final { return 0; }
// --------------------------------------------------------------------------------------------
// OpenGLPlatform Interface
void terminate() noexcept override;
uint32_t getDefaultFramebufferObject() noexcept override;
bool isExtraContextSupported() const noexcept override;
void createContext(bool shared) override;
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) override;
void commit(SwapChain* swapChain) noexcept override;
ExternalTexture* createExternalImageTexture() noexcept override;
void destroyExternalImageTexture(ExternalTexture* texture) noexcept override;
void retainExternalImage(void* externalImage) noexcept override;
bool setExternalImage(void* externalImage, ExternalTexture* texture) noexcept override;
void retainExternalImage(ExternalImageHandleRef externalImage) noexcept override;
bool setExternalImage(ExternalImageHandleRef externalImage, ExternalTexture* texture) noexcept override;
private:
PlatformCocoaTouchGLImpl* pImpl = nullptr;
};
using ContextManager = PlatformCocoaTouchGL;
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_COCOA_TOUCH_GL_H

View File

@ -0,0 +1,242 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_H
#define TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_H
#include <backend/DriverEnums.h>
#include <backend/Platform.h>
#include <backend/platforms/OpenGLPlatform.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <EGL/eglplatform.h>
#include <utils/compiler.h>
#include <utils/Invocable.h>
#include <initializer_list>
#include <utility>
#include <vector>
#include <stddef.h>
#include <stdint.h>
namespace filament::backend {
/**
* A concrete implementation of OpenGLPlatform that supports EGL.
*/
class PlatformEGL : public OpenGLPlatform {
public:
PlatformEGL() noexcept;
// Return true if we're on an OpenGL platform (as opposed to OpenGL ES). false by default.
virtual bool isOpenGL() const noexcept;
/**
* Creates an ExternalImage from a EGLImageKHR
*/
ExternalImageHandle UTILS_PUBLIC createExternalImage(EGLImageKHR eglImage) noexcept;
protected:
// --------------------------------------------------------------------------------------------
// Helper for EGL configs and attributes parameters
class Config {
public:
Config();
Config(std::initializer_list<std::pair<EGLint, EGLint>> list);
EGLint& operator[](EGLint name);
EGLint operator[](EGLint name) const;
void erase(EGLint name) noexcept;
EGLint const* data() const noexcept {
return reinterpret_cast<EGLint const*>(mConfig.data());
}
size_t size() const noexcept {
return mConfig.size();
}
private:
std::vector<std::pair<EGLint, EGLint>> mConfig = {{ EGL_NONE, EGL_NONE }};
};
// --------------------------------------------------------------------------------------------
// Platform Interface
/**
* Initializes EGL, creates the OpenGL context and returns a concrete Driver implementation
* that supports OpenGL/OpenGL ES.
*/
Driver* createDriver(void* sharedContext, const DriverConfig& driverConfig) override;
/**
* This returns zero. This method can be overridden to return something more useful.
* @return zero
*/
int getOSVersion() const noexcept override;
// --------------------------------------------------------------------------------------------
// OpenGLPlatform Interface
bool isExtraContextSupported() const noexcept override;
void createContext(bool shared) override;
void releaseContext() noexcept override;
void terminate() noexcept override;
bool isProtectedContextSupported() const noexcept override;
bool isSRGBSwapChainSupported() const noexcept override;
bool isMSAASwapChainSupported(uint32_t samples) const noexcept override;
bool isSwapChainProtected(SwapChain* swapChain) noexcept override;
ContextType getCurrentContextType() const noexcept override;
bool makeCurrent(ContextType type,
SwapChain* drawSwapChain,
SwapChain* readSwapChain) override;
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain,
utils::Invocable<void()> preContextChange,
utils::Invocable<void(size_t index)> postContextChange) override;
void commit(SwapChain* swapChain) noexcept override;
bool canCreateFence() noexcept override;
Fence* createFence() noexcept override;
void destroyFence(Fence* fence) noexcept override;
FenceStatus waitFence(Fence* fence, uint64_t timeout) noexcept override;
ExternalTexture* createExternalImageTexture() noexcept override;
void destroyExternalImageTexture(ExternalTexture* texture) noexcept override;
bool setExternalImage(void* externalImage, ExternalTexture* texture) noexcept override;
bool setExternalImage(ExternalImageHandleRef externalImage, ExternalTexture* texture) noexcept override;
/**
* Logs glGetError() to LOG(ERROR)
* @param name a string giving some context on the error. Typically __func__.
*/
static void logEglError(const char* name) noexcept;
static void logEglError(const char* name, EGLint error) noexcept;
static const char* getEglErrorName(EGLint error) noexcept;
/**
* Calls glGetError() to clear the current error flags. logs a warning to log.w if
* an error was pending.
*/
static void clearGlError() noexcept;
/**
* Always use this instead of eglMakeCurrent(), as it tracks some state.
*/
EGLContext getContextForType(ContextType type) const noexcept;
// makes the draw and read surface current without changing the current context
EGLBoolean makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) {
return egl.makeCurrent(drawSurface, readSurface);
}
// makes context current and set draw and read surfaces to EGL_NO_SURFACE
EGLBoolean makeCurrent(EGLContext context) {
return egl.makeCurrent(context, mEGLDummySurface, mEGLDummySurface);
}
EGLDisplay getEglDisplay() const noexcept { return mEGLDisplay; }
EGLConfig getEglConfig() const noexcept { return mEGLConfig; }
EGLConfig getSuitableConfigForSwapChain(uint64_t flags, bool window, bool pbuffer) const;
// Sets the EGLDisplay to be used by this platform. This should only be called by derived
// classes before invoking createDriver. Calling it after that point will result in
// undefined behaviour. This class will take ownership of the display and call eglTerminate
// on it during shutdown.
void setEglDisplay(EGLDisplay display) noexcept;
// supported extensions detected at runtime
struct {
struct {
bool OES_EGL_image_external_essl3 = false;
} gl;
struct {
bool ANDROID_recordable = false;
bool KHR_create_context = false;
bool KHR_gl_colorspace = false;
bool KHR_no_config_context = false;
bool KHR_surfaceless_context = false;
bool EXT_protected_content = false;
} egl;
} ext;
struct SwapChainEGL : public SwapChain {
SwapChainEGL(PlatformEGL const& platform, void* nativeWindow, uint64_t flags);
SwapChainEGL(PlatformEGL const& platform, uint32_t width, uint32_t height, uint64_t flags);
void terminate(PlatformEGL& platform);
EGLSurface sur = EGL_NO_SURFACE;
Config attribs{};
EGLNativeWindowType nativeWindow{};
EGLConfig config{};
uint64_t flags{};
};
void initializeGlExtensions() noexcept;
struct ExternalImageEGL : public ExternalImage {
EGLImageKHR eglImage = EGL_NO_IMAGE;
protected:
~ExternalImageEGL() override;
};
private:
// prevent derived classes' implementations to call through
[[nodiscard]] SwapChain* createSwapChain(void* nativeWindow, uint64_t flags) override;
[[nodiscard]] SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
EGLConfig findSwapChainConfig(uint64_t flags, bool window, bool pbuffer) const;
EGLDisplay mEGLDisplay = EGL_NO_DISPLAY;
EGLContext mEGLContext = EGL_NO_CONTEXT;
EGLContext mEGLContextProtected = EGL_NO_CONTEXT;
EGLSurface mEGLDummySurface = EGL_NO_SURFACE;
ContextType mCurrentContextType = ContextType::NONE;
// mEGLConfig is valid only if ext.egl.KHR_no_config_context is false
EGLConfig mEGLConfig = EGL_NO_CONFIG_KHR;
Config mContextAttribs;
std::vector<EGLContext> mAdditionalContexts;
bool mMSAA4XSupport = false;
class EGL {
EGLDisplay& mEGLDisplay;
EGLSurface mCurrentDrawSurface = EGL_NO_SURFACE;
EGLSurface mCurrentReadSurface = EGL_NO_SURFACE;
EGLContext mCurrentContext = EGL_NO_CONTEXT;
public:
explicit EGL(EGLDisplay& dpy) : mEGLDisplay(dpy) {}
EGLBoolean makeCurrent(EGLContext context,
EGLSurface drawSurface, EGLSurface readSurface);
EGLBoolean makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) {
return makeCurrent(mCurrentContext, drawSurface, readSurface);
}
} egl{ mEGLDisplay };
bool checkIfMSAASwapChainSupported(uint32_t samples) const noexcept;
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_H

View File

@ -0,0 +1,200 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_ANDROID_H
#define TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_ANDROID_H
#include "AndroidNdk.h"
#include <backend/AcquiredImage.h>
#include <backend/DriverEnums.h>
#include <backend/Platform.h>
#include <backend/platforms/OpenGLPlatform.h>
#include <backend/platforms/PlatformEGL.h>
#include <utils/android/PerformanceHintManager.h>
#include <utils/compiler.h>
#include <math/mat3.h>
#include <chrono>
#include <stddef.h>
#include <stdint.h>
namespace filament::backend {
class ExternalStreamManagerAndroid;
/**
* A concrete implementation of OpenGLPlatform and subclass of PlatformEGL that supports
* EGL on Android. It adds Android streaming functionality to PlatformEGL.
*/
class PlatformEGLAndroid : public PlatformEGL, public AndroidNdk {
public:
PlatformEGLAndroid() noexcept;
~PlatformEGLAndroid() noexcept override;
/**
* Creates an ExternalImage from a EGLImageKHR
*/
ExternalImageHandle UTILS_PUBLIC createExternalImage(AHardwareBuffer const* buffer, bool sRGB) noexcept;
struct UTILS_PUBLIC ExternalImageDescAndroid {
uint32_t width; // Texture width
uint32_t height; // Texture height
TextureFormat format;// Texture format
TextureUsage usage; // Texture usage flags
};
ExternalImageDescAndroid UTILS_PUBLIC getExternalImageDesc(ExternalImageHandle externalImage) noexcept;
/**
* Converts a sync to an external file descriptor, if possible. Accepts an
* opaque handle to a sync, as well as a pointer to where the fd should be
* stored.
* @param sync The sync to be converted to a file descriptor.
* @param fd A pointer to where the file descriptor should be stored.
* @return `true` on success, `false` on failure. The default implementation
* returns `false`.
*/
bool convertSyncToFd(Sync* sync, int* fd) noexcept;
protected:
struct {
struct {
bool ANDROID_presentation_time = false;
bool ANDROID_get_frame_timestamps = false;
bool ANDROID_native_fence_sync = false;
} egl;
} ext;
// --------------------------------------------------------------------------------------------
// Platform Interface
/**
* Returns the Android SDK version.
* @return Android SDK version.
*/
int getOSVersion() const noexcept override;
Driver* createDriver(void* sharedContext,
const DriverConfig& driverConfig) override;
bool isCompositorTimingSupported() const noexcept override;
bool queryCompositorTiming(SwapChain const* swapchain,
CompositorTiming* outCompositorTiming) const noexcept override;
bool setPresentFrameId(SwapChain const* swapchain, uint64_t frameId) noexcept override;
bool queryFrameTimestamps(SwapChain const* swapchain, uint64_t frameId,
FrameTimestamps* outFrameTimestamps) const noexcept override;
// --------------------------------------------------------------------------------------------
// OpenGLPlatform Interface
struct SyncEGLAndroid : public Sync {
EGLSyncKHR sync;
};
void terminate() noexcept override;
void beginFrame(
int64_t monotonic_clock_ns,
int64_t refreshIntervalNs,
uint32_t frameId) noexcept override;
void preCommit() noexcept override;
/**
* Set the presentation time using `eglPresentationTimeANDROID`
* @param presentationTimeInNanosecond
*/
void setPresentationTime(int64_t presentationTimeInNanosecond) noexcept override;
Stream* createStream(void* nativeStream) noexcept override;
void destroyStream(Stream* stream) noexcept override;
Sync* createSync() noexcept override;
void destroySync(Sync* sync) noexcept override;
void attach(Stream* stream, intptr_t tname) noexcept override;
void detach(Stream* stream) noexcept override;
void updateTexImage(Stream* stream, int64_t* timestamp) noexcept override;
math::mat3f getTransformMatrix(Stream* stream) noexcept override;
/**
* Converts a AHardwareBuffer to EGLImage
* @param source source.image is a AHardwareBuffer
* @return source.image contains an EGLImage
*/
AcquiredImage transformAcquiredImage(AcquiredImage source) noexcept override;
ExternalTexture* createExternalImageTexture() noexcept override;
void destroyExternalImageTexture(ExternalTexture* texture) noexcept override;
struct ExternalImageEGLAndroid : public ExternalImageEGL {
AHardwareBuffer* aHardwareBuffer = nullptr;
uint32_t width; // Texture width
uint32_t height; // Texture height
TextureFormat format;// Texture format
TextureUsage usage; // Texture usage flags
bool sRGB = false;
protected:
~ExternalImageEGLAndroid() override;
};
bool setExternalImage(ExternalImageHandleRef externalImage,
ExternalTexture* texture) noexcept override;
bool setImage(ExternalImageEGLAndroid const* eglExternalImage,
ExternalTexture* texture) noexcept;
bool makeCurrent(ContextType type,
SwapChain* drawSwapChain,
SwapChain* readSwapChain) override;
private:
struct SwapChainEGLAndroid;
struct AndroidDetails;
// prevent derived classes' implementations to call through
[[nodiscard]] SwapChain* createSwapChain(void* nativeWindow, uint64_t flags) override;
[[nodiscard]] SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
bool isProducerThrottlingControlSupported() const;
int32_t setProducerThrottlingEnabled(EGLNativeWindowType nativeWindow, bool enabled) const;
struct ExternalTextureAndroid : public ExternalTexture {
EGLImageKHR eglImage = EGL_NO_IMAGE;
};
int mOSVersion;
ExternalStreamManagerAndroid* mExternalStreamManager = nullptr;
AndroidDetails& mAndroidDetails;
utils::PerformanceHintManager mPerformanceHintManager;
utils::PerformanceHintManager::Session mPerformanceHintSession;
using clock = std::chrono::high_resolution_clock;
clock::time_point mStartTimeOfActualWork;
SwapChainEGLAndroid* mCurrentDrawSwapChain{};
bool mAssertNativeWindowIsValid = false;
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_ANDROID_H

View File

@ -0,0 +1,40 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_DRIVER_OPENGL_PLATFORM_EGL_HEADLESS_H
#define TNT_FILAMENT_DRIVER_OPENGL_PLATFORM_EGL_HEADLESS_H
#include "PlatformEGL.h"
namespace filament::backend {
/**
* A concrete implementation of OpenGLPlatform that supports EGL with only headless swapchains.
*/
class PlatformEGLHeadless : public PlatformEGL {
public:
PlatformEGLHeadless() noexcept;
Driver* createDriver(void* sharedContext,
const Platform::DriverConfig& driverConfig) override;
protected:
bool isOpenGL() const noexcept override;
};
} // namespace filament
#endif // TNT_FILAMENT_DRIVER_OPENGL_PLATFORM_EGL_HEADLESS_H

View File

@ -0,0 +1,78 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_GLX_H
#define TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_GLX_H
#include <stdint.h>
#include "bluegl/BlueGL.h"
#include <GL/glx.h>
#include <backend/platforms/OpenGLPlatform.h>
#include <backend/DriverEnums.h>
#include <shared_mutex>
#include <thread>
#include <vector>
#include <unordered_map>
namespace filament::backend {
/**
* A concrete implementation of OpenGLPlatform that supports GLX.
*/
class PlatformGLX : public OpenGLPlatform {
protected:
// --------------------------------------------------------------------------------------------
// Platform Interface
Driver* createDriver(void* sharedGLContext,
const DriverConfig& driverConfig) override;
int getOSVersion() const noexcept final override { return 0; }
// --------------------------------------------------------------------------------------------
// OpenGLPlatform Interface
void terminate() noexcept override;
bool isExtraContextSupported() const noexcept override;
void createContext(bool shared) override;
void releaseContext() noexcept override;
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) override;
void commit(SwapChain* swapChain) noexcept override;
private:
Display* mGLXDisplay;
GLXContext mGLXContext{};
GLXFBConfig mGLXConfig{};
GLXPbuffer mDummySurface;
std::vector<GLXPbuffer> mPBuffers;
// Variables for shared contexts
std::unordered_map<std::thread::id, GLXContext> mAdditionalContexts;
std::shared_mutex mAdditionalContextsLock;
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_GLX_H

View File

@ -0,0 +1,38 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_PRIVATE_PLATFORMMETAL_OBJC_H
#define TNT_FILAMENT_BACKEND_PRIVATE_PLATFORMMETAL_OBJC_H
#import <Metal/Metal.h>
namespace filament::backend {
struct MetalDevice {
id<MTLDevice> device;
};
struct MetalCommandQueue {
id<MTLCommandQueue> commandQueue;
};
struct MetalCommandBuffer {
id<MTLCommandBuffer> commandBuffer;
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_PRIVATE_PLATFORMMETAL_OBJC_H

View File

@ -0,0 +1,119 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_PRIVATE_PLATFORMMETAL_H
#define TNT_FILAMENT_BACKEND_PRIVATE_PLATFORMMETAL_H
#include <backend/DriverEnums.h>
#include <backend/Platform.h>
namespace filament::backend {
struct PlatformMetalImpl;
// In order for this header to be compatible with Objective-C and C++, we use these wrappers around
// id<MTL*> objects.
// See PlatformMetal-Objc.h.
struct MetalDevice;
struct MetalCommandQueue;
struct MetalCommandBuffer;
class PlatformMetal final : public Platform {
public:
PlatformMetal();
~PlatformMetal() noexcept override;
Driver* createDriver(void* sharedContext, const Platform::DriverConfig& driverConfig) override;
int getOSVersion() const noexcept override { return 0; }
utils::CString getDeviceInfo(DeviceInfoType, Driver*) const noexcept override { return {}; }
/**
* Optionally initializes the Metal platform by acquiring resources necessary for rendering.
*
* This method attempts to acquire a Metal device and command queue, returning true if both are
* successfully obtained, or false otherwise. Typically, these objects are acquired when
* the Metal backend is initialized. This method allows clients to check for their availability
* earlier.
*
* Calling initialize() is optional and safe to do so multiple times. After initialize() returns
* true, subsequent calls will continue to return true but have no effect.
*
* initialize() must be called from the main thread.
*
* @returns true if the device and command queue have been successfully obtained; false
* otherwise.
*/
bool initialize() noexcept;
/**
* Obtain the preferred Metal device object for the backend to use.
*
* On desktop platforms, there may be multiple GPUs suitable for rendering, and this method is
* free to decide which one to use. On mobile systems with a single GPU, implementations should
* simply return the result of MTLCreateSystemDefaultDevice();
*
* createDevice is called by the Metal backend from the backend thread.
*/
void createDevice(MetalDevice& outDevice) noexcept;
/**
* Create a command submission queue on the Metal device object.
*
* createCommandQueue is called by the Metal backend from the backend thread.
*
* @param device The device which was returned from createDevice()
*/
void createCommandQueue(
MetalDevice& device, MetalCommandQueue& outCommandQueue) noexcept;
/**
* Obtain a MTLCommandBuffer enqueued on this Platform's MTLCommandQueue. The command buffer is
* guaranteed to execute before all subsequent command buffers created either by Filament, or
* further calls to this method.
*
* createAndEnqueueCommandBuffer must be called from the main thread.
*/
void createAndEnqueueCommandBuffer(MetalCommandBuffer& outCommandBuffer) noexcept;
/**
* The action to take if a Drawable cannot be acquired.
*
* Each frame rendered requires a CAMetalDrawable texture, which is presented on-screen at the
* completion of each frame. These are limited and provided round-robin style by the system.
*
* setDrawableFailureBehavior must be called from the main thread.
*/
enum class DrawableFailureBehavior : uint8_t {
/**
* Terminates the application and reports an error message (default).
*/
PANIC,
/*
* Aborts execution of the current frame. The Metal backend will attempt to acquire a new
* drawable at the next frame.
*/
ABORT_FRAME
};
void setDrawableFailureBehavior(DrawableFailureBehavior behavior) noexcept;
DrawableFailureBehavior getDrawableFailureBehavior() const noexcept;
private:
PlatformMetalImpl* pImpl = nullptr;
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_PRIVATE_PLATFORMMETAL_H

View File

@ -0,0 +1,85 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_OSMESA_H
#define TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_OSMESA_H
#include <stdint.h>
#include "bluegl/BlueGL.h"
#if defined(__linux__)
#include <osmesa.h>
#elif defined(__APPLE__)
#undef GLAPI
#include <GL/osmesa.h>
#endif
#include <backend/platforms/OpenGLPlatform.h>
#include <backend/DriverEnums.h>
#include <unordered_map>
#include <thread>
#include <shared_mutex>
#include <memory>
namespace filament::backend {
/**
* A concrete implementation of OpenGLPlatform that uses OSMesa, which is an offscreen
* context that can be used in conjunction with Mesa for software rasterization.
* See https://docs.mesa3d.org/osmesa.html for more information.
*/
class PlatformOSMesa : public OpenGLPlatform {
protected:
// --------------------------------------------------------------------------------------------
// Platform Interface
Driver* createDriver(void* sharedGLContext, const DriverConfig& driverConfig) override;
int getOSVersion() const noexcept final override { return 0; }
// --------------------------------------------------------------------------------------------
// OpenGLPlatform Interface
void terminate() noexcept override;
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
bool makeCurrent(ContextType type, SwapChain* drawSwapChain,
SwapChain* readSwapChain) override;
void commit(SwapChain* swapChain) noexcept override;
bool isExtraContextSupported() const noexcept override;
void createContext(bool shared) override;
void releaseContext() noexcept override;
private:
OSMesaContext mContext;
void* mOsMesaApi = nullptr;
struct ContextInfo {
OSMesaContext context;
std::unique_ptr<uint8_t[]> buffer;
};
using ContextMap = std::unordered_map<std::thread::id, ContextInfo>;
ContextMap mAdditionalContexts;
mutable std::shared_mutex mAdditionalContextsLock;
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_OSMESA_H

View File

@ -0,0 +1,74 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_WGL_H
#define TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_WGL_H
#include <stdint.h>
#include <windows.h>
#include "utils/unwindows.h"
#include <backend/platforms/OpenGLPlatform.h>
#include <backend/DriverEnums.h>
#include <vector>
namespace filament::backend {
/**
* A concrete implementation of OpenGLPlatform that supports WGL.
*/
class PlatformWGL : public OpenGLPlatform {
protected:
// --------------------------------------------------------------------------------------------
// Platform Interface
Driver* createDriver(void* sharedGLContext,
const Platform::DriverConfig& driverConfig) override;
int getOSVersion() const noexcept final override { return 0; }
// --------------------------------------------------------------------------------------------
// OpenGLPlatform Interface
void terminate() noexcept override;
bool isExtraContextSupported() const noexcept override;
void createContext(bool shared) override;
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) override;
void commit(SwapChain* swapChain) noexcept override;
protected:
HGLRC mContext = NULL;
HWND mHWnd = NULL;
HDC mWhdc = NULL;
PIXELFORMATDESCRIPTOR mPfd = {};
std::vector<int> mAttribs;
// For shared contexts
static constexpr int SHARED_CONTEXT_NUM = 2;
std::vector<HGLRC> mAdditionalContexts;
std::atomic<int> mNextFreeSharedContextIndex{0};
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_GLX_H

View File

@ -0,0 +1,54 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_WEBGL_H
#define TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_WEBGL_H
#include <stdint.h>
#include <backend/platforms/OpenGLPlatform.h>
#include <backend/DriverEnums.h>
namespace filament::backend {
/**
* A concrete implementation of OpenGLPlatform that supports WebGL.
*/
class PlatformWebGL : public OpenGLPlatform {
protected:
// --------------------------------------------------------------------------------------------
// Platform Interface
Driver* createDriver(void* sharedGLContext, const DriverConfig& driverConfig) override;
int getOSVersion() const noexcept override;
// --------------------------------------------------------------------------------------------
// OpenGLPlatform Interface
void terminate() noexcept override;
SwapChain* createSwapChain(void* nativeWindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) override;
void commit(SwapChain* swapChain) noexcept override;
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_WEBGL_H

View File

@ -0,0 +1,573 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORM_H
#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORM_H
#include <backend/CallbackHandler.h>
#include <backend/DriverEnums.h>
#include <backend/Platform.h>
#include <bluevk/BlueVK.h>
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Hash.h>
#include <utils/PrivateImplementation.h>
#include <cstring>
#include <cstddef>
#include <functional>
#include <tuple>
#include <unordered_set>
#include <stddef.h>
#include <stdint.h>
namespace filament::backend {
using SwapChain = Platform::SwapChain;
/**
* Private implementation details for the provided vulkan platform.
*/
struct VulkanPlatformPrivate;
// Forward declare the fence status that will be maintained by the command
// buffer manager.
struct VulkanCmdFence;
/**
* A Platform interface that creates a Vulkan backend.
*/
class VulkanPlatform : public Platform, utils::PrivateImplementation<VulkanPlatformPrivate> {
public:
/**
* Encapsulates information required to instantiate a known external format,
* typically for the purpose of preloading a pipeline cache for materials using
* external formats for samplers.
*/
struct ExternalYcbcrFormat {
uint64_t externalFormat;
VkSamplerYcbcrModelConversion ycbcrModelConversion;
VkSamplerYcbcrRange ycbcrRange;
};
struct ExtensionHashFn {
std::size_t operator()(utils::CString const& s) const noexcept {
return std::hash<utils::CString>{}(s.data());
}
};
// Note: utils::CString::operator== has an edge case that breaks for the extension set.
// Instead, we'll provide our own comparator.
struct ExtensionEqualFn {
bool operator()(utils::CString const& a, utils::CString const& b) const noexcept {
return strcmp(a.c_str(), b.c_str()) == 0;
}
};
// Utility for managing device or instance extensions during initialization.
using ExtensionSet = std::unordered_set<utils::CString, ExtensionHashFn, ExtensionEqualFn>;
/**
* A collection of handles to objects and metadata that comprises a Vulkan context. The client
* can instantiate this struct and pass to Engine::Builder::sharedContext if they wishes to
* share their vulkan context. This is specifically necessary if the client wishes to override
* the swapchain API.
*/
struct VulkanSharedContext {
VkInstance instance = VK_NULL_HANDLE;
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
VkDevice logicalDevice = VK_NULL_HANDLE;
uint32_t graphicsQueueFamilyIndex = 0xFFFFFFFF;
// In the usual case, the client needs to allocate at least one more graphics queue
// for Filament, and this index is the param to pass into vkGetDeviceQueue. In the case
// where the gpu only has one graphics queue. Then the client needs to ensure that no
// concurrent access can occur.
uint32_t graphicsQueueIndex = 0xFFFFFFFF;
bool debugUtilsSupported = false;
bool debugMarkersSupported = false;
bool multiviewSupported = false;
};
/**
* Shorthand for the pointer to the Platform SwapChain struct, we use it also as a handle (i.e.
* identifier for the swapchain).
*/
using SwapChainPtr = Platform::SwapChain*;
/**
* Collection of images, formats, and extent (width/height) that defines the swapchain.
*/
struct SwapChainBundle {
utils::FixedCapacityVector<VkImage> colors;
VkImage depth = VK_NULL_HANDLE;
VkFormat colorFormat = VK_FORMAT_UNDEFINED;
VkFormat depthFormat = VK_FORMAT_UNDEFINED;
VkExtent2D extent = {0, 0};
uint32_t layerCount = 1;
bool isProtected = false;
};
struct ImageSyncData {
static constexpr uint32_t INVALID_IMAGE_INDEX = UINT32_MAX;
// The index of the next image as returned by vkAcquireNextImage or equivalent.
uint32_t imageIndex = INVALID_IMAGE_INDEX;
// Semaphore to be signaled once the image is available.
VkSemaphore imageReadySemaphore = VK_NULL_HANDLE;
};
VulkanPlatform();
~VulkanPlatform() override;
Driver* createDriver(void* sharedContext,
Platform::DriverConfig const& driverConfig) override;
int getOSVersion() const noexcept override {
return 0;
}
utils::CString getDeviceInfo(DeviceInfoType infoType, Driver* driver) const noexcept override;
// ----------------------------------------------------
// ---------- Platform Customization options ----------
struct Customization {
/**
* The client can specify the GPU (i.e. VkDevice) for the platform. We allow the
* following preferences:
* 1) A substring to match against `VkPhysicalDeviceProperties.deviceName`. Empty string
* by default.
* 2) Index of the device in the list as returned by
* `vkEnumeratePhysicalDevices`. -1 by default to indicate no preference.
*/
struct GPUPreference {
utils::CString deviceName;
int8_t index = -1;
} gpu;
/**
* Whether the platform supports sRGB swapchain. Default is true.
*/
bool isSRGBSwapChainSupported = true;
/**
* When the platform window is resized, we will flush and wait on the command queues
* before recreating the swapchain. Default is true.
*/
bool flushAndWaitOnWindowResize = true;
/**
* Whether the swapchain image should be transitioned to a layout suitable for
* presentation. Default is true.
*/
bool transitionSwapChainImageLayoutForPresent = true;
/**
* The number of frames before an unused framebuffer is evicted from the cache.
* Default is 3.
*/
uint32_t timeBeforeEvictionFbo = 3;
};
/**
* Client can override to indicate customized behavior or parameter for their platform.
* @return `Customization` struct that indicates the client's platform
* customizations.
*/
virtual Customization getCustomization() const noexcept {
return {};
}
// -------- End platform customization options --------
// ----------------------------------------------------
/**
* Get the images handles and format of the memory backing the swapchain. This should be called
* after createSwapChain() or after recreateIfResized().
* @param swapchain The handle returned by createSwapChain()
* @return An array of VkImages
*/
virtual SwapChainBundle getSwapChainBundle(SwapChainPtr handle);
/**
* Acquire the next image for rendering. The `index` will be written with an non-negative
* integer that the backend can use to index into the `SwapChainBundle.colors` array. The
* corresponding VkImage will be used as the output color attachment. The client should signal
* the `clientSignal` semaphore when the image is ready to be used by the backend.
* @param handle The handle returned by createSwapChain()
* @param outImageSyncData The synchronization data used for image readiness
* @return Result of acquire
*/
virtual VkResult acquire(SwapChainPtr handle, ImageSyncData* outImageSyncData);
/**
* Present the image corresponding to `index` to the display. The client should wait on
* `finishedDrawing` before presenting.
* @param handle The handle returned by createSwapChain()
* @param index Index that corresponding to an image in the
* `SwapChainBundle.colors` array.
* @param finishedDrawing Backend passes in a semaphore that the client will signal to
* indicate that the client may render into the image.
* @return Result of present
*/
virtual VkResult present(SwapChainPtr handle, uint32_t index, VkSemaphore finishedDrawing);
/**
* Check if the surface size has changed.
* @param handle The handle returned by createSwapChain()
* @return Whether the swapchain has been resized
*/
virtual bool hasResized(SwapChainPtr handle);
/**
* Check if the surface is protected.
* @param handle The handle returned by createSwapChain()
* @return Whether the swapchain is protected
*/
virtual bool isProtected(SwapChainPtr handle);
/**
* Carry out a recreation of the swapchain.
* @param handle The handle returned by createSwapChain()
* @return Result of the recreation
*/
virtual VkResult recreate(SwapChainPtr handle);
/**
* Create a swapchain given a platform window, or if given a null `nativeWindow`, then we
* try to create a headless swapchain with the given `extent`.
* @param flags Optional parameters passed to the client as defined in
* Filament::SwapChain.h.
* @param extent Optional width and height that indicates the size of the headless swapchain.
* @return Result of the operation
*/
virtual SwapChainPtr createSwapChain(void* nativeWindow, uint64_t flags = 0,
VkExtent2D extent = {0, 0});
/**
* Creates a Platform::Sync object, which tracks a fence and its status,
* and allows conversion to an external sync.
* @param fence The underlying VkFence to use for synchronization.
* @param fenceStatus An object tracking the fence's state
* @return A Platform::Sync object tracking the provided fence.
*/
virtual Platform::Sync* createSync(VkFence fence,
std::shared_ptr<VulkanCmdFence> fenceStatus) noexcept;
/**
* Destroys a sync. If called with a sync not created by this platform
* object, this will lead to undefined behavior.
*
* @param sync The sync to destroy, which was created by this platform
* instance.
*/
virtual void destroySync(Platform::Sync* sync) noexcept;
/**
* Allows implementers to provide instance extensions that they'd like to include in the
* instance creation.
* @return A set of extensions to enable for the instance.
*/
virtual ExtensionSet getRequiredInstanceExtensions() { return {}; }
/**
* Destroy the swapchain.
* @param handle The handle returned by createSwapChain()
*/
virtual void destroy(SwapChainPtr handle);
/**
* Clean up any resources owned by the Platform. For example, if the Vulkan instance handle was
* generated by the platform, we need to clean it up in this method.
*/
virtual void terminate();
/**
* @return The instance (VkInstance) for the Vulkan backend.
*/
VkInstance getInstance() const noexcept;
/**
* @return The logical device (VkDevice) that was selected as the backend device.
*/
VkDevice getDevice() const noexcept;
/**
* @return The physical device (i.e gpu) that was selected as the backend physical device.
*/
VkPhysicalDevice getPhysicalDevice() const noexcept;
/**
* @return The family index of the graphics queue selected for the Vulkan backend.
*/
uint32_t getGraphicsQueueFamilyIndex() const noexcept;
/**
* @return The index of the graphics queue (if there are multiple graphics queues)
* selected for the Vulkan backend.
*/
uint32_t getGraphicsQueueIndex() const noexcept;
/**
* @return The queue that was selected for the Vulkan backend.
*/
VkQueue getGraphicsQueue() const noexcept;
/**
* @return The family index of the protected graphics queue selected for the
* Vulkan backend.
*/
uint32_t getProtectedGraphicsQueueFamilyIndex() const noexcept;
/**
* @return The index of the protected graphics queue (if there are multiple
* graphics queues) selected for the Vulkan backend.
*/
uint32_t getProtectedGraphicsQueueIndex() const noexcept;
/**
* @return The protected queue that was selected for the Vulkan backend.
*/
VkQueue getProtectedGraphicsQueue() const noexcept;
struct ExternalImageMetadata {
/**
* The Filament texture format.
*/
TextureFormat filamentFormat;
/**
* The Filament texture usage.
*/
TextureUsage filamentUsage;
/**
* The width of the external image
*/
uint32_t width;
/**
* The height of the external image
*/
uint32_t height;
/**
* The layer count of the external image
*/
uint32_t layers;
/**
* The numbers of samples per texel
*/
VkSampleCountFlagBits samples;
/**
* The format of the external image
*/
VkFormat format;
/**
* The type of external format (opaque int) if used.
*/
uint64_t externalFormat;
/**
* Image usage
*/
VkImageUsageFlags usage;
/**
* Allocation size
*/
VkDeviceSize allocationSize;
/**
* Heap information
*/
uint32_t memoryTypeBits;
/**
* Ycbcr conversion components
*/
VkComponentMapping ycbcrConversionComponents;
/**
* Ycbcr model
*/
VkSamplerYcbcrModelConversion ycbcrModel;
/**
* Ycbcr range
*/
VkSamplerYcbcrRange ycbcrRange;
/**
* Ycbcr x chroma offset
*/
VkChromaLocation xChromaOffset;
/**
* Ycbcr y chroma offset
*/
VkChromaLocation yChromaOffset;
};
// Note that the image metadata might change per-frame, hence we need a method for extracting
// it.
virtual ExternalImageMetadata extractExternalImageMetadata(ExternalImageHandleRef image) const {
return {};
}
struct ImageData {
struct Bundle {
VkImage image = VK_NULL_HANDLE;
VkDeviceMemory memory = VK_NULL_HANDLE;
inline bool valid() const noexcept {
return image != VK_NULL_HANDLE;
}
};
// It's possible for the external image to also have a known VK format. We need to create an
// image for that in case we are not looking to use an external "sampler" with this image.
Bundle internal;
// If we get a externalFormat in the metadata, then we should create an image with
// VK_FORMAT_UNDEFINED
Bundle external;
};
virtual ImageData createVkImageFromExternal(ExternalImageHandleRef image) const {
return {};
}
protected:
struct VulkanSync : public Platform::Sync {
VkFence fence;
std::shared_ptr<VulkanCmdFence> fenceStatus;
};
/**
* Creates the VkInstance used by Filament's Vulkan backend.
*
* This method can be overridden in subclasses to customize VkInstance creation, such as
* adding application-specific layers or extensions.
*
* The provided `createInfo` contains layers and extensions required by Filament.
* If you override this method and need to modify the `createInfo` struct, you must first
* make a copy of it and modify the copy.
*
* @param createInfo The VkInstanceCreateInfo prepared by Filament.
* @return The created VkInstance, or VK_NULL_HANDLE on failure.
*/
virtual VkInstance createVkInstance(VkInstanceCreateInfo const& createInfo) noexcept;
/**
* Selects a VkPhysicalDevice (GPU) for Filament's Vulkan backend to use.
*
* This method can be overridden in subclasses to implement custom GPU selection logic.
* For example, an application might override this to prefer a discrete GPU over an
* integrated one based on device properties.
*
* The default implementation selects the first device that meets Filament's requirements.
*
* @param instance The VkInstance to enumerate devices from.
* @return The selected VkPhysicalDevice, or VK_NULL_HANDLE if no suitable device is found.
*/
virtual VkPhysicalDevice selectVkPhysicalDevice(VkInstance instance) noexcept;
/**
* Creates the VkDevice used by Filament's Vulkan backend.
*
* This method can be overridden in subclasses to customize VkDevice creation, such as
* adding application-specific extensions or enabling features.
*
* The provided `createInfo` contains extensions and features required by Filament.
* If you override this method and need to modify the `createInfo` struct, you must first
* make a copy of it and modify the copy.
*
* @param createInfo The VkDeviceCreateInfo prepared by Filament.
* @return The created VkDevice, or VK_NULL_HANDLE on failure.
*/
virtual VkDevice createVkDevice(VkDeviceCreateInfo const& createInfo) noexcept;
using SurfaceBundle = std::tuple<VkSurfaceKHR, VkExtent2D>;
virtual ExtensionSet getSwapchainInstanceExtensions() const = 0;
virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) const noexcept = 0;
virtual VkExternalFenceHandleTypeFlagBits getFenceExportFlags() const noexcept;
/**
* Query if transient attachments are supported by the backend.
*/
bool isTransientAttachmentSupported() const noexcept;
/**
* For pipeline cache prewarming, if external samplers are present, we need to build
* the fake pipeline using the proper formats specified. Since there's no way to
* get these at material build time, we allow the app to register them before
* creating materials.
*
* @param format The format, containing the external format value which should be
* extracted from an AHardwareBuffer.
*/
void registerPipelineCachePrewarmExternalFormat(const ExternalYcbcrFormat& format) noexcept;
private:
/**
* Contains information about features that should be requested
* when calling vkCreateDevice, based on feature support from
* vkGetPhysicalDeviceFeatures2.
*/
struct MiscDeviceFeatures {
/**
* This allows creation of a VkGraphicsPipeline without a
* render pass specified.
*/
bool dynamicRendering;
/**
* Allows creation of a 2d image view, or 2d image view array,
* to be created from a 3d VkImage.
*/
bool imageView2Don3DImage;
/**
* Desired global priority value for all VkQueue at a system level.
*/
Platform::GpuContextPriority gpuContextPriority = Platform::GpuContextPriority::DEFAULT;
};
void createInstance(ExtensionSet const& requiredExts) noexcept;
void queryAndSetDeviceFeatures(Platform::DriverConfig const& driverConfig,
ExtensionSet const& instExts, ExtensionSet const& deviceExts,
void* sharedContext) noexcept;
void createLogicalDeviceAndQueues(ExtensionSet const& deviceExtensions,
VkPhysicalDeviceFeatures const& features,
VkPhysicalDeviceVulkan11Features const& vk11Features, bool createProtectedQueue,
MiscDeviceFeatures const& requestedFeatures) noexcept;
friend struct VulkanPlatformPrivate;
};
}// namespace filament::backend
#endif// TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORM_H

View File

@ -0,0 +1,110 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
#include "AndroidNdk.h"
#include <backend/DriverEnums.h>
#include <backend/platforms/VulkanPlatform.h>
#include <utils/compiler.h>
#include <android/hardware_buffer.h>
namespace filament::backend {
class VulkanPlatformAndroid : public VulkanPlatform, public AndroidNdk {
public:
ExternalImageHandle UTILS_PUBLIC createExternalImage(AHardwareBuffer const* buffer,
bool sRGB) noexcept;
struct UTILS_PUBLIC ExternalImageDescAndroid {
uint32_t width; // Texture width
uint32_t height; // Texture height
TextureFormat format;// Texture format
TextureUsage usage; // Texture usage flags
};
VulkanPlatformAndroid();
~VulkanPlatformAndroid() noexcept override;
ExternalImageDescAndroid UTILS_PUBLIC getExternalImageDesc(
ExternalImageHandleRef externalImage) const noexcept;
ExternalImageMetadata extractExternalImageMetadata(
ExternalImageHandleRef image) const override;
ImageData createVkImageFromExternal(ExternalImageHandleRef image) const override;
/**
* Converts a sync to an external file descriptor, if possible. Accepts an
* opaque handle to a sync, as well as a pointer to where the fd should be
* stored.
* @param sync The sync to be converted to a file descriptor.
* @param fd A pointer to where the file descriptor should be stored.
* @return `true` on success, `false` on failure. The default implementation
* returns `false`.
*/
bool convertSyncToFd(Sync* sync, int* fd) const noexcept;
int getOSVersion() const noexcept override;
void terminate() override;
Driver* createDriver(void* sharedContext,
DriverConfig const& driverConfig) override;
bool isCompositorTimingSupported() const noexcept override;
bool queryCompositorTiming(SwapChain const* swapchain,
CompositorTiming* outCompositorTiming) const noexcept override;
bool setPresentFrameId(SwapChain const* swapchain, uint64_t frameId) noexcept override;
bool queryFrameTimestamps(SwapChain const* swapchain, uint64_t frameId,
FrameTimestamps* outFrameTimestamps) const noexcept override;
protected:
ExtensionSet getSwapchainInstanceExtensions() const override;
using SurfaceBundle = SurfaceBundle;
SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) const noexcept override;
VkExternalFenceHandleTypeFlagBits getFenceExportFlags() const noexcept override;
private:
struct AndroidDetails;
struct ExternalImageVulkanAndroid : public ExternalImage {
AHardwareBuffer* aHardwareBuffer = nullptr;
bool sRGB = false;
protected:
~ExternalImageVulkanAndroid() override;
};
AndroidDetails& mAndroidDetails;
int mOSVersion{};
};
}// namespace filament::backend
#endif// TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H

View File

@ -0,0 +1,33 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORMAPPLE_H
#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORMAPPLE_H
#include <backend/platforms/VulkanPlatform.h>
namespace filament::backend {
class VulkanPlatformApple : public VulkanPlatform {
protected:
ExtensionSet getSwapchainInstanceExtensions() const override;
SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) const noexcept override;
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORMAPPLE_H

View File

@ -0,0 +1,33 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORMLINUX_H
#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORMLINUX_H
#include <backend/platforms/VulkanPlatform.h>
namespace filament::backend {
class VulkanPlatformLinux : public VulkanPlatform {
protected:
ExtensionSet getSwapchainInstanceExtensions() const override;
SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) const noexcept override;
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORMLINUX_H

View File

@ -0,0 +1,33 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORMWINDOWS_H
#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORMWINDOWS_H
#include <backend/platforms/VulkanPlatform.h>
namespace filament::backend {
class VulkanPlatformWindows : public VulkanPlatform {
protected:
ExtensionSet getSwapchainInstanceExtensions() const override;
SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) const noexcept override;
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORMWINDOWS_H

View File

@ -0,0 +1,95 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORM_H
#define TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORM_H
#include <backend/Platform.h>
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_X11)
// Resolve the conflicts between webgpu_cpp.h and X11 defines
#undef Always
#undef Success
#undef None
#undef True
#undef False
#undef Status
#undef Bool
#endif
#include <webgpu/webgpu_cpp.h>
#if defined(__EMSCRIPTEN__)
// We need to polyfill some Dawn extensions that are not in Emscripten.
#include "WebGPUWasmPolyfill.h"
#endif
#include <cstdint>
#include <vector>
namespace filament::backend {
/**
* A Platform interface, handling the environment-specific concerns, e.g. OS, for creating a WebGPU
* driver (backend).
*/
class WebGPUPlatform : public Platform {
public:
WebGPUPlatform();
~WebGPUPlatform() override = default;
[[nodiscard]] int getOSVersion() const noexcept final { return 0; }
[[nodiscard]] utils::CString getDeviceInfo(DeviceInfoType, Driver*) const noexcept override {
return {};
}
[[nodiscard]] wgpu::Instance& getInstance() noexcept { return mInstance; }
// TODO consider that this functionality is not WebGPU-specific, and thus could be
// placed in a generic place and even reused across backends. Alternatively,
// a 3rd party library could be considered. However, this was a simple and
// quick change and works for now.
// gets the size (height and width) of the surface/window
[[nodiscard]] virtual wgpu::Extent2D getSurfaceExtent(void* nativeWindow) const = 0;
// either returns a valid surface or panics
[[nodiscard]] virtual wgpu::Surface createSurface(void* nativeWindow, uint64_t flags) = 0;
// either returns a valid adapter or panics
[[nodiscard]] virtual wgpu::Adapter requestAdapter(wgpu::Surface const& surface);
// either returns a valid device or panics
[[nodiscard]] virtual wgpu::Device requestDevice(wgpu::Adapter const& adapter);
struct Configuration {
wgpu::BackendType forceBackendType = wgpu::BackendType::Undefined;
};
[[nodiscard]] virtual Configuration getConfiguration() const noexcept {
return {};
}
protected:
[[nodiscard]] Driver* createDriver(void* sharedContext,
const Platform::DriverConfig& driverConfig) override;
// returns adapter request option variations applicable for the particular
// platform
[[nodiscard]] virtual std::vector<wgpu::RequestAdapterOptions> getAdapterOptions() = 0;
// we may consider having the driver own this in the future
wgpu::Instance mInstance;
};
}// namespace filament::backend
#endif// TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORM_H

View File

@ -0,0 +1,35 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMANDROID_H
#define TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMANDROID_H
#include <backend/platforms/WebGPUPlatform.h>
namespace filament::backend {
class WebGPUPlatformAndroid : public WebGPUPlatform {
public:
wgpu::Extent2D getSurfaceExtent(void* nativeWindow) const override;
wgpu::Surface createSurface(void* nativeWindow, uint64_t /*flags*/) override;
protected:
std::vector<wgpu::RequestAdapterOptions> getAdapterOptions() override;
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMANDROID_H

View File

@ -0,0 +1,35 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMAPPLE_H
#define TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMAPPLE_H
#include <backend/platforms/WebGPUPlatform.h>
namespace filament::backend {
class WebGPUPlatformApple : public WebGPUPlatform {
public:
wgpu::Extent2D getSurfaceExtent(void* nativeWindow) const override;
wgpu::Surface createSurface(void* nativeWindow, uint64_t /*flags*/) override;
protected:
std::vector<wgpu::RequestAdapterOptions> getAdapterOptions() override;
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMAPPLE_H

View File

@ -0,0 +1,35 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMLINUX_H
#define TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMLINUX_H
#include <backend/platforms/WebGPUPlatform.h>
namespace filament::backend {
class WebGPUPlatformLinux : public WebGPUPlatform {
public:
wgpu::Extent2D getSurfaceExtent(void* nativeWindow) const override;
wgpu::Surface createSurface(void* nativeWindow, uint64_t /*flags*/) override;
protected:
std::vector<wgpu::RequestAdapterOptions> getAdapterOptions() override;
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMLINUX_H

View File

@ -0,0 +1,40 @@
/*
* Copyright (C) 2026 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMWASM_H
#define TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMWASM_H
#include <backend/platforms/WebGPUPlatform.h>
namespace filament::backend {
class WebGPUPlatformWasm : public WebGPUPlatform {
public:
wgpu::Extent2D getSurfaceExtent(void* nativeWindow) const override;
wgpu::Surface createSurface(void* nativeWindow, uint64_t /*flags*/) override;
wgpu::Adapter requestAdapter(wgpu::Surface const& surface) override;
wgpu::Device requestDevice(wgpu::Adapter const& adapter) override;
static wgpu::Device sDevice;
protected:
std::vector<wgpu::RequestAdapterOptions> getAdapterOptions() override;
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMWASM_H

View File

@ -0,0 +1,35 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMWINDOWS_H
#define TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMWINDOWS_H
#include <backend/platforms/WebGPUPlatform.h>
namespace filament::backend {
class WebGPUPlatformWindows : public WebGPUPlatform {
public:
wgpu::Extent2D getSurfaceExtent(void* nativeWindow) const override;
wgpu::Surface createSurface(void* nativeWindow, uint64_t /*flags*/) override;
protected:
std::vector<wgpu::RequestAdapterOptions> getAdapterOptions() override;
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMWINDOWS_H

View File

@ -0,0 +1,70 @@
/*
* Copyright (C) 2026 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_WEBGPU_WASM_POLYFILL_H
#define TNT_FILAMENT_BACKEND_WEBGPU_WASM_POLYFILL_H
#if defined(__EMSCRIPTEN__)
#include <webgpu/webgpu_cpp.h>
#include <cstdint>
namespace wgpu {
struct Extent2D {
uint32_t width = 0;
uint32_t height = 0;
operator Extent3D() const { return {width, height, 1}; }
};
struct Origin2D {
uint32_t x = 0;
uint32_t y = 0;
operator Origin3D() const { return {x, y, 0}; }
};
// b/508270158
enum class ComponentSwizzle : uint32_t {
Undefined = 0,
Zero = 1,
One = 2,
R = 3,
G = 4,
B = 5,
A = 6,
};
struct TextureComponentSwizzle {
ComponentSwizzle r = ComponentSwizzle::Undefined;
ComponentSwizzle g = ComponentSwizzle::Undefined;
ComponentSwizzle b = ComponentSwizzle::Undefined;
ComponentSwizzle a = ComponentSwizzle::Undefined;
};
struct DawnTogglesDescriptor : public ChainedStruct {
uint32_t enabledToggleCount = 0;
const char* const* enabledToggles = nullptr;
};
struct DawnAdapterPropertiesPowerPreference : public ChainedStructOut {
PowerPreference powerPreference = PowerPreference::Undefined;
};
} // namespace wgpu
#endif // __EMSCRIPTEN__
#endif // TNT_FILAMENT_BACKEND_WEBGPU_WASM_POLYFILL_H

View File

@ -0,0 +1,85 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CAMUTILS_BOOKMARK_H
#define CAMUTILS_BOOKMARK_H
#include <camutils/compiler.h>
#include <math/vec2.h>
#include <math/vec3.h>
namespace filament {
namespace camutils {
template <typename FLOAT> class FreeFlightManipulator;
template <typename FLOAT> class OrbitManipulator;
template <typename FLOAT> class MapManipulator;
template <typename FLOAT> class Manipulator;
enum class Mode { ORBIT, MAP, FREE_FLIGHT };
/**
* Opaque memento to a viewing position and orientation (e.g. the "home" camera position).
*
* This little struct is meant to be passed around by value and can be used to track camera
* animation between waypoints. In map mode this implements Van Wijk interpolation.
*
* @see Manipulator::getCurrentBookmark, Manipulator::jumpToBookmark
*/
template <typename FLOAT>
struct CAMUTILS_PUBLIC Bookmark {
/**
* Interpolates between two bookmarks. The t argument must be between 0 and 1 (inclusive), and
* the two endpoints must have the same mode (ORBIT or MAP).
*/
static Bookmark<FLOAT> interpolate(Bookmark<FLOAT> a, Bookmark<FLOAT> b, double t);
/**
* Recommends a duration for animation between two MAP endpoints. The return value is a unitless
* multiplier.
*/
static double duration(Bookmark<FLOAT> a, Bookmark<FLOAT> b);
private:
struct MapParams {
FLOAT extent;
filament::math::vec2<FLOAT> center;
};
struct OrbitParams {
FLOAT phi;
FLOAT theta;
FLOAT distance;
filament::math::vec3<FLOAT> pivot;
};
struct FlightParams {
FLOAT pitch;
FLOAT yaw;
filament::math::vec3<FLOAT> position;
};
Mode mode;
MapParams map;
OrbitParams orbit;
FlightParams flight;
friend class FreeFlightManipulator<FLOAT>;
friend class OrbitManipulator<FLOAT>;
friend class MapManipulator<FLOAT>;
};
} // namespace camutils
} // namespace filament
#endif // CAMUTILS_BOOKMARK_H

View File

@ -0,0 +1,301 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CAMUTILS_MANIPULATOR_H
#define CAMUTILS_MANIPULATOR_H
#include <camutils/Bookmark.h>
#include <camutils/compiler.h>
#include <math/vec2.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <cstdint>
namespace filament {
namespace camutils {
enum class Fov { VERTICAL, HORIZONTAL };
/**
* Helper that enables camera interaction similar to sketchfab or Google Maps.
*
* Clients notify the camera manipulator of various mouse or touch events, then periodically call
* its getLookAt() method so that they can adjust their camera(s). Three modes are supported: ORBIT,
* MAP, and FREE_FLIGHT. To construct a manipulator instance, the desired mode is passed into the
* create method.
*
* Usage example:
*
* using CameraManipulator = camutils::Manipulator<float>;
* CameraManipulator* manip;
*
* void init() {
* manip = CameraManipulator::Builder()
* .viewport(1024, 768)
* .build(camutils::Mode::ORBIT);
* }
*
* void onMouseDown(int x, int y) {
* manip->grabBegin(x, y, false);
* }
*
* void onMouseMove(int x, int y) {
* manip->grabUpdate(x, y);
* }
*
* void onMouseUp(int x, int y) {
* manip->grabEnd();
* }
*
* void gameLoop() {
* while (true) {
* filament::math::float3 eye, center, up;
* manip->getLookAt(&eye, &center, &up);
* camera->lookAt(eye, center, up);
* render();
* }
* }
*
* @see Bookmark
*/
template <typename FLOAT>
class CAMUTILS_PUBLIC Manipulator {
public:
using vec2 = filament::math::vec2<FLOAT>;
using vec3 = filament::math::vec3<FLOAT>;
using vec4 = filament::math::vec4<FLOAT>;
/** Opaque handle to a viewing position and orientation to facilitate camera animation. */
using Bookmark = filament::camutils::Bookmark<FLOAT>;
/** Optional raycasting function to enable perspective-correct panning. */
typedef bool (*RayCallback)(const vec3& origin, const vec3& dir, FLOAT* t, void* userdata);
/** Builder state, direct access is allowed but Builder methods are preferred. **/
struct Config {
int viewport[2];
vec3 targetPosition;
vec3 upVector;
FLOAT zoomSpeed;
vec3 orbitHomePosition;
vec2 orbitSpeed;
Fov fovDirection;
FLOAT fovDegrees;
FLOAT farPlane;
vec2 mapExtent;
FLOAT mapMinDistance;
vec3 flightStartPosition;
FLOAT flightStartPitch;
FLOAT flightStartYaw;
FLOAT flightMaxSpeed;
FLOAT flightSpeedSteps;
vec2 flightPanSpeed;
FLOAT flightMoveDamping;
vec4 groundPlane;
RayCallback raycastCallback;
void* raycastUserdata;
bool panning = true;
};
struct Builder {
// Common properties
Builder& viewport(int width, int height); //! Width and height of the viewing area
Builder& targetPosition(FLOAT x, FLOAT y, FLOAT z); //! World-space position of interest, defaults to (0,0,0)
Builder& upVector(FLOAT x, FLOAT y, FLOAT z); //! Orientation for the home position, defaults to (0,1,0)
Builder& zoomSpeed(FLOAT val); //! Multiplied with scroll delta, defaults to 0.01
// Orbit mode properties
Builder& orbitHomePosition(FLOAT x, FLOAT y, FLOAT z); //! Initial eye position in world space, defaults to (0,0,1)
Builder& orbitSpeed(FLOAT x, FLOAT y); //! Multiplied with viewport delta, defaults to 0.01
// Map mode properties
Builder& fovDirection(Fov fov); //! The axis that's held constant when viewport changes
Builder& fovDegrees(FLOAT degrees); //! The full FOV (not the half-angle)
Builder& farPlane(FLOAT distance); //! The distance to the far plane
Builder& mapExtent(FLOAT worldWidth, FLOAT worldHeight); //! The ground size for computing home position
Builder& mapMinDistance(FLOAT mindist); //! Constrains the zoom-in level
// Free flight properties
Builder& flightStartPosition(FLOAT x, FLOAT y, FLOAT z); //! Initial eye position in world space, defaults to (0,0,0)
Builder& flightStartOrientation(FLOAT pitch, FLOAT yaw); //! Initial orientation in pitch and yaw, defaults to (0,0)
Builder& flightMaxMoveSpeed(FLOAT maxSpeed); //! The maximum camera speed in world units per second, defaults to 10
Builder& flightSpeedSteps(int steps); //! The number of speed steps adjustable with scroll wheel, defaults to 80
Builder& flightPanSpeed(FLOAT x, FLOAT y); //! Multiplied with viewport delta, defaults to 0.01,0.01
Builder& flightMoveDamping(FLOAT damping); //! Applies a deceleration to camera movement, defaults to 0 (no damping)
//! Lower values give slower damping times, a good default is 15
//! Too high a value may lead to instability
// Raycast properties
Builder& groundPlane(FLOAT a, FLOAT b, FLOAT c, FLOAT d); //! Plane equation used as a raycast fallback
Builder& raycastCallback(RayCallback cb, void* userdata); //! Raycast function for accurate grab-and-pan
Builder& panning(bool enabled); //! Sets whether panning is enabled
/**
* Creates a new camera manipulator, either ORBIT, MAP, or FREE_FLIGHT.
*
* Clients can simply use "delete" to destroy the manipulator.
*/
Manipulator* build(Mode mode);
Config details = {};
};
virtual ~Manipulator() = default;
/**
* Gets the immutable mode of the manipulator.
*/
Mode getMode() const { return mMode; }
/**
* Sets the viewport dimensions. The manipulator uses this to process grab events and raycasts.
*/
void setViewport(int width, int height);
/**
* Gets the current orthonormal basis; this is usually called once per frame.
*/
void getLookAt(vec3* eyePosition, vec3* targetPosition, vec3* upward) const;
/**
* Given a viewport coordinate, picks a point in the ground plane, or in the actual scene if the
* raycast callback was provided.
*/
bool raycast(int x, int y, vec3* result) const;
/**
* Given a viewport coordinate, computes a picking ray (origin + direction).
*/
void getRay(int x, int y, vec3* origin, vec3* dir) const;
/**
* Starts a grabbing session (i.e. the user is dragging around in the viewport).
*
* In MAP mode, this starts a panning session.
* In ORBIT mode, this starts either rotating or strafing.
* In FREE_FLIGHT mode, this starts a nodal panning session.
*
* @param x X-coordinate for point of interest in viewport space
* @param y Y-coordinate for point of interest in viewport space
* @param strafe ORBIT mode only; if true, starts a translation rather than a rotation
*/
virtual void grabBegin(int x, int y, bool strafe) = 0;
/**
* Updates a grabbing session.
*
* This must be called at least once between grabBegin / grabEnd to dirty the camera.
*/
virtual void grabUpdate(int x, int y) = 0;
/**
* Ends a grabbing session.
*/
virtual void grabEnd() = 0;
/**
* Keys used to translate the camera in FREE_FLIGHT mode.
* FORWARD and BACKWARD dolly the camera forwards and backwards.
* LEFT and RIGHT strafe the camera left and right.
* UP and DOWN boom the camera upwards and downwards.
*/
enum class Key {
FORWARD,
LEFT,
BACKWARD,
RIGHT,
UP,
DOWN,
COUNT
};
/**
* Signals that a key is now in the down state.
*
* In FREE_FLIGHT mode, the camera is translated forward and backward and strafed left and right
* depending on the depressed keys. This allows WASD-style movement.
*/
virtual void keyDown(Key key);
/**
* Signals that a key is now in the up state.
*
* @see keyDown
*/
virtual void keyUp(Key key);
/**
* In MAP and ORBIT modes, dollys the camera along the viewing direction.
* In FREE_FLIGHT mode, adjusts the move speed of the camera.
*
* @param x X-coordinate for point of interest in viewport space, ignored in FREE_FLIGHT mode
* @param y Y-coordinate for point of interest in viewport space, ignored in FREE_FLIGHT mode
* @param scrolldelta In MAP and ORBIT modes, negative means "zoom in", positive means "zoom out"
* In FREE_FLIGHT mode, negative means "slower", positive means "faster"
*/
virtual void scroll(int x, int y, FLOAT scrolldelta) = 0;
/**
* Processes input and updates internal state.
*
* This must be called once every frame before getLookAt is valid.
*
* @param deltaTime The amount of time, in seconds, passed since the previous call to update.
*/
virtual void update(FLOAT deltaTime);
/**
* Gets a handle that can be used to reset the manipulator back to its current position.
*
* @see jumpToBookmark
*/
virtual Bookmark getCurrentBookmark() const = 0;
/**
* Gets a handle that can be used to reset the manipulator back to its home position.
*
* @see jumpToBookmark
*/
virtual Bookmark getHomeBookmark() const = 0;
/**
* Sets the manipulator position and orientation back to a stashed state.
*
* @see getCurrentBookmark, getHomeBookmark
*/
virtual void jumpToBookmark(const Bookmark& bookmark) = 0;
protected:
Manipulator(Mode mode, const Config& props);
virtual void setProperties(const Config& props);
vec3 raycastFarPlane(int x, int y) const;
const Mode mMode;
Config mProps;
vec3 mEye;
vec3 mTarget;
};
} // namespace camutils
} // namespace filament
#endif /* CAMUTILS_MANIPULATOR_H */

View File

@ -0,0 +1,26 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CAMUTILS_COMPILER_H
#define CAMUTILS_COMPILER_H
#if __has_attribute(visibility)
# define CAMUTILS_PUBLIC __attribute__((visibility("default")))
#else
# define CAMUTILS_PUBLIC
#endif
#endif // CAMUTILS_COMPILER_H

View File

@ -0,0 +1,100 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_ENUMMANAGER_H
#define TNT_ENUMMANAGER_H
#include <algorithm>
#include <string_view>
#include <unordered_map>
#include <filamat/MaterialBuilder.h>
namespace filamat {
using Property = MaterialBuilder::Property;
using UniformType = MaterialBuilder::UniformType;
using SamplerType = MaterialBuilder::SamplerType;
using SubpassType = MaterialBuilder::SubpassType;
using SamplerFormat = MaterialBuilder::SamplerFormat;
using ParameterPrecision = MaterialBuilder::ParameterPrecision;
using OutputTarget = MaterialBuilder::OutputTarget;
using OutputQualifier = MaterialBuilder::VariableQualifier;
using OutputType = MaterialBuilder::OutputType;
using ConstantType = MaterialBuilder::ConstantType;
using ShaderStageType = MaterialBuilder::ShaderStageFlags;
// Convenience methods to convert std::string_view to Enum and also iterate over Enum values.
class Enums {
public:
// Returns true if string "s" is a valid string representation of an element of enum T.
template<typename T>
static bool isValid(const std::string_view& s) noexcept {
std::unordered_map<std::string_view, T>& map = getMap<T>();
return map.find(s) != map.end();
}
// Return enum matching its string representation. Returns undefined if s is not a valid enum T
// value. You should always call isValid() first to validate a string before calling toEnum().
template<typename T>
static T toEnum(const std::string_view& s) noexcept {
std::unordered_map<std::string_view, T>& map = getMap<T>();
return map.at(s);
}
template<typename T>
static std::string_view toString(T t) noexcept;
// Return a map of all values in an enum with their string representation.
template<typename T>
static std::unordered_map<std::string_view, T>& map() noexcept {
auto& map = getMap<T>();
return map;
};
private:
template<typename T>
static std::unordered_map<std::string_view, T>& getMap() noexcept;
static std::unordered_map<std::string_view, Property> mStringToProperty;
static std::unordered_map<std::string_view, UniformType> mStringToUniformType;
static std::unordered_map<std::string_view, SamplerType> mStringToSamplerType;
static std::unordered_map<std::string_view, SubpassType> mStringToSubpassType;
static std::unordered_map<std::string_view, SamplerFormat> mStringToSamplerFormat;
static std::unordered_map<std::string_view, ParameterPrecision> mStringToSamplerPrecision;
static std::unordered_map<std::string_view, OutputTarget> mStringToOutputTarget;
static std::unordered_map<std::string_view, OutputQualifier> mStringToOutputQualifier;
static std::unordered_map<std::string_view, OutputType> mStringToOutputType;
static std::unordered_map<std::string_view, ConstantType> mStringToConstantType;
static std::unordered_map<std::string_view, ShaderStageType> mStringToShaderStageType;
};
template<typename T>
std::string_view Enums::toString(T t) noexcept {
auto& map = getMap<T>();
auto result = std::find_if(map.begin(), map.end(), [t](auto& pair) {
return pair.second == t;
});
if (result != map.end()) {
return result->first;
}
return "";
}
} // namespace filamat
#endif //TNT_ENUMMANAGER_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,104 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMAT_PACKAGE_H
#define TNT_FILAMAT_PACKAGE_H
#include <assert.h>
#include <inttypes.h>
#include <stdlib.h>
#include <string.h> // memcpy
#include <cstddef>
#include <functional>
#include <utils/compiler.h>
namespace filamat {
class UTILS_PUBLIC Package {
public:
Package() = default;
// Regular constructor
explicit Package(size_t size) : mSize(size) {
mPayload = new uint8_t[size];
}
Package(const void* src, size_t size) : Package(size) {
memcpy(mPayload, src, size);
}
// Move Constructor
Package(Package&& other) noexcept : mPayload(other.mPayload), mSize(other.mSize),
mValid(other.mValid) {
other.mPayload = nullptr;
other.mSize = 0;
other.mValid = false;
}
// Move assignment
Package& operator=(Package&& other) noexcept {
std::swap(mPayload, other.mPayload);
std::swap(mSize, other.mSize);
std::swap(mValid, other.mValid);
return *this;
}
// Copy assignment operator disallowed.
Package& operator=(const Package& other) = delete;
// Copy constructor disallowed.
Package(const Package& other) = delete;
~Package() {
delete[] mPayload;
}
uint8_t* getData() const noexcept {
return mPayload;
}
size_t getSize() const noexcept {
return mSize;
}
uint8_t* getEnd() const noexcept {
return mPayload + mSize;
}
void setValid(bool valid) noexcept {
mValid = valid;
}
bool isValid() const noexcept {
return mValid;
}
static Package invalidPackage() {
Package package(0);
package.setValid(false);
return package;
}
private:
uint8_t* mPayload = nullptr;
size_t mSize = 0;
bool mValid = true;
};
} // namespace filamat
#endif

View File

@ -0,0 +1,94 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <utils/compiler.h>
#include <stddef.h>
#include <stdint.h>
namespace filament {
namespace backend {
class PixelBufferDescriptor;
}
class Engine;
class Texture;
struct UTILS_PUBLIC FaceOffsets {
using size_type = size_t;
union {
struct {
size_type px; //!< +x face offset in bytes
size_type nx; //!< -x face offset in bytes
size_type py; //!< +y face offset in bytes
size_type ny; //!< -y face offset in bytes
size_type pz; //!< +z face offset in bytes
size_type nz; //!< -z face offset in bytes
};
size_type offsets[6];
};
size_type operator[](size_t const n) const noexcept { return offsets[n]; }
size_type& operator[](size_t const n) { return offsets[n]; }
FaceOffsets() noexcept = default;
explicit FaceOffsets(size_type const faceSize) noexcept {
px = faceSize * 0;
nx = faceSize * 1;
py = faceSize * 2;
ny = faceSize * 3;
pz = faceSize * 4;
nz = faceSize * 5;
}
FaceOffsets(const FaceOffsets& rhs) noexcept {
px = rhs.px;
nx = rhs.nx;
py = rhs.py;
ny = rhs.ny;
pz = rhs.pz;
nz = rhs.nz;
}
FaceOffsets& operator=(const FaceOffsets& rhs) noexcept {
px = rhs.px;
nx = rhs.nx;
py = rhs.py;
ny = rhs.ny;
pz = rhs.pz;
nz = rhs.nz;
return *this;
}
};
/**
* Options for environment prefiltering into reflection map
*
* @see generatePrefilterMipmap()
*/
struct UTILS_PUBLIC PrefilterOptions {
uint16_t sampleCount = 8; //!< sample count used for filtering
bool mirror = true; //!< whether the environment must be mirrored
private:
UTILS_UNUSED uintptr_t reserved[3] = {};
};
UTILS_PUBLIC
void generatePrefilterMipmap(Texture* UTILS_NONNULL texture, Engine& engine,
backend::PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets,
PrefilterOptions const* UTILS_NULLABLE options = nullptr);
} // namespace filament

View File

@ -0,0 +1,349 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_IBL_PREFILTER_IBLPREFILTER_H
#define TNT_IBL_PREFILTER_IBLPREFILTER_H
#include <utils/compiler.h>
#include <utils/Entity.h>
#include <filament/Texture.h>
namespace filament {
class Engine;
class View;
class Scene;
class Renderer;
class Material;
class MaterialInstance;
class VertexBuffer;
class IndexBuffer;
class Camera;
class Texture;
} // namespace filament
/**
* IBLPrefilterContext creates and initializes GPU state common to all environment map filters
* supported. Typically, only one instance per filament Engine of this object needs to exist.
*
* Usage Example:
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* #include <filament/Engine.h>
* using namespace filament;
*
* Engine* engine = Engine::create();
*
* IBLPrefilterContext context(engine);
* IBLPrefilterContext::SpecularFilter filter(context);
* Texture* texture = filter(environment_cubemap);
*
* IndirectLight* indirectLight = IndirectLight::Builder()
* .reflections(texture)
* .build(engine);
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
class UTILS_PUBLIC IBLPrefilterContext {
public:
enum class Kernel : uint8_t {
D_GGX, // Trowbridge-reitz distribution
};
/**
* Creates an IBLPrefilter context.
* @param engine filament engine to use
*/
explicit IBLPrefilterContext(filament::Engine& engine);
/**
* Destroys all GPU resources created during initialization.
*/
~IBLPrefilterContext() noexcept;
// not copyable
IBLPrefilterContext(IBLPrefilterContext const&) = delete;
IBLPrefilterContext& operator=(IBLPrefilterContext const&) = delete;
// movable
IBLPrefilterContext(IBLPrefilterContext&& rhs) noexcept;
IBLPrefilterContext& operator=(IBLPrefilterContext&& rhs) noexcept;
// -------------------------------------------------------------------------------------------
/**
* EquirectangularToCubemap is use to convert an equirectangluar image to a cubemap.
*/
class EquirectangularToCubemap {
public:
struct Config {
bool mirror = true; //!< mirror the source horizontally
};
/**
* Creates a EquirectangularToCubemap processor using the default Config
* @param context IBLPrefilterContext to use
*/
explicit EquirectangularToCubemap(IBLPrefilterContext& context);
/**
* Creates a EquirectangularToCubemap processor using the provided Config
* @param context IBLPrefilterContext to use
*/
EquirectangularToCubemap(IBLPrefilterContext& context, Config const& config);
/**
* Destroys all GPU resources created during initialization.
*/
~EquirectangularToCubemap() noexcept;
EquirectangularToCubemap(EquirectangularToCubemap const&) = delete;
EquirectangularToCubemap& operator=(EquirectangularToCubemap const&) = delete;
EquirectangularToCubemap(EquirectangularToCubemap&& rhs) noexcept;
EquirectangularToCubemap& operator=(EquirectangularToCubemap&& rhs) noexcept;
/**
* Converts an equirectangular image to a cubemap.
* @param equirectangular Texture to convert to a cubemap.
* - Can't be null.
* - Must be a 2d texture
* - Must have equirectangular geometry, that is width == 2*height.
* - Must be allocated with all mip levels.
* - Must be SAMPLEABLE
* @param outCubemap Output cubemap. If null the texture is automatically created
* with default parameters (size of 256 with 9 levels).
* - Must be a cubemap
* - Must have SAMPLEABLE and COLOR_ATTACHMENT usage bits
* @return returns outCubemap
*/
filament::Texture* operator()(
filament::Texture const* equirectangular,
filament::Texture* outCubemap = nullptr);
private:
IBLPrefilterContext& mContext;
filament::Material* mEquirectMaterial = nullptr;
Config mConfig{};
};
/**
* IrradianceFilter is a GPU based implementation of the diffuse probe pre-integration filter.
* An instance of IrradianceFilter is needed per filter configuration. A filter configuration
* contains the filter's kernel and sample count.
*/
class IrradianceFilter {
public:
using Kernel = Kernel;
/**
* Filter configuration.
*/
struct Config {
uint16_t sampleCount = 1024u; //!< filter sample count (max 2048)
Kernel kernel = Kernel::D_GGX; //!< filter kernel
};
/**
* Filtering options for the current environment.
*/
struct Options {
float hdrLinear = 1024.0f; //!< no HDR compression up to this value
float hdrMax = 16384.0f; //!< HDR compression between hdrLinear and hdrMax
float lodOffset = 2.0f; //!< Good values are 2.0 or 3.0. Higher values help with heavily HDR inputs.
bool generateMipmap = true; //!< set to false if the input environment map already has mipmaps
};
/**
* Creates a IrradianceFilter processor.
* @param context IBLPrefilterContext to use
* @param config Configuration of the filter
*/
IrradianceFilter(IBLPrefilterContext& context, Config config);
/**
* Creates a filter with the default configuration.
* @param context IBLPrefilterContext to use
*/
explicit IrradianceFilter(IBLPrefilterContext& context);
/**
* Destroys all GPU resources created during initialization.
*/
~IrradianceFilter() noexcept;
IrradianceFilter(IrradianceFilter const&) = delete;
IrradianceFilter& operator=(IrradianceFilter const&) = delete;
IrradianceFilter(IrradianceFilter&& rhs) noexcept;
IrradianceFilter& operator=(IrradianceFilter&& rhs) noexcept;
/**
* Generates an irradiance cubemap. Mipmaps are not generated even if present.
* @param options Options for this environment
* @param environmentCubemap Environment cubemap (input). Can't be null.
* This cubemap must be SAMPLEABLE and must have all its
* levels allocated. If Options.generateMipmap is true,
* the mipmap levels will be overwritten, otherwise
* it is assumed that all levels are correctly initialized.
* @param outIrradianceTexture Output irradiance texture or, if null, it is
* automatically created with some default parameters.
* outIrradianceTexture must be a cubemap, it must have
* at least COLOR_ATTACHMENT and SAMPLEABLE usages.
*
* @return returns outIrradianceTexture
*/
filament::Texture* operator()(Options options,
filament::Texture const* environmentCubemap,
filament::Texture* outIrradianceTexture = nullptr);
/**
* Generates a prefiltered cubemap.
* @param environmentCubemap Environment cubemap (input). Can't be null.
* This cubemap must be SAMPLEABLE and must have all its
* levels allocated. If Options.generateMipmap is true,
* the mipmap levels will be overwritten, otherwise
* it is assumed that all levels are correctly initialized.
* @param outIrradianceTexture Output irradiance texture or, if null, it is
* automatically created with some default parameters.
* outIrradianceTexture must be a cubemap, it must have
* at least COLOR_ATTACHMENT and SAMPLEABLE usages.
*
* @return returns outReflectionsTexture
*/
filament::Texture* operator()(
filament::Texture const* environmentCubemap,
filament::Texture* outIrradianceTexture = nullptr);
private:
IBLPrefilterContext& mContext;
filament::Material* mKernelMaterial = nullptr;
filament::Texture* mKernelTexture = nullptr;
uint32_t mSampleCount = 0u;
};
/**
* SpecularFilter is a GPU based implementation of the specular probe pre-integration filter.
* An instance of SpecularFilter is needed per filter configuration. A filter configuration
* contains the filter's kernel and sample count.
*/
class SpecularFilter {
public:
using Kernel = Kernel;
/**
* Filter configuration.
*/
struct Config {
uint16_t sampleCount = 1024u; //!< filter sample count (max 2048)
uint8_t levelCount = 5u; //!< number of roughness levels
Kernel kernel = Kernel::D_GGX; //!< filter kernel
};
/**
* Filtering options for the current environment.
*/
struct Options {
float hdrLinear = 1024.0f; //!< no HDR compression up to this value
float hdrMax = 16384.0f; //!< HDR compression between hdrLinear and hdrMax
float lodOffset = 1.0f; //!< Good values are 1.0 or 2.0. Higher values help with heavily HDR inputs.
bool generateMipmap = true; //!< set to false if the input environment map already has mipmaps
};
/**
* Creates a SpecularFilter processor.
* @param context IBLPrefilterContext to use
* @param config Configuration of the filter
*/
SpecularFilter(IBLPrefilterContext& context, Config config);
/**
* Creates a filter with the default configuration.
* @param context IBLPrefilterContext to use
*/
explicit SpecularFilter(IBLPrefilterContext& context);
/**
* Destroys all GPU resources created during initialization.
*/
~SpecularFilter() noexcept;
SpecularFilter(SpecularFilter const&) = delete;
SpecularFilter& operator=(SpecularFilter const&) = delete;
SpecularFilter(SpecularFilter&& rhs) noexcept;
SpecularFilter& operator=(SpecularFilter&& rhs) noexcept;
/**
* Generates a prefiltered cubemap.
* @param options Options for this environment
* @param environmentCubemap Environment cubemap (input). Can't be null.
* This cubemap must be SAMPLEABLE and must have all its
* levels allocated. If Options.generateMipmap is true,
* the mipmap levels will be overwritten, otherwise
* it is assumed that all levels are correctly initialized.
* @param outReflectionsTexture Output prefiltered texture or, if null, it is
* automatically created with some default parameters.
* outReflectionsTexture must be a cubemap, it must have
* at least COLOR_ATTACHMENT and SAMPLEABLE usages and at
* least the same number of levels than requested by Config.
* @return returns outReflectionsTexture
*/
filament::Texture* operator()(Options options,
filament::Texture const* environmentCubemap,
filament::Texture* outReflectionsTexture = nullptr);
/**
* Generates a prefiltered cubemap.
* @param environmentCubemap Environment cubemap (input). Can't be null.
* This cubemap must be SAMPLEABLE and must have all its
* levels allocated. All mipmap levels will be overwritten.
* @param outReflectionsTexture Output prefiltered texture or, if null, it is
* automatically created with some default parameters.
* outReflectionsTexture must be a cubemap, it must have
* at least COLOR_ATTACHMENT and SAMPLEABLE usages and at
* least the same number of levels than requested by Config.
* @return returns outReflectionsTexture
*/
filament::Texture* operator()(
filament::Texture const* environmentCubemap,
filament::Texture* outReflectionsTexture = nullptr);
// TODO: option for progressive filtering
// TODO: add a callback for when the processing is done?
private:
IBLPrefilterContext& mContext;
filament::Material* mKernelMaterial = nullptr;
filament::Texture* mKernelTexture = nullptr;
uint32_t mSampleCount = 0u;
uint8_t mLevelCount = 1u;
};
private:
friend class Filter;
filament::Engine& mEngine;
filament::Renderer* mRenderer{};
filament::Scene* mScene{};
filament::VertexBuffer* mVertexBuffer{};
filament::IndexBuffer* mIndexBuffer{};
filament::Camera* mCamera{};
utils::Entity mFullScreenQuadEntity{};
utils::Entity mCameraEntity{};
filament::View* mView{};
filament::Material* mIntegrationMaterial{};
filament::Material* mIrradianceIntegrationMaterial{};
};
#endif //TNT_IBL_PREFILTER_IBLPREFILTER_H

View File

@ -0,0 +1,188 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_CONFIG_H
#define TNT_CONFIG_H
#include <filamat/MaterialBuilder.h>
#include <filament/MaterialEnums.h>
#include <backend/DriverEnums.h>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <limits>
#include <map>
#include <memory>
#include <ostream>
#include <string>
namespace matp {
class Config {
public:
enum class OutputFormat {
BLOB,
C_HEADER,
MAT,
};
using Platform = filamat::MaterialBuilder::Platform;
using TargetApi = filamat::MaterialBuilder::TargetApi;
using Optimization = filamat::MaterialBuilder::Optimization;
using Workarounds = filamat::MaterialBuilder::Workarounds;
// For defines, template, and material parameters, we use an ordered map with a transparent comparator.
// Even though the key is stored using std::string, this allows you to make lookups using
// std::string_view. There is no need to construct a std::string object just to make a lookup.
using StringReplacementMap = std::map<std::string, std::string, std::less<>>;
enum class Metadata {
NONE,
PARAMETERS
};
virtual ~Config() = default;
class Output {
public:
virtual ~Output() = default;
virtual bool open() noexcept = 0;
virtual bool write(const uint8_t* data, size_t size) noexcept = 0;
virtual std::ostream& getOutputStream() noexcept = 0;
virtual bool close() noexcept = 0;
};
virtual Output* getOutput() const noexcept = 0;
class Input {
public:
virtual ~Input() = default;
virtual ssize_t open() noexcept = 0;
virtual std::unique_ptr<const char[]> read() noexcept = 0;
virtual bool close() noexcept = 0;
virtual const char* getName() const noexcept = 0;
};
virtual Input* getInput() const noexcept = 0;
virtual std::string toString() const noexcept = 0;
virtual std::string toPIISafeString() const noexcept = 0;
bool isDebug() const noexcept {
return mDebug;
}
Platform getPlatform() const noexcept {
return mPlatform;
}
OutputFormat getOutputFormat() const noexcept {
return mOutputFormat;
}
bool isValid() const noexcept {
return mIsValid;
}
Optimization getOptimizationLevel() const noexcept {
return mOptimizationLevel;
}
Metadata getReflectionTarget() const noexcept {
return mReflectionTarget;
}
TargetApi getTargetApi() const noexcept {
return mTargetApi;
}
bool printShaders() const noexcept {
return mPrintShaders;
}
bool saveRawVariants() const noexcept {
return mSaveRawVariants;
}
bool rawShaderMode() const noexcept {
return mRawShaderMode;
}
bool noSamplerValidation() const noexcept {
return mNoSamplerValidation;
}
bool includeEssl1() const noexcept {
return mIncludeEssl1;
}
filament::UserVariantFilterMask getVariantFilter() const noexcept {
return mVariantFilter;
}
const StringReplacementMap& getDefines() const noexcept {
return mDefines;
}
const StringReplacementMap& getTemplateMap() const noexcept {
return mTemplateMap;
}
const StringReplacementMap& getMaterialParameters() const noexcept {
return mMaterialParameters;
}
filament::backend::FeatureLevel getFeatureLevel() const noexcept {
return mFeatureLevel;
}
Workarounds getWorkarounds() const noexcept {
return mWorkarounds;
}
bool getInsertLineDirectives() const noexcept { return mInsertLineDirectives; }
bool getInsertLineDirectiveChecks() const noexcept { return mInsertLineDirectiveChecks; }
bool getIncludeSourceMaterial() const noexcept { return mIncludeSourceMaterial; }
protected:
bool mDebug = false;
bool mIsValid = true;
bool mPrintShaders = false;
bool mRawShaderMode = false;
bool mNoSamplerValidation = false;
bool mSaveRawVariants = false;
Optimization mOptimizationLevel = Optimization::PERFORMANCE;
Metadata mReflectionTarget = Metadata::NONE;
Platform mPlatform = Platform::ALL;
OutputFormat mOutputFormat = OutputFormat::BLOB;
TargetApi mTargetApi = (TargetApi) 0;
filament::backend::FeatureLevel mFeatureLevel = filament::backend::FeatureLevel::FEATURE_LEVEL_3;
StringReplacementMap mDefines;
StringReplacementMap mTemplateMap;
StringReplacementMap mMaterialParameters;
filament::UserVariantFilterMask mVariantFilter = 0;
Workarounds mWorkarounds = Workarounds::ALL;
bool mIncludeEssl1 = true;
bool mInsertLineDirectives = true;
bool mInsertLineDirectiveChecks = true;
bool mIncludeSourceMaterial = false;
};
} // namespace matp
#endif //TNT_CONFIG_H

View File

@ -0,0 +1,109 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_MATERIALPARSER_H
#define TNT_MATERIALPARSER_H
#include <unordered_map>
#include "Config.h"
#include <utils/Path.h>
#include <utils/Status.h>
namespace filamat {
class MaterialBuilder;
}
class TestMaterialParser;
namespace matp {
class JsonishValue;
class MaterialLexeme;
class MaterialParser {
public:
MaterialParser();
// Resolves #include directives in the material by looking at the given file structure.
// Returns the string of resolved material with the state that indicates whether the include
// resolution was successful.
std::pair<utils::Status, utils::CString> resolveIncludes(
const std::unique_ptr<const char[]>& buffer, ssize_t size,
const utils::Path& materialFilePath,
bool insertLineDirectives, bool insertLineDirectiveCheck);
// Parses a string material so that it can be used in MaterialBuilder.
// Call MaterialBuilder::init before passing in the builder; call MaterialBuilder::build to
// create filamat::Package after.
// When the input shader has #includes, it has to be resolved before calling into parse.
utils::Status parse(
filamat::MaterialBuilder& builder,
const Config& config, ssize_t& size, std::unique_ptr<const char[]>& buffer);
// Replaces macro keywords with user specified ones. Must be called before parse.
utils::Status processTemplateSubstitutions(
const Config& config, ssize_t& size, std::unique_ptr<const char[]>& buffer);
private:
friend class ::TestMaterialParser;
utils::Status parseMaterial(const char* buffer, size_t size,
filamat::MaterialBuilder& builder) const noexcept;
utils::Status processMaterial(const MaterialLexeme&,
filamat::MaterialBuilder& builder) const noexcept;
utils::Status processVertexShader(const MaterialLexeme&,
filamat::MaterialBuilder& builder) const noexcept;
utils::Status processFragmentShader(const MaterialLexeme&,
filamat::MaterialBuilder& builder) const noexcept;
utils::Status processComputeShader(const MaterialLexeme&,
filamat::MaterialBuilder& builder) const noexcept;
utils::Status ignoreLexeme(
const MaterialLexeme&, filamat::MaterialBuilder& builder) const noexcept;
utils::Status parseMaterialAsJSON(const char* buffer, size_t size,
filamat::MaterialBuilder& builder) const noexcept;
utils::Status processMaterialJSON(const JsonishValue*,
filamat::MaterialBuilder& builder) const noexcept;
utils::Status processVertexShaderJSON(const JsonishValue*,
filamat::MaterialBuilder& builder) const noexcept;
utils::Status processFragmentShaderJSON(const JsonishValue*,
filamat::MaterialBuilder& builder) const noexcept;
utils::Status processComputeShaderJSON(const JsonishValue*,
filamat::MaterialBuilder& builder) const noexcept;
utils::Status ignoreLexemeJSON(
const JsonishValue*, filamat::MaterialBuilder& builder) const noexcept;
utils::Status isValidJsonStart(const char* buffer, size_t size) const noexcept;
utils::Status processMaterialParameters(
filamat::MaterialBuilder& builder, const Config& config) const;
// Member function pointer type, this is used to implement a Command design
// pattern.
using MaterialConfigProcessor = utils::Status (MaterialParser::*)
(const MaterialLexeme&, filamat::MaterialBuilder& builder) const;
// Map used to store Command pattern function pointers.
// Using string_view is generally not recommended in a map, but the string keys are program constants,
// which guarantees that the strings will outlive lifetime of the map. See MaterialParser.cpp
std::unordered_map<std::string_view, MaterialConfigProcessor> mConfigProcessor;
// The same, but for pure JSON syntax
using MaterialConfigProcessorJSON = utils::Status (MaterialParser::*)
(const JsonishValue*, filamat::MaterialBuilder& builder) const;
std::unordered_map<std::string_view, MaterialConfigProcessorJSON> mConfigProcessorJSON;
};
} // namespace matp
#endif // TNT_MATERIALPARSER_H

View File

@ -0,0 +1,253 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_BOX_H
#define TNT_FILAMENT_BOX_H
#include <utils/compiler.h>
#include <math/mat3.h>
#include <math/mat4.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <float.h>
#include <stddef.h>
namespace filament {
/**
* An axis aligned 3D box represented by its center and half-extent.
*/
class UTILS_PUBLIC Box {
public:
/** Center of the 3D box */
math::float3 center = {};
/** Half extent from the center on all 3 axis */
math::float3 halfExtent = {};
/**
* Whether the box is empty, i.e.: its extents are zero.
* @return true if the extents of the box are zero
*/
constexpr bool isEmpty() const noexcept {
return length2(halfExtent) == 0;
}
/**
* Computes the lowest coordinates corner of the box.
* @return center - halfExtent
*/
constexpr math::float3 getMin() const noexcept {
return center - halfExtent;
}
/**
* Computes the largest coordinates corner of the box.
* @return center + halfExtent
*/
constexpr math::float3 getMax() const noexcept {
return center + halfExtent;
}
/**
* Initializes the 3D box from its min / max coordinates on each axis
* @param min lowest coordinates corner of the box
* @param max largest coordinates corner of the box
* @return This bounding box
*/
Box& set(const math::float3& min, const math::float3& max) noexcept {
// float3 ctor needed for Visual Studio
center = (max + min) * math::float3(0.5f);
halfExtent = (max - min) * math::float3(0.5f);
return *this;
}
/**
* Computes the bounding box of the union of two boxes
* @param box The box to be combined with
* @return The bounding box of the union of *this and box
*/
Box& unionSelf(const Box& box) noexcept {
set(min(getMin(), box.getMin()), max(getMax(), box.getMax()));
return *this;
}
/**
* Translates the box *to* a given center position
* @param tr position to translate the box to
* @return A box centered in \p tr with the same extent than *this
*/
constexpr Box translateTo(const math::float3& tr) const noexcept {
return Box{ tr, halfExtent };
}
/**
* Computes the smallest bounding sphere of the box.
* @return The smallest sphere defined by its center (.xyz) and radius (.w) that contains *this
*/
math::float4 getBoundingSphere() const noexcept {
return { center, length(halfExtent) };
}
/**
* Transform a Box by a linear transform and a translation.
*
* @param m a 3x3 matrix, the linear transform
* @param t a float3, the translation
* @param box the box to transform
* @return the bounding box of the transformed box
*/
static Box transform(const math::mat3f& m, math::float3 const& t, const Box& box) noexcept {
return { m * box.center + t, abs(m) * box.halfExtent };
}
/**
* Transform a Box by a linear transform and a translation.
*
* @param m a linear transform matrix
* @param box the box to transform
* @return the bounding box of the transformed box
*/
friend Box rigidTransform(Box const& box, const math::mat4f& m) noexcept {
return transform(m.upperLeft(), m[3].xyz, box);
}
};
/**
* An axis aligned box represented by its min and max coordinates
*/
struct UTILS_PUBLIC Aabb {
/** min coordinates */
math::float3 min = FLT_MAX;
/** max coordinates */
math::float3 max = -FLT_MAX;
/**
* Computes the center of the box.
* @return (max + min)/2
*/
math::float3 center() const noexcept {
// float3 ctor needed for Visual Studio
return (max + min) * math::float3(0.5f);
}
/**
* Computes the half-extent of the box.
* @return (max - min)/2
*/
math::float3 extent() const noexcept {
// float3 ctor needed for Visual Studio
return (max - min) * math::float3(0.5f);
}
/**
* Whether the box is empty, i.e.: it's volume is null or negative.
* @return true if min >= max, i.e: the volume of the box is null or negative
*/
bool isEmpty() const noexcept {
return any(greaterThanEqual(min, max));
}
struct Corners {
using value_type = math::float3;
value_type const* begin() const { return vertices; }
value_type const* end() const { return vertices + 8; }
value_type * begin() { return vertices; }
value_type * end() { return vertices + 8; }
value_type const* data() const { return vertices; }
value_type * data() { return vertices; }
size_t size() const { return 8; }
value_type const& operator[](size_t i) const noexcept { return vertices[i]; }
value_type& operator[](size_t i) noexcept { return vertices[i]; }
value_type vertices[8];
};
/**
* Returns the 8 corner vertices of the AABB.
*/
Corners getCorners() const {
return Corners{ .vertices = {
{ min.x, min.y, min.z },
{ max.x, min.y, min.z },
{ min.x, max.y, min.z },
{ max.x, max.y, min.z },
{ min.x, min.y, max.z },
{ max.x, min.y, max.z },
{ min.x, max.y, max.z },
{ max.x, max.y, max.z },
}};
}
/**
* Returns whether the box contains a given point.
*
* @param p the point to test
* @return the maximum signed distance to the box. Negative if p is in the box
*/
float contains(math::float3 p) const noexcept {
// we don't use std::max to avoid a dependency on <algorithm>
auto const maximum = [](auto a, auto b) { return a > b ? a : b; };
float d = min.x - p.x;
d = maximum(d, min.y - p.y);
d = maximum(d, min.z - p.z);
d = maximum(d, p.x - max.x);
d = maximum(d, p.y - max.y);
d = maximum(d, p.z - max.z);
return d;
}
/**
* Applies an affine transformation to the AABB.
*
* @param m the 3x3 transformation to apply
* @param t the translation
* @return the transformed box
*/
static Aabb transform(const math::mat3f& m, math::float3 const& t, const Aabb& box) noexcept {
// Fast AABB transformation per Jim Arvo in Graphics Gems (1990).
Aabb result{ t, t };
for (size_t col = 0; col < 3; ++col) {
for (size_t row = 0; row < 3; ++row) {
const float a = m[col][row] * box.min[col];
const float b = m[col][row] * box.max[col];
result.min[row] += a < b ? a : b;
result.max[row] += a < b ? b : a;
}
}
return result;
}
/**
* Applies an affine transformation to the AABB.
*
* @param m the affine transformation to apply
* @return the bounding box of the transformed box
*/
Aabb transform(const math::mat4f& m) const noexcept {
return transform(m.upperLeft(), m[3].xyz, *this);
}
};
} // namespace filament
#endif // TNT_FILAMENT_BOX_H

View File

@ -0,0 +1,150 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_BUFFEROBJECT_H
#define TNT_FILAMENT_BUFFEROBJECT_H
#include <filament/FilamentAPI.h>
#include <backend/DriverEnums.h>
#include <backend/BufferDescriptor.h>
#include <utils/compiler.h>
#include <utils/StaticString.h>
#include <stdint.h>
#include <stddef.h>
namespace filament {
class FBufferObject;
class Engine;
/**
* A generic GPU buffer containing data.
*
* Usage of this BufferObject is optional. For simple use cases it is not necessary. It is useful
* only when you need to share data between multiple VertexBuffer instances. It also allows you to
* efficiently swap-out the buffers in VertexBuffer.
*
* NOTE: For now this is only used for vertex data, but in the future we may use it for other things
* (e.g. compute).
*
* @see VertexBuffer
*/
class UTILS_PUBLIC BufferObject : public FilamentAPI {
struct BuilderDetails;
public:
using BufferDescriptor = backend::BufferDescriptor;
using BindingType = backend::BufferObjectBinding;
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
friend struct BuilderDetails;
public:
Builder() noexcept;
Builder(Builder const& rhs) noexcept;
Builder(Builder&& rhs) noexcept;
~Builder() noexcept;
Builder& operator=(Builder const& rhs) noexcept;
Builder& operator=(Builder&& rhs) noexcept;
/**
* Size of the buffer in bytes.
* @param byteCount Maximum number of bytes the BufferObject can hold.
* @return A reference to this Builder for chaining calls.
*/
Builder& size(uint32_t byteCount) noexcept;
/**
* The binding type for this buffer object. (defaults to VERTEX)
* @param bindingType Distinguishes between SSBO, VBO, etc. For now this must be VERTEX.
* @return A reference to this Builder for chaining calls.
*/
Builder& bindingType(BindingType bindingType) noexcept;
/**
* Associate an optional name with this BufferObject for debugging purposes.
*
* name will show in error messages and should be kept as short as possible. The name is
* truncated to a maximum of 128 characters.
*
* The name string is copied during this method so clients may free its memory after
* the function returns.
*
* @param name A string to identify this BufferObject
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
* @deprecated Use name(utils::StaticString const&) instead.
*/
UTILS_DEPRECATED
Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept;
/**
* Associate an optional name with this BufferObject for debugging purposes.
*
* name will show in error messages and should be kept as short as possible.
*
* @param name A string literal to identify this BufferObject
* @return This Builder, for chaining calls.
*/
Builder& name(utils::StaticString const& name) noexcept;
/**
* Creates the BufferObject and returns a pointer to it. After creation, the buffer
* object is uninitialized. Use BufferObject::setBuffer() to initialize it.
*
* @param engine Reference to the filament::Engine to associate this BufferObject with.
*
* @return pointer to the newly created object
*
* @exception utils::PostConditionPanic if a runtime error occurred, such as running out of
* memory or other resources.
* @exception utils::PreConditionPanic if a parameter to a builder function was invalid.
*
* @see IndexBuffer::setBuffer
*/
BufferObject* UTILS_NONNULL build(Engine& engine);
private:
friend class FBufferObject;
};
/**
* Asynchronously copy-initializes a region of this BufferObject from the data provided.
*
* @param engine Reference to the filament::Engine associated with this BufferObject.
* @param buffer A BufferDescriptor representing the data used to initialize the BufferObject.
* @param byteOffset Offset in bytes into the BufferObject. Must be multiple of 4.
*/
void setBuffer(Engine& engine, BufferDescriptor&& buffer, uint32_t byteOffset = 0);
/**
* Returns the size of this BufferObject in elements.
* @return The maximum capacity of the BufferObject.
*/
size_t getByteCount() const noexcept;
protected:
// prevent heap allocation
~BufferObject() = default;
};
} // namespace filament
#endif // TNT_FILAMENT_BUFFEROBJECT_H

View File

@ -0,0 +1,584 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_CAMERA_H
#define TNT_FILAMENT_CAMERA_H
#include <filament/FilamentAPI.h>
#include <utils/compiler.h>
#include <math/mathfwd.h>
#include <math/vec2.h>
#include <math/vec4.h>
#include <math/mat4.h>
#include <math.h>
#include <stdint.h>
#include <stddef.h>
namespace utils {
class Entity;
} // namespace utils
namespace filament {
/**
* Camera represents the eye(s) through which the scene is viewed.
*
* A Camera has a position and orientation and controls the projection and exposure parameters.
*
* For stereoscopic rendering, a Camera maintains two separate "eyes": Eye 0 and Eye 1. These are
* arbitrary and don't necessarily need to correspond to "left" and "right".
*
* Creation and destruction
* ========================
*
* In Filament, Camera is a component that must be associated with an entity. To do so,
* use Engine::createCamera(Entity). A Camera component is destroyed using
* Engine::destroyCameraComponent(Entity).
*
* ~~~~~~~~~~~{.cpp}
* filament::Engine* engine = filament::Engine::create();
*
* utils::Entity myCameraEntity = utils::EntityManager::get().create();
* filament::Camera* myCamera = engine->createCamera(myCameraEntity);
* myCamera->setProjection(45, 16.0/9.0, 0.1, 1.0);
* myCamera->lookAt({0, 1.60, 1}, {0, 0, 0});
* engine->destroyCameraComponent(myCameraEntity);
* ~~~~~~~~~~~
*
*
* Coordinate system
* =================
*
* The camera coordinate system defines the *view space*. The camera points towards its -z axis
* and is oriented such that its top side is in the direction of +y, and its right side in the
* direction of +x.
*
* @note
* Since the *near* and *far* planes are defined by the distance from the camera,
* their respective coordinates are -\p distance(near) and -\p distance(far).
*
* Clipping planes
* ===============
*
* The camera defines six *clipping planes* which together create a *clipping volume*. The
* geometry outside this volume is clipped.
*
* The clipping volume can either be a box or a frustum depending on which projection is used,
* respectively Projection.ORTHO or Projection.PERSPECTIVE. The six planes are specified either
* directly or indirectly using setProjection().
*
* The six planes are:
* - left
* - right
* - bottom
* - top
* - near
* - far
*
* @note
* To increase the depth-buffer precision, the *far* clipping plane is always assumed to be at
* infinity for rendering. That is, it is not used to clip geometry during rendering.
* However, it is used during the culling phase (objects entirely behind the *far*
* plane are culled).
*
*
* Choosing the *near* plane distance
* ==================================
*
* The *near* plane distance greatly affects the depth-buffer resolution.
*
* Example: Precision at 1m, 10m, 100m and 1Km for various near distances assuming a 32-bit float
* depth-buffer:
*
* near (m) | 1 m | 10 m | 100 m | 1 Km
* -----------:|:------:|:-------:|:--------:|:--------:
* 0.001 | 7.2e-5 | 0.0043 | 0.4624 | 48.58
* 0.01 | 6.9e-6 | 0.0001 | 0.0430 | 4.62
* 0.1 | 3.6e-7 | 7.0e-5 | 0.0072 | 0.43
* 1.0 | 0 | 3.8e-6 | 0.0007 | 0.07
*
* As can be seen in the table above, the depth-buffer precision drops rapidly with the
* distance to the camera.
*
* Make sure to pick the highest *near* plane distance possible.
*
* On Vulkan and Metal platforms (or OpenGL platforms supporting either EXT_clip_control or
* ARB_clip_control extensions), the depth-buffer precision is much less dependent on the *near*
* plane value:
*
* near (m) | 1 m | 10 m | 100 m | 1 Km
* -----------:|:------:|:-------:|:--------:|:--------:
* 0.001 | 1.2e-7 | 9.5e-7 | 7.6e-6 | 6.1e-5
* 0.01 | 1.2e-7 | 9.5e-7 | 7.6e-6 | 6.1e-5
* 0.1 | 5.9e-8 | 9.5e-7 | 1.5e-5 | 1.2e-4
* 1.0 | 0 | 9.5e-7 | 7.6e-6 | 1.8e-4
*
*
* Choosing the *far* plane distance
* =================================
*
* The far plane distance is always set internally to infinity for rendering, however it is used for
* culling and shadowing calculations. It is important to keep a reasonable ratio between
* the near and far plane distances. Typically a ratio in the range 1:100 to 1:100000 is
* commanded. Larger values may causes rendering artifacts or trigger assertions in debug builds.
*
*
* Exposure
* ========
*
* The Camera is also used to set the scene's exposure, just like with a real camera. The lights
* intensity and the Camera exposure interact to produce the final scene's brightness.
*
*
* Stereoscopic rendering
* ======================
*
* The Camera's transform (as set by setModelMatrix or via TransformManager) defines a "head" space,
* which typically corresponds to the location of the viewer's head. Each eye's transform is set
* relative to this head space by setEyeModelMatrix.
*
* Each eye also maintains its own projection matrix. These can be set with setCustomEyeProjection.
* Care must be taken to correctly set the projectionForCulling matrix, as well as its corresponding
* near and far values. The projectionForCulling matrix must define a frustum (in head space) that
* bounds the frustums of both eyes. Alternatively, culling may be disabled with
* View::setFrustumCullingEnabled.
*
* \see Frustum, View
*/
class UTILS_PUBLIC Camera : public FilamentAPI {
public:
//! Denotes the projection type used by this camera. \see setProjection
enum class Projection : int {
PERSPECTIVE, //!< perspective projection, objects get smaller as they are farther
ORTHO //!< orthonormal projection, preserves distances
};
//! Denotes a field-of-view direction. \see setProjection
enum class Fov : int {
VERTICAL, //!< the field-of-view angle is defined on the vertical axis
HORIZONTAL //!< the field-of-view angle is defined on the horizontal axis
};
/** Returns the projection matrix from the field-of-view.
*
* @param fovInDegrees full field-of-view in degrees. 0 < \p fov < 180.
* @param aspect aspect ratio \f$ \frac{width}{height} \f$. \p aspect > 0.
* @param near distance in world units from the camera to the near plane. \p near > 0.
* @param far distance in world units from the camera to the far plane. \p far > \p near.
* @param direction direction of the \p fovInDegrees parameter.
*
* @see Fov.
*/
static math::mat4 projection(Fov direction, double fovInDegrees,
double aspect, double near, double far = INFINITY);
/** Returns the projection matrix from the focal length.
*
* @param focalLengthInMillimeters lens's focal length in millimeters. \p focalLength > 0.
* @param aspect aspect ratio \f$ \frac{width}{height} \f$. \p aspect > 0.
* @param near distance in world units from the camera to the near plane. \p near > 0.
* @param far distance in world units from the camera to the far plane. \p far > \p near.
*/
static math::mat4 projection(double focalLengthInMillimeters,
double aspect, double near, double far = INFINITY);
/** Sets the projection matrix from a frustum defined by six planes.
*
* @param projection type of #Projection to use.
*
* @param left distance in world units from the camera to the left plane,
* at the near plane.
* Precondition: \p left != \p right.
*
* @param right distance in world units from the camera to the right plane,
* at the near plane.
* Precondition: \p left != \p right.
*
* @param bottom distance in world units from the camera to the bottom plane,
* at the near plane.
* Precondition: \p bottom != \p top.
*
* @param top distance in world units from the camera to the top plane,
* at the near plane.
* Precondition: \p left != \p right.
*
* @param near distance in world units from the camera to the near plane. The near plane's
* position in view space is z = -\p near.
* Precondition: \p near > 0 for PROJECTION::PERSPECTIVE or
* \p near != far for PROJECTION::ORTHO
*
* @param far distance in world units from the camera to the far plane. The far plane's
* position in view space is z = -\p far.
* Precondition: \p far > near for PROJECTION::PERSPECTIVE or
* \p far != near for PROJECTION::ORTHO
*
* @see Projection, Frustum
*/
void setProjection(Projection projection,
double left, double right,
double bottom, double top,
double near, double far);
/** Utility to set the projection matrix from the field-of-view.
*
* @param fovInDegrees full field-of-view in degrees. 0 < \p fov < 180.
* @param aspect aspect ratio \f$ \frac{width}{height} \f$. \p aspect > 0.
* @param near distance in world units from the camera to the near plane. \p near > 0.
* @param far distance in world units from the camera to the far plane. \p far > \p near.
* @param direction direction of the \p fovInDegrees parameter.
*
* @see Fov.
*/
void setProjection(double fovInDegrees, double aspect, double near, double far,
Fov direction = Fov::VERTICAL);
/** Utility to set the projection matrix from the focal length.
*
* @param focalLengthInMillimeters lens's focal length in millimeters. \p focalLength > 0.
* @param aspect aspect ratio \f$ \frac{width}{height} \f$. \p aspect > 0.
* @param near distance in world units from the camera to the near plane. \p near > 0.
* @param far distance in world units from the camera to the far plane. \p far > \p near.
*/
void setLensProjection(double focalLengthInMillimeters,
double aspect, double near, double far);
/** Sets a custom projection matrix.
*
* The projection matrix must define an NDC system that must match the OpenGL convention,
* that is all 3 axis are mapped to [-1, 1].
*
* @param projection custom projection matrix used for rendering and culling
* @param near distance in world units from the camera to the near plane.
* @param far distance in world units from the camera to the far plane. \p far != \p near.
*/
void setCustomProjection(math::mat4 const& projection, double near, double far);
/** Sets the projection matrix.
*
* The projection matrices must define an NDC system that must match the OpenGL convention,
* that is all 3 axis are mapped to [-1, 1].
*
* @param projection custom projection matrix used for rendering
* @param projectionForCulling custom projection matrix used for culling
* @param near distance in world units from the camera to the near plane.
* @param far distance in world units from the camera to the far plane. \p far != \p near.
*/
void setCustomProjection(math::mat4 const& projection, math::mat4 const& projectionForCulling,
double near, double far);
/** Sets a custom projection matrix for each eye.
*
* The projectionForCulling, near, and far parameters establish a "culling frustum" which must
* encompass anything any eye can see. All projection matrices must be set simultaneously. The
* number of stereoscopic eyes is controlled by the stereoscopicEyeCount setting inside of
* Engine::Config.
*
* @param projection an array of projection matrices, only the first config.stereoscopicEyeCount
* are read
* @param count size of the projection matrix array to set, must be
* >= config.stereoscopicEyeCount
* @param projectionForCulling custom projection matrix for culling, must encompass both eyes
* @param near distance in world units from the camera to the culling near plane. \p near > 0.
* @param far distance in world units from the camera to the culling far plane. \p far > \p
* near.
* @see setCustomProjection
* @see Engine::Config::stereoscopicEyeCount
*/
void setCustomEyeProjection(math::mat4 const* UTILS_NONNULL projection, size_t count,
math::mat4 const& projectionForCulling, double near, double far);
/** Sets an additional matrix that scales the projection matrix.
*
* This is useful to adjust the aspect ratio of the camera independent of its projection.
* First, pass an aspect of 1.0 to setProjection. Then set the scaling with the desired aspect
* ratio:
*
* const double aspect = width / height;
*
* // with Fov::HORIZONTAL passed to setProjection:
* camera->setScaling(double4 {1.0, aspect});
*
* // with Fov::VERTICAL passed to setProjection:
* camera->setScaling(double4 {1.0 / aspect, 1.0});
*
*
* By default, this is an identity matrix.
*
* @param scaling diagonal of the 2x2 scaling matrix to be applied after the projection matrix.
*
* @see setProjection, setLensProjection, setCustomProjection
*/
void setScaling(math::double2 scaling) noexcept;
/**
* Sets an additional matrix that shifts the projection matrix.
* By default, this is an identity matrix.
*
* @param shift x and y translation added to the projection matrix, specified in NDC
* coordinates, that is, if the translation must be specified in pixels,
* shift must be scaled by 1.0 / { viewport.width, viewport.height }.
*
* @see setProjection, setLensProjection, setCustomProjection
*/
void setShift(math::double2 shift) noexcept;
/** Returns the scaling amount used to scale the projection matrix.
*
* @return the diagonal of the scaling matrix applied after the projection matrix.
*
* @see setScaling
*/
math::double4 getScaling() const noexcept;
/** Returns the shift amount used to translate the projection matrix.
*
* @return the 2D translation x and y offsets applied after the projection matrix.
*
* @see setShift
*/
math::double2 getShift() const noexcept;
/** Returns the projection matrix used for rendering.
*
* The projection matrix used for rendering always has its far plane set to infinity. This
* is why it may differ from the matrix set through setProjection() or setLensProjection().
*
* @param eyeId the index of the eye to return the projection matrix for, must be
* < config.stereoscopicEyeCount
* @return The projection matrix used for rendering
*
* @see setProjection, setLensProjection, setCustomProjection, getCullingProjectionMatrix,
* setCustomEyeProjection
*/
math::mat4 getProjectionMatrix(uint8_t eyeId = 0) const;
/** Returns the projection matrix used for culling (far plane is finite).
*
* @return The projection matrix set by setProjection or setLensProjection.
*
* @see setProjection, setLensProjection, getProjectionMatrix
*/
math::mat4 getCullingProjectionMatrix() const noexcept;
//! Returns the frustum's near plane
double getNear() const noexcept;
//! Returns the frustum's far plane used for culling
double getCullingFar() const noexcept;
/** Sets the camera's model matrix.
*
* Helper method to set the camera's entity transform component.
* It has the same effect as calling:
*
* ~~~~~~~~~~~{.cpp}
* engine.getTransformManager().setTransform(
* engine.getTransformManager().getInstance(camera->getEntity()), model);
* ~~~~~~~~~~~
*
* @param modelMatrix The camera position and orientation provided as a rigid transform matrix.
*
* @note The Camera "looks" towards its -z axis
*
* @warning \p model must be a rigid transform
*/
void setModelMatrix(const math::mat4& modelMatrix) noexcept;
void setModelMatrix(const math::mat4f& modelMatrix) noexcept; //!< @overload
/** Set the position of an eye relative to this Camera (head).
*
* By default, both eyes' model matrices are identity matrices.
*
* For example, to position Eye 0 3cm leftwards and Eye 1 3cm rightwards:
* ~~~~~~~~~~~{.cpp}
* const mat4 leftEye = mat4::translation(double3{-0.03, 0.0, 0.0});
* const mat4 rightEye = mat4::translation(double3{ 0.03, 0.0, 0.0});
* camera.setEyeModelMatrix(0, leftEye);
* camera.setEyeModelMatrix(1, rightEye);
* ~~~~~~~~~~~
*
* This method is not intended to be called every frame. Instead, to update the position of the
* head, use Camera::setModelMatrix.
*
* @param eyeId the index of the eye to set, must be < config.stereoscopicEyeCount
* @param model the model matrix for an individual eye
*/
void setEyeModelMatrix(uint8_t eyeId, math::mat4 const& model);
/** Sets the camera's model matrix
*
* @param eye The position of the camera in world space.
* @param center The point in world space the camera is looking at.
* @param up A unit vector denoting the camera's "up" direction.
*/
void lookAt(math::double3 const& eye,
math::double3 const& center,
math::double3 const& up = math::double3{0, 1, 0}) noexcept;
/** Returns the camera's model matrix
*
* Helper method to return the camera's entity transform component.
* It has the same effect as calling:
*
* ~~~~~~~~~~~{.cpp}
* engine.getTransformManager().getWorldTransform(
* engine.getTransformManager().getInstance(camera->getEntity()));
* ~~~~~~~~~~~
*
* @return The camera's pose in world space as a rigid transform. Parent transforms, if any,
* are taken into account.
*/
math::mat4 getModelMatrix() const noexcept;
//! Returns the camera's view matrix (inverse of the model matrix)
math::mat4 getViewMatrix() const noexcept;
//! Returns the camera's position in world space
math::double3 getPosition() const noexcept;
//! Returns the camera's normalized left vector
math::float3 getLeftVector() const noexcept;
//! Returns the camera's normalized up vector
math::float3 getUpVector() const noexcept;
//! Returns the camera's forward vector
math::float3 getForwardVector() const noexcept;
//! Returns the camera's field of view in degrees
float getFieldOfViewInDegrees(Fov direction) const noexcept;
//! Returns the camera's culling Frustum in world space
class Frustum getFrustum() const noexcept;
//! Returns the entity representing this camera
utils::Entity getEntity() const noexcept;
/** Sets this camera's exposure (default is f/16, 1/125s, 100 ISO)
*
* The exposure ultimately controls the scene's brightness, just like with a real camera.
* The default values provide adequate exposure for a camera placed outdoors on a sunny day
* with the sun at the zenith.
*
* @param aperture Aperture in f-stops, clamped between 0.5 and 64.
* A lower \p aperture value *increases* the exposure, leading to
* a brighter scene. Realistic values are between 0.95 and 32.
*
* @param shutterSpeed Shutter speed in seconds, clamped between 1/25,000 and 60.
* A lower shutter speed increases the exposure. Realistic values are
* between 1/8000 and 30.
*
* @param sensitivity Sensitivity in ISO, clamped between 10 and 204,800.
* A higher \p sensitivity increases the exposure. Realistic values are
* between 50 and 25600.
*
* @note
* With the default parameters, the scene must contain at least one Light of intensity
* similar to the sun (e.g.: a 100,000 lux directional light).
*
* @see LightManager, Exposure
*/
void setExposure(float aperture, float shutterSpeed, float sensitivity) noexcept;
/** Sets this camera's exposure directly. Calling this method will set the aperture
* to 1.0, the shutter speed to 1.2 and the sensitivity will be computed to match
* the requested exposure (for a desired exposure of 1.0, the sensitivity will be
* set to 100 ISO).
*
* This method is useful when trying to match the lighting of other engines or tools.
* Many engines/tools use unit-less light intensities, which can be matched by setting
* the exposure manually. This can be typically achieved by setting the exposure to
* 1.0.
*/
void setExposure(float exposure) noexcept {
setExposure(1.0f, 1.2f, 100.0f * (1.0f / exposure));
}
//! returns this camera's aperture in f-stops
float getAperture() const noexcept;
//! returns this camera's shutter speed in seconds
float getShutterSpeed() const noexcept;
//! returns this camera's sensitivity in ISO
float getSensitivity() const noexcept;
/** Returns the focal length in meters [m] for a 35mm camera.
* Eye 0's projection matrix is used to compute the focal length.
*/
double getFocalLength() const noexcept;
/**
* Sets the camera focus distance. This is used by the Depth-of-field PostProcessing effect.
* @param distance Distance from the camera to the plane of focus in world units.
* Must be positive and larger than the near clipping plane.
*/
void setFocusDistance(float distance) noexcept;
//! Returns the focus distance in world units
float getFocusDistance() const noexcept;
/**
* Returns the inverse of a projection matrix.
*
* \param p the projection matrix to inverse
* \returns the inverse of the projection matrix \p p
*/
static math::mat4 inverseProjection(const math::mat4& p) noexcept;
/**
* Returns the inverse of a projection matrix.
* @see inverseProjection(const math::mat4&)
*/
static math::mat4f inverseProjection(const math::mat4f& p) noexcept;
/**
* Helper to compute the effective focal length taking into account the focus distance
*
* @param focalLength focal length in any unit (e.g. [m] or [mm])
* @param focusDistance focus distance in same unit as focalLength
* @return the effective focal length in same unit as focalLength
*/
static double computeEffectiveFocalLength(double focalLength, double focusDistance) noexcept;
/**
* Helper to compute the effective field-of-view taking into account the focus distance
*
* @param fovInDegrees full field of view in degrees
* @param focusDistance focus distance in meters [m]
* @return effective full field of view in degrees
*/
static double computeEffectiveFov(double fovInDegrees, double focusDistance) noexcept;
protected:
// prevent heap allocation
~Camera() = default;
};
} // namespace filament
#endif // TNT_FILAMENT_CAMERA_H

View File

@ -0,0 +1,217 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_COLOR_H
#define TNT_FILAMENT_COLOR_H
#include <utils/compiler.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <stdint.h>
#include <stddef.h>
namespace filament {
//! RGB color in linear space
using LinearColor = math::float3;
//! RGB color in sRGB space
using sRGBColor = math::float3;
//! RGBA color in linear space, with alpha
using LinearColorA = math::float4;
//! RGBA color in sRGB space, with alpha
using sRGBColorA = math::float4;
//! types of RGB colors
enum class RgbType : uint8_t {
sRGB, //!< the color is defined in Rec.709-sRGB-D65 (sRGB) space
LINEAR, //!< the color is defined in Rec.709-Linear-D65 ("linear sRGB") space
};
//! types of RGBA colors
enum class RgbaType : uint8_t {
/**
* the color is defined in Rec.709-sRGB-D65 (sRGB) space and the RGB values
* have not been pre-multiplied by the alpha (for instance, a 50%
* transparent red is <1,0,0,0.5>)
*/
sRGB,
/**
* the color is defined in Rec.709-Linear-D65 ("linear sRGB") space and the
* RGB values have not been pre-multiplied by the alpha (for instance, a 50%
* transparent red is <1,0,0,0.5>)
*/
LINEAR,
/**
* the color is defined in Rec.709-sRGB-D65 (sRGB) space and the RGB values
* have been pre-multiplied by the alpha (for instance, a 50%
* transparent red is <0.5,0,0,0.5>)
*/
PREMULTIPLIED_sRGB,
/**
* the color is defined in Rec.709-Linear-D65 ("linear sRGB") space and the
* RGB values have been pre-multiplied by the alpha (for instance, a 50%
* transparent red is <0.5,0,0,0.5>)
*/
PREMULTIPLIED_LINEAR
};
//! type of color conversion to use when converting to/from sRGB and linear spaces
enum ColorConversion {
ACCURATE, //!< accurate conversion using the sRGB standard
FAST //!< fast conversion using a simple gamma 2.2 curve
};
/**
* Utilities to manipulate and convert colors
*/
class UTILS_PUBLIC Color {
public:
//! converts an RGB color to linear space, the conversion depends on the specified type
static LinearColor toLinear(RgbType type, math::float3 color);
//! converts an RGBA color to linear space, the conversion depends on the specified type
static LinearColorA toLinear(RgbaType type, math::float4 color);
//! converts an RGB color in sRGB space to an RGB color in linear space
template<ColorConversion = ACCURATE>
static LinearColor toLinear(sRGBColor const& color);
/**
* Converts an RGB color in Rec.709-Linear-D65 ("linear sRGB") space to an
* RGB color in Rec.709-sRGB-D65 (sRGB) space.
*/
template<ColorConversion = ACCURATE>
static sRGBColor toSRGB(LinearColor const& color);
/**
* Converts an RGBA color in Rec.709-sRGB-D65 (sRGB) space to an RGBA color in
* Rec.709-Linear-D65 ("linear sRGB") space the alpha component is left unmodified.
*/
template<ColorConversion = ACCURATE>
static LinearColorA toLinear(sRGBColorA const& color);
/**
* Converts an RGBA color in Rec.709-Linear-D65 ("linear sRGB") space to
* an RGBA color in Rec.709-sRGB-D65 (sRGB) space the alpha component is
* left unmodified.
*/
template<ColorConversion = ACCURATE>
static sRGBColorA toSRGB(LinearColorA const& color);
/**
* Converts a correlated color temperature to a linear RGB color in sRGB
* space the temperature must be expressed in kelvin and must be in the
* range 1,000K to 15,000K.
*/
static LinearColor cct(float K);
/**
* Converts a CIE standard illuminant series D to a linear RGB color in
* sRGB space the temperature must be expressed in kelvin and must be in
* the range 4,000K to 25,000K
*/
static LinearColor illuminantD(float K);
/**
* Computes the Beer-Lambert absorption coefficients from the specified
* transmittance color and distance. The computed absorption will guarantee
* the white light will become the specified color at the specified distance.
* The output of this function can be used as the absorption parameter of
* materials that use refraction.
*
* @param color the desired linear RGB color in sRGB space
* @param distance the distance at which white light should become the specified color
*
* @return absorption coefficients for the Beer-Lambert law
*/
static math::float3 absorptionAtDistance(LinearColor const& color, float distance);
private:
static math::float3 sRGBToLinear(math::float3 color) noexcept;
static math::float3 linearToSRGB(math::float3 color) noexcept;
};
// Use the default implementation from the header
template<>
inline LinearColor Color::toLinear<FAST>(sRGBColor const& color) {
return pow(color, 2.2f);
}
template<>
inline LinearColorA Color::toLinear<FAST>(sRGBColorA const& color) {
return LinearColorA{pow(color.rgb, 2.2f), color.a};
}
template<>
inline LinearColor Color::toLinear<ACCURATE>(sRGBColor const& color) {
return sRGBToLinear(color);
}
template<>
inline LinearColorA Color::toLinear<ACCURATE>(sRGBColorA const& color) {
return LinearColorA{sRGBToLinear(color.rgb), color.a};
}
// Use the default implementation from the header
template<>
inline sRGBColor Color::toSRGB<FAST>(LinearColor const& color) {
return pow(color, 1.0f / 2.2f);
}
template<>
inline sRGBColorA Color::toSRGB<FAST>(LinearColorA const& color) {
return sRGBColorA{pow(color.rgb, 1.0f / 2.2f), color.a};
}
template<>
inline sRGBColor Color::toSRGB<ACCURATE>(LinearColor const& color) {
return linearToSRGB(color);
}
template<>
inline sRGBColorA Color::toSRGB<ACCURATE>(LinearColorA const& color) {
return sRGBColorA{linearToSRGB(color.rgb), color.a};
}
inline LinearColor Color::toLinear(RgbType type, math::float3 color) {
return (type == RgbType::LINEAR) ? color : toLinear<ACCURATE>(color);
}
// converts an RGBA color to linear space
// the conversion depends on the specified type
inline LinearColorA Color::toLinear(RgbaType type, math::float4 color) {
switch (type) {
case RgbaType::sRGB:
return toLinear<ACCURATE>(color) * math::float4{color.a, color.a, color.a, 1.0f};
case RgbaType::LINEAR:
return color * math::float4{color.a, color.a, color.a, 1.0f};
case RgbaType::PREMULTIPLIED_sRGB:
return toLinear<ACCURATE>(color);
case RgbaType::PREMULTIPLIED_LINEAR:
return color;
}
}
} // namespace filament
#endif // TNT_FILAMENT_COLOR_H

View File

@ -0,0 +1,514 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! \file
#ifndef TNT_FILAMENT_COLORGRADING_H
#define TNT_FILAMENT_COLORGRADING_H
#include <filament/FilamentAPI.h>
#include <filament/ToneMapper.h>
#include <utils/compiler.h>
#include <utils/FixedCapacityVector.h>
#include <math/mathfwd.h>
#include <stdint.h>
#include <stddef.h>
namespace filament {
class Engine;
class FColorGrading;
namespace color {
class ColorSpace;
}
/**
* ColorGrading is used to transform (either to modify or correct) the colors of the HDR buffer
* rendered by Filament. Color grading transforms are applied after lighting, and after any lens
* effects (bloom for instance), and include tone mapping.
*
* Creation, usage and destruction
* ===============================
*
* A ColorGrading object is created using the ColorGrading::Builder and destroyed by calling
* Engine::destroy(const ColorGrading*). A ColorGrading object is meant to be set on a View.
*
* ~~~~~~~~~~~{.cpp}
* filament::Engine* engine = filament::Engine::create();
*
* filament::ColorGrading* colorGrading = filament::ColorGrading::Builder()
* .toneMapping(filament::ColorGrading::ToneMapping::ACES)
* .build(*engine);
*
* myView->setColorGrading(colorGrading);
*
* engine->destroy(colorGrading);
* ~~~~~~~~~~~
*
* Performance
* ===========
*
* Creating a new ColorGrading object may be more expensive than other Filament objects as a LUT may
* need to be generated. The generation of this LUT, if necessary, may happen on the CPU.
*
* Ordering
* ========
*
* The various transforms held by ColorGrading are applied in the following order:
* - Exposure
* - Night adaptation
* - White balance
* - Channel mixer
* - Shadows/mid-tones/highlights
* - Slope/offset/power (CDL)
* - Contrast
* - Vibrance
* - Saturation
* - Curves
* - Tone mapping
* - Luminance scaling
* - Gamut mapping
*
* Defaults
* ========
*
* Here are the default color grading options:
* - Exposure: 0.0
* - Night adaptation: 0.0
* - White balance: temperature 0, and tint 0
* - Channel mixer: red {1,0,0}, green {0,1,0}, blue {0,0,1}
* - Shadows/mid-tones/highlights: shadows {1,1,1,0}, mid-tones {1,1,1,0}, highlights {1,1,1,0},
* ranges {0,0.333,0.550,1}
* - Slope/offset/power: slope 1.0, offset 0.0, and power 1.0
* - Contrast: 1.0
* - Vibrance: 1.0
* - Saturation: 1.0
* - Curves: gamma {1,1,1}, midPoint {1,1,1}, and scale {1,1,1}
* - Tone mapping: ACESLegacyToneMapper
* - Luminance scaling: false
* - Gamut mapping: false
* - Output color space: Rec709-sRGB-D65
*
* @see View
*/
class UTILS_PUBLIC ColorGrading : public FilamentAPI {
struct BuilderDetails;
public:
enum class QualityLevel : uint8_t {
LOW,
MEDIUM,
HIGH,
ULTRA
};
enum class LutFormat : uint8_t {
INTEGER, //!< 10 bits per component
FLOAT, //!< 16 bits per component (10 bits mantissa precision)
};
/**
* List of available tone-mapping operators.
*
* @deprecated Use Builder::toneMapper(ToneMapper*) instead
*/
enum class UTILS_DEPRECATED ToneMapping : uint8_t {
LINEAR = 0, //!< Linear tone mapping (i.e. no tone mapping)
ACES_LEGACY = 1, //!< ACES tone mapping, with a brightness modifier to match Filament's legacy tone mapper
ACES = 2, //!< ACES tone mapping
FILMIC = 3, //!< Filmic tone mapping, modelled after ACES but applied in sRGB space
DISPLAY_RANGE = 4, //!< Tone mapping used to validate/debug scene exposure
};
//! Use Builder to construct a ColorGrading object instance
class Builder : public BuilderBase<BuilderDetails> {
friend struct BuilderDetails;
public:
Builder() noexcept;
Builder(Builder const& rhs) noexcept;
Builder(Builder&& rhs) noexcept;
~Builder() noexcept;
Builder& operator=(Builder const& rhs) noexcept;
Builder& operator=(Builder&& rhs) noexcept;
/**
* Sets the quality level of the color grading. When color grading is implemented using
* a 3D LUT, the quality level may impact the resolution and bit depth of the backing
* 3D texture. For instance, a low quality level will use a 16x16x16 10 bit LUT, a medium
* quality level will use a 32x32x32 10 bit LUT, a high quality will use a 32x32x32 16 bit
* LUT, and a ultra quality will use a 64x64x64 16 bit LUT.
*
* This setting has no effect if generating a 1D LUT.
*
* This overrides the values set by format() and dimensions().
*
* The default quality is medium.
*
* @param qualityLevel The desired quality of the color grading process
*
* @return This Builder, for chaining calls
*/
Builder& quality(QualityLevel qualityLevel) noexcept;
/**
* When color grading is implemented using a 3D LUT, this sets the texture format of
* of the LUT. This overrides the value set by quality().
*
* This setting has no effect if generating a 1D LUT.
*
* The default is INTEGER
*
* @param format The desired format of the 3D LUT.
*
* @return This Builder, for chaining calls
*/
Builder& format(LutFormat format) noexcept;
/**
* When color grading is implemented using a 3D LUT, this sets the dimension of the LUT.
* This overrides the value set by quality().
*
* This setting has no effect if generating a 1D LUT.
*
* The default is 32
*
* @param dim The desired dimension of the LUT. Between 16 and 64.
*
* @return This Builder, for chaining calls
*/
Builder& dimensions(uint8_t dim) noexcept;
/**
* Selects the tone mapping operator to apply to the HDR color buffer as the last
* operation of the color grading post-processing step.
*
* The default tone mapping operator is ACESLegacyToneMapper.
*
* The specified tone mapper must have a lifecycle that exceeds the lifetime of
* this builder. Since the build(Engine&) method is synchronous, it is safe to
* delete the tone mapper object after that finishes executing.
*
* @param toneMapper The tone mapping operator to apply to the HDR color buffer
*
* @return This Builder, for chaining calls
*/
Builder& toneMapper(ToneMapper const* UTILS_NULLABLE toneMapper) noexcept;
/**
* Selects the tone mapping operator to apply to the HDR color buffer as the last
* operation of the color grading post-processing step.
*
* The default tone mapping operator is ACES_LEGACY.
*
* @param toneMapping The tone mapping operator to apply to the HDR color buffer
*
* @return This Builder, for chaining calls
*
* @deprecated Use toneMapper(ToneMapper*) instead
*/
UTILS_DEPRECATED
Builder& toneMapping(ToneMapping toneMapping) noexcept;
/**
* Enables or disables the luminance scaling component (LICH) from the exposure value
* invariant luminance system (EVILS). When this setting is enabled, pixels with high
* chromatic values will roll-off to white to offer a more natural rendering. This step
* also helps avoid undesirable hue skews caused by out of gamut colors clipped
* to the destination color gamut.
*
* When luminance scaling is enabled, tone mapping is performed on the luminance of each
* pixel instead of per-channel.
*
* @param luminanceScaling Enables or disables luminance scaling post-tone mapping
*
* @return This Builder, for chaining calls
*/
Builder& luminanceScaling(bool luminanceScaling) noexcept;
/**
* Enables or disables gamut mapping to the destination color space's gamut. When gamut
* mapping is turned off, out-of-gamut colors are clipped to the destination's gamut,
* which may produce hue skews (blue skewing to purple, green to yellow, etc.). When
* gamut mapping is enabled, out-of-gamut colors are brought back in gamut by trying to
* preserve the perceived chroma and lightness of the original values.
*
* @param gamutMapping Enables or disables gamut mapping
*
* @return This Builder, for chaining calls
*/
Builder& gamutMapping(bool gamutMapping) noexcept;
/**
* Adjusts the exposure of this image. The exposure is specified in stops:
* each stop brightens (positive values) or darkens (negative values) the image by
* a factor of 2. This means that an exposure of 3 will brighten the image 8 times
* more than an exposure of 0 (2^3 = 8 and 2^0 = 1). Contrary to the camera's exposure,
* this setting is applied after all post-processing (bloom, etc.) are applied.
*
* @param exposure Value in EV stops. Can be negative, 0, or positive.
*
* @return This Builder, for chaining calls
*/
Builder& exposure(float exposure) noexcept;
/**
* Controls the amount of night adaptation to replicate a more natural representation of
* low-light conditions as perceived by the human vision system. In low-light conditions,
* peak luminance sensitivity of the eye shifts toward the blue end of the color spectrum:
* darker tones appear brighter, reducing contrast, and colors are blue shifted (the darker
* the more intense the effect).
*
* @param adaptation Amount of adaptation, between 0 (no adaptation) and 1 (full adaptation).
*
* @return This Builder, for chaining calls
*/
Builder& nightAdaptation(float adaptation) noexcept;
/**
* Adjusts the while balance of the image. This can be used to remove color casts
* and correct the appearance of the white point in the scene, or to alter the
* overall chromaticity of the image for artistic reasons (to make the image appear
* cooler or warmer for instance).
*
* The while balance adjustment is defined with two values:
* - Temperature, to modify the color temperature. This value will modify the colors
* on a blue/yellow axis. Lower values apply a cool color temperature, and higher
* values apply a warm color temperature. The lowest value, -1.0f, is equivalent to
* a temperature of 50,000K. The highest value, 1.0f, is equivalent to a temperature
* of 2,000K.
* - Tint, to modify the colors on a green/magenta axis. The lowest value, -1.0f, will
* apply a strong green cast, and the highest value, 1.0f, will apply a strong magenta
* cast.
*
* Both values are expected to be in the range [-1.0..+1.0]. Values outside of that
* range will be clipped to that range.
*
* @param temperature Modification on the blue/yellow axis, as a value between -1.0 and +1.0.
* @param tint Modification on the green/magenta axis, as a value between -1.0 and +1.0.
*
* @return This Builder, for chaining calls
*/
Builder& whiteBalance(float temperature, float tint) noexcept;
/**
* The channel mixer adjustment modifies each output color channel using the specified
* mix of the source color channels.
*
* By default each output color channel is set to use 100% of the corresponding source
* channel and 0% of the other channels. For instance, the output red channel is set to
* {1.0, 0.0, 1.0} or 100% red, 0% green and 0% blue.
*
* Each output channel can add or subtract data from the source channel by using values
* in the range [-2.0..+2.0]. Values outside of that range will be clipped to that range.
*
* Using the channel mixer adjustment you can for instance create a monochrome output
* by setting all 3 output channels to the same mix. For instance: {0.4, 0.4, 0.2} for
* all 3 output channels(40% red, 40% green and 20% blue).
*
* More complex mixes can be used to create more complex effects. For instance, here is
* a mix that creates a sepia tone effect:
* - outRed = {0.255, 0.858, 0.087}
* - outGreen = {0.213, 0.715, 0.072}
* - outBlue = {0.170, 0.572, 0.058}
*
* @param outRed The mix of source RGB for the output red channel, between -2.0 and +2.0
* @param outGreen The mix of source RGB for the output green channel, between -2.0 and +2.0
* @param outBlue The mix of source RGB for the output blue channel, between -2.0 and +2.0
*
* @return This Builder, for chaining calls
*/
Builder& channelMixer(
math::float3 outRed, math::float3 outGreen, math::float3 outBlue) noexcept;
/**
* Adjusts the colors separately in 3 distinct tonal ranges or zones: shadows, mid-tones,
* and highlights.
*
* The tonal zones are by the ranges parameter: the x and y components define the beginning
* and end of the transition from shadows to mid-tones, and the z and w components define
* the beginning and end of the transition from mid-tones to highlights.
*
* A smooth transition is applied between the zones which means for instance that the
* correction color of the shadows range will partially apply to the mid-tones, and the
* other way around. This ensure smooth visual transitions in the final image.
*
* Each correction color is defined as a linear RGB color and a weight. The weight is a
* value (which may be positive or negative) that is added to the linear RGB color before
* mixing. This can be used to darken or brighten the selected tonal range.
*
* Shadows/mid-tones/highlights adjustment are performed linear space.
*
* @param shadows Linear RGB color (.rgb) and weight (.w) to apply to the shadows
* @param midtones Linear RGB color (.rgb) and weight (.w) to apply to the mid-tones
* @param highlights Linear RGB color (.rgb) and weight (.w) to apply to the highlights
* @param ranges Range of the shadows (x and y), and range of the highlights (z and w)
*
* @return This Builder, for chaining calls
*/
Builder& shadowsMidtonesHighlights(
math::float4 shadows, math::float4 midtones, math::float4 highlights,
math::float4 ranges) noexcept;
/**
* Applies a slope, offset, and power, as defined by the ASC CDL (American Society of
* Cinematographers Color Decision List) to the image. The CDL can be used to adjust the
* colors of different tonal ranges in the image.
*
* The ASC CDL is similar to the lift/gamma/gain controls found in many color grading tools.
* Lift is equivalent to a combination of offset and slope, gain is equivalent to slope,
* and gamma is equivalent to power.
*
* The slope and power values must be strictly positive. Values less than or equal to 0 will
* be clamped to a small positive value, offset can be any positive or negative value.
*
* Version 1.2 of the ASC CDL adds saturation control, which is here provided as a separate
* API. See the saturation() method for more information.
*
* Slope/offset/power adjustments are performed in log space.
*
* @param slope Multiplier of the input color, must be a strictly positive number
* @param offset Added to the input color, can be a negative or positive number, including 0
* @param power Power exponent of the input color, must be a strictly positive number
*
* @return This Builder, for chaining calls
*/
Builder& slopeOffsetPower(math::float3 slope, math::float3 offset, math::float3 power) noexcept;
/**
* Adjusts the contrast of the image. Lower values decrease the contrast of the image
* (the tonal range is narrowed), and higher values increase the contrast of the image
* (the tonal range is widened). A value of 1.0 has no effect.
*
* The contrast is defined as a value in the range [0.0...2.0]. Values outside of that
* range will be clipped to that range.
*
* Contrast adjustment is performed in log space.
*
* @param contrast Contrast expansion, between 0.0 and 2.0. 1.0 leaves contrast unaffected
*
* @return This Builder, for chaining calls
*/
Builder& contrast(float contrast) noexcept;
/**
* Adjusts the saturation of the image based on the input color's saturation level.
* Colors with a high level of saturation are less affected than colors with low saturation
* levels.
*
* Lower vibrance values decrease intensity of the colors present in the image, and
* higher values increase the intensity of the colors in the image. A value of 1.0 has
* no effect.
*
* The vibrance is defined as a value in the range [0.0...2.0]. Values outside of that
* range will be clipped to that range.
*
* Vibrance adjustment is performed in linear space.
*
* @param vibrance Vibrance, between 0.0 and 2.0. 1.0 leaves vibrance unaffected
*
* @return This Builder, for chaining calls
*/
Builder& vibrance(float vibrance) noexcept;
/**
* Adjusts the saturation of the image. Lower values decrease intensity of the colors
* present in the image, and higher values increase the intensity of the colors in the
* image. A value of 1.0 has no effect.
*
* The saturation is defined as a value in the range [0.0...2.0]. Values outside of that
* range will be clipped to that range.
*
* Saturation adjustment is performed in linear space.
*
* @param saturation Saturation, between 0.0 and 2.0. 1.0 leaves saturation unaffected
*
* @return This Builder, for chaining calls
*/
Builder& saturation(float saturation) noexcept;
/**
* Applies a curve to each RGB channel of the image. Each curve is defined by 3 values:
* a gamma value applied to the shadows only, a mid-point indicating where shadows stop
* and highlights start, and a scale factor for the highlights.
*
* The gamma and mid-point must be strictly positive values. If they are not, they will be
* clamped to a small positive value. The scale can be any negative of positive value.
*
* Curves are applied in linear space.
*
* @param shadowGamma Power value to apply to the shadows, must be strictly positive
* @param midPoint Mid-point defining where shadows stop and highlights start, must be strictly positive
* @param highlightScale Scale factor for the highlights, can be any negative or positive value
*
* @return This Builder, for chaining calls
*/
Builder& curves(math::float3 shadowGamma, math::float3 midPoint, math::float3 highlightScale) noexcept;
/**
* Specifies a custom 3D color grading LUT to map the final sRGB color.
* The LUT is applied after post-processing and in LDR (sRGB space).
* The data must be a 3D array of float3 (RGB) values.
* The dimension does not need to be a power of two, but must be non-zero.
* The values are always interpolated (trilinear) because the input color from previous steps is continuous.
* The dimension doesn't need to match dimensions().
* If the dimension is 0 or the data is empty, the custom LUT is skipped (ignored).
*
* @param data FixedCapacityVector containing the custom LUT data (3D array of float3).
* @param dimension Dimension of the custom LUT.
*
* @return This Builder, for chaining calls
*/
Builder& customLut(utils::FixedCapacityVector<math::float3> data, uint8_t dimension) noexcept;
/**
* Sets the output color space for this ColorGrading object. After all color grading steps
* have been applied, the final color will be converted in the desired color space.
*
* NOTE: Currently the output color space must be one of Rec709-sRGB-D65 or
* Rec709-Linear-D65. Only the transfer function is taken into account.
*
* @param colorSpace The output color space.
*
* @return This Builder, for chaining calls
*/
Builder& outputColorSpace(const color::ColorSpace& colorSpace) noexcept;
/**
* Creates the ColorGrading object and returns a pointer to it.
*
* @param engine Reference to the filament::Engine to associate this ColorGrading with.
*
* @return pointer to the newly created object.
*/
ColorGrading* UTILS_NONNULL build(Engine& engine);
private:
friend class FColorGrading;
};
protected:
// prevent heap allocation
~ColorGrading() = default;
};
} // namespace filament
#endif // TNT_FILAMENT_COLORGRADING_H

Some files were not shown because too many files have changed in this diff Show More