<commit_before>//
//  TitleScene.cpp
//  yamiHikariGame
//
//  Created by slightair on 2013/07/28.
//
//

#include "TitleScene.h"
#include "Constants.h"
#include "GameEngine.h"

CCScene* TitleScene::scene()
{
    CCScene *scene = CCScene::create();

    TitleScene *layer = TitleScene::create();
    scene->addChild(layer);

    return scene;
}

void TitleScene::onEnter()
{
    CCLayer::onEnter();

    CCSize windowSize = CCDirector::sharedDirector()->getWinSize();

    _groundNode = Ground::create();
    this->addChild(_groundNode);

    CCSprite *monster = CCSprite::createWithSpriteFrameName("monster.png");
    monster->setPosition(ccp(windowSize.width / 2, windowSize.height / 2));
    this->addChild(monster);

    CCMenuItem *startGameItem = CCMenuItemLabel::create(CCLabelTTF::create("スタート", DefaultFontName, DefaultFontSize),
                                                        GameEngine::sharedEngine(),
                                                        menu_selector(GameEngine::startNewGame));

    CCMenu *menu = CCMenu::create(startGameItem, NULL);
    menu->alignItemsVertically();
    menu->setPosition(ccp(windowSize.width / 2, windowSize.height * 0.2));
    this->addChild(menu);
}
<commit_msg>とりあえずタイトルを追加した<commit_after>//
//  TitleScene.cpp
//  yamiHikariGame
//
//  Created by slightair on 2013/07/28.
//
//

#include "TitleScene.h"
#include "Constants.h"
#include "GameEngine.h"

CCScene* TitleScene::scene()
{
    CCScene *scene = CCScene::create();

    TitleScene *layer = TitleScene::create();
    scene->addChild(layer);

    return scene;
}

void TitleScene::onEnter()
{
    CCLayer::onEnter();

    CCSize windowSize = CCDirector::sharedDirector()->getWinSize();

    _groundNode = Ground::create();
    this->addChild(_groundNode);

    CCSprite *monster = CCSprite::createWithSpriteFrameName("monster.png");
    monster->setPosition(ccp(windowSize.width / 2, windowSize.height / 2));
    this->addChild(monster);

    CCLabelTTF *titleLabel = CCLabelTTF::create("ヤミからのトウソウ", DefaultFontName, DefaultFontSize);
    titleLabel->setPosition(ccp(windowSize.width / 2, windowSize.height * 0.8));
    this->addChild(titleLabel);

    CCMenuItem *startGameItem = CCMenuItemLabel::create(CCLabelTTF::create("スタート", DefaultFontName, DefaultFontSize),
                                                        GameEngine::sharedEngine(),
                                                        menu_selector(GameEngine::startNewGame));

    CCMenu *menu = CCMenu::create(startGameItem, NULL);
    menu->alignItemsVertically();
    menu->setPosition(ccp(windowSize.width / 2, windowSize.height * 0.2));
    this->addChild(menu);
}
<|endoftext|>


<commit_before>/*******************************
Copyright (c) 2016-2022 Grégoire Angerand

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**********************************/

#include "Swapchain.h"

#include <yave/window/Window.h>

#include <yave/graphics/commands/CmdQueue.h>
#include <yave/graphics/memory/DeviceMemoryHeapBase.h>
#include <yave/graphics/barriers/Barrier.h>

#include <yave/graphics/device/extensions/DebugUtils.h>
#include <yave/utils/gpuprofile.h>

#include <y/core/FixedArray.h>
#include <y/utils/log.h>

namespace yave {

static VkSurfaceCapabilitiesKHR compute_capabilities(VkSurfaceKHR surface) {
    VkSurfaceCapabilitiesKHR capabilities = {};
    if(is_error(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vk_physical_device(), surface, &capabilities))) {
        return {};
    }
    return capabilities;
}

static VkSurfaceFormatKHR surface_format(VkSurfaceKHR surface) {
    Y_TODO(Find best format instead of always returning first)
    u32 format_count = 1;
    VkSurfaceFormatKHR format = {};
    vk_check_or_incomplete(vkGetPhysicalDeviceSurfaceFormatsKHR(vk_physical_device(), surface, &format_count, &format));
    y_always_assert(format_count, "No swapchain format supported");
    return format;
}

static VkPresentModeKHR present_mode(VkSurfaceKHR surface) {
    std::array<VkPresentModeKHR, 16> modes = {};
    u32 mode_count = u32(modes.size());
    vk_check(vkGetPhysicalDeviceSurfacePresentModesKHR(vk_physical_device(), surface, &mode_count, modes.data()));
    y_always_assert(mode_count, "No presentation mode supported");
    for(u32 i = 0; i != mode_count; ++i) {
        if(modes[i] == VK_PRESENT_MODE_MAILBOX_KHR) {
            return VK_PRESENT_MODE_MAILBOX_KHR;
        }
    }
    return VK_PRESENT_MODE_FIFO_KHR;
}

static u32 compute_image_count(VkSurfaceCapabilitiesKHR capabilities) {
    const u32 ideal = 3;
    if(capabilities.maxImageCount < ideal && capabilities.maxImageCount > capabilities.minImageCount) {
        return capabilities.maxImageCount;
    }
    if(capabilities.minImageCount > ideal) {
        return capabilities.minImageCount;
    }
    return ideal;
}

static VkImageView create_image_view(VkImage image, VkFormat format) {
    const VkComponentMapping mapping = {
        VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
        VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY
    };

    VkImageSubresourceRange subrange = {};
    {
        subrange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
        subrange.layerCount = 1;
        subrange.levelCount = 1;
    }

    VkImageViewCreateInfo create_info = vk_struct();
    {
        create_info.image = image;
        create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
        create_info.format = format;
        create_info.components = mapping;
        create_info.subresourceRange = subrange;
    }

    VkImageView view = {};
    vk_check(vkCreateImageView(vk_device(), &create_info, vk_allocation_callbacks(), &view));
    return view;
}

static bool has_wsi_support(VkSurfaceKHR surface) {
    const u32 index =  command_queue().family_index();
    VkBool32 supported = false;
    vk_check(vkGetPhysicalDeviceSurfaceSupportKHR(vk_physical_device(), index, surface, &supported));
    return supported;
}

#ifdef Y_OS_WIN
static VkSurfaceKHR create_surface(HINSTANCE_ instance, HWND_ handle) {
    VkWin32SurfaceCreateInfoKHR create_info = vk_struct();
    {
        create_info.hinstance = instance;
        create_info.hwnd = handle;
    }

    VkSurfaceKHR surface = {};
    vk_check(vkCreateWin32SurfaceKHR(vk_device_instance(), &create_info, vk_allocation_callbacks(), &surface));

    if(!has_wsi_support(surface)) {
        y_fatal("No WSI support.");
    }
    log_msg("Vulkan WSI supported!");

    return surface;
}
#endif

#ifdef Y_OS_LINUX
static VkSurfaceKHR create_surface(xcb_connection_t* connection, u32 window) {
    VkXcbSurfaceCreateInfoKHR create_info = vk_struct();
    {
        create_info.connection = connection;
        create_info.window = window;
    }

    VkSurfaceKHR surface = {};
    vk_check(vkCreateXcbSurfaceKHR(vk_device_instance(), &create_info, vk_allocation_callbacks(), &surface));

    if(!has_wsi_support(surface)) {
        y_fatal("No WSI support.");
    }
    log_msg("Vulkan WSI supported!");

    return surface;
}
#endif

static VkSurfaceKHR create_surface(Window* window) {
    y_profile();

#if defined(Y_OS_WIN) || defined(Y_OS_LINUX)
    return create_surface(window->instance(), window->handle());
#endif

    unused(window);
    return vk_null();
}

Swapchain::Swapchain(Window* window) : Swapchain(create_surface(window)) {
}

Swapchain::Swapchain(VkSurfaceKHR surface) : _surface(surface) {
    build_swapchain();
    build_semaphores();
}

bool Swapchain::reset() {
    wait_all_queues();

    _images.clear();

    const VkSwapchainKHR old = _swapchain;

    if(build_swapchain()) {
        device_destroy(old);
        return true;
    }

    return false;
}

Swapchain::~Swapchain() {
    _images.clear();

    destroy_semaphores();

    device_destroy(_swapchain);
    device_destroy(_surface);
}

bool Swapchain::build_swapchain() {
    y_debug_assert(_images.is_empty());

    const VkSurfaceCapabilitiesKHR capabilities = compute_capabilities(_surface);

    _size = {capabilities.currentExtent.width, capabilities.currentExtent.height};

    if(!_size.x() || !_size.y()) {
        return false;
    }

    const VkSurfaceFormatKHR format = surface_format(_surface);
    _color_format = VkFormat(format.format);

    const VkImageUsageFlagBits image_usage_flags = VkImageUsageFlagBits(SwapchainImageUsage & ~ImageUsage::SwapchainBit);
    if((capabilities.supportedUsageFlags & image_usage_flags) != image_usage_flags) {
        y_fatal("Swapchain does not support required usage flags.");
    }

    {
        y_profile_zone("create swapchain");
        VkSwapchainCreateInfoKHR create_info = vk_struct();
        {
            create_info.imageUsage = image_usage_flags;
            create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
            create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
            create_info.imageArrayLayers = 1;
            create_info.clipped = true;
            create_info.surface = _surface;
            create_info.preTransform = capabilities.currentTransform;
            create_info.imageFormat = format.format;
            create_info.imageColorSpace = format.colorSpace;
            create_info.imageExtent = capabilities.currentExtent;
            create_info.minImageCount = compute_image_count(capabilities);
            create_info.presentMode = present_mode(_surface);
            create_info.oldSwapchain = _swapchain;
        }
        vk_check(vkCreateSwapchainKHR(vk_device(), &create_info, vk_allocation_callbacks(), &_swapchain.get()));
    }

    y_profile_zone("image setup");

    u32 image_count = 0;
    vk_check(vkGetSwapchainImagesKHR(vk_device(), _swapchain, &image_count, nullptr));

    core::FixedArray<VkImage> images(image_count);
    vk_check(vkGetSwapchainImagesKHR(vk_device(), _swapchain, &image_count, images.data()));

    for(auto image : images) {
        const VkImageView view = create_image_view(image, _color_format.vk_format());

        struct SwapchainImageMemory : DeviceMemory {
            SwapchainImageMemory() : DeviceMemory(vk_null(), 0, 0) {
            }
        };

        auto swapchain_image = SwapchainImage();

        swapchain_image._memory = SwapchainImageMemory();

        swapchain_image._size = math::Vec3ui(_size, 1);
        swapchain_image._format = _color_format;
        swapchain_image._usage = SwapchainImageUsage;

        // prevent the images to delete their handles: the swapchain already does that.
        swapchain_image._image = image;
        swapchain_image._view = view;

#ifdef Y_DEBUG
        if(const auto* debug = debug_utils()) {
            debug->set_resource_name(image, "Swapchain Image");
            debug->set_resource_name(view, "Swapchain Image View");
        }
#endif

        _images << std::move(swapchain_image);
    }

    CmdBufferRecorder recorder(create_disposable_cmd_buffer());
    for(auto& i : _images) {
        recorder.barriers({ImageBarrier::transition_barrier(i, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR)});
    }
    command_queue().submit(std::move(recorder)).wait();

    return true;
}

