Exemple #1
0
VkBool32 VKTS_APIENTRY engineAddUpdateThread(const IUpdateThreadSP& updateThread)
{
    if (g_engineState != VKTS_ENGINE_INIT_STATE)
    {
        logPrint(VKTS_LOG_ERROR, "Engine: Adding update thread failed! Not in initialize state.");

        return VK_FALSE;
    }

    if (!updateThread.get())
    {
        logPrint(VKTS_LOG_ERROR, "Engine: Adding update thread failed! No update thread.");

        return VK_FALSE;
    }

    if (engineGetNumberUpdateThreads() >= VKTS_MAX_UPDATE_THREADS)
    {
        logPrint(VKTS_LOG_ERROR, "Engine: Adding update thread failed! Too many update threads.");

        return VK_FALSE;
    }

    g_allUpdateThreads.append(updateThread);

    return VK_TRUE;
}
Exemple #2
0
    VkBool32 removeAt(const size_t index)
    {
        if (index >= allKeys.size())
        {
            return VK_FALSE;
        }

        allKeys.removeAt(index);
        allValues.removeAt(index);

        return VK_TRUE;
    }
Exemple #3
0
void SubMesh::bindDrawIndexedRecursive(const std::string& nodeName, const ICommandBuffersSP& cmdBuffer, const SmartPointerVector<IGraphicsPipelineSP>& allGraphicsPipelines, const overwrite* renderOverwrite, const uint32_t bufferIndex) const
{
    const overwrite* currentOverwrite = renderOverwrite;
    while (currentOverwrite)
    {
    	if (!currentOverwrite->submeshBindDrawIndexedRecursive(*this, cmdBuffer, allGraphicsPipelines, bufferIndex))
    	{
    		return;
    	}

    	currentOverwrite = currentOverwrite->getNextOverwrite();
    }

    if (bsdfMaterial.get())
    {
    	// TODO: Add again.
    }
    else if (phongMaterial.get())
    {
		IGraphicsPipelineSP graphicsPipeline;

		for (size_t i = 0; i < allGraphicsPipelines.size(); i++)
		{
			if (allGraphicsPipelines[i]->getVertexBufferType() == vertexBufferType)
			{
				graphicsPipeline = allGraphicsPipelines[i];

				break;
			}
		}

		if (!graphicsPipeline.get())
		{
	        logPrint(VKTS_LOG_SEVERE, "SubMesh: Vertex buffer type not found %x", vertexBufferType);

	        return;
		}

		vkCmdBindPipeline(cmdBuffer->getCommandBuffer(), VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline->getPipeline());

		phongMaterial->bindDrawIndexedRecursive(nodeName,cmdBuffer, graphicsPipeline, renderOverwrite, bufferIndex);
    }
    else
    {
        return;
    }

    bindIndexBuffer(cmdBuffer, bufferIndex);
    bindVertexBuffers(cmdBuffer, bufferIndex);
    drawIndexed(cmdBuffer, bufferIndex);
}
Exemple #4
0
    VkBool32 set(const K& key, const V& value)
    {
        for (size_t i = 0; i < allKeys.size(); i++)
        {
            if (key == allKeys[i])
            {
                allValues[i] = value;

                return VK_TRUE;
            }
            else if (key < allKeys[i])
            {
                allKeys.insert(i, key);
                allValues.insert(i, value);

                return VK_TRUE;
            }
        }

        allKeys.append(key);
        allValues.append(value);

        return VK_TRUE;
    }
Exemple #5
0
 void clear()
 {
     allKeys.clear();
     allValues.clear();
 }
