Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions 01_HelloCoreSystemAsset/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,11 @@ int main(int argc, char** argv)
}
else
{
#ifdef _NBL_PLATFORM_WINDOWS_
logger = make_smart_refctd_ptr<system::CColoredStdoutLoggerWin32>(LogLevel);
#else
logger = make_smart_refctd_ptr<system::CColoredStdoutLoggerANSI>(LogLevel);
#endif
logger->log("Could not create \"%s\\log.txt\" logging to STDOUT instead!\n",ILogger::ELL_ERROR,CWD.string().c_str());
}
}
Expand Down
7 changes: 6 additions & 1 deletion 08_HelloSwapchain/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,12 @@ class HelloSwapchainApp final : public examples::SimpleWindowedApplication
auto window = m_winMgr->createWindow(std::move(params));
// uncomment for some nasty testing of swapchain creation!
//m_winMgr->minimize(window.get());
const_cast<std::remove_const_t<decltype(m_surface)>&>(m_surface) = CSmoothResizeSurface<CSwapchainResources>::create(CSurfaceVulkanWin32::create(smart_refctd_ptr(m_api),move_and_static_cast<IWindowWin32>(window)));
#ifdef _NBL_PLATFORM_WINDOWS_
auto surface = CSurfaceVulkanWin32::create(smart_refctd_ptr(m_api),move_and_static_cast<IWindowWin32>(window));
#elif defined(_NBL_PLATFORM_LINUX_)
auto surface = CSurfaceVulkanXcb::create(smart_refctd_ptr(m_api),move_and_static_cast<IWindowXcb>(window));
#endif
const_cast<std::remove_const_t<decltype(m_surface)>&>(m_surface) = CSmoothResizeSurface<CSwapchainResources>::create(std::move(surface));
}
return {{m_surface->getSurface()/*,EQF_NONE*/}};
}
Expand Down
2 changes: 1 addition & 1 deletion 09_GeometryCreator/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ class GeometryCreatorApp final : public MonoWindowApplication, public BuiltinRes
if (ev.type==nbl::ui::SMouseEvent::EET_SCROLL && m_renderer)
{
gcIndex += int16_t(core::sign(ev.scrollEvent.verticalScroll));
gcIndex = core::clamp(gcIndex,0ull,m_renderer->getGeometries().size()-1);
gcIndex = core::clamp(gcIndex,size_t(0),m_renderer->getGeometries().size()-1);
}
}
}
Expand Down
5 changes: 4 additions & 1 deletion 23_Arithmetic2UnitTest/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@ class Workgroup2ScanTestApp final : public application_templates::BasicMultiQueu
const auto MaxWorkgroupSize = m_physicalDevice->getLimits().maxComputeWorkGroupInvocations;
const auto MinSubgroupSize = m_physicalDevice->getLimits().minSubgroupSize;
const auto MaxSubgroupSize = m_physicalDevice->getLimits().maxSubgroupSize;
// a required subgroup size caps the workgroup at `requiredSubgroupSize*maxComputeWorkgroupSubgroups` (VUID-VkPipelineShaderStageCreateInfo-pNext-02756),
// Intel ANV reports 64 subgroups so SIMD8 can't go past 512 invocations and the GPU hangs
const auto MaxWorkgroupSubgroups = m_physicalDevice->getLimits().maxComputeWorkgroupSubgroups;
for (uint32_t useNative = 0; useNative <= uint32_t(m_physicalDevice->getProperties().limits.shaderSubgroupArithmetic); useNative++)
{
if (useNative)
Expand All @@ -191,7 +194,7 @@ class Workgroup2ScanTestApp final : public application_templates::BasicMultiQueu
for (auto subgroupSize = MinSubgroupSize; subgroupSize <= MaxSubgroupSize; subgroupSize *= 2u)
{
const uint8_t subgroupSizeLog2 = hlsl::findMSB(subgroupSize);
for (uint32_t workgroupSize = subgroupSize; workgroupSize <= MaxWorkgroupSize; workgroupSize *= 2u)
for (uint32_t workgroupSize = subgroupSize; workgroupSize <= hlsl::min<uint32_t>(MaxWorkgroupSize,subgroupSize*MaxWorkgroupSubgroups); workgroupSize *= 2u)
{
// make sure renderdoc captures everything for debugging
m_api->startCapture();
Expand Down
9 changes: 9 additions & 0 deletions 70_FLIPFluids/README.md
Original file line number Diff line number Diff line change
@@ -1 +1,10 @@
https://github.com/user-attachments/assets/fc8bfac9-c8fb-49f1-87d6-251aae015945

## Running on Linux (X11)

```bash
cmake --build build/linux-clang-release -j4 --target 70_flipfluids
(cd examples_tests/70_FLIPFluids/bin && DISPLAY=:1 ./70_flipfluids)
```

XCB windows don't deliver mouse or keyboard input yet, so when no input device is connected the camera orbits the tank on its own and the water is dropped again every 12 seconds. Stop it with Ctrl+C.
167 changes: 37 additions & 130 deletions 70_FLIPFluids/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,132 +44,6 @@ struct SMVPParams
float P[4*4];
};

class CSwapchainFramebuffersAndDepth final : public nbl::video::CDefaultSwapchainFramebuffers
{
using scbase_t = CDefaultSwapchainFramebuffers;

public:
template<typename... Args>
inline CSwapchainFramebuffersAndDepth(ILogicalDevice* device, const asset::E_FORMAT _desiredDepthFormat, Args&&... args)
: CDefaultSwapchainFramebuffers(device, std::forward<Args>(args)...)
{
const IPhysicalDevice::SImageFormatPromotionRequest req = {
.originalFormat = _desiredDepthFormat,
.usages = {IGPUImage::EUF_RENDER_ATTACHMENT_BIT}
};
m_depthFormat = m_device->getPhysicalDevice()->promoteImageFormat(req, IGPUImage::TILING::OPTIMAL);

const static IGPURenderpass::SCreationParams::SDepthStencilAttachmentDescription depthAttachments[] = {
{{
{
.format = m_depthFormat,
.samples = IGPUImage::ESCF_1_BIT,
.mayAlias = false
},
/*.loadOp = */{IGPURenderpass::LOAD_OP::CLEAR},
/*.storeOp = */{IGPURenderpass::STORE_OP::STORE},
/*.initialLayout = */{IGPUImage::LAYOUT::UNDEFINED}, // because we clear we don't care about contents
/*.finalLayout = */{IGPUImage::LAYOUT::ATTACHMENT_OPTIMAL} // transition to presentation right away so we can skip a barrier
}},
IGPURenderpass::SCreationParams::DepthStencilAttachmentsEnd
};
m_params.depthStencilAttachments = depthAttachments;

static IGPURenderpass::SCreationParams::SSubpassDescription subpasses[] = {
m_params.subpasses[0],
IGPURenderpass::SCreationParams::SubpassesEnd
};
subpasses[0].depthStencilAttachment.render = { .attachmentIndex = 0,.layout = IGPUImage::LAYOUT::ATTACHMENT_OPTIMAL };
m_params.subpasses = subpasses;

// TODO: Two subpass external dependencies SRC and DST needed!
}

protected:
inline bool onCreateSwapchain_impl(const uint8_t qFam) override
{
auto device = const_cast<ILogicalDevice*>(m_renderpass->getOriginDevice());

const auto depthFormat = m_renderpass->getCreationParameters().depthStencilAttachments[0].format;
const auto& sharedParams = getSwapchain()->getCreationParameters().sharedParams;
auto image = device->createImage({ IImage::SCreationParams{
.type = IGPUImage::ET_2D,
.samples = IGPUImage::ESCF_1_BIT,
.format = depthFormat,
.extent = {sharedParams.width,sharedParams.height,1},
.mipLevels = 1,
.arrayLayers = 1,
.depthUsage = IGPUImage::EUF_RENDER_ATTACHMENT_BIT
} });

device->allocate(image->getMemoryReqs(), { image.get() });

m_depthBuffer = device->createImageView({
.flags = IGPUImageView::ECF_NONE,
.subUsages = IGPUImage::EUF_RENDER_ATTACHMENT_BIT,
.image = std::move(image),
.viewType = IGPUImageView::ET_2D,
.format = depthFormat,
.subresourceRange = {IGPUImage::EAF_DEPTH_BIT,0,1,0,1}
});

const auto retval = scbase_t::onCreateSwapchain_impl(qFam);
m_depthBuffer = nullptr;
return retval;
}

inline smart_refctd_ptr<IGPUFramebuffer> createFramebuffer(IGPUFramebuffer::SCreationParams&& params) override
{
params.depthStencilAttachments = &m_depthBuffer.get();
return m_device->createFramebuffer(std::move(params));
}

E_FORMAT m_depthFormat;
smart_refctd_ptr<IGPUImageView> m_depthBuffer;
};

class CEventCallback : public ISimpleManagedSurface::ICallback
{
public:
CEventCallback(nbl::core::smart_refctd_ptr<InputSystem>&& m_inputSystem, nbl::system::logger_opt_smart_ptr&& logger) : m_inputSystem(std::move(m_inputSystem)), m_logger(std::move(logger)) {}
CEventCallback() {}

void setLogger(nbl::system::logger_opt_smart_ptr& logger)
{
m_logger = logger;
}
void setInputSystem(nbl::core::smart_refctd_ptr<InputSystem>&& m_inputSystem)
{
m_inputSystem = std::move(m_inputSystem);
}
private:

void onMouseConnected_impl(nbl::core::smart_refctd_ptr<nbl::ui::IMouseEventChannel>&& mch) override
{
m_logger.log("A mouse %p has been connected", nbl::system::ILogger::ELL_INFO, mch.get());
m_inputSystem.get()->add(m_inputSystem.get()->m_mouse, std::move(mch));
}
void onMouseDisconnected_impl(nbl::ui::IMouseEventChannel* mch) override
{
m_logger.log("A mouse %p has been disconnected", nbl::system::ILogger::ELL_INFO, mch);
m_inputSystem.get()->remove(m_inputSystem.get()->m_mouse, mch);
}
void onKeyboardConnected_impl(nbl::core::smart_refctd_ptr<nbl::ui::IKeyboardEventChannel>&& kbch) override
{
m_logger.log("A keyboard %p has been connected", nbl::system::ILogger::ELL_INFO, kbch.get());
m_inputSystem.get()->add(m_inputSystem.get()->m_keyboard, std::move(kbch));
}
void onKeyboardDisconnected_impl(nbl::ui::IKeyboardEventChannel* kbch) override
{
m_logger.log("A keyboard %p has been disconnected", nbl::system::ILogger::ELL_INFO, kbch);
m_inputSystem.get()->remove(m_inputSystem.get()->m_keyboard, kbch);
}

private:
nbl::core::smart_refctd_ptr<InputSystem> m_inputSystem = nullptr;
nbl::system::logger_opt_smart_ptr m_logger = nullptr;
};

class FLIPFluidsApp final : public SimpleWindowedApplication, public BuiltinResourcesApplication
{
using device_base_t = SimpleWindowedApplication;
Expand Down Expand Up @@ -211,7 +85,11 @@ class FLIPFluidsApp final : public SimpleWindowedApplication, public BuiltinReso
const_cast<std::remove_const_t<decltype(m_window)>&>(m_window) = m_winMgr->createWindow(std::move(params));
}

#ifdef _NBL_PLATFORM_WINDOWS_
auto surface = CSurfaceVulkanWin32::create(smart_refctd_ptr(m_api), smart_refctd_ptr_static_cast<IWindowWin32>(m_window));
#elif defined(_NBL_PLATFORM_LINUX_)
auto surface = CSurfaceVulkanXcb::create(smart_refctd_ptr(m_api), smart_refctd_ptr_static_cast<IWindowXcb>(m_window));
#endif
const_cast<std::remove_const_t<decltype(m_surface)>&>(m_surface) = nbl::video::CSimpleResizeSurface<CSwapchainFramebuffersAndDepth>::create(std::move(surface));
}

Expand Down Expand Up @@ -295,7 +173,7 @@ class FLIPFluidsApp final : public SimpleWindowedApplication, public BuiltinReso
params.size = numParticles * 6 * sizeof(VertexInfo);
createBuffer(particleVertexBuffer, params, IDeviceMemoryAllocation::EMAF_DEVICE_ADDRESS_BIT);

asset::VkExtent3D gridExtent = { m_gridData.gridSize.x, m_gridData.gridSize.y, m_gridData.gridSize.z };
asset::VkExtent3D gridExtent = { uint32_t(m_gridData.gridSize.x), uint32_t(m_gridData.gridSize.y), uint32_t(m_gridData.gridSize.z) };

// cell materials
createGridTexture(gridCellMaterialImageView, asset::EF_R16_UINT, gridExtent, asset::IImage::EUF_STORAGE_BIT, "cell material0");
Expand Down Expand Up @@ -908,8 +786,9 @@ class FLIPFluidsApp final : public SimpleWindowedApplication, public BuiltinReso

const auto resourceIx = m_realFrameIx % MaxFramesInFlight;

m_inputSystem->getDefaultMouse(&mouse);
m_inputSystem->getDefaultKeyboard(&keyboard);
// `getDefault*` blocks until a device connects and windows without input (XCB for now) never get one,
// the `tryGetDefault*` variants check and bind under one lock, so a disconnect falls back to autoplay
const bool hasInput = m_inputSystem->tryGetDefaultMouse(&mouse) && m_inputSystem->tryGetDefaultKeyboard(&keyboard);

auto updatePresentationTimestamp = [&]()
{
Expand All @@ -931,6 +810,9 @@ class FLIPFluidsApp final : public SimpleWindowedApplication, public BuiltinReso
cmdbuf->reset(IGPUCommandBuffer::RESET_FLAGS::RELEASE_RESOURCES_BIT);
cmdbuf->begin(IGPUCommandBuffer::USAGE::ONE_TIME_SUBMIT_BIT);
cmdbuf->beginDebugMarker("Frame Debug FLIP sim begin");
if (!hasInput)
autoplay();
else
{
std::vector<SMouseEvent> cameraMouseEvents;
std::vector<SKeyboardEvent> cameraKeyboardEvents;
Expand Down Expand Up @@ -1554,7 +1436,7 @@ class FLIPFluidsApp final : public SimpleWindowedApplication, public BuiltinReso
return logFail("Failed to create render semaphore!\n");

ISwapchain::SCreationParams swapchainParams{
.surface = m_surface->getSurface()
.surface = smart_refctd_ptr<ISurface>(m_surface->getSurface())
};
if (!swapchainParams.deduceFormat(m_physicalDevice))
return logFail("Could not choose a surface format for the swapchain!\n");
Expand Down Expand Up @@ -1711,6 +1593,27 @@ class FLIPFluidsApp final : public SimpleWindowedApplication, public BuiltinReso
}
}

// Without input devices, orbit the camera around the tank and drop the water again periodically
void autoplay()
{
const auto now = clock_t::now();
if (m_autoplayStart == clock_t::time_point{})
m_autoplayStart = now;
if (now - m_lastDropTime > AutoplayDropPeriod)
{
m_lastDropTime = now;
m_shouldInitParticles = true;
}

const double t = std::chrono::duration<double>(now - m_autoplayStart).count();
const double angle = t * 2.0 * core::PI<double>() / AutoplayOrbitSeconds;
const auto center = hlsl::float64_t3(m_gridData.worldMax.x, m_gridData.worldMax.y * 0.5f, m_gridData.worldMax.z) * 0.5;
const auto eye = center + hlsl::float64_t3(std::cos(angle) * 13.0, 6.0, std::sin(angle) * 13.0);
hlsl::math::quaternion<hlsl::float64_t> orientation;
if (ext::cameras::CCameraMathUtilities::tryCreateQuaternionFromLookAt(eye, center, hlsl::float64_t3(0.0, 1.0, 0.0), orientation))
camera = core::make_smart_refctd_ptr<ext::cameras::CFPSCamera>(eye, orientation);
}


// in-loop functions
void initializeParticles(IGPUCommandBuffer* cmdbuf)
Expand Down Expand Up @@ -1861,6 +1764,10 @@ class FLIPFluidsApp final : public SimpleWindowedApplication, public BuiltinReso
InputSystem::ChannelReader<IKeyboardEventChannel> keyboard;

core::smart_refctd_ptr<ext::cameras::CFPSCamera> camera;
constexpr static inline clock_t::duration AutoplayDropPeriod = std::chrono::seconds(12);
constexpr static inline double AutoplayOrbitSeconds = 40.0;
clock_t::time_point m_autoplayStart = {};
clock_t::time_point m_lastDropTime = clock_t::now();
ext::cameras::CCameraMouseKeyboardController cameraController;
hlsl::float32_t4x4 cameraProjection = hlsl::float32_t4x4(1.0f);
video::CDumbPresentationOracle oracle;
Expand Down
2 changes: 2 additions & 0 deletions common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,5 @@ set(NBL_EXAMPLES_API_TARGET ${LIB_NAME} PARENT_SCOPE)
set(NBL_EXAMPLES_API_LIBRARIES ${TARGETS} PARENT_SCOPE)

NBL_ADJUST_FOLDERS(common)

add_subdirectory(test)
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class CSwapchainFramebuffersAndDepth final : public video::CDefaultSwapchainFram
m_params.subpasses[0],
IGPURenderpass::SCreationParams::SubpassesEnd
};
subpasses[0].depthStencilAttachment.render = { .attachmentIndex = 0,.layout = IGPUImage::LAYOUT::ATTACHMENT_OPTIMAL };
subpasses[0].depthStencilAttachment.render = { .attachmentIndex = 0,.layout = {.depth = IGPUImage::LAYOUT::ATTACHMENT_OPTIMAL} };
m_params.subpasses = subpasses;
}