void Swapchain::build_semaphores() {
    const VkFenceCreateInfo fence_create_info = vk_struct();
    const VkSemaphoreCreateInfo semaphore_create_info = vk_struct();

    for(usize i = 0; i != image_count(); ++i) {
        auto& semaphores = _semaphores.emplace_back();
        vk_check(vkCreateSemaphore(vk_device(), &semaphore_create_info, vk_allocation_callbacks(), &semaphores.render_complete));
        vk_check(vkCreateSemaphore(vk_device(), &semaphore_create_info, vk_allocation_callbacks(), &semaphores.image_aquired));
        vk_check(vkCreateFence(vk_device(), &fence_create_info, vk_allocation_callbacks(), &semaphores.fence));
    }

    y_debug_assert(image_count() == _semaphores.size());
}

void Swapchain::destroy_semaphores() {
    for(const auto& semaphores : _semaphores) {
        device_destroy(semaphores.render_complete);
        device_destroy(semaphores.image_aquired);
        device_destroy(semaphores.fence);
    }
    _semaphores.clear();
}

core::Result<FrameToken> Swapchain::next_frame() {
    y_profile();

    if(_images.is_empty()) {
        return core::Err();
    }

    y_debug_assert(_semaphores.size());

    const usize semaphore_index = ++_frame_id % image_count();
    const Semaphores& frame_semaphores = _semaphores[semaphore_index];

    u32 image_index = u32(-1);
    while(vk_swapchain_out_of_date(vkAcquireNextImageKHR(vk_device(), _swapchain, u64(-1), frame_semaphores.image_aquired, frame_semaphores.fence, &image_index))) {
        if(!reset()) {
            return core::Err();
        }
    }

    y_debug_assert(image_index < _images.size());

    {
        y_profile_zone("image fence");
        vk_check(vkWaitForFences(vk_device(), 1, &frame_semaphores.fence, true, u64(-1)));
        vk_check(vkResetFences(vk_device(), 1, &frame_semaphores.fence));
    }

    return core::Ok(FrameToken {
        _frame_id,
        image_index,
        u32(_images.size()),
        SwapchainImageView(_images[image_index]),
    });
}

void Swapchain::present(const FrameToken& token, CmdBufferRecorder&& recorder, const CmdQueue& queue) {
    y_profile();

    const usize semaphore_index = token.id % image_count();
    const Semaphores& frame_semaphores = _semaphores[semaphore_index];

    {
        queue.submit(std::move(recorder), frame_semaphores.image_aquired, frame_semaphores.render_complete);

        const auto lock = y_profile_unique_lock(queue._lock);

        y_profile_zone("present");

        VkPresentInfoKHR present_info = vk_struct();
        {
            present_info.swapchainCount = 1;
            present_info.pSwapchains = &_swapchain.get();
            present_info.pImageIndices = &token.image_index;
            present_info.waitSemaphoreCount = 1;
            present_info.pWaitSemaphores = &frame_semaphores.render_complete;
        }

        if(vk_swapchain_out_of_date(vkQueuePresentKHR(queue.vk_queue(), &present_info))) {
            // Nothing ?
        }
    }

    y_profile_frame_end();
}

VkSwapchainKHR Swapchain::vk_swapchain() const {
    return _swapchain;
}

const math::Vec2ui& Swapchain::size() const {
    return _size;
}

usize Swapchain::image_count() const {
    return _images.size();
}

ImageFormat Swapchain::color_format() const {
    return _color_format;
}

}
<commit_msg>Fixed swapchain not surviving being minimized<commit_after>/*******************************
Copyright (c) 2016-2022 Grégoire Angerand

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**********************************/

#include "Swapchain.h"

#include <yave/window/Window.h>

#include <yave/graphics/commands/CmdQueue.h>
#include <yave/graphics/memory/DeviceMemoryHeapBase.h>
#include <yave/graphics/barriers/Barrier.h>

#include <yave/graphics/device/extensions/DebugUtils.h>
#include <yave/utils/gpuprofile.h>

#include <y/core/FixedArray.h>
#include <y/utils/log.h>

namespace yave {

static VkSurfaceCapabilitiesKHR compute_capabilities(VkSurfaceKHR surface) {
    VkSurfaceCapabilitiesKHR capabilities = {};
    if(is_error(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vk_physical_device(), surface, &capabilities))) {
        return {};
    }
    return capabilities;
}

static VkSurfaceFormatKHR surface_format(VkSurfaceKHR surface) {
    Y_TODO(Find best format instead of always returning first)
    u32 format_count = 1;
    VkSurfaceFormatKHR format = {};
    vk_check_or_incomplete(vkGetPhysicalDeviceSurfaceFormatsKHR(vk_physical_device(), surface, &format_count, &format));
    y_always_assert(format_count, "No swapchain format supported");
    return format;
}

static VkPresentModeKHR present_mode(VkSurfaceKHR surface) {
    std::array<VkPresentModeKHR, 16> modes = {};
    u32 mode_count = u32(modes.size());
    vk_check(vkGetPhysicalDeviceSurfacePresentModesKHR(vk_physical_device(), surface, &mode_count, modes.data()));
    y_always_assert(mode_count, "No presentation mode supported");
    for(u32 i = 0; i != mode_count; ++i) {
        if(modes[i] == VK_PRESENT_MODE_MAILBOX_KHR) {
            return VK_PRESENT_MODE_MAILBOX_KHR;
        }
    }
    return VK_PRESENT_MODE_FIFO_KHR;
}

static u32 compute_image_count(VkSurfaceCapabilitiesKHR capabilities) {
    const u32 ideal = 3;
    if(capabilities.maxImageCount < ideal && capabilities.maxImageCount > capabilities.minImageCount) {
        return capabilities.maxImageCount;
    }
    if(capabilities.minImageCount > ideal) {
        return capabilities.minImageCount;
    }
    return ideal;
}

static VkImageView create_image_view(VkImage image, VkFormat format) {
    const VkComponentMapping mapping = {
        VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
        VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY
    };

    VkImageSubresourceRange subrange = {};
    {
        subrange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
        subrange.layerCount = 1;
        subrange.levelCount = 1;
    }

    VkImageViewCreateInfo create_info = vk_struct();
    {
        create_info.image = image;
        create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
        create_info.format = format;
        create_info.components = mapping;
        create_info.subresourceRange = subrange;
    }

    VkImageView view = {};
    vk_check(vkCreateImageView(vk_device(), &create_info, vk_allocation_callbacks(), &view));
    return view;
}

static bool has_wsi_support(VkSurfaceKHR surface) {
    const u32 index =  command_queue().family_index();
    VkBool32 supported = false;
    vk_check(vkGetPhysicalDeviceSurfaceSupportKHR(vk_physical_device(), index, surface, &supported));
    return supported;
}

#ifdef Y_OS_WIN
static VkSurfaceKHR create_surface(HINSTANCE_ instance, HWND_ handle) {
    VkWin32SurfaceCreateInfoKHR create_info = vk_struct();
    {
        create_info.hinstance = instance;
        create_info.hwnd = handle;
    }

    VkSurfaceKHR surface = {};
    vk_check(vkCreateWin32SurfaceKHR(vk_device_instance(), &create_info, vk_allocation_callbacks(), &surface));

    if(!has_wsi_support(surface)) {
        y_fatal("No WSI support.");
    }
    log_msg("Vulkan WSI supported!");

    return surface;
}
#endif

#ifdef Y_OS_LINUX
static VkSurfaceKHR create_surface(xcb_connection_t* connection, u32 window) {
    VkXcbSurfaceCreateInfoKHR create_info = vk_struct();
    {
        create_info.connection = connection;
        create_info.window = window;
    }

    VkSurfaceKHR surface = {};
    vk_check(vkCreateXcbSurfaceKHR(vk_device_instance(), &create_info, vk_allocation_callbacks(), &surface));

    if(!has_wsi_support(surface)) {
        y_fatal("No WSI support.");
    }
    log_msg("Vulkan WSI supported!");

    return surface;
}
#endif

static VkSurfaceKHR create_surface(Window* window) {
    y_profile();

#if defined(Y_OS_WIN) || defined(Y_OS_LINUX)
    return create_surface(window->instance(), window->handle());
#endif

    unused(window);
    return vk_null();
}

Swapchain::Swapchain(Window* window) : Swapchain(create_surface(window)) {
}

Swapchain::Swapchain(VkSurfaceKHR surface) : _surface(surface) {
    build_swapchain();
    build_semaphores();
}

bool Swapchain::reset() {
    wait_all_queues();

    _images.clear();

    const VkSwapchainKHR old = _swapchain;

    if(build_swapchain()) {
        device_destroy(old);
        return true;
    }

    return false;
}

Swapchain::~Swapchain() {
    _images.clear();

    destroy_semaphores();

    device_destroy(_swapchain);
    device_destroy(_surface);
}

bool Swapchain::build_swapchain() {
    y_debug_assert(_images.is_empty());

    const VkSurfaceCapabilitiesKHR capabilities = compute_capabilities(_surface);

    _size = {capabilities.currentExtent.width, capabilities.currentExtent.height};

    if(!_size.x() || !_size.y()) {
        return false;
    }

    const VkSurfaceFormatKHR format = surface_format(_surface);
    _color_format = VkFormat(format.format);

    const VkImageUsageFlagBits image_usage_flags = VkImageUsageFlagBits(SwapchainImageUsage & ~ImageUsage::SwapchainBit);
    if((capabilities.supportedUsageFlags & image_usage_flags) != image_usage_flags) {
        y_fatal("Swapchain does not support required usage flags.");
    }

    {
        y_profile_zone("create swapchain");
        VkSwapchainCreateInfoKHR create_info = vk_struct();
        {
            create_info.imageUsage = image_usage_flags;
            create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
            create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
            create_info.imageArrayLayers = 1;
            create_info.clipped = true;
            create_info.surface = _surface;
            create_info.preTransform = capabilities.currentTransform;
            create_info.imageFormat = format.format;
            create_info.imageColorSpace = format.colorSpace;
            create_info.imageExtent = capabilities.currentExtent;
            create_info.minImageCount = compute_image_count(capabilities);
            create_info.presentMode = present_mode(_surface);
            create_info.oldSwapchain = _swapchain;
        }
        vk_check(vkCreateSwapchainKHR(vk_device(), &create_info, vk_allocation_callbacks(), &_swapchain.get()));
    }

    y_profile_zone("image setup");

    u32 image_count = 0;
    vk_check(vkGetSwapchainImagesKHR(vk_device(), _swapchain, &image_count, nullptr));

    core::FixedArray<VkImage> images(image_count);
    vk_check(vkGetSwapchainImagesKHR(vk_device(), _swapchain, &image_count, images.data()));

    for(auto image : images) {
        const VkImageView view = create_image_view(image, _color_format.vk_format());

        struct SwapchainImageMemory : DeviceMemory {
            SwapchainImageMemory() : DeviceMemory(vk_null(), 0, 0) {
            }
        };

        auto swapchain_image = SwapchainImage();

        swapchain_image._memory = SwapchainImageMemory();

        swapchain_image._size = math::Vec3ui(_size, 1);
        swapchain_image._format = _color_format;
        swapchain_image._usage = SwapchainImageUsage;

        // prevent the images to delete their handles: the swapchain already does that.
        swapchain_image._image = image;
        swapchain_image._view = view;

#ifdef Y_DEBUG
        if(const auto* debug = debug_utils()) {
            debug->set_resource_name(image, "Swapchain Image");
            debug->set_resource_name(view, "Swapchain Image View");
        }
#endif

        _images << std::move(swapchain_image);
    }

    CmdBufferRecorder recorder(create_disposable_cmd_buffer());
    for(auto& i : _images) {
        recorder.barriers({ImageBarrier::transition_barrier(i, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR)});
    }
    command_queue().submit(std::move(recorder)).wait();

    return true;
}

