Startup and Shutdown

Loading the Flow Library

To start using the Flow library, first the DLL/so must be loaded and symbols for root interfaces resolved. Then some NvFlowContext implementation must be provided, in this case the default Vulkan implementation is used. Once there is a context, then the grid can be created and parameter streaming initialized.

void printError(const char* str, void* userdata);

NvFlowLoader loader = {};
NvFlowContextInterface contextInterface = {};

NvFlowDeviceManager* deviceManager = nullptr;
NvFlowDevice* device = nullptr;
NvFlowDeviceQueue* deviceQueue = nullptr;

NvFlowGrid* grid = nullptr;
NvFlowGridParamsNamed* gridParamsNamed = nullptr;

NvFlowLoaderInit(&loader, printError, nullptr);

if (!loader.module_nvflow || !loader.module_nvflowext)
{
	printf("FlowLoader init() failed!!!\n");
	return false;
}

// initialize compute/graphics
{
	NvFlowBool32 validation = NV_FLOW_TRUE;

	deviceManager = loader.deviceInterface.createDeviceManager(validation, nullptr, 0u);

	NvFlowDeviceDesc deviceDesc = {};
	deviceDesc.deviceIndex = deviceIdx;
	deviceDesc.enableExternalUsage = cudaInteropEnabled;
	deviceDesc.logPrint = logPrint;

	device = loader.deviceInterface.createDevice(deviceManager, &deviceDesc);

	deviceQueue = loader.deviceInterface.getDeviceQueue(device);

	NvFlowContextInterface_duplicate(&contextInterface, loader.deviceInterface.getContextInterface(deviceQueue));
}

NvFlowContext* context = loader.deviceInterface.getContext(deviceQueue);

// initialize grid
{
	NvFlowGridDesc gridDesc = NvFlowGridDesc_default;

	if (maxBlocks > 0u)
	{
		gridDesc.maxLocations = maxBlocks;
	}

	grid = loader.gridInterface.createGrid(&contextInterface, context, &loader.opList, &loader.extOpList, &gridDesc);
	gridParamsNamed = loader.gridParamsInterface.createGridParamsNamed("flowUsd");
}

Shutting Down

Shutting down is similar to initialization, with the caveat that GPU work should be flushed before waiting for completion. The wait idle before destroyGrid maximizes the resources destroyGrid can safely release. The flush and waitIdle makes sure any deferred resource release is processed before destroyDevice.

// wait idle
{
	loader.deviceInterface.waitIdle(deviceQueue);
}

// safe to release any interop buffers here

// destroy grid
{
	NvFlowContext* context = loader.deviceInterface.getContext(deviceQueue);

	loader.gridInterface.destroyGrid(context, grid);
	loader.gridParamsInterface.destroyGridParamsNamed(gridParamsNamed);
}

// wait idle
{
	NvFlowUint64 flushedFrameID = 0u;
	loader.deviceInterface.flush(deviceQueue, &flushedFrameID, nullptr, nullptr);

	loader.deviceInterface.waitIdle(deviceQueue);
}

// destroy compute/graphics
{
	loader.deviceInterface.destroyDevice(deviceManager, device);
	loader.deviceInterface.destroyDeviceManager(deviceManager);
}

NvFlowLoaderDestroy(&loader);