feat: add tracy / various fixes
This commit is contained in:
@@ -56,7 +56,7 @@ set(SRC_FILES
|
||||
"src/Physics/PhysicsWorld.cpp"
|
||||
"src/Physics/SimplePhysicsWorld.cpp"
|
||||
"src/Physics/PhysicsSceneBridge.cpp"
|
||||
src/Components/Physics/BoxCollider.cpp
|
||||
"src/Physics/JoltPhysicsWorld.cpp"
|
||||
)
|
||||
|
||||
add_library(destrum ${SRC_FILES})
|
||||
@@ -89,6 +89,7 @@ target_link_libraries(destrum
|
||||
assimp
|
||||
imgui
|
||||
Jolt
|
||||
Tracy::TracyClient
|
||||
|
||||
PRIVATE
|
||||
freetype::freetype
|
||||
@@ -99,6 +100,7 @@ target_compile_definitions(destrum
|
||||
PUBLIC
|
||||
VK_NO_PROTOTYPES
|
||||
VMA_VULKAN_VERSION=1003000
|
||||
TRACY_VK_USE_SYMBOL_TABLE
|
||||
# VOLK_DEFAULT_VISIBILITY # FIXME: doesn't work for some reason
|
||||
)
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 50 KiB |
Binary file not shown.
@@ -60,6 +60,14 @@ protected:
|
||||
bool resizePending = false;
|
||||
std::chrono::steady_clock::time_point lastResizeTime{};
|
||||
|
||||
bool m_PhysicsPaused = false;
|
||||
bool m_PhysicsStepOnce = false;
|
||||
|
||||
float m_PhysicsTimeScale = 1.0f;
|
||||
int m_PhysicsStepsLastFrame = 0;
|
||||
|
||||
void drawPhysicsPanel(float dt, float fixedDt, float accumulator);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -29,6 +29,13 @@
|
||||
class MeshCache;
|
||||
class ImguiPass;
|
||||
|
||||
#if defined(TRACY_ENABLE)
|
||||
namespace tracy { class VkCtx; }
|
||||
using DestrumTracyVkCtx = tracy::VkCtx*;
|
||||
#else
|
||||
using DestrumTracyVkCtx = void*;
|
||||
#endif
|
||||
|
||||
|
||||
namespace {
|
||||
using ImmediateExecuteFunction = std::function<void(VkCommandBuffer)>;
|
||||
@@ -126,12 +133,16 @@ public:
|
||||
return swapchain.getImageView(imageIndex);
|
||||
}
|
||||
|
||||
DestrumTracyVkCtx getTracyVkCtx() const { return tracyVkCtx; }
|
||||
|
||||
private:
|
||||
vkb::Instance instance;
|
||||
vkb::PhysicalDevice physicalDevice;
|
||||
vkb::Device device;
|
||||
VmaAllocator allocator;
|
||||
|
||||
DestrumTracyVkCtx tracyVkCtx = nullptr;
|
||||
|
||||
std::uint32_t graphicsQueueFamily;
|
||||
VkQueue graphicsQueue;
|
||||
|
||||
@@ -149,6 +160,8 @@ private:
|
||||
|
||||
ImageCache imageCache;
|
||||
|
||||
bool m_vSync = false;
|
||||
|
||||
static uint32_t BytesPerTexel(VkFormat fmt) {
|
||||
switch (fmt) {
|
||||
case VK_FORMAT_R8_UNORM: return 1;
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include <destrum/Physics/PhysicsWorld.h>
|
||||
|
||||
class JoltPhysicsWorld final : public PhysicsWorld {
|
||||
public:
|
||||
struct Settings {
|
||||
std::uint32_t maxBodies{65536};
|
||||
std::uint32_t numBodyMutexes{0};
|
||||
std::uint32_t maxBodyPairs{65536};
|
||||
std::uint32_t maxContactConstraints{10240};
|
||||
|
||||
// Jolt recommends a temp allocator for physics update allocations.
|
||||
// 10 MB is the value used in their HelloWorld sample.
|
||||
std::uint32_t tempAllocatorSizeBytes{10u * 1024u * 1024u};
|
||||
|
||||
// 0 means "hardware_concurrency - 1".
|
||||
std::uint32_t workerThreadCount{0};
|
||||
|
||||
glm::vec3 gravity{0.0f, -9.81f, 0.0f};
|
||||
};
|
||||
|
||||
explicit JoltPhysicsWorld(const Settings& settings = Settings{});
|
||||
~JoltPhysicsWorld() override;
|
||||
|
||||
JoltPhysicsWorld(const JoltPhysicsWorld&) = delete;
|
||||
JoltPhysicsWorld(JoltPhysicsWorld&&) noexcept = delete;
|
||||
JoltPhysicsWorld& operator=(const JoltPhysicsWorld&) = delete;
|
||||
JoltPhysicsWorld& operator=(JoltPhysicsWorld&&) noexcept = delete;
|
||||
|
||||
void Step(float fixedDt) override;
|
||||
|
||||
void SyncKinematicBodiesToPhysics() override;
|
||||
void SyncDynamicBodiesToTransforms() override;
|
||||
|
||||
PhysicsBodyHandle CreateBody(const PhysicsBodyDesc& desc) override;
|
||||
void DestroyBody(PhysicsBodyHandle body) override;
|
||||
|
||||
void SetBodyTransform(PhysicsBodyHandle body, const PhysicsTransform& transform) override;
|
||||
PhysicsTransform GetBodyTransform(PhysicsBodyHandle body) const override;
|
||||
|
||||
void SetLinearVelocity(PhysicsBodyHandle body, const glm::vec3& velocity) override;
|
||||
glm::vec3 GetLinearVelocity(PhysicsBodyHandle body) const override;
|
||||
|
||||
void AddForce(PhysicsBodyHandle body, const glm::vec3& force) override;
|
||||
void AddImpulse(PhysicsBodyHandle body, const glm::vec3& impulse) override;
|
||||
|
||||
bool Raycast(const glm::vec3& origin,
|
||||
const glm::vec3& direction,
|
||||
float maxDistance,
|
||||
PhysicsRaycastHit& hit) const override;
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> m_Impl;
|
||||
};
|
||||
@@ -25,8 +25,8 @@ public:
|
||||
|
||||
// - Kinematic: Transform -> physics body before Step()
|
||||
// - Dynamic: physics body -> Transform after Step()
|
||||
void SyncKinematicBodiesToPhysics();
|
||||
void SyncDynamicBodiesToTransforms();
|
||||
virtual void SyncKinematicBodiesToPhysics();
|
||||
virtual void SyncDynamicBodiesToTransforms();
|
||||
|
||||
virtual PhysicsBodyHandle CreateBody(const PhysicsBodyDesc& desc) = 0;
|
||||
virtual void DestroyBody(PhysicsBodyHandle body) = 0;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <destrum/Event.h>
|
||||
#include <destrum/Scene/SceneManager.h>
|
||||
|
||||
#include "destrum/Physics/JoltPhysicsWorld.h"
|
||||
#include "destrum/Physics/PhysicsSceneBridge.h"
|
||||
|
||||
class GameObject;
|
||||
@@ -68,7 +69,7 @@ public:
|
||||
private:
|
||||
explicit Scene(const std::string& name);
|
||||
|
||||
PhysicsSceneBridge m_Physics{std::make_unique<SimplePhysicsWorld>()};
|
||||
PhysicsSceneBridge m_Physics{std::make_unique<JoltPhysicsWorld>()};
|
||||
|
||||
|
||||
std::string m_name;
|
||||
|
||||
+216
-78
@@ -10,7 +10,16 @@
|
||||
#include "glm/gtx/transform.hpp"
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
#include <Jolt/Jolt.h>
|
||||
#include <tracy/Tracy.hpp>
|
||||
#include <common/TracySystem.hpp>
|
||||
|
||||
struct TracyFrameScope
|
||||
{
|
||||
~TracyFrameScope()
|
||||
{
|
||||
FrameMark;
|
||||
}
|
||||
};
|
||||
|
||||
App::App()
|
||||
{
|
||||
@@ -19,7 +28,9 @@ App::App()
|
||||
void App::init(const AppParams& params)
|
||||
{
|
||||
m_params = params;
|
||||
|
||||
ZoneScopedN("App::init");
|
||||
tracy::SetThreadName("Main Thread");
|
||||
TracySetProgramName(params.appName.c_str());
|
||||
AssetFS::GetInstance().Init(params.exeDir);
|
||||
// AssetFS::GetInstance().Mount("engine", params.exeDir / "assets" / "engine");
|
||||
// AssetFS::GetInstance().Mount("game", params.exeDir / "assets" / "game");
|
||||
@@ -53,16 +64,21 @@ void App::init(const AppParams& params)
|
||||
|
||||
void App::run()
|
||||
{
|
||||
Time::GetInstance().Update(); // initialize delta timing
|
||||
ZoneScopedN("App::run");
|
||||
|
||||
Time::GetInstance().Update();
|
||||
|
||||
const float fixedDt = static_cast<float>(Time::GetInstance().FixedDeltaTime());
|
||||
const int maxSteps = 5; // prevent spiral of death
|
||||
const int maxSteps = 5;
|
||||
float accumulator = 0.0f;
|
||||
|
||||
isRunning = true;
|
||||
|
||||
while (isRunning)
|
||||
{
|
||||
// ---- Update timing ---
|
||||
TracyFrameScope tracyFrame;
|
||||
ZoneScopedN("App::Frame");
|
||||
|
||||
Time::GetInstance().Update();
|
||||
float dt = static_cast<float>(Time::GetInstance().DeltaTime());
|
||||
|
||||
@@ -72,117 +88,193 @@ void App::run()
|
||||
{
|
||||
float newFPS = 1.0f / dt;
|
||||
avgFPS = std::lerp(avgFPS, newFPS, 0.1f);
|
||||
TracyPlot("FPS", avgFPS);
|
||||
TracyPlot("Delta Time ms", dt * 1000.0f);
|
||||
}
|
||||
|
||||
accumulator += dt;
|
||||
|
||||
InputManager::GetInstance().BeginFrame();
|
||||
camera.Update(dt);
|
||||
|
||||
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event))
|
||||
{
|
||||
imguiPass.handleEvent(event);
|
||||
if (event.type == SDL_QUIT)
|
||||
ZoneScopedN("Input BeginFrame + Camera");
|
||||
InputManager::GetInstance().BeginFrame();
|
||||
camera.Update(dt);
|
||||
}
|
||||
|
||||
{
|
||||
ZoneScopedN("SDL Events");
|
||||
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event))
|
||||
{
|
||||
isRunning = false;
|
||||
break;
|
||||
}
|
||||
if (event.type == SDL_WINDOWEVENT)
|
||||
{
|
||||
switch (event.window.event)
|
||||
ZoneScopedN("SDL Event");
|
||||
|
||||
imguiPass.handleEvent(event);
|
||||
|
||||
if (event.type == SDL_QUIT)
|
||||
{
|
||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||
case SDL_WINDOWEVENT_RESIZED:
|
||||
isRunning = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (event.type == SDL_WINDOWEVENT)
|
||||
{
|
||||
switch (event.window.event)
|
||||
{
|
||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||
case SDL_WINDOWEVENT_RESIZED:
|
||||
resizePending = true;
|
||||
lastResizeTime = std::chrono::steady_clock::now();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
const bool mouseEvent =
|
||||
event.type == SDL_MOUSEBUTTONDOWN ||
|
||||
event.type == SDL_MOUSEBUTTONUP ||
|
||||
event.type == SDL_MOUSEMOTION ||
|
||||
event.type == SDL_MOUSEWHEEL;
|
||||
|
||||
const bool keyboardEvent =
|
||||
event.type == SDL_KEYDOWN ||
|
||||
event.type == SDL_KEYUP ||
|
||||
event.type == SDL_TEXTINPUT;
|
||||
const bool mouseEvent =
|
||||
event.type == SDL_MOUSEBUTTONDOWN ||
|
||||
event.type == SDL_MOUSEBUTTONUP ||
|
||||
event.type == SDL_MOUSEMOTION ||
|
||||
event.type == SDL_MOUSEWHEEL;
|
||||
|
||||
const bool capturedByImgui =
|
||||
(mouseEvent && imguiPass.wantsMouse()) ||
|
||||
(keyboardEvent && imguiPass.wantsKeyboard());
|
||||
const bool keyboardEvent =
|
||||
event.type == SDL_KEYDOWN ||
|
||||
event.type == SDL_KEYUP ||
|
||||
event.type == SDL_TEXTINPUT;
|
||||
|
||||
if (!capturedByImgui)
|
||||
{
|
||||
if (InputManager::GetInstance().ProcessEvent(event))
|
||||
const bool capturedByImgui =
|
||||
(mouseEvent && imguiPass.wantsMouse()) ||
|
||||
(keyboardEvent && imguiPass.wantsKeyboard());
|
||||
|
||||
if (!capturedByImgui)
|
||||
{
|
||||
isRunning = false;
|
||||
ZoneScopedN("Input ProcessEvent");
|
||||
|
||||
if (InputManager::GetInstance().ProcessEvent(event))
|
||||
{
|
||||
isRunning = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isRunning) break;
|
||||
imguiPass.beginFrame();
|
||||
|
||||
customUpdate(dt);
|
||||
|
||||
ImGui::Begin("Debug");
|
||||
ImGui::Text("FPS: %.2f", avgFPS);
|
||||
ImGui::End();
|
||||
|
||||
imguiPass.endFrame();
|
||||
int steps = 0;
|
||||
while (accumulator >= fixedDt && steps < maxSteps)
|
||||
{
|
||||
// physics.Update(fixedDt);
|
||||
customFixedUpdate(fixedDt);
|
||||
// physics.Step(fixedDt);
|
||||
// physics.SyncTransforms();
|
||||
ZoneScopedN("ImGui BeginFrame");
|
||||
imguiPass.beginFrame();
|
||||
}
|
||||
|
||||
accumulator -= fixedDt;
|
||||
steps++;
|
||||
{
|
||||
ZoneScopedN("customUpdate");
|
||||
customUpdate(dt);
|
||||
}
|
||||
|
||||
{
|
||||
ZoneScopedN("Debug ImGui");
|
||||
ImGui::Begin("Debug");
|
||||
ImGui::Text("FPS: %.2f", avgFPS);
|
||||
ImGui::End();
|
||||
|
||||
drawPhysicsPanel(dt, fixedDt, accumulator);
|
||||
|
||||
TracyPlot("FPS", avgFPS);
|
||||
TracyPlot("Frame Time ms", dt * 1000.0f);
|
||||
TracyPlot("Accumulator ms", accumulator * 1000.0f);
|
||||
}
|
||||
|
||||
{
|
||||
ZoneScopedN("ImGui EndFrame");
|
||||
imguiPass.endFrame();
|
||||
}
|
||||
|
||||
{
|
||||
ZoneScopedN("FixedUpdate");
|
||||
|
||||
int steps = 0;
|
||||
|
||||
if (m_PhysicsPaused)
|
||||
{
|
||||
// Important: prevent physics from building up a huge backlog while paused.
|
||||
accumulator = 0.0f;
|
||||
|
||||
if (m_PhysicsStepOnce)
|
||||
{
|
||||
ZoneScopedN("Single Physics Step");
|
||||
|
||||
customFixedUpdate(fixedDt * m_PhysicsTimeScale);
|
||||
|
||||
steps = 1;
|
||||
m_PhysicsStepOnce = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (accumulator >= fixedDt && steps < maxSteps)
|
||||
{
|
||||
ZoneScopedN("Fixed Step");
|
||||
|
||||
customFixedUpdate(fixedDt * m_PhysicsTimeScale);
|
||||
|
||||
accumulator -= fixedDt;
|
||||
steps++;
|
||||
}
|
||||
|
||||
if (steps == maxSteps)
|
||||
{
|
||||
accumulator = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
m_PhysicsStepsLastFrame = steps;
|
||||
|
||||
TracyPlot("Fixed Steps", static_cast<int64_t>(steps));
|
||||
}
|
||||
if (steps == maxSteps) accumulator = 0.0f;
|
||||
|
||||
const float alpha = accumulator / fixedDt;
|
||||
(void)alpha;
|
||||
|
||||
if (gfxDevice.needsSwapchainRecreate() || resizePending)
|
||||
{
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
ZoneScopedN("Swapchain Resize Check");
|
||||
|
||||
if (resizePending &&
|
||||
now - lastResizeTime < std::chrono::milliseconds(100))
|
||||
if (gfxDevice.needsSwapchainRecreate() || resizePending)
|
||||
{
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
|
||||
if (resizePending &&
|
||||
now - lastResizeTime < std::chrono::milliseconds(100))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
SDL_Vulkan_GetDrawableSize(window, &w, &h);
|
||||
|
||||
if (w == 0 || h == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
spdlog::info("Recreating swapchain to size: {}x{}", w, h);
|
||||
|
||||
{
|
||||
ZoneScopedN("Recreate Swapchain");
|
||||
gfxDevice.recreateSwapchain(w, h);
|
||||
onWindowResize(w, h);
|
||||
}
|
||||
|
||||
resizePending = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
SDL_Vulkan_GetDrawableSize(window, &w, &h);
|
||||
|
||||
if (w == 0 || h == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
spdlog::info("Recreating swapchain to size: {}x{}", w, h);
|
||||
|
||||
gfxDevice.recreateSwapchain(w, h);
|
||||
onWindowResize(w, h);
|
||||
|
||||
resizePending = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
customDraw();
|
||||
|
||||
{
|
||||
ZoneScopedN("customDraw");
|
||||
customDraw();
|
||||
}
|
||||
|
||||
if (frameLimit)
|
||||
{
|
||||
ZoneScopedN("Frame Limit Sleep");
|
||||
|
||||
auto sleepTime = Time::GetInstance().SleepDuration();
|
||||
if (sleepTime.count() > 0)
|
||||
{
|
||||
@@ -199,3 +291,49 @@ void App::cleanup()
|
||||
spdlog::info("Cleaning up");
|
||||
customCleanup();
|
||||
}
|
||||
|
||||
void App::drawPhysicsPanel(float dt, float fixedDt, float accumulator)
|
||||
{
|
||||
ImGui::Begin("Physics");
|
||||
|
||||
ImGui::Text("Frame dt: %.3f ms", dt * 1000.0f);
|
||||
ImGui::Text("Fixed dt: %.3f ms", fixedDt * 1000.0f);
|
||||
ImGui::Text("Accumulator: %.3f ms", accumulator * 1000.0f);
|
||||
ImGui::Text("Steps last frame: %d", m_PhysicsStepsLastFrame);
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::Button(m_PhysicsPaused ? "Resume Physics" : "Pause Physics"))
|
||||
{
|
||||
m_PhysicsPaused = !m_PhysicsPaused;
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (!m_PhysicsPaused)
|
||||
{
|
||||
ImGui::BeginDisabled();
|
||||
ImGui::Button("Step Physics");
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ImGui::Button("Step Physics"))
|
||||
{
|
||||
m_PhysicsStepOnce = true;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Checkbox("Paused", &m_PhysicsPaused);
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::SliderFloat("Physics Time Scale", &m_PhysicsTimeScale, 0.0f, 2.0f, "%.2fx");
|
||||
|
||||
if (ImGui::Button("Reset Time Scale"))
|
||||
{
|
||||
m_PhysicsTimeScale = 1.0f;
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
@@ -19,13 +19,17 @@
|
||||
#include "destrum/Graphics/imageLoader.h"
|
||||
#include "destrum/Util/GameState.h"
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "tracy/Tracy.hpp"
|
||||
|
||||
#include "tracy/Tracy.hpp"
|
||||
#include "tracy/TracyVulkan.hpp"
|
||||
|
||||
GfxDevice::GfxDevice(): imageCache(*this) {
|
||||
}
|
||||
|
||||
void GfxDevice::init(SDL_Window* window, const std::string& appName, bool vSync) {
|
||||
VK_CHECK(volkInitialize());
|
||||
|
||||
m_vSync = vSync;
|
||||
instance = vkb::InstanceBuilder{}
|
||||
.set_app_name(appName.c_str())
|
||||
.set_app_version(1, 0, 0)
|
||||
@@ -131,6 +135,34 @@ void GfxDevice::init(SDL_Window* window, const std::string& appName, bool vSync)
|
||||
VK_CHECK(vkAllocateCommandBuffers(device, &cmdAllocInfo, &mainCommandBuffer));
|
||||
}
|
||||
|
||||
#if defined(TRACY_ENABLE)
|
||||
{
|
||||
VkCommandBuffer tracyInitCmd = frames[0].commandBuffer;
|
||||
|
||||
#if defined(TRACY_VK_USE_SYMBOL_TABLE)
|
||||
tracyVkCtx = TracyVkContext(
|
||||
instance,
|
||||
physicalDevice,
|
||||
device,
|
||||
graphicsQueue,
|
||||
tracyInitCmd,
|
||||
vkGetInstanceProcAddr,
|
||||
vkGetDeviceProcAddr
|
||||
);
|
||||
#else
|
||||
tracyVkCtx = TracyVkContext(
|
||||
physicalDevice,
|
||||
device,
|
||||
graphicsQueue,
|
||||
tracyInitCmd
|
||||
);
|
||||
#endif
|
||||
|
||||
static constexpr char ctxName[] = "Graphics Queue";
|
||||
TracyVkContextName(tracyVkCtx, ctxName, sizeof(ctxName) - 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
{ // create white texture
|
||||
std::uint32_t pixel = 0xFFFFFFFF;
|
||||
whiteImageId = createImage(
|
||||
@@ -166,19 +198,30 @@ void GfxDevice::init(SDL_Window* window, const std::string& appName, bool vSync)
|
||||
void GfxDevice::recreateSwapchain(int width, int height) {
|
||||
assert(width != 0 && height != 0);
|
||||
waitIdle();
|
||||
swapchain.recreateSwapchain(*this, swapchainFormat, width, height, true);
|
||||
swapchain.recreateSwapchain(*this, swapchainFormat, width, height, m_vSync);
|
||||
}
|
||||
|
||||
VkCommandBuffer GfxDevice::beginFrame() {
|
||||
swapchain.beginFrame(getCurrentFrameIndex());
|
||||
VkCommandBuffer GfxDevice::beginFrame()
|
||||
{
|
||||
ZoneScopedN("GfxDevice::beginFrame");
|
||||
|
||||
{
|
||||
ZoneScopedN("Swapchain BeginFrame");
|
||||
swapchain.beginFrame(getCurrentFrameIndex());
|
||||
}
|
||||
|
||||
const auto& frame = getCurrentFrame();
|
||||
const auto& cmd = frame.commandBuffer;
|
||||
|
||||
const auto cmdBeginInfo = VkCommandBufferBeginInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
|
||||
};
|
||||
VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo));
|
||||
|
||||
{
|
||||
ZoneScopedN("vkBeginCommandBuffer");
|
||||
VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo));
|
||||
}
|
||||
|
||||
return cmd;
|
||||
}
|
||||
@@ -188,17 +231,34 @@ VulkanImmediateExecutor& GfxDevice::GetImmediateExecuter() {
|
||||
}
|
||||
|
||||
void GfxDevice::endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const EndFrameProps& props) {
|
||||
ZoneScopedN("GfxDevice::endFrame");
|
||||
|
||||
// get swapchain image
|
||||
const auto [swapchainImage, swapchainImageIndex] = swapchain.acquireNextImage(getCurrentFrameIndex());
|
||||
VkImage swapchainImage = VK_NULL_HANDLE;
|
||||
std::uint32_t swapchainImageIndex = 0;
|
||||
|
||||
{
|
||||
ZoneScopedN("Swapchain AcquireNextImage");
|
||||
|
||||
const auto result = swapchain.acquireNextImage(getCurrentFrameIndex());
|
||||
swapchainImage = result.first;
|
||||
swapchainImageIndex = result.second;
|
||||
}
|
||||
|
||||
if (swapchainImage == VK_NULL_HANDLE) {
|
||||
spdlog::info("Swapchain is freaky, skipping frame...");
|
||||
return;
|
||||
}
|
||||
|
||||
// Fences are reset here to prevent the deadlock in case swapchain becomes dirty
|
||||
swapchain.resetFences(getCurrentFrameIndex());
|
||||
{
|
||||
ZoneScopedN("Swapchain ResetFences");
|
||||
swapchain.resetFences(getCurrentFrameIndex());
|
||||
}
|
||||
|
||||
auto swapchainLayout = VK_IMAGE_LAYOUT_UNDEFINED; {
|
||||
ZoneScopedN("Clear Swapchain Image");
|
||||
|
||||
const VkImageSubresourceRange clearRange = vkinit::imageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT);
|
||||
vkutil::transitionImage(cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_GENERAL);
|
||||
swapchainLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
@@ -208,6 +268,8 @@ void GfxDevice::endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const E
|
||||
}
|
||||
|
||||
if (true) {
|
||||
ZoneScopedN("Copy DrawImage To Swapchain");
|
||||
|
||||
// copy from draw image into swapchain
|
||||
vkutil::transitionImage(
|
||||
cmd,
|
||||
@@ -248,6 +310,8 @@ void GfxDevice::endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const E
|
||||
// swapchainLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
|
||||
if (props.imguiPass) {
|
||||
ZoneScopedN("ImGui Render");
|
||||
|
||||
props.imguiPass->render(
|
||||
cmd,
|
||||
swapchainImage,
|
||||
@@ -258,18 +322,37 @@ void GfxDevice::endFrame(VkCommandBuffer cmd, const GPUImage& drawImage, const E
|
||||
}
|
||||
|
||||
// prepare for present
|
||||
vkutil::transitionImage(cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
|
||||
swapchainLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
{
|
||||
ZoneScopedN("Transition Swapchain To Present");
|
||||
|
||||
VK_CHECK(vkEndCommandBuffer(cmd));
|
||||
vkutil::transitionImage(cmd, swapchainImage, swapchainLayout, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR);
|
||||
swapchainLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
}
|
||||
#if defined(TRACY_ENABLE)
|
||||
TracyVkCollect(tracyVkCtx, cmd);
|
||||
#endif
|
||||
{
|
||||
ZoneScopedN("vkEndCommandBuffer");
|
||||
VK_CHECK(vkEndCommandBuffer(cmd));
|
||||
}
|
||||
|
||||
// swapchain.submitAndPresent(cmd, graphicsQueue, getCurrentFrameIndex(), swapchainImageIndex);
|
||||
swapchain.submitAndPresent(cmd, graphicsQueue, swapchainImageIndex, getCurrentFrameIndex());
|
||||
{
|
||||
ZoneScopedN("Swapchain SubmitAndPresent");
|
||||
swapchain.submitAndPresent(cmd, graphicsQueue, swapchainImageIndex, getCurrentFrameIndex());
|
||||
}
|
||||
|
||||
frameNumber++;
|
||||
FrameMark;
|
||||
}
|
||||
|
||||
void GfxDevice::cleanup() {
|
||||
#if defined(TRACY_ENABLE)
|
||||
if (tracyVkCtx) {
|
||||
TracyVkDestroy(tracyVkCtx);
|
||||
tracyVkCtx = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void GfxDevice::waitIdle() {
|
||||
|
||||
@@ -98,9 +98,9 @@ void MeshPipeline::draw(VkCommandBuffer cmd,
|
||||
for (const auto& dcIdx : drawCommands) {
|
||||
const auto& dc = dcIdx;
|
||||
|
||||
// if (!edge::isInFrustum(frustum, dc.worldBoundingSphere)) {
|
||||
// continue;
|
||||
// }
|
||||
if (!edge::isInFrustum(frustum, dc.worldBoundingSphere)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ActualDrawCalls++;
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "destrum/Util/GameState.h"
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
#include "tracy/TracyVulkan.hpp"
|
||||
|
||||
GameRenderer::GameRenderer(MeshCache& meshCache, MaterialCache& matCache): meshCache{meshCache}, materialCache{matCache} {
|
||||
}
|
||||
|
||||
@@ -41,13 +43,20 @@ void GameRenderer::endDrawing() {
|
||||
}
|
||||
|
||||
void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera& camera, const SceneData& sceneData) {
|
||||
ZoneScopedN("GameRenderer::draw");
|
||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "GameRenderer::draw");
|
||||
|
||||
for (const auto& dc : meshDrawCommands) {
|
||||
if (!dc.skinnedMesh) {
|
||||
continue;
|
||||
{
|
||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Skinning");
|
||||
|
||||
for (const auto& dc : meshDrawCommands) {
|
||||
if (!dc.skinnedMesh) {
|
||||
continue;
|
||||
}
|
||||
skinningPipeline->doSkinning(cmd, gfxDevice.getCurrentFrameIndex(), meshCache, dc);
|
||||
}
|
||||
skinningPipeline->doSkinning(cmd, gfxDevice.getCurrentFrameIndex(), meshCache, dc);
|
||||
}
|
||||
|
||||
const auto gpuSceneData = GPUSceneData{
|
||||
.view = sceneData.camera.GetViewMatrix(),
|
||||
.proj = sceneData.camera.GetProjectionMatrix(),
|
||||
@@ -59,40 +68,71 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
|
||||
.fogDensity = sceneData.fogDensity,
|
||||
.materialsBuffer = materialCache.getMaterialDataBufferAddress(),
|
||||
};
|
||||
vkutil::bufferHostWriteToShaderReadBarrier(
|
||||
cmd,
|
||||
materialCache.getMaterialDataBuffer().buffer,
|
||||
0,
|
||||
VK_WHOLE_SIZE
|
||||
);
|
||||
sceneDataBuffer.uploadNewData(cmd, gfxDevice.getCurrentFrameIndex(), (void*)&gpuSceneData, sizeof(GPUSceneData));
|
||||
|
||||
{
|
||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Material Buffer Barrier");
|
||||
|
||||
vkutil::bufferHostWriteToShaderReadBarrier(
|
||||
cmd,
|
||||
materialCache.getMaterialDataBuffer().buffer,
|
||||
0,
|
||||
VK_WHOLE_SIZE
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Upload Scene Data");
|
||||
|
||||
sceneDataBuffer.uploadNewData(
|
||||
cmd,
|
||||
gfxDevice.getCurrentFrameIndex(),
|
||||
(void*)&gpuSceneData,
|
||||
sizeof(GPUSceneData)
|
||||
);
|
||||
}
|
||||
|
||||
const auto& drawImage = gfxDevice.getImage(drawImageId);
|
||||
const auto& depthImage = gfxDevice.getImage(depthImageId);
|
||||
|
||||
// vkutil::cmdBeginLabel(cmd, "Geometry");
|
||||
vkutil::transitionImage(
|
||||
{
|
||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Transition Draw Image");
|
||||
|
||||
vkutil::transitionImage(
|
||||
cmd,
|
||||
drawImage.image,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
}
|
||||
|
||||
vkutil::transitionImage(
|
||||
cmd,
|
||||
depthImage.image,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL);
|
||||
{
|
||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "Transition Depth Image");
|
||||
|
||||
vkutil::transitionImage(
|
||||
cmd,
|
||||
depthImage.image,
|
||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||||
VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL);
|
||||
}
|
||||
|
||||
const auto renderInfo = vkutil::createRenderingInfo({
|
||||
.renderExtent = drawImage.getExtent2D(),
|
||||
.colorImageView = drawImage.imageView,
|
||||
.colorImageClearValue = glm::vec4{0.f, 0.f, 0.f, 1.f},
|
||||
.depthImageView = depthImage.imageView,
|
||||
.depthImageClearValue = 1.f,
|
||||
});
|
||||
.renderExtent = drawImage.getExtent2D(),
|
||||
.colorImageView = drawImage.imageView,
|
||||
.colorImageClearValue = glm::vec4{0.f, 0.f, 0.f, 1.f},
|
||||
.depthImageView = depthImage.imageView,
|
||||
.depthImageClearValue = 1.f,
|
||||
});
|
||||
|
||||
vkCmdBeginRendering(cmd, &renderInfo.renderingInfo);
|
||||
meshPipeline->draw(
|
||||
{
|
||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "vkCmdBeginRendering");
|
||||
|
||||
vkCmdBeginRendering(cmd, &renderInfo.renderingInfo);
|
||||
}
|
||||
|
||||
{
|
||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "MeshPipeline::draw");
|
||||
|
||||
meshPipeline->draw(
|
||||
cmd,
|
||||
drawImage.getExtent2D(),
|
||||
gfxDevice,
|
||||
@@ -102,14 +142,20 @@ void GameRenderer::draw(VkCommandBuffer cmd, GfxDevice& gfxDevice, const Camera&
|
||||
sceneDataBuffer.getBuffer(),
|
||||
meshDrawCommands,
|
||||
sortedMeshDrawCommands);
|
||||
}
|
||||
|
||||
skyboxPipeline->draw(cmd, gfxDevice, camera);
|
||||
{
|
||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "SkyboxPipeline::draw");
|
||||
|
||||
skyboxPipeline->draw(cmd, gfxDevice, camera);
|
||||
}
|
||||
|
||||
vkCmdEndRendering(cmd);
|
||||
{
|
||||
TracyVkZone(gfxDevice.getTracyVkCtx(), cmd, "vkCmdEndRendering");
|
||||
|
||||
vkCmdEndRendering(cmd);
|
||||
}
|
||||
// vkutil::cmdEndLabel(cmd);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void GameRenderer::cleanup(GfxDevice& gfxDevice) {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <destrum/Graphics/Init.h>
|
||||
|
||||
#include "volk.h"
|
||||
#include "tracy/Tracy.hpp"
|
||||
|
||||
|
||||
void Swapchain::initSync(VkDevice device) {
|
||||
@@ -23,30 +24,40 @@ void Swapchain::initSync(VkDevice device) {
|
||||
}
|
||||
|
||||
void Swapchain::createSwapchain(GfxDevice* gfxDevice, VkFormat format, std::uint32_t width, std::uint32_t height, bool vSync) {
|
||||
ZoneScopedN("Swapchain::createSwapchain");
|
||||
|
||||
m_gfxDevice = gfxDevice;
|
||||
assert(format == VK_FORMAT_B8G8R8A8_SRGB && "TODO: test other formats");
|
||||
vSync = true;
|
||||
// vSync = true;
|
||||
|
||||
auto res = vkb::SwapchainBuilder{gfxDevice->getDevice()}
|
||||
.set_desired_format(VkSurfaceFormatKHR{
|
||||
.format = format,
|
||||
.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
|
||||
})
|
||||
.add_image_usage_flags(VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)
|
||||
.set_desired_present_mode(
|
||||
vSync ? VK_PRESENT_MODE_FIFO_KHR : VK_PRESENT_MODE_IMMEDIATE_KHR)
|
||||
.set_desired_extent(width, height)
|
||||
.build();
|
||||
if (!res.has_value()) {
|
||||
// throw std::runtime_error(std::format(
|
||||
// "failed to create swapchain: error = {}, vk result = {}",
|
||||
// res.full_error().type.message(),
|
||||
// string_VkResult(res.full_error().vk_result)));
|
||||
{
|
||||
ZoneScopedN("vkb::SwapchainBuilder::build");
|
||||
|
||||
auto res = vkb::SwapchainBuilder{gfxDevice->getDevice()}
|
||||
.set_desired_format(VkSurfaceFormatKHR{
|
||||
.format = format,
|
||||
.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
|
||||
})
|
||||
.add_image_usage_flags(VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)
|
||||
.set_desired_present_mode(
|
||||
vSync ? VK_PRESENT_MODE_FIFO_KHR : VK_PRESENT_MODE_IMMEDIATE_KHR)
|
||||
.set_desired_extent(width, height)
|
||||
.build();
|
||||
if (!res.has_value()) {
|
||||
// throw std::runtime_error(std::format(
|
||||
// "failed to create swapchain: error = {}, vk result = {}",
|
||||
// res.full_error().type.message(),
|
||||
// string_VkResult(res.full_error().vk_result)));
|
||||
}
|
||||
m_swapchain = res.value();
|
||||
}
|
||||
m_swapchain = res.value();
|
||||
|
||||
images = m_swapchain.get_images().value();
|
||||
imageViews = m_swapchain.get_image_views().value();
|
||||
{
|
||||
ZoneScopedN("Get Swapchain Images / Views");
|
||||
|
||||
images = m_swapchain.get_images().value();
|
||||
imageViews = m_swapchain.get_image_views().value();
|
||||
}
|
||||
|
||||
imageRenderSemaphores.resize(images.size());
|
||||
|
||||
@@ -55,6 +66,8 @@ void Swapchain::createSwapchain(GfxDevice* gfxDevice, VkFormat format, std::uint
|
||||
};
|
||||
|
||||
for (auto& sem: imageRenderSemaphores) {
|
||||
ZoneScopedN("Create Image Render Semaphore");
|
||||
|
||||
VK_CHECK(vkCreateSemaphore(m_gfxDevice->getDevice(), &sci, nullptr, &sem));
|
||||
}
|
||||
|
||||
@@ -71,6 +84,8 @@ void Swapchain::recreateSwapchain(
|
||||
std::uint32_t height,
|
||||
bool vSync)
|
||||
{
|
||||
ZoneScopedN("Swapchain::recreateSwapchain");
|
||||
|
||||
if (width == 0 || height == 0) {
|
||||
dirty = true;
|
||||
return;
|
||||
@@ -78,45 +93,67 @@ void Swapchain::recreateSwapchain(
|
||||
|
||||
VkDevice device = gfxDevice.getDevice();
|
||||
|
||||
vkDeviceWaitIdle(device);
|
||||
{
|
||||
ZoneScopedN("vkDeviceWaitIdle");
|
||||
vkDeviceWaitIdle(device);
|
||||
}
|
||||
|
||||
auto oldSwapchain = m_swapchain;
|
||||
|
||||
auto res = vkb::SwapchainBuilder{gfxDevice.getVkbDevice()}
|
||||
.set_old_swapchain(oldSwapchain)
|
||||
.set_desired_format(VkSurfaceFormatKHR{
|
||||
.format = format,
|
||||
.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
|
||||
})
|
||||
.add_image_usage_flags(VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)
|
||||
.set_desired_present_mode(
|
||||
vSync ? VK_PRESENT_MODE_FIFO_KHR : VK_PRESENT_MODE_IMMEDIATE_KHR)
|
||||
.set_desired_extent(width, height)
|
||||
.build();
|
||||
{
|
||||
ZoneScopedN("vkb::SwapchainBuilder::rebuild");
|
||||
|
||||
if (!res.has_value()) {
|
||||
// throw std::runtime_error(std::format(
|
||||
// "failed to create swapchain: error = {}, vk result = {}",
|
||||
// res.full_error().type.message(),
|
||||
// string_VkResult(res.full_error().vk_result)));
|
||||
auto res = vkb::SwapchainBuilder{gfxDevice.getVkbDevice()}
|
||||
.set_old_swapchain(oldSwapchain)
|
||||
.set_desired_format(VkSurfaceFormatKHR{
|
||||
.format = format,
|
||||
.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
|
||||
})
|
||||
.add_image_usage_flags(VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)
|
||||
.set_desired_present_mode(
|
||||
vSync ? VK_PRESENT_MODE_FIFO_KHR : VK_PRESENT_MODE_IMMEDIATE_KHR)
|
||||
.set_desired_extent(width, height)
|
||||
.build();
|
||||
|
||||
if (!res.has_value()) {
|
||||
// throw std::runtime_error(std::format(
|
||||
// "failed to create swapchain: error = {}, vk result = {}",
|
||||
// res.full_error().type.message(),
|
||||
// string_VkResult(res.full_error().vk_result)));
|
||||
}
|
||||
|
||||
m_swapchain = res.value();
|
||||
}
|
||||
|
||||
for (auto sem : imageRenderSemaphores) {
|
||||
vkDestroySemaphore(device, sem, nullptr);
|
||||
{
|
||||
ZoneScopedN("Destroy Old Image Render Semaphores");
|
||||
|
||||
for (auto sem : imageRenderSemaphores) {
|
||||
vkDestroySemaphore(device, sem, nullptr);
|
||||
}
|
||||
imageRenderSemaphores.clear();
|
||||
}
|
||||
imageRenderSemaphores.clear();
|
||||
|
||||
for (auto imageView : imageViews) {
|
||||
vkDestroyImageView(device, imageView, nullptr);
|
||||
{
|
||||
ZoneScopedN("Destroy Old Image Views");
|
||||
|
||||
for (auto imageView : imageViews) {
|
||||
vkDestroyImageView(device, imageView, nullptr);
|
||||
}
|
||||
imageViews.clear();
|
||||
}
|
||||
imageViews.clear();
|
||||
|
||||
vkb::destroy_swapchain(oldSwapchain);
|
||||
{
|
||||
ZoneScopedN("Destroy Old Swapchain");
|
||||
vkb::destroy_swapchain(oldSwapchain);
|
||||
}
|
||||
|
||||
m_swapchain = res.value();
|
||||
{
|
||||
ZoneScopedN("Get New Swapchain Images / Views");
|
||||
|
||||
images = m_swapchain.get_images().value();
|
||||
imageViews = m_swapchain.get_image_views().value();
|
||||
images = m_swapchain.get_images().value();
|
||||
imageViews = m_swapchain.get_image_views().value();
|
||||
}
|
||||
|
||||
VkSemaphoreCreateInfo sci{
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
|
||||
@@ -125,6 +162,8 @@ void Swapchain::recreateSwapchain(
|
||||
imageRenderSemaphores.resize(images.size());
|
||||
|
||||
for (auto& sem : imageRenderSemaphores) {
|
||||
ZoneScopedN("Create New Image Render Semaphore");
|
||||
|
||||
VK_CHECK(vkCreateSemaphore(device, &sci, nullptr, &sem));
|
||||
}
|
||||
|
||||
@@ -148,13 +187,25 @@ void Swapchain::cleanup() {
|
||||
}
|
||||
|
||||
void Swapchain::beginFrame(int index) const {
|
||||
ZoneScopedN("Swapchain::beginFrame");
|
||||
|
||||
auto& frame = frames[index];
|
||||
VK_CHECK(vkWaitForFences(m_gfxDevice->getDevice(), 1, &frame.renderFence, true, std::numeric_limits<std::uint64_t>::max()));
|
||||
|
||||
{
|
||||
ZoneScopedN("vkWaitForFences");
|
||||
VK_CHECK(vkWaitForFences(m_gfxDevice->getDevice(), 1, &frame.renderFence, true, std::numeric_limits<std::uint64_t>::max()));
|
||||
}
|
||||
}
|
||||
|
||||
void Swapchain::resetFences(int index) const {
|
||||
ZoneScopedN("Swapchain::resetFences");
|
||||
|
||||
auto& frame = frames[index];
|
||||
VK_CHECK(vkResetFences(m_gfxDevice->getDevice(), 1, &frame.renderFence));
|
||||
|
||||
{
|
||||
ZoneScopedN("vkResetFences");
|
||||
VK_CHECK(vkResetFences(m_gfxDevice->getDevice(), 1, &frame.renderFence));
|
||||
}
|
||||
}
|
||||
|
||||
struct SwapchainAcquireResult {
|
||||
@@ -164,14 +215,24 @@ struct SwapchainAcquireResult {
|
||||
};
|
||||
|
||||
std::pair<VkImage, int> Swapchain::acquireNextImage(int index) {
|
||||
ZoneScopedN("Swapchain::acquireNextImage");
|
||||
|
||||
std::uint32_t swapchainImageIndex{};
|
||||
const auto result = vkAcquireNextImageKHR(
|
||||
m_gfxDevice->getDevice(),
|
||||
m_swapchain,
|
||||
std::numeric_limits<std::uint64_t>::max(),
|
||||
frames[index].swapchainSemaphore,
|
||||
VK_NULL_HANDLE,
|
||||
&swapchainImageIndex);
|
||||
|
||||
VkResult result = VK_SUCCESS;
|
||||
|
||||
{
|
||||
ZoneScopedN("vkAcquireNextImageKHR");
|
||||
|
||||
result = vkAcquireNextImageKHR(
|
||||
m_gfxDevice->getDevice(),
|
||||
m_swapchain,
|
||||
std::numeric_limits<std::uint64_t>::max(),
|
||||
frames[index].swapchainSemaphore,
|
||||
VK_NULL_HANDLE,
|
||||
&swapchainImageIndex);
|
||||
}
|
||||
|
||||
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
|
||||
dirty = true;
|
||||
return {images[swapchainImageIndex], swapchainImageIndex};
|
||||
@@ -188,6 +249,8 @@ void Swapchain::submitAndPresent(
|
||||
uint32_t imageIndex, // from vkAcquireNextImageKHR
|
||||
uint32_t frameIndex) // 0..FRAMES_IN_FLIGHT-1
|
||||
{
|
||||
ZoneScopedN("Swapchain::submitAndPresent");
|
||||
|
||||
auto& frame = frames[frameIndex]; // ✅ per-frame
|
||||
|
||||
VkSemaphore renderFinished = imageRenderSemaphores[imageIndex]; // ✅ per-image
|
||||
@@ -209,7 +272,11 @@ void Swapchain::submitAndPresent(
|
||||
renderFinished); // ✅ signal semaphore (per-image)
|
||||
|
||||
VkSubmitInfo2 submit = vkinit::submitInfo(&cmdInfo, &waitInfo, &signalInfo);
|
||||
VK_CHECK(vkQueueSubmit2(graphicsQueue, 1, &submit, frame.renderFence)); // ✅ fence (per-frame)
|
||||
|
||||
{
|
||||
ZoneScopedN("vkQueueSubmit2");
|
||||
VK_CHECK(vkQueueSubmit2(graphicsQueue, 1, &submit, frame.renderFence)); // ✅ fence (per-frame)
|
||||
}
|
||||
|
||||
// present
|
||||
VkPresentInfoKHR presentInfo{
|
||||
@@ -221,7 +288,13 @@ void Swapchain::submitAndPresent(
|
||||
.pImageIndices = &imageIndex, // ✅ imageIndex, NOT frameIndex
|
||||
};
|
||||
|
||||
VkResult res = vkQueuePresentKHR(graphicsQueue, &presentInfo);
|
||||
VkResult res = VK_SUCCESS;
|
||||
|
||||
{
|
||||
ZoneScopedN("vkQueuePresentKHR");
|
||||
res = vkQueuePresentKHR(graphicsQueue, &presentInfo);
|
||||
}
|
||||
|
||||
if (res == VK_ERROR_OUT_OF_DATE_KHR || res == VK_SUBOPTIMAL_KHR) dirty = true;
|
||||
else if (res != VK_SUCCESS) dirty = true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,692 @@
|
||||
#include <destrum/Physics/JoltPhysicsWorld.h>
|
||||
|
||||
// Jolt/Jolt.h must be included before other Jolt headers.
|
||||
#include <Jolt/Jolt.h>
|
||||
|
||||
#include <Jolt/Core/Factory.h>
|
||||
#include <Jolt/Core/JobSystemThreadPool.h>
|
||||
#include <Jolt/Core/TempAllocator.h>
|
||||
#include <Jolt/Physics/Body/BodyCreationSettings.h>
|
||||
#include <Jolt/Physics/Body/BodyInterface.h>
|
||||
#include <Jolt/Physics/Body/MotionType.h>
|
||||
#include <Jolt/Physics/Collision/BroadPhase/BroadPhaseLayer.h>
|
||||
#include <Jolt/Physics/Collision/CastResult.h>
|
||||
#include <Jolt/Physics/Collision/ObjectLayer.h>
|
||||
#include <Jolt/Physics/Collision/RayCast.h>
|
||||
#include <Jolt/Physics/Collision/Shape/BoxShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/CapsuleShape.h>
|
||||
#include <Jolt/Physics/Collision/Shape/SphereShape.h>
|
||||
#include <Jolt/Physics/PhysicsSystem.h>
|
||||
#include <Jolt/RegisterTypes.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <glm/gtx/norm.hpp>
|
||||
|
||||
#include <destrum/Components/Physics/Rigidbody.h>
|
||||
#include <destrum/ObjectModel/GameObject.h>
|
||||
#include <destrum/ObjectModel/Transform.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace JPH;
|
||||
|
||||
static std::atomic_uint32_t g_JoltInstanceCount{0};
|
||||
|
||||
static void TraceImpl(const char* inFMT, ...)
|
||||
{
|
||||
va_list list;
|
||||
va_start(list, inFMT);
|
||||
std::vprintf(inFMT, list);
|
||||
std::printf("\n");
|
||||
va_end(list);
|
||||
}
|
||||
|
||||
#ifdef JPH_ENABLE_ASSERTS
|
||||
static bool AssertFailedImpl(const char* inExpression,
|
||||
const char* inMessage,
|
||||
const char* inFile,
|
||||
JPH::uint inLine)
|
||||
{
|
||||
std::printf("%s:%u: (%s) %s\n",
|
||||
inFile,
|
||||
inLine,
|
||||
inExpression,
|
||||
inMessage != nullptr ? inMessage : "");
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
void InitJoltGlobals()
|
||||
{
|
||||
if (g_JoltInstanceCount.fetch_add(1) == 0)
|
||||
{
|
||||
RegisterDefaultAllocator();
|
||||
|
||||
Trace = TraceImpl;
|
||||
JPH_IF_ENABLE_ASSERTS(AssertFailed = AssertFailedImpl;)
|
||||
|
||||
Factory::sInstance = new Factory();
|
||||
|
||||
RegisterTypes();
|
||||
}
|
||||
}
|
||||
|
||||
void ShutdownJoltGlobals()
|
||||
{
|
||||
if (g_JoltInstanceCount.fetch_sub(1) == 1)
|
||||
{
|
||||
UnregisterTypes();
|
||||
|
||||
delete Factory::sInstance;
|
||||
Factory::sInstance = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Layers
|
||||
{
|
||||
static constexpr ObjectLayer NON_MOVING = 0;
|
||||
static constexpr ObjectLayer MOVING = 1;
|
||||
static constexpr ObjectLayer NUM_LAYERS = 2;
|
||||
}
|
||||
|
||||
namespace BroadPhaseLayers
|
||||
{
|
||||
static constexpr BroadPhaseLayer NON_MOVING(0);
|
||||
static constexpr BroadPhaseLayer MOVING(1);
|
||||
static constexpr JPH::uint NUM_LAYERS(2);
|
||||
}
|
||||
|
||||
class ObjectLayerPairFilterImpl final : public ObjectLayerPairFilter
|
||||
{
|
||||
public:
|
||||
bool ShouldCollide(ObjectLayer inObject1, ObjectLayer inObject2) const override
|
||||
{
|
||||
switch (inObject1)
|
||||
{
|
||||
case Layers::NON_MOVING:
|
||||
return inObject2 == Layers::MOVING;
|
||||
|
||||
case Layers::MOVING:
|
||||
return true;
|
||||
|
||||
default:
|
||||
JPH_ASSERT(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class BPLayerInterfaceImpl final : public BroadPhaseLayerInterface
|
||||
{
|
||||
public:
|
||||
BPLayerInterfaceImpl()
|
||||
{
|
||||
m_ObjectToBroadPhase[Layers::NON_MOVING] = BroadPhaseLayers::NON_MOVING;
|
||||
m_ObjectToBroadPhase[Layers::MOVING] = BroadPhaseLayers::MOVING;
|
||||
}
|
||||
|
||||
JPH::uint GetNumBroadPhaseLayers() const override
|
||||
{
|
||||
return BroadPhaseLayers::NUM_LAYERS;
|
||||
}
|
||||
|
||||
BroadPhaseLayer GetBroadPhaseLayer(ObjectLayer inLayer) const override
|
||||
{
|
||||
JPH_ASSERT(inLayer < Layers::NUM_LAYERS);
|
||||
return m_ObjectToBroadPhase[inLayer];
|
||||
}
|
||||
|
||||
#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED)
|
||||
const char* GetBroadPhaseLayerName(BroadPhaseLayer inLayer) const override
|
||||
{
|
||||
switch ((BroadPhaseLayer::Type)inLayer)
|
||||
{
|
||||
case (BroadPhaseLayer::Type)BroadPhaseLayers::NON_MOVING:
|
||||
return "NON_MOVING";
|
||||
|
||||
case (BroadPhaseLayer::Type)BroadPhaseLayers::MOVING:
|
||||
return "MOVING";
|
||||
|
||||
default:
|
||||
JPH_ASSERT(false);
|
||||
return "INVALID";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
BroadPhaseLayer m_ObjectToBroadPhase[Layers::NUM_LAYERS];
|
||||
};
|
||||
|
||||
class ObjectVsBroadPhaseLayerFilterImpl final : public ObjectVsBroadPhaseLayerFilter
|
||||
{
|
||||
public:
|
||||
bool ShouldCollide(ObjectLayer inLayer1, BroadPhaseLayer inLayer2) const override
|
||||
{
|
||||
switch (inLayer1)
|
||||
{
|
||||
case Layers::NON_MOVING:
|
||||
return inLayer2 == BroadPhaseLayers::MOVING;
|
||||
|
||||
case Layers::MOVING:
|
||||
return true;
|
||||
|
||||
default:
|
||||
JPH_ASSERT(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
[[nodiscard]] Vec3 ToJoltVec3(const glm::vec3& v)
|
||||
{
|
||||
return Vec3(v.x, v.y, v.z);
|
||||
}
|
||||
|
||||
[[nodiscard]] RVec3 ToJoltRVec3(const glm::vec3& v)
|
||||
{
|
||||
return RVec3(v.x, v.y, v.z);
|
||||
}
|
||||
|
||||
[[nodiscard]] Quat ToJoltQuat(const glm::quat& q)
|
||||
{
|
||||
return Quat(q.x, q.y, q.z, q.w);
|
||||
}
|
||||
|
||||
[[nodiscard]] glm::vec3 FromJoltVec3(Vec3Arg v)
|
||||
{
|
||||
return glm::vec3(v.GetX(), v.GetY(), v.GetZ());
|
||||
}
|
||||
|
||||
[[nodiscard]] glm::vec3 FromJoltRVec3(RVec3Arg v)
|
||||
{
|
||||
return glm::vec3(static_cast<float>(v.GetX()),
|
||||
static_cast<float>(v.GetY()),
|
||||
static_cast<float>(v.GetZ()));
|
||||
}
|
||||
|
||||
[[nodiscard]] glm::quat FromJoltQuat(QuatArg q)
|
||||
{
|
||||
return glm::quat(q.GetW(), q.GetX(), q.GetY(), q.GetZ());
|
||||
}
|
||||
|
||||
[[nodiscard]] EMotionType ToJoltMotionType(RigidbodyType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case RigidbodyType::Static:
|
||||
return EMotionType::Static;
|
||||
|
||||
case RigidbodyType::Dynamic:
|
||||
return EMotionType::Dynamic;
|
||||
|
||||
case RigidbodyType::Kinematic:
|
||||
return EMotionType::Kinematic;
|
||||
|
||||
default:
|
||||
return EMotionType::Static;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] ObjectLayer ToJoltObjectLayer(RigidbodyType type)
|
||||
{
|
||||
return type == RigidbodyType::Static ? Layers::NON_MOVING : Layers::MOVING;
|
||||
}
|
||||
|
||||
[[nodiscard]] ShapeRefC CreateJoltShape(const PhysicsShapeDesc& desc)
|
||||
{
|
||||
switch (desc.type)
|
||||
{
|
||||
case PhysicsShapeType::Box:
|
||||
{
|
||||
BoxShapeSettings settings(ToJoltVec3(desc.halfExtents));
|
||||
ShapeSettings::ShapeResult result = settings.Create();
|
||||
|
||||
if (!result.IsValid())
|
||||
{
|
||||
throw std::runtime_error(
|
||||
std::string("Failed to create Jolt box shape: ") + std::string(result.GetError()));
|
||||
}
|
||||
|
||||
return result.Get();
|
||||
}
|
||||
|
||||
case PhysicsShapeType::Sphere:
|
||||
return new SphereShape(desc.radius);
|
||||
|
||||
case PhysicsShapeType::Capsule:
|
||||
{
|
||||
// Jolt expects half-height of the cylindrical section, not full capsule height.
|
||||
const float halfCylinderHeight = std::max(0.0f, (desc.height * 0.5f) - desc.radius);
|
||||
return new CapsuleShape(halfCylinderHeight, desc.radius);
|
||||
}
|
||||
|
||||
case PhysicsShapeType::None:
|
||||
default:
|
||||
throw std::runtime_error("Cannot create Jolt body without a valid shape.");
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class JoltPhysicsWorld::Impl
|
||||
{
|
||||
public:
|
||||
explicit Impl(const Settings& settings)
|
||||
: m_Settings(settings)
|
||||
{
|
||||
InitJoltGlobals();
|
||||
|
||||
const JPH::uint hardwareThreads = std::thread::hardware_concurrency();
|
||||
const JPH::uint workerThreads =
|
||||
settings.workerThreadCount != 0
|
||||
? settings.workerThreadCount
|
||||
: std::max<JPH::uint>(1, hardwareThreads > 0 ? hardwareThreads - 1 : 1);
|
||||
|
||||
m_TempAllocator = std::make_unique<TempAllocatorImpl>(settings.tempAllocatorSizeBytes);
|
||||
m_JobSystem = std::make_unique<JobSystemThreadPool>(
|
||||
cMaxPhysicsJobs,
|
||||
cMaxPhysicsBarriers,
|
||||
static_cast<int>(workerThreads));
|
||||
|
||||
m_PhysicsSystem.Init(
|
||||
settings.maxBodies,
|
||||
settings.numBodyMutexes,
|
||||
settings.maxBodyPairs,
|
||||
settings.maxContactConstraints,
|
||||
m_BPLayerInterface,
|
||||
m_ObjectVsBroadPhaseLayerFilter,
|
||||
m_ObjectLayerPairFilter);
|
||||
|
||||
m_PhysicsSystem.SetGravity(ToJoltVec3(settings.gravity));
|
||||
}
|
||||
|
||||
~Impl()
|
||||
{
|
||||
BodyInterface& bodyInterface = m_PhysicsSystem.GetBodyInterface();
|
||||
|
||||
for (auto& [handleId, record] : m_Bodies)
|
||||
{
|
||||
if (!record.alive)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bodyInterface.RemoveBody(record.bodyID);
|
||||
bodyInterface.DestroyBody(record.bodyID);
|
||||
}
|
||||
|
||||
m_Bodies.clear();
|
||||
|
||||
ShutdownJoltGlobals();
|
||||
}
|
||||
|
||||
PhysicsBodyHandle CreateBody(const PhysicsBodyDesc& desc)
|
||||
{
|
||||
ShapeRefC shape = CreateJoltShape(desc.shape);
|
||||
|
||||
// This first backend ignores centerOffset for now.
|
||||
// Later, use JPH::OffsetCenterOfMassShapeSettings for offset colliders.
|
||||
|
||||
BodyCreationSettings settings(
|
||||
shape,
|
||||
ToJoltRVec3(desc.transform.position),
|
||||
ToJoltQuat(desc.transform.rotation),
|
||||
ToJoltMotionType(desc.type),
|
||||
ToJoltObjectLayer(desc.type));
|
||||
|
||||
settings.mFriction = desc.material.friction;
|
||||
settings.mRestitution = desc.material.restitution;
|
||||
settings.mIsSensor = desc.shape.isTrigger;
|
||||
settings.mAllowSleeping = desc.allowSleep;
|
||||
settings.mGravityFactor = desc.useGravity ? 1.0f : 0.0f;
|
||||
settings.mUserData = reinterpret_cast<JPH::uint64>(desc.owner);
|
||||
|
||||
if (desc.type == RigidbodyType::Dynamic)
|
||||
{
|
||||
settings.mOverrideMassProperties = EOverrideMassProperties::CalculateInertia;
|
||||
settings.mMassPropertiesOverride.mMass = std::max(0.0001f, desc.mass);
|
||||
}
|
||||
|
||||
const EActivation activation =
|
||||
desc.type == RigidbodyType::Dynamic ? EActivation::Activate : EActivation::DontActivate;
|
||||
|
||||
BodyInterface& bodyInterface = m_PhysicsSystem.GetBodyInterface();
|
||||
BodyID bodyID = bodyInterface.CreateAndAddBody(settings, activation);
|
||||
|
||||
if (bodyID.IsInvalid())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
PhysicsBodyHandle handle{m_NextHandle++};
|
||||
|
||||
BodyRecord record{};
|
||||
record.alive = true;
|
||||
record.bodyID = bodyID;
|
||||
record.desc = desc;
|
||||
|
||||
m_Bodies.emplace(handle.id, record);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
void DestroyBody(PhysicsBodyHandle body)
|
||||
{
|
||||
auto it = m_Bodies.find(body.id);
|
||||
if (it == m_Bodies.end() || !it->second.alive)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BodyInterface& bodyInterface = m_PhysicsSystem.GetBodyInterface();
|
||||
bodyInterface.RemoveBody(it->second.bodyID);
|
||||
bodyInterface.DestroyBody(it->second.bodyID);
|
||||
|
||||
m_Bodies.erase(it);
|
||||
}
|
||||
|
||||
void Step(float fixedDt)
|
||||
{
|
||||
if (fixedDt <= 0.0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const int collisionSteps = std::max(1, static_cast<int>(std::ceil(fixedDt / (1.0f / 60.0f))));
|
||||
|
||||
m_PhysicsSystem.Update(
|
||||
fixedDt,
|
||||
collisionSteps,
|
||||
m_TempAllocator.get(),
|
||||
m_JobSystem.get());
|
||||
}
|
||||
|
||||
void SyncKinematicBodiesToPhysics()
|
||||
{
|
||||
BodyInterface& bodyInterface = m_PhysicsSystem.GetBodyInterface();
|
||||
|
||||
for (auto& [handleId, record] : m_Bodies)
|
||||
{
|
||||
if (!record.alive || record.desc.type != RigidbodyType::Kinematic || !record.desc.owner)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Transform& transform = record.desc.owner->GetTransform();
|
||||
|
||||
bodyInterface.SetPositionAndRotation(
|
||||
record.bodyID,
|
||||
ToJoltRVec3(transform.GetWorldPosition()),
|
||||
ToJoltQuat(transform.GetWorldRotation()),
|
||||
EActivation::Activate);
|
||||
}
|
||||
}
|
||||
|
||||
void SyncDynamicBodiesToTransforms()
|
||||
{
|
||||
BodyInterface& bodyInterface = m_PhysicsSystem.GetBodyInterface();
|
||||
|
||||
for (auto& [handleId, record] : m_Bodies)
|
||||
{
|
||||
if (!record.alive || record.desc.type != RigidbodyType::Dynamic || !record.desc.owner)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
RVec3 position{};
|
||||
Quat rotation{};
|
||||
bodyInterface.GetPositionAndRotation(record.bodyID, position, rotation);
|
||||
|
||||
Transform& transform = record.desc.owner->GetTransform();
|
||||
transform.SetWorldPosition(FromJoltRVec3(position));
|
||||
transform.SetWorldRotation(FromJoltQuat(rotation));
|
||||
}
|
||||
}
|
||||
|
||||
void SetBodyTransform(PhysicsBodyHandle body, const PhysicsTransform& transform)
|
||||
{
|
||||
if (BodyRecord* record = FindBody(body))
|
||||
{
|
||||
m_PhysicsSystem.GetBodyInterface().SetPositionAndRotation(
|
||||
record->bodyID,
|
||||
ToJoltRVec3(transform.position),
|
||||
ToJoltQuat(transform.rotation),
|
||||
EActivation::Activate);
|
||||
}
|
||||
}
|
||||
|
||||
PhysicsTransform GetBodyTransform(PhysicsBodyHandle body) const
|
||||
{
|
||||
const BodyRecord* record = FindBody(body);
|
||||
if (!record)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
RVec3 position{};
|
||||
Quat rotation{};
|
||||
m_PhysicsSystem.GetBodyInterface().GetPositionAndRotation(record->bodyID, position, rotation);
|
||||
|
||||
return PhysicsTransform{
|
||||
.position = FromJoltRVec3(position),
|
||||
.rotation = FromJoltQuat(rotation)
|
||||
};
|
||||
}
|
||||
|
||||
void SetLinearVelocity(PhysicsBodyHandle body, const glm::vec3& velocity)
|
||||
{
|
||||
if (BodyRecord* record = FindBody(body))
|
||||
{
|
||||
m_PhysicsSystem.GetBodyInterface().SetLinearVelocity(record->bodyID, ToJoltVec3(velocity));
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 GetLinearVelocity(PhysicsBodyHandle body) const
|
||||
{
|
||||
const BodyRecord* record = FindBody(body);
|
||||
if (!record)
|
||||
{
|
||||
return glm::vec3{0.0f};
|
||||
}
|
||||
|
||||
return FromJoltVec3(m_PhysicsSystem.GetBodyInterface().GetLinearVelocity(record->bodyID));
|
||||
}
|
||||
|
||||
void AddForce(PhysicsBodyHandle body, const glm::vec3& force)
|
||||
{
|
||||
if (BodyRecord* record = FindBody(body))
|
||||
{
|
||||
m_PhysicsSystem.GetBodyInterface().AddForce(record->bodyID, ToJoltVec3(force));
|
||||
}
|
||||
}
|
||||
|
||||
void AddImpulse(PhysicsBodyHandle body, const glm::vec3& impulse)
|
||||
{
|
||||
if (BodyRecord* record = FindBody(body))
|
||||
{
|
||||
m_PhysicsSystem.GetBodyInterface().AddImpulse(record->bodyID, ToJoltVec3(impulse));
|
||||
}
|
||||
}
|
||||
|
||||
bool Raycast(const glm::vec3& origin,
|
||||
const glm::vec3& direction,
|
||||
float maxDistance,
|
||||
PhysicsRaycastHit& hit) const
|
||||
{
|
||||
if (maxDistance <= 0.0f || glm::length2(direction) <= 0.0000001f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const glm::vec3 directionNormalized = glm::normalize(direction);
|
||||
|
||||
RRayCast ray{
|
||||
ToJoltRVec3(origin),
|
||||
ToJoltVec3(directionNormalized * maxDistance)
|
||||
};
|
||||
|
||||
RayCastResult result{};
|
||||
if (!m_PhysicsSystem.GetNarrowPhaseQuery().CastRay(ray, result))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
hit.body = FindHandle(result.mBodyID);
|
||||
hit.distance = result.mFraction * maxDistance;
|
||||
hit.point = origin + directionNormalized * hit.distance;
|
||||
hit.object = nullptr;
|
||||
hit.normal = glm::vec3{0.0f, 1.0f, 0.0f};
|
||||
|
||||
BodyLockRead lock(m_PhysicsSystem.GetBodyLockInterface(), result.mBodyID);
|
||||
if (lock.Succeeded())
|
||||
{
|
||||
const Body& body = lock.GetBody();
|
||||
|
||||
hit.object = reinterpret_cast<GameObject*>(body.GetUserData());
|
||||
hit.normal = FromJoltVec3(
|
||||
body.GetWorldSpaceSurfaceNormal(
|
||||
result.mSubShapeID2,
|
||||
ray.GetPointOnRay(result.mFraction)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
struct BodyRecord
|
||||
{
|
||||
bool alive{false};
|
||||
BodyID bodyID{};
|
||||
PhysicsBodyDesc desc{};
|
||||
};
|
||||
|
||||
BodyRecord* FindBody(PhysicsBodyHandle body)
|
||||
{
|
||||
auto it = m_Bodies.find(body.id);
|
||||
if (it == m_Bodies.end())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
const BodyRecord* FindBody(PhysicsBodyHandle body) const
|
||||
{
|
||||
auto it = m_Bodies.find(body.id);
|
||||
if (it == m_Bodies.end())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
PhysicsBodyHandle FindHandle(BodyID bodyID) const
|
||||
{
|
||||
for (const auto& [handleId, record] : m_Bodies)
|
||||
{
|
||||
if (record.alive && record.bodyID == bodyID)
|
||||
{
|
||||
return PhysicsBodyHandle{handleId};
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Settings m_Settings{};
|
||||
|
||||
BPLayerInterfaceImpl m_BPLayerInterface{};
|
||||
ObjectVsBroadPhaseLayerFilterImpl m_ObjectVsBroadPhaseLayerFilter{};
|
||||
ObjectLayerPairFilterImpl m_ObjectLayerPairFilter{};
|
||||
|
||||
PhysicsSystem m_PhysicsSystem{};
|
||||
|
||||
std::unique_ptr<TempAllocatorImpl> m_TempAllocator;
|
||||
std::unique_ptr<JobSystemThreadPool> m_JobSystem;
|
||||
|
||||
std::unordered_map<std::uint32_t, BodyRecord> m_Bodies;
|
||||
std::uint32_t m_NextHandle{0};
|
||||
};
|
||||
|
||||
JoltPhysicsWorld::JoltPhysicsWorld(const Settings& settings)
|
||||
: m_Impl(std::make_unique<Impl>(settings))
|
||||
{
|
||||
}
|
||||
|
||||
JoltPhysicsWorld::~JoltPhysicsWorld() = default;
|
||||
|
||||
void JoltPhysicsWorld::Step(float fixedDt)
|
||||
{
|
||||
m_Impl->Step(fixedDt);
|
||||
}
|
||||
|
||||
void JoltPhysicsWorld::SyncKinematicBodiesToPhysics()
|
||||
{
|
||||
m_Impl->SyncKinematicBodiesToPhysics();
|
||||
}
|
||||
|
||||
void JoltPhysicsWorld::SyncDynamicBodiesToTransforms()
|
||||
{
|
||||
m_Impl->SyncDynamicBodiesToTransforms();
|
||||
}
|
||||
|
||||
PhysicsBodyHandle JoltPhysicsWorld::CreateBody(const PhysicsBodyDesc& desc)
|
||||
{
|
||||
return m_Impl->CreateBody(desc);
|
||||
}
|
||||
|
||||
void JoltPhysicsWorld::DestroyBody(PhysicsBodyHandle body)
|
||||
{
|
||||
m_Impl->DestroyBody(body);
|
||||
}
|
||||
|
||||
void JoltPhysicsWorld::SetBodyTransform(PhysicsBodyHandle body, const PhysicsTransform& transform)
|
||||
{
|
||||
m_Impl->SetBodyTransform(body, transform);
|
||||
}
|
||||
|
||||
PhysicsTransform JoltPhysicsWorld::GetBodyTransform(PhysicsBodyHandle body) const
|
||||
{
|
||||
return m_Impl->GetBodyTransform(body);
|
||||
}
|
||||
|
||||
void JoltPhysicsWorld::SetLinearVelocity(PhysicsBodyHandle body, const glm::vec3& velocity)
|
||||
{
|
||||
m_Impl->SetLinearVelocity(body, velocity);
|
||||
}
|
||||
|
||||
glm::vec3 JoltPhysicsWorld::GetLinearVelocity(PhysicsBodyHandle body) const
|
||||
{
|
||||
return m_Impl->GetLinearVelocity(body);
|
||||
}
|
||||
|
||||
void JoltPhysicsWorld::AddForce(PhysicsBodyHandle body, const glm::vec3& force)
|
||||
{
|
||||
m_Impl->AddForce(body, force);
|
||||
}
|
||||
|
||||
void JoltPhysicsWorld::AddImpulse(PhysicsBodyHandle body, const glm::vec3& impulse)
|
||||
{
|
||||
m_Impl->AddImpulse(body, impulse);
|
||||
}
|
||||
|
||||
bool JoltPhysicsWorld::Raycast(const glm::vec3& origin,
|
||||
const glm::vec3& direction,
|
||||
float maxDistance,
|
||||
PhysicsRaycastHit& hit) const
|
||||
{
|
||||
return m_Impl->Raycast(origin, direction, maxDistance, hit);
|
||||
}
|
||||
@@ -24,12 +24,7 @@ void PhysicsSceneBridge::UnregisterGameObject(GameObject& object) {
|
||||
}
|
||||
|
||||
void PhysicsSceneBridge::FixedUpdate(float fixedDt) {
|
||||
if (auto* simple = dynamic_cast<SimplePhysicsWorld*>(m_World.get())) {
|
||||
simple->SyncKinematicBodiesToPhysics();
|
||||
simple->Step(fixedDt);
|
||||
simple->SyncDynamicBodiesToTransforms();
|
||||
return;
|
||||
}
|
||||
|
||||
m_World->SyncKinematicBodiesToPhysics();
|
||||
m_World->Step(fixedDt);
|
||||
m_World->SyncDynamicBodiesToTransforms();
|
||||
}
|
||||
|
||||
Vendored
+7
@@ -122,3 +122,10 @@ set(USE_F16C ON CACHE BOOL "" FORCE)
|
||||
set(USE_FMADD ON CACHE BOOL "" FORCE)
|
||||
|
||||
add_subdirectory(jolt/Build)
|
||||
|
||||
# 3rdparty/tracy is the Tracy repo
|
||||
option(TRACY_ENABLE "" ON)
|
||||
option(TRACY_ON_DEMAND "" ON)
|
||||
|
||||
add_subdirectory(tracy)
|
||||
|
||||
|
||||
Vendored
+1
-1
Submodule destrum/third_party/assimp updated: e13e0b5b7d...818f73f99a
Vendored
+1
-1
Submodule destrum/third_party/fmt updated: 7ad8004d57...588b3a0f8f
Vendored
+1
-1
Submodule destrum/third_party/freetype updated: 23b6cd27ff...25a08f24cf
Vendored
+1
-1
Submodule destrum/third_party/glfw updated: dbadda2683...567b1ec244
Vendored
+1
-1
Submodule destrum/third_party/glm updated: 8f6213d379...6f14f4792a
Vendored
+1
-1
Submodule destrum/third_party/jolt updated: 33a8797521...f26e382dd0
Vendored
+1
-1
Submodule destrum/third_party/json updated: 30b28175e4...25c58ac6bd
Vendored
+1
-1
Submodule destrum/third_party/sdl updated: 3eba0b6f8a...b8b3f5ef20
Vendored
+1
-1
Submodule destrum/third_party/spdlog updated: 32dd298dc2...f544fce006
Vendored
+1
-1
Submodule destrum/third_party/tinyexr updated: 90a147c711...97b265d8ce
Vendored
+1
-1
Submodule destrum/third_party/tinygltf updated: 81bd50c106...a434ee0206
+1
Submodule destrum/third_party/tracy added at 05cceee0df
Vendored
+1
-1
Submodule destrum/third_party/vk-bootstrap updated: 7028d6f652...95392756ec
Vendored
+1
-1
Submodule destrum/third_party/vma updated: e722e57c89...3aa921224c
Vendored
+1
-1
Submodule destrum/third_party/volk updated: bf84ce9573...477a354c50
Reference in New Issue
Block a user