void Swapchain::build_semaphores() {
    const VkFenceCreateInfo fence_create_info = vk_struct();
    const VkSemaphoreCreateInfo semaphore_create_info = vk_struct();

    for(usize i = 0; i != image_count(); ++i) {
        auto& semaphores = _semaphores.emplace_back();
        vk_check(vkCreateSemaphore(vk_device(), &semaphore_create_info, vk_allocation_callbacks(), &semaphores.render_complete));
        vk_check(vkCreateSemaphore(vk_device(), &semaphore_create_info, vk_allocation_callbacks(), &semaphores.image_aquired));
        vk_check(vkCreateFence(vk_device(), &fence_create_info, vk_allocation_callbacks(), &semaphores.fence));
    }

    y_debug_assert(image_count() == _semaphores.size());
}

void Swapchain::destroy_semaphores() {
    for(const auto& semaphores : _semaphores) {
        device_destroy(semaphores.render_complete);
        device_destroy(semaphores.image_aquired);
        device_destroy(semaphores.fence);
    }
    _semaphores.clear();
}

core::Result<FrameToken> Swapchain::next_frame() {
    y_profile();

    if(_images.is_empty()) {
        if(!reset()) {
            return core::Err();
        }
    }

    y_debug_assert(_semaphores.size());

    const usize semaphore_index = ++_frame_id % image_count();
    const Semaphores& frame_semaphores = _semaphores[semaphore_index];

    u32 image_index = u32(-1);
    while(vk_swapchain_out_of_date(vkAcquireNextImageKHR(vk_device(), _swapchain, u64(-1), frame_semaphores.image_aquired, frame_semaphores.fence, &image_index))) {
        if(!reset()) {
            return core::Err();
        }
    }

    y_debug_assert(image_index < _images.size());

    {
        y_profile_zone("image fence");
        vk_check(vkWaitForFences(vk_device(), 1, &frame_semaphores.fence, true, u64(-1)));
        vk_check(vkResetFences(vk_device(), 1, &frame_semaphores.fence));
    }

    return core::Ok(FrameToken {
        _frame_id,
        image_index,
        u32(_images.size()),
        SwapchainImageView(_images[image_index]),
    });
}

void Swapchain::present(const FrameToken& token, CmdBufferRecorder&& recorder, const CmdQueue& queue) {
    y_profile();

    const usize semaphore_index = token.id % image_count();
    const Semaphores& frame_semaphores = _semaphores[semaphore_index];

    {
        queue.submit(std::move(recorder), frame_semaphores.image_aquired, frame_semaphores.render_complete);

        const auto lock = y_profile_unique_lock(queue._lock);

        y_profile_zone("present");

        VkPresentInfoKHR present_info = vk_struct();
        {
            present_info.swapchainCount = 1;
            present_info.pSwapchains = &_swapchain.get();
            present_info.pImageIndices = &token.image_index;
            present_info.waitSemaphoreCount = 1;
            present_info.pWaitSemaphores = &frame_semaphores.render_complete;
        }

        if(vk_swapchain_out_of_date(vkQueuePresentKHR(queue.vk_queue(), &present_info))) {
            // Nothing ?
        }
    }

    y_profile_frame_end();
}

VkSwapchainKHR Swapchain::vk_swapchain() const {
    return _swapchain;
}

const math::Vec2ui& Swapchain::size() const {
    return _size;
}

usize Swapchain::image_count() const {
    return _images.size();
}

ImageFormat Swapchain::color_format() const {
    return _color_format;
}

}
<|endoftext|>


<commit_before>#include "anki/scene/Sector.h"
#include "anki/scene/Spatial.h"
#include "anki/scene/SceneNode.h"
#include "anki/scene/Renderable.h"
#include "anki/scene/Light.h"
#include "anki/scene/VisibilityTestResults.h"
#include "anki/scene/Frustumable.h"
#include "anki/scene/Scene.h"
#include "anki/core/Logger.h"
#include "anki/renderer/Renderer.h"
#include "anki/core/ThreadPool.h"

namespace anki {

//==============================================================================
// Misc                                                                        =
//==============================================================================

//==============================================================================
struct DistanceSortFunctor
{
	Vec3 origin;

	Bool operator()(SceneNode* a, SceneNode* b)
	{
		ANKI_ASSERT(a->getSpatial() != nullptr && b->getSpatial() != nullptr);

		F32 dist0 = origin.getDistanceSquared(
			a->getSpatial()->getSpatialOrigin());
		F32 dist1 = origin.getDistanceSquared(
			b->getSpatial()->getSpatialOrigin());

		return dist0 < dist1;
	}
};

//==============================================================================
struct MaterialSortFunctor
{
	Bool operator()(SceneNode* a, SceneNode* b)
	{
		ANKI_ASSERT(a->getRenderable() != nullptr
			&& b->getRenderable() != nullptr);

		return a->getRenderable()->getRenderableMaterial()
			< b->getRenderable()->getRenderableMaterial();
	}
};

//==============================================================================
struct DistanceSortJob: ThreadJob
{
	U nodesCount;
	VisibilityTestResults::Container::iterator nodes;
	Vec3 origin;

	void operator()(U threadId, U threadsCount)
	{
		DistanceSortFunctor comp;
		comp.origin = origin;
		std::sort(nodes, nodes + nodesCount, comp);
	}
};

//==============================================================================
struct MaterialSortJob: ThreadJob
{
	U nodesCount;
	VisibilityTestResults::Container::iterator nodes;

	void operator()(U threadId, U threadsCount)
	{
		std::sort(nodes, nodes + nodesCount, MaterialSortFunctor());
	}
};

//==============================================================================
// Portal                                                                      =
//==============================================================================

//==============================================================================
Portal::Portal()
{
	sectors[0] = sectors[1] = nullptr;
	open = true;
}

//==============================================================================
// Sector                                                                      =
//==============================================================================

//==============================================================================
Sector::Sector(SectorGroup* group_, const Aabb& box)
	: group(group_), octree(this, box, 3),
		portals(group->getScene().getAllocator())
{}

//==============================================================================
Bool Sector::placeSceneNode(SceneNode* sn)
{
	// XXX Optimize

	if(!sn->getSpatial()->getAabb().collide(octree.getRoot().getAabb()))
	{
		return false;
	}

	octree.placeSceneNode(sn);
	return true;
}

//==============================================================================
// SectorGroup                                                                 =
//==============================================================================

//==============================================================================
SectorGroup::SectorGroup(Scene* scene_)
	:	scene(scene_),
		sectors(scene->getAllocator()),
		portals(scene->getAllocator())
{
	ANKI_ASSERT(scene != nullptr);
}

//==============================================================================
SectorGroup::~SectorGroup()
{
	for(Sector* sector : sectors)
	{
		ANKI_DELETE(sector, scene->getAllocator());
	}

	for(Portal* portal : portals)
	{
		ANKI_DELETE(portal, scene->getAllocator());
	}
}

//==============================================================================
Sector* SectorGroup::createNewSector(const Aabb& aabb)
{
	Sector* out = ANKI_NEW(Sector, scene->getAllocator(), this, aabb);
	sectors.push_back(out);
	return out;
}

//==============================================================================
Portal* SectorGroup::createNewPortal(Sector* a, Sector* b,
	const Obb& collisionShape)
{
	ANKI_ASSERT(a && b);
	Portal* out = ANKI_NEW_0(Portal, scene->getAllocator());

	out->sectors[0] = a;
	out->sectors[1] = b;
	out->shape = collisionShape;

	return out;
}

//==============================================================================
void SectorGroup::placeSceneNode(SceneNode* sn)
{
	ANKI_ASSERT(sn != nullptr);
	Spatial* sp = sn->getSpatial();
	ANKI_ASSERT(sp);
	const Aabb& spAabb = sp->getAabb();

	// Find the candidates first. Sectors overlap, chose the smaller(??!!??)
	Sector* sector = nullptr;
	for(Sector* s : sectors)
	{
		// Spatial inside the sector?
		if(s->getAabb().collide(spAabb))
		{
			// No other candidate?
			if(sector == nullptr)
			{
				sector = s;
			}
			else
			{
				// Other candidata so chose the smaller
				F32 lengthSqA = (sector->getAabb().getMax()
					- sector->getAabb().getMin()).getLengthSquared();

				F32 lengthSqB = (s->getAabb().getMax()
					- s->getAabb().getMin()).getLengthSquared();

				if(lengthSqB < lengthSqA)
				{
					sector = s;
				}
			}
		}
	}

	// Ask the octree to place it
	if(sector != nullptr)
	{
		sector->octree.placeSceneNode(sn);
	}
	else
	{
		ANKI_LOGW("Spatial outside all sectors");
	}
}

//==============================================================================
void SectorGroup::doVisibilityTests(SceneNode& sn, VisibilityTest test,
	Renderer* r)
{
	Frustumable* fr = sn.getFrustumable();
	ANKI_ASSERT(fr != nullptr && "sn should be frustumable");

	// Set all sectors to not visible
	for(Sector* sector : sectors)
	{
		sector->visible = false;
	}

	//
	// Find the visible sectors
	//

	SceneVector<Sector*> visibleSectors(scene->getFrameAllocator());

	Spatial* sp = sn.getSpatial();
	ANKI_ASSERT(sp != nullptr);

	// Find the sector that contains the frustumable
	Sector& containerSector = sp->getOctreeNode().getOctree().getSector();
	containerSector.visible = true;
	visibleSectors.push_back(&containerSector);

	// Loop all portals and add other sectors
	for(Portal* portal : portals)
	{
		if(ANKI_UNLIKELY(!portal->open))
		{
			continue;
		}

		// Get the "other" sector of that portal
		Sector* testAgainstSector;

		if(portal->sectors[0] == &containerSector)
		{
			testAgainstSector = portal->sectors[1];
		}
		else
		{
			ANKI_ASSERT(portal->sectors[1] == &containerSector);
			testAgainstSector = portal->sectors[0];
		}
		ANKI_ASSERT(testAgainstSector != nullptr);

		// Search if portal is in the container from another portal
		SceneVector<Sector*>::iterator it = std::find(visibleSectors.begin(),
			visibleSectors.end(), testAgainstSector);

		if(it == visibleSectors.end())
		{
			// Not found so test the portal

			// Portal is visible
			if(fr->insideFrustum(portal->shape))
			{
				if(r == nullptr || r->doVisibilityTests(portal->shape))
				{
					testAgainstSector->visible = true;
					visibleSectors.push_back(testAgainstSector);
				}
			}
		}
	}

	//
	// For all visible sectors do the tests
	//

	// Create a few VisibilityTestResults to pass to every octree
	SceneVector<VisibilityTestResults> testResults(scene->getFrameAllocator());
	testResults.resize(visibleSectors.size(),
		VisibilityTestResults(scene->getFrameAllocator()));
	U renderablesCount = 0;
	U lightsCount = 0;

	// Run tests for every octree
	for(U i = 0; i < visibleSectors.size(); i++)
	{
		Sector* sector = visibleSectors[i];

		sector->octree.doVisibilityTests(sn, test, testResults[i]);

		renderablesCount += testResults[i].renderables.size();
		lightsCount += testResults[i].lights.size();
	}

	// If you don't want lights then the octree should do tests with them
	ANKI_ASSERT(!((test & VT_LIGHTS) == 0 && lightsCount != 0));
	// Same as ^
	ANKI_ASSERT(!((test & VT_RENDERABLES) == 0 && renderablesCount != 0));

	//
	// Combine test results and try doing renderer tests
	//

	// Create the visibility container
	VisibilityTestResults* visible =
		ANKI_NEW(VisibilityTestResults, scene->getFrameAllocator(),
		scene->getFrameAllocator());

	// Set the sizes to save some moves
	visible->renderables.reserve(renderablesCount);
	visible->lights.reserve(lightsCount);

	// Iterate previous test results and append to the combined one
	if(r != nullptr)
	{
		for(VisibilityTestResults& testResult : testResults)
		{
			visible->renderables.insert(
				visible->renderables.end(),
				testResult.renderables.begin(),
				testResult.renderables.end());

			visible->lights.insert(
				visible->lights.end(),
				testResult.lights.begin(),
				testResult.lights.end());
		}
	}
	else
	{
		for(VisibilityTestResults& testResult : testResults)
		{
			// First the renderables
			for(SceneNode* renderable : testResult.renderables)
			{
				if(r->doVisibilityTests(
					renderable->getSpatial()->getOptimalCollisionShape()))
				{
					visible->renderables.push_back(renderable);
				}
			}

			// Then the lights
			for(SceneNode* light : testResult.lights)
			{
				if(r->doVisibilityTests(
					light->getSpatial()->getOptimalCollisionShape()))
				{
					visible->lights.push_back(light);
				}
			}
		}
	}

	// The given frustumable is finished
	fr->visible = visible;

	//
	// Sort
	//

	ThreadPool& threadPool = ThreadPoolSingleton::get();

	// Sort the renderables in a another thread
	DistanceSortJob dsjob;
	dsjob.nodes = visible->renderables.begin();
	dsjob.nodesCount = visible->renderables.size();
	dsjob.origin = sp->getSpatialOrigin();
	threadPool.assignNewJob(0, &dsjob);

	// The rest of the jobs are dummy
	Array<ThreadJobDummy, ThreadPool::MAX_THREADS>  dummyjobs;
	for(U i = 1; i < threadPool.getThreadsCount(); i++)
	{
		threadPool.assignNewJob(i, &dummyjobs[i]);
	}

	// Sort the lights in the main thread
	std::sort(visible->lights.begin(),
		visible->lights.end(), DistanceSortFunctor{sp->getSpatialOrigin()});

	threadPool.waitForAllJobsToFinish();

	//
	// Continue with testing the lights
	//

	for(SceneNode* lsn : visible->lights)
	{
		Light* l = lsn->getLight();
		ANKI_ASSERT(l != nullptr);

		if(l->getShadowEnabled())
		{
			ANKI_ASSERT(lsn->getFrustumable() != nullptr);

			doVisibilityTests(*lsn,
				(VisibilityTest)(VT_RENDERABLES | VT_ONLY_SHADOW_CASTERS),
				nullptr);
		}
	}
}

} // end namespace anki
<commit_msg>Fixing a small bug<commit_after>#include "anki/scene/Sector.h"
#include "anki/scene/Spatial.h"
#include "anki/scene/SceneNode.h"
#include "anki/scene/Renderable.h"
#include "anki/scene/Light.h"
#include "anki/scene/VisibilityTestResults.h"
#include "anki/scene/Frustumable.h"
#include "anki/scene/Scene.h"
#include "anki/core/Logger.h"
#include "anki/renderer/Renderer.h"
#include "anki/core/ThreadPool.h"