Exemple #6
0
int32_t VKTS_APIENTRY engineGetNumberUpdateThreads()
{
    return static_cast<int32_t>(g_allUpdateThreads.size());
}
Exemple #7
0
namespace vkts
{

typedef std::shared_ptr<std::thread> ThreadSP;

static VKTS_ENGINE_STATES g_engineState = VKTS_ENGINE_INIT_STATE;

static SmartPointerVector<IUpdateThreadSP> g_allUpdateThreads;

static double g_tickTime = 1.0 / VKTS_TICKS_PER_SECOND;

static std::multimap<IUpdateThreadSP, const NativeDisplaySP> g_allAttachedDisplays;

static std::multimap<IUpdateThreadSP, const NativeWindowSP> g_allAttachedWindows;

static uint32_t g_taskExecutorCount = 0;

VkBool32 VKTS_APIENTRY engineInit()
{
    if (!processorInit())
    {
        VKTS_PRINTF("LOG [VKTS_LOG_ERROR]: Engine: Initialization failed! Could not initialize the processor!\n");

        return VK_FALSE;
    }

    if (!logInit())
    {
        VKTS_PRINTF("LOG [VKTS_LOG_ERROR]: Engine: Initialization failed! Could not initialize logging.\n");

        return VK_FALSE;
    }

    if (!timeInit())
    {
        logPrint(VKTS_LOG_ERROR, "Engine: Initialization failed! Could not initialize the timer!");

        return VK_FALSE;
    }

    if (!barrierInit())
    {
        logPrint(VKTS_LOG_ERROR, "Engine: Initialization failed! Could not initialize the barrier!");

        return VK_FALSE;
    }

    if (!fileInit())
    {
        logPrint(VKTS_LOG_ERROR, "Engine: Initialization failed! Could not initialize the file system!");

        return VK_FALSE;
    }

    return VK_TRUE;
}

VkBool32 VKTS_APIENTRY engineAddUpdateThread(const IUpdateThreadSP& updateThread)
{
    if (g_engineState != VKTS_ENGINE_INIT_STATE)
    {
        logPrint(VKTS_LOG_ERROR, "Engine: Adding update thread failed! Not in initialize state.");

        return VK_FALSE;
    }

    if (!updateThread.get())
    {
        logPrint(VKTS_LOG_ERROR, "Engine: Adding update thread failed! No update thread.");

        return VK_FALSE;
    }

    if (engineGetNumberUpdateThreads() >= VKTS_MAX_UPDATE_THREADS)
    {
        logPrint(VKTS_LOG_ERROR, "Engine: Adding update thread failed! Too many update threads.");

        return VK_FALSE;
    }

    g_allUpdateThreads.append(updateThread);

    return VK_TRUE;
}

VkBool32 VKTS_APIENTRY engineAttachDisplayToUpdateThread(const INativeDisplayWP& display, const IUpdateThreadSP& updateThread)
{
    if (g_engineState != VKTS_ENGINE_INIT_STATE)
    {
        logPrint(VKTS_LOG_ERROR, "Engine: Adding attaching display failed! Not in initialize state.");

        return VK_FALSE;
    }

    if (!updateThread.get())
    {
        logPrint(VKTS_LOG_ERROR, "Engine: No display or update thread.");

        return VK_FALSE;
    }

    auto sharedDisplay = display.lock();

    if (!sharedDisplay.get())
    {
        return VK_FALSE;
    }

    const auto nativeDisplay = visualGetDisplayInternal(sharedDisplay->getIndex());

    if (!nativeDisplay.get())
    {
        return VK_FALSE;
    }

    g_allAttachedDisplays.insert(std::pair<IUpdateThreadSP, const NativeDisplaySP>(updateThread, nativeDisplay));

    return VK_TRUE;
}

VkBool32 VKTS_APIENTRY engineAttachWindowToUpdateThread(const INativeWindowWP& window, const IUpdateThreadSP& updateThread)
{
    if (g_engineState != VKTS_ENGINE_INIT_STATE)
    {
        logPrint(VKTS_LOG_ERROR, "Engine: Adding attaching window failed! Not in initialize state.");

        return VK_FALSE;
    }

    if (!updateThread.get())
    {
        logPrint(VKTS_LOG_ERROR, "Engine: No window or update thread.");

        return VK_FALSE;
    }

    auto sharedWindow = window.lock();

    if (!sharedWindow.get())
    {
        return VK_FALSE;
    }

    const auto nativeWindow = visualGetWindowInternal(sharedWindow->getIndex());

    if (!nativeWindow.get())
    {
        return VK_FALSE;
    }

    g_allAttachedWindows.insert(std::pair<IUpdateThreadSP, const NativeWindowSP>(updateThread, nativeWindow));

    return VK_TRUE;
}

VkBool32 VKTS_APIENTRY engineSetTicksPerSecond(const double ticksPerSecond)
{
    if (g_engineState != VKTS_ENGINE_INIT_STATE)
    {
        logPrint(VKTS_LOG_ERROR, "Engine: Setting ticks failed! Not in initialize state.");

        return VK_FALSE;
    }

    double newTicksPerSecond = glm::clamp(ticksPerSecond, VKTS_TICKS_PER_SECOND_MIN, VKTS_TICKS_PER_SECOND_MAX);

    g_tickTime = 1.0 / newTicksPerSecond;

    return VK_TRUE;
}

VkBool32 VKTS_APIENTRY engineRun()
{
    if (g_engineState != VKTS_ENGINE_INIT_STATE)
    {
        logPrint(VKTS_LOG_ERROR, "Engine: Run failed! Not in initialize state.");

        return VK_FALSE;
    }

    if (engineGetNumberUpdateThreads() < VKTS_MIN_UPDATE_THREADS || engineGetNumberUpdateThreads() > VKTS_MAX_UPDATE_THREADS)
    {
        logPrint(VKTS_LOG_ERROR, "Engine: Run failed! Number of update threads not correct.");

        return VK_FALSE;
    }

    //
    // Main thread gets all displays and windows attached.
    //

    const auto& displayList = _visualGetActiveDisplays();

    for (size_t i = 0; i < displayList.size(); i++)
    {
        engineAttachDisplayToUpdateThread(displayList[i], g_allUpdateThreads[g_allUpdateThreads.size() - 1]);
    }

    const auto& windowList = _visualGetActiveWindows();

    for (size_t i = 0; i < windowList.size(); i++)
    {
        engineAttachWindowToUpdateThread(windowList[i], g_allUpdateThreads[g_allUpdateThreads.size() - 1]);
    }

    //

    g_engineState = VKTS_ENGINE_UPDATE_STATE;

    logPrint(VKTS_LOG_INFO, "Engine: Started.");

    // Task queue creation.

    TaskQueueSP sendTaskQueue;
    TaskQueueSP executedTaskQueue;

    if (g_taskExecutorCount > 0)
    {
        sendTaskQueue = TaskQueueSP(new TaskQueue);

        if (!sendTaskQueue.get())
        {
            logPrint(VKTS_LOG_ERROR, "Engine: Run failed! Could not create task queue.");

            return VK_FALSE;
        }

        executedTaskQueue = TaskQueueSP(new TaskQueue);

        if (!executedTaskQueue.get())
        {
            logPrint(VKTS_LOG_ERROR, "Engine: Run failed! Could not create task queue.");

            return VK_FALSE;
        }
    }

    // Message dispatcher creation.

    MessageDispatcherSP messageDispatcher = MessageDispatcherSP(new MessageDispatcher());

    if (!messageDispatcher.get())
    {
        logPrint(VKTS_LOG_ERROR, "Engine: Run failed! Could not create message dispatcher.");

        return VK_FALSE;
    }

    // Object, needed for synchronizing the executors.

    ExecutorSync executorSync;

    //
    // Task executor creation and launching,
    //

    SmartPointerVector<TaskExecutorSP> realTaskExecutors;
    SmartPointerVector<ThreadSP> realTaskThreads;

    for (uint32_t i = 0; i < g_taskExecutorCount; i++)
    {
        auto currentTaskExecutor = TaskExecutorSP(new TaskExecutor(i, executorSync, sendTaskQueue, executedTaskQueue));

        if (!currentTaskExecutor.get())
        {
            logPrint(VKTS_LOG_ERROR, "Engine: Run failed! Could not create current task executor.");

            return VK_FALSE;
        }

        auto currentRealThread = ThreadSP(new std::thread(&TaskExecutor::run, currentTaskExecutor));

        if (!currentRealThread.get())
        {
            logPrint(VKTS_LOG_ERROR, "Engine: Run failed! Could not create current real thread.");

            return VK_FALSE;
        }

        //

        realTaskExecutors.append(currentTaskExecutor);
        realTaskThreads.append(currentRealThread);

        logPrint(VKTS_LOG_INFO, "Engine: Task %d started.", currentTaskExecutor->getIndex());
    }

    //
    // Update Thread creation and launching.
    //

    UpdateThreadExecutorSP mainUpdateThreadExecutor;

    SmartPointerVector<UpdateThreadExecutorSP> realUpdateThreadExecutors;
    SmartPointerVector<ThreadSP> realUpdateThreads;

    int32_t index = 0;

    for (size_t updateThreadIndex = 0; updateThreadIndex < g_allUpdateThreads.size(); updateThreadIndex++)
    {
        const auto& currentUpdateThread = g_allUpdateThreads[updateThreadIndex];

        //

        const auto currentMessageDispatcher = (index == engineGetNumberUpdateThreads() - 1) ? messageDispatcher : MessageDispatcherSP();

        //

        auto currentUpdateThreadContext = UpdateThreadContextSP(new UpdateThreadContext((int32_t) updateThreadIndex, (int32_t) g_allUpdateThreads.size(), g_tickTime, sendTaskQueue, executedTaskQueue));

        if (!currentUpdateThreadContext.get())
        {
            logPrint(VKTS_LOG_ERROR, "Engine: Run failed! Could not create update thread context.");

            return VK_FALSE;
        }

        //

        for (auto currentDisplayWalker = g_allAttachedDisplays.lower_bound(currentUpdateThread); currentDisplayWalker != g_allAttachedDisplays.upper_bound(currentUpdateThread); currentDisplayWalker++)
        {
            currentUpdateThreadContext->attachDisplay(currentDisplayWalker->second);
        }

        //

        for (auto currentWindowWalker = g_allAttachedWindows.lower_bound(currentUpdateThread); currentWindowWalker != g_allAttachedWindows.upper_bound(currentUpdateThread); currentWindowWalker++)
        {
            currentUpdateThreadContext->attachWindow(currentWindowWalker->second);
        }

        //

        if (index == engineGetNumberUpdateThreads() - 1)
        {
            // Last thread is the main thread.
            mainUpdateThreadExecutor = UpdateThreadExecutorSP(new UpdateThreadExecutor(index, executorSync, currentUpdateThread, currentUpdateThreadContext, currentMessageDispatcher));

            if (!mainUpdateThreadExecutor.get())
            {
                logPrint(VKTS_LOG_ERROR, "Engine: Run failed! Could not create main update thread executor.");

                return VK_FALSE;
            }
        }
        else
        {
            // Receive queue is the threads send queue.
            auto currentUpdateThreadExecutor = UpdateThreadExecutorSP(new UpdateThreadExecutor(index, executorSync, currentUpdateThread, currentUpdateThreadContext, currentMessageDispatcher));

            if (!currentUpdateThreadExecutor.get())
            {
                logPrint(VKTS_LOG_ERROR, "Engine: Run failed! Could not create current update thread executor.");

                return VK_FALSE;
            }

            realUpdateThreadExecutors.append(currentUpdateThreadExecutor);

            logPrint(VKTS_LOG_INFO, "Engine: Thread %d started.", currentUpdateThreadExecutor->getIndex());

            auto currentRealThread = ThreadSP(new std::thread(&UpdateThreadExecutor::run, currentUpdateThreadExecutor));

            if (!currentRealThread.get())
            {
                logPrint(VKTS_LOG_ERROR, "Engine: Run failed! Could not create current real thread.");

                return VK_FALSE;
            }
            realUpdateThreads.append(currentRealThread);
        }
        index++;
    }

    // Run last thread and loop.
    logPrint(VKTS_LOG_INFO, "Engine: Thread %d started.", mainUpdateThreadExecutor->getIndex());

    mainUpdateThreadExecutor->run();

    //
    // Stopping everything.
    //

    logPrint(VKTS_LOG_INFO, "Engine: Thread %d stopped.", mainUpdateThreadExecutor->getIndex());

    // Wait for all threads to finish in the reverse order they were created.
    for (auto reverseIndex = static_cast<int32_t>(realUpdateThreads.size()) - 1; reverseIndex >= 0; reverseIndex--)
    {
        const auto& currentRealThread = realUpdateThreads[reverseIndex];

        currentRealThread->join();

        logPrint(VKTS_LOG_INFO, "Engine: Thread %d stopped.", reverseIndex);
    }

    realUpdateThreadExecutors.clear();
    realUpdateThreads.clear();

    //

    if (sendTaskQueue.get())
    {
    	// Empty the queue.
    	// As no update thread can feed the queue anymore, it is save to call reset.

    	sendTaskQueue->reset();

    	//

    	ITaskSP stopTask;

        logPrint(VKTS_LOG_SEVERE, "Engine: Disabling task queue.");

    	for (uint32_t i = 0; i < g_taskExecutorCount; i++)
    	{
    		// Send an empty task to the queue, to exit the thread.
    		sendTaskQueue->addTask(stopTask);
    	}
    }

    // Wait for all tasks to finish in the reverse order they were created.
    for (auto reverseIndex = static_cast<int32_t>(realTaskThreads.size()) - 1; reverseIndex >= 0; reverseIndex--)
    {
        const auto& currentRealThread = realTaskThreads[reverseIndex];

        currentRealThread->join();

        logPrint(VKTS_LOG_INFO, "Engine: Task %d stopped.", reverseIndex);
    }

    realTaskExecutors.clear();
    realTaskThreads.clear();

    //

    g_engineState = VKTS_ENGINE_INIT_STATE;

    logPrint(VKTS_LOG_INFO, "Engine: Stopped.");

    return VK_TRUE;
}

int32_t VKTS_APIENTRY engineGetNumberUpdateThreads()
{
    return static_cast<int32_t>(g_allUpdateThreads.size());
}

VkBool32 VKTS_APIENTRY engineSetTaskExecutorCount(const uint32_t count)
{
    g_taskExecutorCount = count;

    return VK_TRUE;
}

uint32_t VKTS_APIENTRY engineGetTaskExecutorCount()
{
    return g_taskExecutorCount;
}

void VKTS_APIENTRY engineTerminate()
{
	fileTerminate();

    barrierTerminate();

    timeTerminate();

    logTerminate();

    processorTerminate();
}

}
Exemple #8
0
VkBool32 VKTS_APIENTRY engineRun()
{
    if (g_engineState != VKTS_ENGINE_INIT_STATE)
    {
        logPrint(VKTS_LOG_ERROR, "Engine: Run failed! Not in initialize state.");

        return VK_FALSE;
    }

    if (engineGetNumberUpdateThreads() < VKTS_MIN_UPDATE_THREADS || engineGetNumberUpdateThreads() > VKTS_MAX_UPDATE_THREADS)
    {
        logPrint(VKTS_LOG_ERROR, "Engine: Run failed! Number of update threads not correct.");

        return VK_FALSE;
    }

    //
    // Main thread gets all displays and windows attached.
    //

    const auto& displayList = _visualGetActiveDisplays();

    for (size_t i = 0; i < displayList.size(); i++)
    {
        engineAttachDisplayToUpdateThread(displayList[i], g_allUpdateThreads[g_allUpdateThreads.size() - 1]);
    }

    const auto& windowList = _visualGetActiveWindows();

    for (size_t i = 0; i < windowList.size(); i++)
    {
        engineAttachWindowToUpdateThread(windowList[i], g_allUpdateThreads[g_allUpdateThreads.size() - 1]);
    }

    //

    g_engineState = VKTS_ENGINE_UPDATE_STATE;

    logPrint(VKTS_LOG_INFO, "Engine: Started.");

    // Task queue creation.

    TaskQueueSP sendTaskQueue;
    TaskQueueSP executedTaskQueue;

    if (g_taskExecutorCount > 0)
    {
        sendTaskQueue = TaskQueueSP(new TaskQueue);

        if (!sendTaskQueue.get())
        {
            logPrint(VKTS_LOG_ERROR, "Engine: Run failed! Could not create task queue.");

            return VK_FALSE;
        }

        executedTaskQueue = TaskQueueSP(new TaskQueue);

        if (!executedTaskQueue.get())
        {
            logPrint(VKTS_LOG_ERROR, "Engine: Run failed! Could not create task queue.");

            return VK_FALSE;
        }
    }

    // Message dispatcher creation.

    MessageDispatcherSP messageDispatcher = MessageDispatcherSP(new MessageDispatcher());

    if (!messageDispatcher.get())
    {
        logPrint(VKTS_LOG_ERROR, "Engine: Run failed! Could not create message dispatcher.");

        return VK_FALSE;
    }

    // Object, needed for synchronizing the executors.

    ExecutorSync executorSync;

    //
    // Task executor creation and launching,
    //

    SmartPointerVector<TaskExecutorSP> realTaskExecutors;
    SmartPointerVector<ThreadSP> realTaskThreads;

    for (uint32_t i = 0; i < g_taskExecutorCount; i++)
    {
        auto currentTaskExecutor = TaskExecutorSP(new TaskExecutor(i, executorSync, sendTaskQueue, executedTaskQueue));

        if (!currentTaskExecutor.get())
        {
            logPrint(VKTS_LOG_ERROR, "Engine: Run failed! Could not create current task executor.");

            return VK_FALSE;
        }

        auto currentRealThread = ThreadSP(new std::thread(&TaskExecutor::run, currentTaskExecutor));

        if (!currentRealThread.get())
        {
            logPrint(VKTS_LOG_ERROR, "Engine: Run failed! Could not create current real thread.");

            return VK_FALSE;
        }

        //

        realTaskExecutors.append(currentTaskExecutor);
        realTaskThreads.append(currentRealThread);

        logPrint(VKTS_LOG_INFO, "Engine: Task %d started.", currentTaskExecutor->getIndex());
    }

    //
    // Update Thread creation and launching.
    //

    UpdateThreadExecutorSP mainUpdateThreadExecutor;

    SmartPointerVector<UpdateThreadExecutorSP> realUpdateThreadExecutors;
    SmartPointerVector<ThreadSP> realUpdateThreads;

    int32_t index = 0;

    for (size_t updateThreadIndex = 0; updateThreadIndex < g_allUpdateThreads.size(); updateThreadIndex++)
    {
        const auto& currentUpdateThread = g_allUpdateThreads[updateThreadIndex];

        //

        const auto currentMessageDispatcher = (index == engineGetNumberUpdateThreads() - 1) ? messageDispatcher : MessageDispatcherSP();

        //

        auto currentUpdateThreadContext = UpdateThreadContextSP(new UpdateThreadContext((int32_t) updateThreadIndex, (int32_t) g_allUpdateThreads.size(), g_tickTime, sendTaskQueue, executedTaskQueue));

        if (!currentUpdateThreadContext.get())
        {
            logPrint(VKTS_LOG_ERROR, "Engine: Run failed! Could not create update thread context.");

            return VK_FALSE;
        }

        //

        for (auto currentDisplayWalker = g_allAttachedDisplays.lower_bound(currentUpdateThread); currentDisplayWalker != g_allAttachedDisplays.upper_bound(currentUpdateThread); currentDisplayWalker++)
        {
            currentUpdateThreadContext->attachDisplay(currentDisplayWalker->second);
        }

        //

        for (auto currentWindowWalker = g_allAttachedWindows.lower_bound(currentUpdateThread); currentWindowWalker != g_allAttachedWindows.upper_bound(currentUpdateThread); currentWindowWalker++)
        {
            currentUpdateThreadContext->attachWindow(currentWindowWalker->second);
        }

        //

        if (index == engineGetNumberUpdateThreads() - 1)
        {
            // Last thread is the main thread.
            mainUpdateThreadExecutor = UpdateThreadExecutorSP(new UpdateThreadExecutor(index, executorSync, currentUpdateThread, currentUpdateThreadContext, currentMessageDispatcher));

            if (!mainUpdateThreadExecutor.get())
            {
                logPrint(VKTS_LOG_ERROR, "Engine: Run failed! Could not create main update thread executor.");

                return VK_FALSE;
            }
        }
        else
        {
            // Receive queue is the threads send queue.
            auto currentUpdateThreadExecutor = UpdateThreadExecutorSP(new UpdateThreadExecutor(index, executorSync, currentUpdateThread, currentUpdateThreadContext, currentMessageDispatcher));

            if (!currentUpdateThreadExecutor.get())
            {
                logPrint(VKTS_LOG_ERROR, "Engine: Run failed! Could not create current update thread executor.");

                return VK_FALSE;
            }

            realUpdateThreadExecutors.append(currentUpdateThreadExecutor);

            logPrint(VKTS_LOG_INFO, "Engine: Thread %d started.", currentUpdateThreadExecutor->getIndex());

            auto currentRealThread = ThreadSP(new std::thread(&UpdateThreadExecutor::run, currentUpdateThreadExecutor));

            if (!currentRealThread.get())
            {
                logPrint(VKTS_LOG_ERROR, "Engine: Run failed! Could not create current real thread.");

                return VK_FALSE;
            }
            realUpdateThreads.append(currentRealThread);
        }
        index++;
    }

    // Run last thread and loop.
    logPrint(VKTS_LOG_INFO, "Engine: Thread %d started.", mainUpdateThreadExecutor->getIndex());

    mainUpdateThreadExecutor->run();

    //
    // Stopping everything.
    //

    logPrint(VKTS_LOG_INFO, "Engine: Thread %d stopped.", mainUpdateThreadExecutor->getIndex());

    // Wait for all threads to finish in the reverse order they were created.
    for (auto reverseIndex = static_cast<int32_t>(realUpdateThreads.size()) - 1; reverseIndex >= 0; reverseIndex--)
    {
        const auto& currentRealThread = realUpdateThreads[reverseIndex];

        currentRealThread->join();

        logPrint(VKTS_LOG_INFO, "Engine: Thread %d stopped.", reverseIndex);
    }

    realUpdateThreadExecutors.clear();
    realUpdateThreads.clear();

    //

    if (sendTaskQueue.get())
    {
    	// Empty the queue.
    	// As no update thread can feed the queue anymore, it is save to call reset.

    	sendTaskQueue->reset();

    	//

    	ITaskSP stopTask;

        logPrint(VKTS_LOG_SEVERE, "Engine: Disabling task queue.");

    	for (uint32_t i = 0; i < g_taskExecutorCount; i++)
    	{
    		// Send an empty task to the queue, to exit the thread.
    		sendTaskQueue->addTask(stopTask);
    	}
    }

    // Wait for all tasks to finish in the reverse order they were created.
    for (auto reverseIndex = static_cast<int32_t>(realTaskThreads.size()) - 1; reverseIndex >= 0; reverseIndex--)
    {
        const auto& currentRealThread = realTaskThreads[reverseIndex];

        currentRealThread->join();

        logPrint(VKTS_LOG_INFO, "Engine: Task %d stopped.", reverseIndex);
    }

    realTaskExecutors.clear();
    realTaskThreads.clear();

    //

    g_engineState = VKTS_ENGINE_INIT_STATE;

    logPrint(VKTS_LOG_INFO, "Engine: Stopped.");

    return VK_TRUE;
}
SmartPointerVector<IImageDataSP> VKTS_APIENTRY imageDataMipmap(const IImageDataSP& sourceImage, VkBool32 addSourceAsCopy, const std::string& name)
{
    if (name.size() == 0 || !sourceImage.get())
    {
        return SmartPointerVector<IImageDataSP>();
    }

    std::string sourceImageFilename = name;

    auto dotIndex = sourceImageFilename.rfind(".");

    if (dotIndex == sourceImageFilename.npos)
    {
        return SmartPointerVector<IImageDataSP>();
    }

    auto sourceImageName = sourceImageFilename.substr(0, dotIndex);
    auto sourceImageExtension = sourceImageFilename.substr(dotIndex);

    IImageDataSP currentSourceImage = sourceImage;
    int32_t width = currentSourceImage->getWidth();
    int32_t height = currentSourceImage->getHeight();
    int32_t depth = currentSourceImage->getDepth();

    IImageDataSP currentTargetImage;
    std::string targetImageFilename;

    int32_t level = 0;

    SmartPointerVector<IImageDataSP> result;

    if (addSourceAsCopy)
    {
        targetImageFilename = sourceImageName + "_L" + std::to_string(level++) + sourceImageExtension;

        currentTargetImage = imageDataCopy(sourceImage, targetImageFilename);

        if (!currentTargetImage.get())
        {
            return SmartPointerVector<IImageDataSP>();
        }

        result.append(currentTargetImage);
    }
    else
    {
        result.append(sourceImage);

        level++;
    }

    while (width > 1 || height > 1 || depth > 1)
    {
        width = glm::max(width / 2, 1);
        height = glm::max(height / 2, 1);
        depth = glm::max(depth / 2, 1);

        targetImageFilename = sourceImageName + "_L" + std::to_string(level++) + sourceImageExtension;

        currentTargetImage = imageDataCreate(targetImageFilename, width, height, depth, 0.0f, 0.0f, 0.0f, 0.0f, sourceImage->getImageType(), sourceImage->getFormat());

        if (!currentTargetImage.get())
        {
            return SmartPointerVector<IImageDataSP>();
        }

        for (int32_t z = 0; z < depth; z++)
        {
            for (int32_t y = 0; y < height; y++)
            {
                for (int32_t x = 0; x < width; x++)
                {
                    glm::vec4 rgba = glm::vec4(0.0f, 0.0f, 0.0f, 0.0f);
                    float sampleCount = 0.0f;

                    for (int32_t iz = z * 2; iz < z * 2 + 1; iz++)
                    {
                        for (int32_t iy = y * 2; iy < y * 2 + 1; iy++)
                        {
                            for (int32_t ix = x * 2; ix < x * 2 + 1; ix++)
                            {
                                rgba += currentSourceImage->getTexel(ix, iy, iz);

                                sampleCount += 1.0f;
                            }
                        }
                    }

                    if (sampleCount > 0.0f)
                    {
                        rgba /= sampleCount;

                        currentTargetImage->setTexel(rgba, x, y, z);
                    }
                }
            }
        }

        result.append(currentTargetImage);

        //

        currentSourceImage = currentTargetImage;
        width = currentSourceImage->getWidth();
        height = currentSourceImage->getHeight();
        depth = currentSourceImage->getDepth();
    }

    return result;
}