Expand All @@ -66,7 +66,7 @@ class CSwapchainFramebuffersAndDepth final : public video::CDefaultSwapchainFram

const auto depthFormat = m_renderpass->getCreationParameters().depthStencilAttachments[0].format;
const auto& sharedParams = getSwapchain()->getCreationParameters().sharedParams;
auto image = device->createImage({ IImage::SCreationParams{
auto image = device->createImage(IGPUImage::SCreationParams{ IImage::SCreationParams{
.type = IGPUImage::ET_2D,
.samples = IGPUImage::ESCF_1_BIT,
.format = depthFormat,
Expand All @@ -78,7 +78,7 @@ class CSwapchainFramebuffersAndDepth final : public video::CDefaultSwapchainFram

device->allocate(image->getMemoryReqs(), { image.get() });

m_depthBuffer = device->createImageView({
m_depthBuffer = device->createImageView(IGPUImageView::SCreationParams{
.flags = IGPUImageView::ECF_NONE,
.subUsages = IGPUImage::EUF_RENDER_ATTACHMENT_BIT,
.image = std::move(image),
Expand Down
49 changes: 38 additions & 11 deletions common/include/nbl/examples/common/InputSystem.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,15 @@ class InputSystem : public core::IReferenceCounted
{
getDefault(m_keyboard,reader);
}
// Nonblocking variants, return false and leave the reader alone when no device is connected
bool tryGetDefaultMouse(ChannelReader<ui::IMouseEventChannel>* reader)
{
return tryGetDefault(m_mouse,reader);
}
bool tryGetDefaultKeyboard(ChannelReader<ui::IKeyboardEventChannel>* reader)
{
return tryGetDefault(m_keyboard,reader);
}
template<class ChannelType>
void add(Channels<ChannelType>& channels, core::smart_refctd_ptr<ChannelType>&& channel)
{
Expand Down Expand Up @@ -83,6 +92,35 @@ class InputSystem : public core::IReferenceCounted
}
template<class ChannelType>
void getDefault(Channels<ChannelType>& channels, ChannelReader<ChannelType>* reader)
{
std::unique_lock lock(channels.lock);
while (channels.channels.empty())
{
m_logger.log("Waiting For Input Device to be connected...",system::ILogger::ELL_INFO);
channels.added.wait(lock);
}
selectDefault(channels,reader);
}
// Checks for a device and binds the reader under the same lock, so a disconnect can't slip in between.
// The reader holds a reference, so a channel removed afterwards stays valid until the next selection.
template<class ChannelType>
bool tryGetDefault(Channels<ChannelType>& channels, ChannelReader<ChannelType>* reader)
{
std::unique_lock lock(channels.lock);
if (channels.channels.empty())
return false;
selectDefault(channels,reader);
return true;
}

system::logger_opt_smart_ptr m_logger;
Channels<ui::IMouseEventChannel> m_mouse;
Channels<ui::IKeyboardEventChannel> m_keyboard;

private:
// `channels.lock` must be held and `channels.channels` must not be empty
template<class ChannelType>
void selectDefault(Channels<ChannelType>& channels, ChannelReader<ChannelType>* reader)
{
/*
* TODO: Improve default device switching.
Expand All @@ -93,13 +131,6 @@ class InputSystem : public core::IReferenceCounted
* Switch the channel choice, but prune away all events younger than the old default's consumption timestamp.
* (Basically switch keyboards but dont try to process events older than the events you've processed from the old keyboard)
*/

std::unique_lock lock(channels.lock);
while (channels.channels.empty())
{
m_logger.log("Waiting For Input Device to be connected...",system::ILogger::ELL_INFO);
channels.added.wait(lock);
}

using namespace std::chrono;
constexpr long long DefaultChannelTimeoutInMicroSeconds = 100*1e3; // 100 mili-seconds
Expand Down Expand Up @@ -179,10 +210,6 @@ class InputSystem : public core::IReferenceCounted
// no `size - capacity` underflow on a channel younger than the ring.
reader->consumedCounter = defaultChannel->getEvents().size();
}

system::logger_opt_smart_ptr m_logger;
Channels<ui::IMouseEventChannel> m_mouse;
Channels<ui::IKeyboardEventChannel> m_keyboard;
};

}
Expand Down
Loading