namespace anki {

//==============================================================================
// Misc                                                                        =
//==============================================================================

//==============================================================================
struct DistanceSortFunctor
{
	Vec3 origin;

	Bool operator()(SceneNode* a, SceneNode* b)
	{
		ANKI_ASSERT(a->getSpatial() != nullptr && b->getSpatial() != nullptr);

		F32 dist0 = origin.getDistanceSquared(
			a->getSpatial()->getSpatialOrigin());
		F32 dist1 = origin.getDistanceSquared(
			b->getSpatial()->getSpatialOrigin());

		return dist0 < dist1;
	}
};

//==============================================================================
struct MaterialSortFunctor
{
	Bool operator()(SceneNode* a, SceneNode* b)
	{
		ANKI_ASSERT(a->getRenderable() != nullptr
			&& b->getRenderable() != nullptr);

		return a->getRenderable()->getRenderableMaterial()
			< b->getRenderable()->getRenderableMaterial();
	}
};

//==============================================================================
struct DistanceSortJob: ThreadJob
{
	U nodesCount;
	VisibilityTestResults::Container::iterator nodes;
	Vec3 origin;

	void operator()(U threadId, U threadsCount)
	{
		DistanceSortFunctor comp;
		comp.origin = origin;
		std::sort(nodes, nodes + nodesCount, comp);
	}
};

//==============================================================================
struct MaterialSortJob: ThreadJob
{
	U nodesCount;
	VisibilityTestResults::Container::iterator nodes;

	void operator()(U threadId, U threadsCount)
	{
		std::sort(nodes, nodes + nodesCount, MaterialSortFunctor());
	}
};

//==============================================================================
// Portal                                                                      =
//==============================================================================

//==============================================================================
Portal::Portal()
{
	sectors[0] = sectors[1] = nullptr;
	open = true;
}

//==============================================================================
// Sector                                                                      =
//==============================================================================

//==============================================================================
Sector::Sector(SectorGroup* group_, const Aabb& box)
	: group(group_), octree(this, box, 3),
		portals(group->getScene().getAllocator())
{}

//==============================================================================
Bool Sector::placeSceneNode(SceneNode* sn)
{
	// XXX Optimize

	if(!sn->getSpatial()->getAabb().collide(octree.getRoot().getAabb()))
	{
		return false;
	}

	octree.placeSceneNode(sn);
	return true;
}

//==============================================================================
// SectorGroup                                                                 =
//==============================================================================

//==============================================================================
SectorGroup::SectorGroup(Scene* scene_)
	:	scene(scene_),
		sectors(scene->getAllocator()),
		portals(scene->getAllocator())
{
	ANKI_ASSERT(scene != nullptr);
}

//==============================================================================
SectorGroup::~SectorGroup()
{
	for(Sector* sector : sectors)
	{
		ANKI_DELETE(sector, scene->getAllocator());
	}

	for(Portal* portal : portals)
	{
		ANKI_DELETE(portal, scene->getAllocator());
	}
}

//==============================================================================
Sector* SectorGroup::createNewSector(const Aabb& aabb)
{
	Sector* out = ANKI_NEW(Sector, scene->getAllocator(), this, aabb);
	sectors.push_back(out);
	return out;
}

//==============================================================================
Portal* SectorGroup::createNewPortal(Sector* a, Sector* b,
	const Obb& collisionShape)
{
	ANKI_ASSERT(a && b);
	Portal* out = ANKI_NEW_0(Portal, scene->getAllocator());

	out->sectors[0] = a;
	out->sectors[1] = b;
	out->shape = collisionShape;

	return out;
}

//==============================================================================
void SectorGroup::placeSceneNode(SceneNode* sn)
{
	ANKI_ASSERT(sn != nullptr);
	Spatial* sp = sn->getSpatial();
	ANKI_ASSERT(sp);
	const Aabb& spAabb = sp->getAabb();

	// Find the candidates first. Sectors overlap, chose the smaller(??!!??)
	Sector* sector = nullptr;
	for(Sector* s : sectors)
	{
		// Spatial inside the sector?
		if(s->getAabb().collide(spAabb))
		{
			// No other candidate?
			if(sector == nullptr)
			{
				sector = s;
			}
			else
			{
				// Other candidata so chose the smaller
				F32 lengthSqA = (sector->getAabb().getMax()
					- sector->getAabb().getMin()).getLengthSquared();

				F32 lengthSqB = (s->getAabb().getMax()
					- s->getAabb().getMin()).getLengthSquared();

				if(lengthSqB < lengthSqA)
				{
					sector = s;
				}
			}
		}
	}

	// Ask the octree to place it
	if(sector != nullptr)
	{
		sector->octree.placeSceneNode(sn);
	}
	else
	{
		ANKI_LOGW("Spatial outside all sectors");
	}
}

//==============================================================================
void SectorGroup::doVisibilityTests(SceneNode& sn, VisibilityTest test,
	Renderer* r)
{
	Frustumable* fr = sn.getFrustumable();
	ANKI_ASSERT(fr != nullptr && "sn should be frustumable");

	// Set all sectors to not visible
	for(Sector* sector : sectors)
	{
		sector->visible = false;
	}

	//
	// Find the visible sectors
	//

	SceneVector<Sector*> visibleSectors(scene->getFrameAllocator());

	Spatial* sp = sn.getSpatial();
	ANKI_ASSERT(sp != nullptr);

	// Find the sector that contains the frustumable
	Sector& containerSector = sp->getOctreeNode().getOctree().getSector();
	containerSector.visible = true;
	visibleSectors.push_back(&containerSector);

	// Loop all portals and add other sectors
	for(Portal* portal : portals)
	{
		if(ANKI_UNLIKELY(!portal->open))
		{
			continue;
		}

		// Get the "other" sector of that portal
		Sector* testAgainstSector;

		if(portal->sectors[0] == &containerSector)
		{
			testAgainstSector = portal->sectors[1];
		}
		else
		{
			ANKI_ASSERT(portal->sectors[1] == &containerSector);
			testAgainstSector = portal->sectors[0];
		}
		ANKI_ASSERT(testAgainstSector != nullptr);

		// Search if portal is in the container from another portal
		SceneVector<Sector*>::iterator it = std::find(visibleSectors.begin(),
			visibleSectors.end(), testAgainstSector);

		if(it == visibleSectors.end())
		{
			// Not found so test the portal

			// Portal is visible
			if(fr->insideFrustum(portal->shape))
			{
				if(r == nullptr || r->doVisibilityTests(portal->shape))
				{
					testAgainstSector->visible = true;
					visibleSectors.push_back(testAgainstSector);
				}
			}
		}
	}

	//
	// For all visible sectors do the tests
	//

	// Create a few VisibilityTestResults to pass to every octree
	SceneVector<VisibilityTestResults> testResults(scene->getFrameAllocator());
	testResults.resize(visibleSectors.size(),
		VisibilityTestResults(scene->getFrameAllocator()));
	U renderablesCount = 0;
	U lightsCount = 0;

	// Run tests for every octree
	for(U i = 0; i < visibleSectors.size(); i++)
	{
		Sector* sector = visibleSectors[i];

		sector->octree.doVisibilityTests(sn, test, testResults[i]);

		renderablesCount += testResults[i].renderables.size();
		lightsCount += testResults[i].lights.size();
	}

	// If you don't want lights then the octree should do tests with them
	ANKI_ASSERT(!((test & VT_LIGHTS) == 0 && lightsCount != 0));
	// Same as ^
	ANKI_ASSERT(!((test & VT_RENDERABLES) == 0 && renderablesCount != 0));

	//
	// Combine test results and try doing renderer tests
	//

	// Create the visibility container
	VisibilityTestResults* visible =
		ANKI_NEW(VisibilityTestResults, scene->getFrameAllocator(),
		scene->getFrameAllocator());

	// Set the sizes to save some moves
	visible->renderables.reserve(renderablesCount);
	visible->lights.reserve(lightsCount);

	// Iterate previous test results and append to the combined one
	if(r == nullptr)
	{
		for(VisibilityTestResults& testResult : testResults)
		{
			visible->renderables.insert(
				visible->renderables.end(),
				testResult.renderables.begin(),
				testResult.renderables.end());

			visible->lights.insert(
				visible->lights.end(),
				testResult.lights.begin(),
				testResult.lights.end());
		}
	}
	else
	{
		for(VisibilityTestResults& testResult : testResults)
		{
			// First the renderables
			for(SceneNode* renderable : testResult.renderables)
			{
				if(r->doVisibilityTests(
					renderable->getSpatial()->getOptimalCollisionShape()))
				{
					visible->renderables.push_back(renderable);
				}
			}

			// Then the lights
			for(SceneNode* light : testResult.lights)
			{
				if(r->doVisibilityTests(
					light->getSpatial()->getOptimalCollisionShape()))
				{
					visible->lights.push_back(light);
				}
			}
		}
	}

	// The given frustumable is finished
	fr->visible = visible;

	//
	// Sort
	//

	ThreadPool& threadPool = ThreadPoolSingleton::get();

	// Sort the renderables in a another thread
	DistanceSortJob dsjob;
	dsjob.nodes = visible->renderables.begin();
	dsjob.nodesCount = visible->renderables.size();
	dsjob.origin = sp->getSpatialOrigin();
	threadPool.assignNewJob(0, &dsjob);

	// The rest of the jobs are dummy
	Array<ThreadJobDummy, ThreadPool::MAX_THREADS>  dummyjobs;
	for(U i = 1; i < threadPool.getThreadsCount(); i++)
	{
		threadPool.assignNewJob(i, &dummyjobs[i]);
	}

	// Sort the lights in the main thread
	std::sort(visible->lights.begin(),
		visible->lights.end(), DistanceSortFunctor{sp->getSpatialOrigin()});

	threadPool.waitForAllJobsToFinish();

	//
	// Continue with testing the lights
	//

	for(SceneNode* lsn : visible->lights)
	{
		Light* l = lsn->getLight();
		ANKI_ASSERT(l != nullptr);

		if(l->getShadowEnabled())
		{
			ANKI_ASSERT(lsn->getFrustumable() != nullptr);

			doVisibilityTests(*lsn,
				(VisibilityTest)(VT_RENDERABLES | VT_ONLY_SHADOW_CASTERS),
				nullptr);
		}
	}
}

} // end namespace anki
<|endoftext|>


<commit_before>#include "bpforc.h"
#include "bpftrace.h"
#include "clang_parser.h"
#include "codegen_llvm.h"
#include "driver.h"
#include "fake_map.h"
#include "field_analyser.h"
#include "mocks.h"
#include "semantic_analyser.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"

namespace bpftrace {
namespace test {
namespace probe {

using bpftrace::ast::AttachPoint;
using bpftrace::ast::AttachPointList;
using bpftrace::ast::Probe;

void gen_bytecode(const std::string &input, std::stringstream &out)
{
  auto bpftrace = get_mock_bpftrace();
  Driver driver(*bpftrace);
  FakeMap::next_mapfd_ = 1;

  ASSERT_EQ(driver.parse_str(input), 0);

  ast::FieldAnalyser fields(driver.root_, *bpftrace);
  EXPECT_EQ(fields.analyse(), 0);

  ClangParser clang;
  clang.parse(driver.root_, *bpftrace);

  // Override to mockbpffeature.
  bpftrace->feature_ = std::make_unique<MockBPFfeature>(true);
  ast::SemanticAnalyser semantics(driver.root_, *bpftrace);
  ASSERT_EQ(semantics.analyse(), 0);
  ASSERT_EQ(semantics.create_maps(true), 0);

  ast::CodegenLLVM codegen(driver.root_, *bpftrace);
  codegen.generate_ir();
  codegen.DumpIR(out);
}

void compare_bytecode(const std::string &input1, const std::string &input2)
{
  std::stringstream expected_output1;
  std::stringstream expected_output2;

  gen_bytecode(input1, expected_output1);
  gen_bytecode(input2, expected_output2);

  EXPECT_EQ(expected_output1.str(), expected_output2.str());
}

TEST(probe, short_name)
{
  compare_bytecode("tracepoint:a:b { args }", "t:a:b { args }");
  compare_bytecode("kprobe:f { pid }", "k:f { pid }");
  compare_bytecode("kretprobe:f { pid }", "kr:f { pid }");
  compare_bytecode("uprobe:sh:f { 1 }", "u:sh:f { 1 }");
  compare_bytecode("profile:hz:997 { 1 }", "p:hz:997 { 1 }");
  compare_bytecode("hardware:cache-references:1000000 { 1 }", "h:cache-references:1000000 { 1 }");
  compare_bytecode("software:faults:1000 { 1 }", "s:faults:1000 { 1 }");
  compare_bytecode("interval:s:1 { 1 }", "i:s:1 { 1 }");
}

#ifdef HAVE_LIBBPF_BTF_DUMP

#include "btf_common.h"

class probe_btf : public test_btf
{
};

TEST_F(probe_btf, short_name)
{
  compare_bytecode("kfunc:func_1 { 1 }", "f:func_1 { 1 }");
  compare_bytecode("kretfunc:func_1 { 1 }", "fr:func_1 { 1 }");
}

#endif // HAVE_LIBBPF_BTF_DUMP

} // namespace probe
} // namespace test
} // namespace bpftrace
<commit_msg>Add iter compare_bytecode test<commit_after>#include "bpforc.h"
#include "bpftrace.h"
#include "clang_parser.h"
#include "codegen_llvm.h"
#include "driver.h"
#include "fake_map.h"
#include "field_analyser.h"
#include "mocks.h"
#include "semantic_analyser.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"

namespace bpftrace {
namespace test {
namespace probe {

using bpftrace::ast::AttachPoint;
using bpftrace::ast::AttachPointList;
using bpftrace::ast::Probe;

void gen_bytecode(const std::string &input, std::stringstream &out)
{
  auto bpftrace = get_mock_bpftrace();
  Driver driver(*bpftrace);
  FakeMap::next_mapfd_ = 1;

  ASSERT_EQ(driver.parse_str(input), 0);

  ast::FieldAnalyser fields(driver.root_, *bpftrace);
  EXPECT_EQ(fields.analyse(), 0);

  ClangParser clang;
  clang.parse(driver.root_, *bpftrace);

  // Override to mockbpffeature.
  bpftrace->feature_ = std::make_unique<MockBPFfeature>(true);
  ast::SemanticAnalyser semantics(driver.root_, *bpftrace);
  ASSERT_EQ(semantics.analyse(), 0);
  ASSERT_EQ(semantics.create_maps(true), 0);

  ast::CodegenLLVM codegen(driver.root_, *bpftrace);
  codegen.generate_ir();
  codegen.DumpIR(out);
}

void compare_bytecode(const std::string &input1, const std::string &input2)
{
  std::stringstream expected_output1;
  std::stringstream expected_output2;

  gen_bytecode(input1, expected_output1);
  gen_bytecode(input2, expected_output2);

  EXPECT_EQ(expected_output1.str(), expected_output2.str());
}

TEST(probe, short_name)
{
  compare_bytecode("tracepoint:a:b { args }", "t:a:b { args }");
  compare_bytecode("kprobe:f { pid }", "k:f { pid }");
  compare_bytecode("kretprobe:f { pid }", "kr:f { pid }");
  compare_bytecode("uprobe:sh:f { 1 }", "u:sh:f { 1 }");
  compare_bytecode("profile:hz:997 { 1 }", "p:hz:997 { 1 }");
  compare_bytecode("hardware:cache-references:1000000 { 1 }", "h:cache-references:1000000 { 1 }");
  compare_bytecode("software:faults:1000 { 1 }", "s:faults:1000 { 1 }");
  compare_bytecode("interval:s:1 { 1 }", "i:s:1 { 1 }");
}

#ifdef HAVE_LIBBPF_BTF_DUMP

#include "btf_common.h"

class probe_btf : public test_btf
{
};

TEST_F(probe_btf, short_name)
{
  compare_bytecode("kfunc:func_1 { 1 }", "f:func_1 { 1 }");
  compare_bytecode("kretfunc:func_1 { 1 }", "fr:func_1 { 1 }");
  compare_bytecode("iter:task { 1 }", "it:task { 1 }");
  compare_bytecode("iter:task_file { 1 }", "it:task_file { 1 }");
}

#endif // HAVE_LIBBPF_BTF_DUMP

} // namespace probe
} // namespace test
} // namespace bpftrace
<|endoftext|>


<commit_before>/* MicroFlo - Flow-Based Programming for microcontrollers
 * Copyright (c) 2016 Jon Nordby <jononor@gmail.com>
 * MicroFlo may be freely distributed under the MIT license
 */

// MQTT and MsgFlo.org support, at least for embedded Linux

#include "mosquitto.h"

#include <string>
#include <vector>

#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <err.h>

#ifdef DEBUG
#define LOG(...) do { printf(__VA_ARGS__); } while (0)
#else
#define LOG(...)
#endif

class MqttMount;

struct Port {
    MicroFlo::NodeId node;
    MicroFlo::PortId port;
    std::string topic;
    std::string name;

    Port(std::string t, MicroFlo::NodeId n, MicroFlo::PortId p, std::string na)
        : node(n)
        , port(p)
        , topic(t)
        , name(na)
    {

    }
};

std::string
toTopic(std::string role, std::string portName) {
    return "/" + role + "/" + portName;
}

struct ParticipantInfo {
    std::vector<Port> inports;
    std::vector<Port> outports;
    std::string role;
    std::string id;
    std::string component;
    std::string icon;

    ParticipantInfo *addInport(std::string name, MicroFlo::NodeId n, MicroFlo::PortId p) {
        Port port(toTopic(role, name), n, p, name);
        inports.push_back(port);
        return this;
    }
    ParticipantInfo *addOutport(std::string name, MicroFlo::NodeId n, MicroFlo::PortId p) {
        Port port(toTopic(role, name), n, p, name);
        outports.push_back(port);
        return this;
    }
};

#define JSON_ATTR_STRING(name, value) \
    +std::string("    \"") +name+std::string("\": \"") + value + std::string("\",\n")
#define JSON_ATTR_ARRAY(name, value) \
    +std::string("    \"") +name+std::string("\": [") + value + std::string("],\n")
#define JSON_ATTR_ENDNULL(name) \
    +std::string("    \"") +name+std::string("\": ") + "null" + std::string("\n")

std::string msgfloPorts(const std::vector<Port> &ports) {
    std::string str;
    // TODO: specify datatype on ports

    for (int i=0; i<(int)ports.size(); i++) {
        const Port &port = ports[i];
        str += "\n    {\n"
            JSON_ATTR_STRING("id", port.name)
            JSON_ATTR_STRING("queue", port.topic)
            JSON_ATTR_STRING("type", "any")
            JSON_ATTR_ENDNULL("_")
        + "    }";
        if (i < (int)ports.size()-1) {
            str =+ ",";
        }
    }
    return str;
}

std::string msgfloDiscoveryMessage(const ParticipantInfo *info) {
    // TODO: add label
    // TODO: add icon
    std::string inports = msgfloPorts(info->inports);
    std::string outports = msgfloPorts(info->outports);

    std::string msg = "{\n"
        JSON_ATTR_STRING("id", info->id)
        JSON_ATTR_STRING("role", info->role)
        JSON_ATTR_STRING("component", info->component)
        JSON_ATTR_STRING("icon", info->icon)
        JSON_ATTR_ARRAY("inports", inports)
        JSON_ATTR_ARRAY("outports", outports)
        JSON_ATTR_ENDNULL("label")
    + "}";
    return msg;
}

struct MqttOptions {
    int brokerPort;
    char * brokerHostname;
    int keepaliveSeconds;
    char * clientId;
    ParticipantInfo info;
};

const Port *
findPortByTopic(const std::vector<Port> &ports, char *topic) {
    for (std::vector<Port>::const_iterator it = ports.begin() ; it != ports.end(); ++it) {
        const Port *port = &(*it);
        if (port->topic == std::string(topic)) {
            return port;
        }
    }
    return NULL;
}

const Port *
findPortByEdge(const std::vector<Port> &ports, MicroFlo::NodeId node, MicroFlo::PortId portId) {
    for (std::vector<Port>::const_iterator it = ports.begin() ; it != ports.end(); ++it) {
        const Port *port = &(*it);
        if (port->node == node && port->port == portId) {
            return port;
        }
    }
    return NULL;
}

// C++ version of the logic in commandstream dataLiteralToCommand etc
Packet decodePacket(const std::string &str) {
    // TODO: handle floats
    // TODO: handle brackets
    // MAYBE: handle hex and octal integers?
    // TODO: handle byte streams
    // TODO: handle brackets
    const long int10 = strtol(str.c_str(), NULL, 10);
    if (str == "null") {
        return Packet(); // void
    } else if (str == "true") {
        return Packet(true);
    } else if (str == "false") {
        return Packet(false);
    } else if (str == "0") {
        return Packet((long)0); // bad strtol error value..
    } else if (int10 != 0) {
        return Packet(int10);
    } else {
        return Packet(MsgInvalid);
    }
}
std::string encodePacket(const Packet &pkg) {
    switch (pkg.type()) {
    case MsgVoid:
        return "null";
    case MsgBoolean:
        return pkg.asBool() ? "true" : "false";
    case MsgInteger:
        return std::to_string(pkg.asInteger());
    case MsgByte: // TODO: handle byte streams
        return "";
    case MsgFloat: // TOOD: handle floats
        return "";

    case MsgBracketStart: // TOOD: handle brackets
    case MsgBracketEnd: // TOOD: handle brackets
        return "";

    // internal types
    case MsgMax:
    case MsgMaxDefined:
    case MsgSetup:
    case MsgTick:
    case MsgInvalid:
        return "";
    }

    return ""; // above should be exclusive but compiler complains...
}

// FIXME: write automated test
class MqttMount : public NetworkNotificationHandler {

public:
    static bool runForever(MqttMount *mount) {
        const int timeoutMs = 10;

        while(1){
            const int status = mosquitto_loop(mount->connection, timeoutMs, 1);
            if (status == MOSQ_ERR_CONN_LOST) {
                mount->connect();
            } else if (status == MOSQ_ERR_SUCCESS) {
                // Run MicroFlo network
                for (int i=0; i<20; i++) {
                    mount->network->runTick();
                }
            } else {
                LOG("mosquitto loop error: %s\n", mosquitto_strerror(status));
                return false;
            }
        }
        return true;
    }

public:
    MqttMount(Network *net, const MqttOptions &o)
        : network(net)
        , options(o)
        , connection(NULL)
    {
        network->setNotificationHandler(this);
    }

    ~MqttMount() {
        mosquitto_destroy(this->connection);
        this->connection = NULL;
        (void)mosquitto_lib_cleanup();
    }

    bool connect() {
        struct mosquitto *m = mosquitto_new(options.clientId, true, this);
        this->connection = m;

        mosquitto_connect_callback_set(m, on_connect);
        mosquitto_publish_callback_set(m, on_publish);
        mosquitto_subscribe_callback_set(m, on_subscribe);
        mosquitto_message_callback_set(m, on_message);

        const int res = mosquitto_connect(m, options.brokerHostname, options.brokerPort,
                                          options.keepaliveSeconds);
        return res == MOSQ_ERR_SUCCESS;
    }
    void disconnect() {} // TODO: implement

public:
    // Not really public, used by C trampolines
    void onConnect(int status) {
        const bool connected = status == 0;
        if (connected) {
            subscribePorts();
            sendDiscovery();
        }
    }

    void onMessage(const struct mosquitto_message *msg) {
        char *payloadStr = strndup((const char *)msg->payload, msg->payloadlen);
        const Packet pkg = decodePacket(std::string(payloadStr));
        LOG("got MQTT message on topic %s: %s", msg->topic, payloadStr);
        free(payloadStr);

        const Port *port = findPortByTopic(options.info.inports, msg->topic);
        if (port) {
            LOG("sending to %d %d \n", port->node, port->port);
            network->sendMessageTo(port->node, port->port, pkg);
        } else {
            LOG("Failed to find port for MQTT topic: %s", msg->topic);
        }
    }

    // implements NetworkNotificationHandler
    virtual void nodeAdded(Component *c, MicroFlo::NodeId parentId) {}
    virtual void nodesConnected(Component *src, MicroFlo::PortId srcPort,
                                Component *target, MicroFlo::PortId targetPort) {}
    virtual void networkStateChanged(Network::State s) {}
    virtual void emitDebug(DebugLevel level, DebugId id) {}
    virtual void debugChanged(DebugLevel level) {}
    virtual void portSubscriptionChanged(MicroFlo::NodeId nodeId, MicroFlo::PortId portId, bool enable) {}
    virtual void subgraphConnected(bool isOutput, MicroFlo::NodeId subgraphNode,
                                   MicroFlo::PortId subgraphPort, MicroFlo::NodeId childNode, MicroFlo::PortId childPort) {}

    virtual void packetSent(const Message &m, const Component *sender, MicroFlo::PortId senderPort) {
        const MicroFlo::NodeId senderId = sender->id();
        //LOG("packet sent %d\n", senderId);

        const Port * port = findPortByEdge(options.info.outports, senderId, senderPort);
        if (port) {
            const char *outTopic = port->topic.c_str();

            const std::string data = encodePacket(m.pkg);
            const int res = mosquitto_publish(this->connection, NULL, outTopic,
                                              data.size(), data.c_str(), 0, false);
            LOG("sending on MQTT topic %s: %s\n", outTopic, data.c_str());

            if (res != MOSQ_ERR_SUCCESS) {
                //die("publish\n");
            }
        } else {
            LOG("no MQTT topic associated\n");
        }
    }

private:
    void subscribePorts() {
        for (std::vector<Port>::iterator it = options.info.inports.begin() ; it != options.info.inports.end(); ++it) {
            const Port &port = *it;
            const char *pattern = port.topic.c_str();
            mosquitto_subscribe(this->connection, NULL, pattern, 0);
            LOG("subscribed inport to MQTT topic: %s\n", pattern);
        }
        for (std::vector<Port>::iterator it = options.info.outports.begin() ; it != options.info.outports.end(); ++it) {
            const Port &port = *it;
            network->subscribeToPort(port.node, port.port, true);
            LOG("setup outport to MQTT topic: %s\n", port.topic.c_str());
        }
    }

    void sendDiscovery() {
        const std::string msgfloDiscoveryTopic = "fbp";
        publish(msgfloDiscoveryTopic, msgfloDiscoveryMessage(&options.info));
        LOG("sent MsgFlo discovery message\n");
    }

    void publish(std::string topic, std::string payload) {
        const int res = mosquitto_publish(this->connection, NULL,
                                          topic.c_str(), payload.size(), payload.c_str(), 0, false);
        if (res != MOSQ_ERR_SUCCESS) {
            //die("publish\n");
        }
    }

private:
    // mosquitto callback trampolines
    static void on_connect(struct mosquitto *m, void *udata, int res) {
        MqttMount *self = (MqttMount *)udata;
        self->onConnect(res);
    }

    static void on_message(struct mosquitto *m, void *udata,
                           const struct mosquitto_message *msg) {
        if (msg == NULL) {
            return;
        }
        MqttMount *self = (MqttMount *)udata;
        self->onMessage(msg);
    }

    static void on_subscribe(struct mosquitto *m, void *udata, int mid,
                             int qos_count, const int *granted_qos) {
        LOG("-- subscribed successfully\n");
    }

    static void on_publish(struct mosquitto *m, void *udata, int m_id) {
        LOG("-- published successfully\n");
    }

private:
    Network *network;
    MqttOptions options;
    struct mosquitto *connection;
};

bool parse_brokerurl(MqttOptions *options, const char *url) {
    if (!url) {
        return true;
    }

    int *ip = &options->brokerPort;
    char *host = options->brokerHostname;
    if (sscanf(url, "mqtt://%99[^:]:%i[^\n]", host, ip) == 2) {
        return true;
    } else if (sscanf(url, "mqtt://%99[^\n]", host) == 1) {
        return true;
    } else {
        return false;
    }
}

bool mqttParseOptions(MqttOptions *options, int argc, char **argv) {

    // defaults
    options->brokerHostname = strndup("localhost", 99);
    options->brokerPort = 1883;
    options->keepaliveSeconds = 60;
    options->clientId = NULL; // MQTT will autogenerate
    options->info.role = "micro";
    options->info.component = "MicroFloDevice";
    options->info.icon = "lightbulb-o";

    if (argc > 1) {
        options->info.role = std::string(argv[1]);
    }

    options->info.id = options->info.role + std::to_string(rand());

    char* broker = getenv("MSGFLO_BROKER");
    return parse_brokerurl(options, broker);
}
<commit_msg>msgflo: Fix discovery message to be compatible<commit_after>/* MicroFlo - Flow-Based Programming for microcontrollers
 * Copyright (c) 2016 Jon Nordby <jononor@gmail.com>
 * MicroFlo may be freely distributed under the MIT license
 */

// MQTT and MsgFlo.org support, at least for embedded Linux

#include "mosquitto.h"

#include <string>
#include <vector>

#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <err.h>

#ifdef DEBUG
#define LOG(...) do { printf(__VA_ARGS__); } while (0)
#else
#define LOG(...)
#endif

class MqttMount;

struct Port {
    MicroFlo::NodeId node;
    MicroFlo::PortId port;
    std::string topic;
    std::string name;

    Port(std::string t, MicroFlo::NodeId n, MicroFlo::PortId p, std::string na)
        : node(n)
        , port(p)
        , topic(t)
        , name(na)
    {

    }
};

std::string
toTopic(std::string role, std::string portName) {
    return "/" + role + "/" + portName;
}

struct ParticipantInfo {
    std::vector<Port> inports;
    std::vector<Port> outports;
    std::string role;
    std::string id;
    std::string component;
    std::string icon;

    ParticipantInfo *addInport(std::string name, MicroFlo::NodeId n, MicroFlo::PortId p) {
        Port port(toTopic(role, name), n, p, name);
        inports.push_back(port);
        return this;
    }
    ParticipantInfo *addOutport(std::string name, MicroFlo::NodeId n, MicroFlo::PortId p) {
        Port port(toTopic(role, name), n, p, name);
        outports.push_back(port);
        return this;
    }
};

#define JSON_ATTR_STRING(name, value) \
    +std::string("    \"") +name+std::string("\": \"") + value + std::string("\",\n")
#define JSON_ATTR_ARRAY(name, value) \
    +std::string("    \"") +name+std::string("\": [") + value + std::string("],\n")
#define JSON_ATTR_ENDNULL(name) \
    +std::string("    \"") +name+std::string("\": ") + "null" + std::string("\n")

std::string msgfloPorts(const std::vector<Port> &ports) {
    std::string str;
    // TODO: specify datatype on ports

    for (int i=0; i<(int)ports.size(); i++) {
        const Port &port = ports[i];
        str += "\n    {\n"
            JSON_ATTR_STRING("id", port.name)
            JSON_ATTR_STRING("queue", port.topic)
            JSON_ATTR_STRING("type", "any")
            JSON_ATTR_ENDNULL("_")
        + "    }";
        if (i < (int)ports.size()-1) {
            str =+ ",";
        }
    }
    return str;
}

std::string msgfloParticipantInfo(const ParticipantInfo *info) {
    // TODO: add label
    // TODO: add icon
    std::string inports = msgfloPorts(info->inports);
    std::string outports = msgfloPorts(info->outports);

    std::string msg = "{\n"
        JSON_ATTR_STRING("id", info->id)
        JSON_ATTR_STRING("role", info->role)
        JSON_ATTR_STRING("component", info->component)
        JSON_ATTR_STRING("icon", info->icon)
        JSON_ATTR_ARRAY("inports", inports)
        JSON_ATTR_ARRAY("outports", outports)
        JSON_ATTR_ENDNULL("label")
    + "}";
    return msg;
}

std::string msgfloDiscoveryMessage(const ParticipantInfo *info) {
    const std::string payload = msgfloParticipantInfo(info);

    std::string msg = "{\n"
        JSON_ATTR_STRING("protocol", "discovery")
        JSON_ATTR_STRING("command", "participant")
        JSON_ATTR_STRING("payload", payload)
        JSON_ATTR_ENDNULL("_")
    + "}";
    return msg;
}

struct MqttOptions {
    int brokerPort;
    char * brokerHostname;
    int keepaliveSeconds;
    char * clientId;
    ParticipantInfo info;
};

const Port *
findPortByTopic(const std::vector<Port> &ports, char *topic) {
    for (std::vector<Port>::const_iterator it = ports.begin() ; it != ports.end(); ++it) {
        const Port *port = &(*it);
        if (port->topic == std::string(topic)) {
            return port;
        }
    }
    return NULL;
}

const Port *
findPortByEdge(const std::vector<Port> &ports, MicroFlo::NodeId node, MicroFlo::PortId portId) {
    for (std::vector<Port>::const_iterator it = ports.begin() ; it != ports.end(); ++it) {
        const Port *port = &(*it);
        if (port->node == node && port->port == portId) {
            return port;
        }
    }
    return NULL;
}

// C++ version of the logic in commandstream dataLiteralToCommand etc
Packet decodePacket(const std::string &str) {
    // TODO: handle floats
    // TODO: handle brackets
    // MAYBE: handle hex and octal integers?
    // TODO: handle byte streams
    // TODO: handle brackets
    const long int10 = strtol(str.c_str(), NULL, 10);
    if (str == "null") {
        return Packet(); // void
    } else if (str == "true") {
        return Packet(true);
    } else if (str == "false") {
        return Packet(false);
    } else if (str == "0") {
        return Packet((long)0); // bad strtol error value..
    } else if (int10 != 0) {
        return Packet(int10);
    } else {
        return Packet(MsgInvalid);
    }
}
std::string encodePacket(const Packet &pkg) {
    switch (pkg.type()) {
    case MsgVoid:
        return "null";
    case MsgBoolean:
        return pkg.asBool() ? "true" : "false";
    case MsgInteger:
        return std::to_string(pkg.asInteger());
    case MsgByte: // TODO: handle byte streams
        return "";
    case MsgFloat: // TOOD: handle floats
        return "";

    case MsgBracketStart: // TOOD: handle brackets
    case MsgBracketEnd: // TOOD: handle brackets
        return "";

    // internal types
    case MsgMax:
    case MsgMaxDefined:
    case MsgSetup:
    case MsgTick:
    case MsgInvalid:
        return "";
    }

    return ""; // above should be exclusive but compiler complains...
}

// FIXME: write automated test
class MqttMount : public NetworkNotificationHandler {

public:
    static bool runForever(MqttMount *mount) {
        const int timeoutMs = 10;

        while(1){
            const int status = mosquitto_loop(mount->connection, timeoutMs, 1);
            if (status == MOSQ_ERR_CONN_LOST) {
                mount->connect();
            } else if (status == MOSQ_ERR_SUCCESS) {
                // Run MicroFlo network
                for (int i=0; i<20; i++) {
                    mount->network->runTick();
                }
            } else {
                LOG("mosquitto loop error: %s\n", mosquitto_strerror(status));
                return false;
            }
        }
        return true;
    }

public:
    MqttMount(Network *net, const MqttOptions &o)
        : network(net)
        , options(o)
        , connection(NULL)
    {
        network->setNotificationHandler(this);
    }

    ~MqttMount() {
        mosquitto_destroy(this->connection);
        this->connection = NULL;
        (void)mosquitto_lib_cleanup();
    }

    bool connect() {
        struct mosquitto *m = mosquitto_new(options.clientId, true, this);
        this->connection = m;

        mosquitto_connect_callback_set(m, on_connect);
        mosquitto_publish_callback_set(m, on_publish);
        mosquitto_subscribe_callback_set(m, on_subscribe);
        mosquitto_message_callback_set(m, on_message);

        const int res = mosquitto_connect(m, options.brokerHostname, options.brokerPort,
                                          options.keepaliveSeconds);
        return res == MOSQ_ERR_SUCCESS;
    }
    void disconnect() {} // TODO: implement

public:
    // Not really public, used by C trampolines
    void onConnect(int status) {
        const bool connected = status == 0;
        if (connected) {
            subscribePorts();
            sendDiscovery();
        }
    }

    void onMessage(const struct mosquitto_message *msg) {
        char *payloadStr = strndup((const char *)msg->payload, msg->payloadlen);
        const Packet pkg = decodePacket(std::string(payloadStr));
        LOG("got MQTT message on topic %s: %s", msg->topic, payloadStr);
        free(payloadStr);

        const Port *port = findPortByTopic(options.info.inports, msg->topic);
        if (port) {
            LOG("sending to %d %d \n", port->node, port->port);
            network->sendMessageTo(port->node, port->port, pkg);
        } else {
            LOG("Failed to find port for MQTT topic: %s", msg->topic);
        }
    }

    // implements NetworkNotificationHandler
    virtual void nodeAdded(Component *c, MicroFlo::NodeId parentId) {}
    virtual void nodesConnected(Component *src, MicroFlo::PortId srcPort,
                                Component *target, MicroFlo::PortId targetPort) {}
    virtual void networkStateChanged(Network::State s) {}
    virtual void emitDebug(DebugLevel level, DebugId id) {}
    virtual void debugChanged(DebugLevel level) {}
    virtual void portSubscriptionChanged(MicroFlo::NodeId nodeId, MicroFlo::PortId portId, bool enable) {}
    virtual void subgraphConnected(bool isOutput, MicroFlo::NodeId subgraphNode,
                                   MicroFlo::PortId subgraphPort, MicroFlo::NodeId childNode, MicroFlo::PortId childPort) {}

    virtual void packetSent(const Message &m, const Component *sender, MicroFlo::PortId senderPort) {
        const MicroFlo::NodeId senderId = sender->id();
        //LOG("packet sent %d\n", senderId);

        const Port * port = findPortByEdge(options.info.outports, senderId, senderPort);
        if (port) {
            const char *outTopic = port->topic.c_str();

            const std::string data = encodePacket(m.pkg);
            const int res = mosquitto_publish(this->connection, NULL, outTopic,
                                              data.size(), data.c_str(), 0, false);
            LOG("sending on MQTT topic %s: %s\n", outTopic, data.c_str());

            if (res != MOSQ_ERR_SUCCESS) {
                //die("publish\n");
            }
        } else {
            LOG("no MQTT topic associated\n");
        }
    }

private:
    void subscribePorts() {
        for (std::vector<Port>::iterator it = options.info.inports.begin() ; it != options.info.inports.end(); ++it) {
            const Port &port = *it;
            const char *pattern = port.topic.c_str();
            mosquitto_subscribe(this->connection, NULL, pattern, 0);
            LOG("subscribed inport to MQTT topic: %s\n", pattern);
        }
        for (std::vector<Port>::iterator it = options.info.outports.begin() ; it != options.info.outports.end(); ++it) {
            const Port &port = *it;
            network->subscribeToPort(port.node, port.port, true);
            LOG("setup outport to MQTT topic: %s\n", port.topic.c_str());
        }
    }

    void sendDiscovery() {
        const std::string msgfloDiscoveryTopic = "fbp";
        publish(msgfloDiscoveryTopic, msgfloDiscoveryMessage(&options.info));
        LOG("sent MsgFlo discovery message\n");
    }

    void publish(std::string topic, std::string payload) {
        const int res = mosquitto_publish(this->connection, NULL,
                                          topic.c_str(), payload.size(), payload.c_str(), 0, false);
        if (res != MOSQ_ERR_SUCCESS) {
            //die("publish\n");
        }
    }

private:
    // mosquitto callback trampolines
    static void on_connect(struct mosquitto *m, void *udata, int res) {
        MqttMount *self = (MqttMount *)udata;
        self->onConnect(res);
    }

    static void on_message(struct mosquitto *m, void *udata,
                           const struct mosquitto_message *msg) {
        if (msg == NULL) {
            return;
        }
        MqttMount *self = (MqttMount *)udata;
        self->onMessage(msg);
    }

    static void on_subscribe(struct mosquitto *m, void *udata, int mid,
                             int qos_count, const int *granted_qos) {
        LOG("-- subscribed successfully\n");
    }

    static void on_publish(struct mosquitto *m, void *udata, int m_id) {
        LOG("-- published successfully\n");
    }

private:
    Network *network;
    MqttOptions options;
    struct mosquitto *connection;
};

bool parse_brokerurl(MqttOptions *options, const char *url) {
    if (!url) {
        return true;
    }

    int *ip = &options->brokerPort;
    char *host = options->brokerHostname;
    if (sscanf(url, "mqtt://%99[^:]:%i[^\n]", host, ip) == 2) {
        return true;
    } else if (sscanf(url, "mqtt://%99[^\n]", host) == 1) {
        return true;
    } else {
        return false;
    }
}

bool mqttParseOptions(MqttOptions *options, int argc, char **argv) {

    // defaults
    options->brokerHostname = strndup("localhost", 99);
    options->brokerPort = 1883;
    options->keepaliveSeconds = 60;
    options->clientId = NULL; // MQTT will autogenerate
    options->info.role = "micro";
    options->info.component = "MicroFloDevice";
    options->info.icon = "lightbulb-o";

    if (argc > 1) {
        options->info.role = std::string(argv[1]);
    }

    options->info.id = options->info.role + std::to_string(rand());

    char* broker = getenv("MSGFLO_BROKER");
    return parse_brokerurl(options, broker);
}
<|endoftext|>


<commit_before>/*
 * Copyright 2010-2017 Branimir Karadzic. All rights reserved.
 * License: https://github.com/bkaradzic/bx#license-bsd-2-clause
 */

#ifndef BX_RNG_H_HEADER_GUARD
#	error "Must be included from bx/rng.h!"
#endif // BX_RNG_H_HEADER_GUARD

#include "bx.h"
#include "fpumath.h"
#include "uint32_t.h"

namespace bx
{
	inline RngMwc::RngMwc(uint32_t _z, uint32_t _w)
		: m_z(_z)
		, m_w(_w)
	{
	}

	inline void RngMwc::reset(uint32_t _z, uint32_t _w)
	{
		m_z = _z;
		m_w = _w;
	}

	inline uint32_t RngMwc::gen()
	{
		m_z = 36969*(m_z&65535)+(m_z>>16);
		m_w = 18000*(m_w&65535)+(m_w>>16);
		return (m_z<<16)+m_w;
	}

	inline RngFib::RngFib(uint32_t _a, uint32_t _b)
		: m_a(_a)
		, m_b(_b)
	{
	}

	inline void RngFib::reset(uint32_t _a, uint32_t _b)
	{
		m_a = _a;
		m_b = _b;
	}

	inline uint32_t RngFib::gen()
	{
		m_b = m_a+m_b;
		m_a = m_b-m_a;
		return m_a;
	}

	inline RngShr3::RngShr3(uint32_t _jsr)
		: m_jsr(_jsr)
	{
	}

	inline void RngShr3::reset(uint32_t _jsr)
	{
		m_jsr = _jsr;
	}

	inline uint32_t RngShr3::gen()
	{
		m_jsr ^= m_jsr<<17;
		m_jsr ^= m_jsr>>13;
		m_jsr ^= m_jsr<<5;
		return m_jsr;
	}

	template <typename Rng>
	inline float frnd(Rng* _rng)
	{
		uint32_t rnd = _rng->gen() & UINT16_MAX;
		return float(rnd) * 1.0f/float(UINT16_MAX);
	}

	template <typename Rng>
	inline float frndh(Rng* _rng)
	{
		return 2.0f * bx::frnd(_rng) - 1.0f;
	}

	template <typename Rng>
	inline void randUnitCircle(float _result[3], Rng* _rng)
	{
		const float angle = frnd(_rng) * pi * 2.0f;

		_result[0] = fcos(angle);
		_result[1] = 0.0f;
		_result[2] = fsin(angle);
	}

	template <typename Rng>
	inline void randUnitSphere(float _result[3], Rng* _rng)
	{
		const float rand0  = frnd(_rng) * 2.0f - 1.0f;
		const float rand1  = frnd(_rng) * pi * 2.0f;
		const float sqrtf1 = fsqrt(1.0f - rand0*rand0);

		_result[0] = sqrtf1 * fcos(rand1);
		_result[1] = sqrtf1 * fsin(rand1);
		_result[2] = rand0;
	}

	template <typename Ty>
	inline void randUnitHemisphere(float _result[3], Ty* _rng, const float _normal[3])
	{
		float dir[3];
		randUnitSphere(dir, _rng);

		float DdotN = dir[0]*_normal[0]
					+ dir[1]*_normal[1]
					+ dir[2]*_normal[2]
					;

		if (0.0f > DdotN)
		{
			dir[0] = -dir[0];
			dir[1] = -dir[1];
			dir[2] = -dir[2];
		}

		_result[0] = dir[0];
		_result[1] = dir[1];
		_result[2] = dir[2];
	}

	inline void generateSphereHammersley(void* _data, uint32_t _stride, uint32_t _num, float _scale)
	{
		uint8_t* data = (uint8_t*)_data;

		for (uint32_t ii = 0; ii < _num; ii++)
		{
			float tt = 0.0f;
			float pp = 0.5;
			for (uint32_t jj = ii; jj; jj >>= 1)
			{
				tt += (jj & 1) ? pp : 0.0f;
				pp *= 0.5f;
			}

			tt = 2.0f * tt - 1.0f;

			const float phi    = (ii + 0.5f) / _num;
			const float phirad =  phi * 2.0f * pi;
			const float st     = fsqrt(1.0f-tt*tt) * _scale;

			float* xyz = (float*)data;
			data += _stride;

			xyz[0] = st * fcos(phirad);
			xyz[1] = st * fsin(phirad);
			xyz[2] = tt * _scale;
		}
	}

	template<typename Rng, typename Ty>
	inline void shuffle(Rng* _rng, Ty* _array, uint32_t _num)
	{
		BX_CHECK(_num != 0, "Number of elements can't be 0!");

		for (uint32_t ii = 0, num = _num-1; ii < num; ++ii)
		{
			uint32_t jj = ii + 1 + _rng->gen() % (num - ii);
			bx::xchg(_array[ii], _array[jj]);
		}
	}

} // namespace bx
<commit_msg>Cleanup.<commit_after>/*
 * Copyright 2010-2017 Branimir Karadzic. All rights reserved.
 * License: https://github.com/bkaradzic/bx#license-bsd-2-clause
 */

#ifndef BX_RNG_H_HEADER_GUARD
#	error "Must be included from bx/rng.h!"
#endif // BX_RNG_H_HEADER_GUARD

namespace bx
{
	inline RngMwc::RngMwc(uint32_t _z, uint32_t _w)
		: m_z(_z)
		, m_w(_w)
	{
	}

	inline void RngMwc::reset(uint32_t _z, uint32_t _w)
	{
		m_z = _z;
		m_w = _w;
	}

	inline uint32_t RngMwc::gen()
	{
		m_z = 36969*(m_z&65535)+(m_z>>16);
		m_w = 18000*(m_w&65535)+(m_w>>16);
		return (m_z<<16)+m_w;
	}

	inline RngFib::RngFib(uint32_t _a, uint32_t _b)
		: m_a(_a)
		, m_b(_b)
	{
	}

	inline void RngFib::reset(uint32_t _a, uint32_t _b)
	{
		m_a = _a;
		m_b = _b;
	}

	inline uint32_t RngFib::gen()
	{
		m_b = m_a+m_b;
		m_a = m_b-m_a;
		return m_a;
	}

	inline RngShr3::RngShr3(uint32_t _jsr)
		: m_jsr(_jsr)
	{
	}

	inline void RngShr3::reset(uint32_t _jsr)
	{
		m_jsr = _jsr;
	}

	inline uint32_t RngShr3::gen()
	{
		m_jsr ^= m_jsr<<17;
		m_jsr ^= m_jsr>>13;
		m_jsr ^= m_jsr<<5;
		return m_jsr;
	}

	template <typename Rng>
	inline float frnd(Rng* _rng)
	{
		uint32_t rnd = _rng->gen() & UINT16_MAX;
		return float(rnd) * 1.0f/float(UINT16_MAX);
	}

	template <typename Rng>
	inline float frndh(Rng* _rng)
	{
		return 2.0f * bx::frnd(_rng) - 1.0f;
	}

	template <typename Rng>
	inline void randUnitCircle(float _result[3], Rng* _rng)
	{
		const float angle = frnd(_rng) * pi * 2.0f;

		_result[0] = fcos(angle);
		_result[1] = 0.0f;
		_result[2] = fsin(angle);
	}

	template <typename Rng>
	inline void randUnitSphere(float _result[3], Rng* _rng)
	{
		const float rand0  = frnd(_rng) * 2.0f - 1.0f;
		const float rand1  = frnd(_rng) * pi * 2.0f;
		const float sqrtf1 = fsqrt(1.0f - rand0*rand0);

		_result[0] = sqrtf1 * fcos(rand1);
		_result[1] = sqrtf1 * fsin(rand1);
		_result[2] = rand0;
	}

	template <typename Ty>
	inline void randUnitHemisphere(float _result[3], Ty* _rng, const float _normal[3])
	{
		float dir[3];
		randUnitSphere(dir, _rng);

		float DdotN = dir[0]*_normal[0]
					+ dir[1]*_normal[1]
					+ dir[2]*_normal[2]
					;

		if (0.0f > DdotN)
		{
			dir[0] = -dir[0];
			dir[1] = -dir[1];
			dir[2] = -dir[2];
		}

		_result[0] = dir[0];
		_result[1] = dir[1];
		_result[2] = dir[2];
	}

	inline void generateSphereHammersley(void* _data, uint32_t _stride, uint32_t _num, float _scale)
	{
		uint8_t* data = (uint8_t*)_data;

		for (uint32_t ii = 0; ii < _num; ii++)
		{
			float tt = 0.0f;
			float pp = 0.5;
			for (uint32_t jj = ii; jj; jj >>= 1)
			{
				tt += (jj & 1) ? pp : 0.0f;
				pp *= 0.5f;
			}

			tt = 2.0f * tt - 1.0f;

			const float phi    = (ii + 0.5f) / _num;
			const float phirad =  phi * 2.0f * pi;
			const float st     = fsqrt(1.0f-tt*tt) * _scale;

			float* xyz = (float*)data;
			data += _stride;

			xyz[0] = st * fcos(phirad);
			xyz[1] = st * fsin(phirad);
			xyz[2] = tt * _scale;
		}
	}

	template<typename Rng, typename Ty>
	inline void shuffle(Rng* _rng, Ty* _array, uint32_t _num)
	{
		BX_CHECK(_num != 0, "Number of elements can't be 0!");

		for (uint32_t ii = 0, num = _num-1; ii < num; ++ii)
		{
			uint32_t jj = ii + 1 + _rng->gen() % (num - ii);
			bx::xchg(_array[ii], _array[jj]);
		}
	}

} // namespace bx
<|endoftext|>