<commit_before>/** \copyright
 * Copyright (c) 2015, Balazs Racz
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file CpuLoad.hxx
 * Class for maintining a CPU load indication.
 *
 * @author Balazs Racz
 * @date 30 August 2015
 */

#ifndef _OS_CPULOAD_HXX_
#define _OS_CPULOAD_HXX_

#include <algorithm>

#include "executor/StateFlow.hxx"
#include "utils/SimpleQueue.hxx"
#include "utils/Singleton.hxx"
#include "utils/StringPrintf.hxx"

extern "C" {
    /// Call this function repeatedly from a hardware timer to feed the CPUload
    /// class.
    /// @param irq should be zero if parent is the main context, otherwise the
    /// irq number.
    void cpuload_tick(unsigned irq);
}

/// Singleton class that records the CPU load under FreeRTOS.
///
/// Usage:
///
/// . create a single (global) instance of this class.
///
/// . repeatedly call cpuload_tick() from a hardware timer. A rate of 100 Hz is
/// usually fine.
///
/// . retrieve CPU load when desired from the object of this class or via
///   Singleton<CpuLoad>::instance().
class CpuLoad : public Singleton<CpuLoad> {
public:
    CpuLoad() {}

    ~CpuLoad()
    {
        while (!perKeyCost_.empty())
        {
            delete perKeyCost_.pop_front();
        }
    }

    /// @returns the CPU load as an integer between 0 and 100. The load is
    /// averaged over the past short amount of time.
    uint8_t get_load();

    /// Return the maximum consecutive count that busy was measured, clipped to
    /// 255.
    uint8_t get_max_consecutive() {
        return maxConsecutive_;
    }

    /// Reset the maximum consecutive busy count.
    void clear_max_consecutive() {
        maxConsecutive_ = 0;
    }

    /// Returns the peak count over 16 ticks.
    uint8_t get_peak_over_16_counts() {
        return peakOver16Counts_;
    }

    /// Reset the peak count over 16 ticks.
    void clear_peak_over_16_counts() {
        peakOver16Counts_ = 0;
    }

    /// @return 0 if we have not found a new key recently, otherwise the value
    /// of the new key found. Clears the new key.
    uintptr_t new_key()
    {
        auto k = newKey_;
        newKey_ = 0;
        return k;
    }

    /// Defines how to print a given key. Must be called from the main executor.
    void set_key_description(uintptr_t key, string description)
    {
        auto it = perKeyCost_.begin();
        for (; it != perKeyCost_.end(); ++it)
        {
            if (it->key == key)
            {
                it->description = std::move(description);
                return;
            }
        }
        auto *kk = new KeyInfo;
        kk->key = key;
        kk->description = std::move(description);
        perKeyCost_.insert(it, kk);
    }

    /// Returns delta usage since last call by utilization key.
    /// @param output will be populated with data, utilization (number of ticks
    /// in this key since last invocation).
    uint32_t get_utilization_delta(
        std::vector<pair<unsigned, string *>> *output)
    {
        HASSERT(output);
        output->clear();
        for (auto it = perKeyCost_.begin(); it != perKeyCost_.end(); ++it)
        {
            volatile unsigned curr = it->rolling_count;
            unsigned diff = curr - it->last_count;
            it->last_count = curr;
            if (diff > 0)
            {
                output->emplace_back(diff, &it->description);
            }
        }
        uint32_t ret = countSinceUpdate_;
        countSinceUpdate_ = 0;
        return ret;
    }

private:
    friend void cpuload_tick(unsigned);
    /// Adds a value to the rolling average.
    /// @param busy false when idle
    /// @param key 0 if CPU is idle at this time, otherwise a key on what is
    /// taking time.
    inline void record_value(bool busy, uintptr_t key);

    /// Internal state for the rolling average (EWMA). This is a 0+24bit fixed
    /// point format, the top 8 bits are always 0 to allow overflow-less
    /// multiplication. 0x01000000 would be 1.0, 0x00ffffff is 0.99999...
    uint32_t avg_{0};

    /// Number of ticks we've seen since last updating the key info.
    uint32_t countSinceUpdate_{0};

    /// Temporary buffer that the interrupt can write unknown keys to.
    uintptr_t newKey_{0};

    struct KeyInfo : public QMember
    {
        /// Which cost key this entry belongs to
        uintptr_t key;
        /// textual description on how to print the cost key
        string description;
        /// what is the last printed cost offset
        uint32_t last_count{0};
        /// rolling count of cost offsets updated by the interrupt
        uint32_t rolling_count{0};
    };

    /// Collects cost information on a per-key basis.
    TypedQueue<KeyInfo> perKeyCost_;

    /// Streak of busy ticks.
    uint8_t consecutive_{0};
    /// Longest streak we've seen.
    uint8_t maxConsecutive_{0};
    /// Rolling window of the last 16 counts.
    uint16_t last16Bits_{0};
    /// Largest value we've seen of how busy we were over 16 counts.
    uint8_t peakOver16Counts_{0};
};

class CpuLoadLog : public StateFlowBase
{
public:
    CpuLoadLog(Service *service)
        : StateFlowBase(service)
    {
        start_flow(STATE(log_and_wait));
    }

private:
    Action log_and_wait()
    {
        auto *l = CpuLoad::instance();
        LOG(INFO, "Load: avg %3d max streak %d max of 16 %d", l->get_load(),
            l->get_max_consecutive(), l->get_peak_over_16_counts());
        l->clear_max_consecutive();
        l->clear_peak_over_16_counts();
        vector<pair<unsigned, string *>> per_task_ticks;
        unsigned c = l->get_utilization_delta(&per_task_ticks);
        std::sort(
            per_task_ticks.begin(), per_task_ticks.end(), std::greater<>());
        string details;
        for (volatile auto it : per_task_ticks)
        {
            int perc = it.first * 1000 / c;
            details += StringPrintf(" | %d.%d:", perc / 10, perc % 10);
            details += *it.second;
        }
        log_output((char *)details.data(), details.size());
        auto k = l->new_key();
        if (k > 300)
        {
            char *name = pcTaskGetName((TaskHandle_t)k);
            l->set_key_description(k, name);
        }
        else
        {
            l->set_key_description(k, StringPrintf("irq-%u", k));
        }
        return sleep_and_call(&timer_, MSEC_TO_NSEC(2000), STATE(log_and_wait));
    }

    StateFlowTimer timer_{this};
};

#endif // _OS_CPULOAD_HXX_
<commit_msg>Fixes a declaration so it will work with older GCC versions.<commit_after>/** \copyright
 * Copyright (c) 2015, Balazs Racz
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file CpuLoad.hxx
 * Class for maintining a CPU load indication.
 *
 * @author Balazs Racz
 * @date 30 August 2015
 */

#ifndef _OS_CPULOAD_HXX_
#define _OS_CPULOAD_HXX_

#include <algorithm>

#include "executor/StateFlow.hxx"
#include "utils/SimpleQueue.hxx"
#include "utils/Singleton.hxx"
#include "utils/StringPrintf.hxx"

extern "C" {
    /// Call this function repeatedly from a hardware timer to feed the CPUload
    /// class.
    /// @param irq should be zero if parent is the main context, otherwise the
    /// irq number.
    void cpuload_tick(unsigned irq);
}

/// Singleton class that records the CPU load under FreeRTOS.
///
/// Usage:
///
/// . create a single (global) instance of this class.
///
/// . repeatedly call cpuload_tick() from a hardware timer. A rate of 100 Hz is
/// usually fine.
///
/// . retrieve CPU load when desired from the object of this class or via
///   Singleton<CpuLoad>::instance().
class CpuLoad : public Singleton<CpuLoad> {
public:
    CpuLoad() {}

    ~CpuLoad()
    {
        while (!perKeyCost_.empty())
        {
            delete perKeyCost_.pop_front();
        }
    }

    /// @returns the CPU load as an integer between 0 and 100. The load is
    /// averaged over the past short amount of time.
    uint8_t get_load();

    /// Return the maximum consecutive count that busy was measured, clipped to
    /// 255.
    uint8_t get_max_consecutive() {
        return maxConsecutive_;
    }

    /// Reset the maximum consecutive busy count.
    void clear_max_consecutive() {
        maxConsecutive_ = 0;
    }

    /// Returns the peak count over 16 ticks.
    uint8_t get_peak_over_16_counts() {
        return peakOver16Counts_;
    }

    /// Reset the peak count over 16 ticks.
    void clear_peak_over_16_counts() {
        peakOver16Counts_ = 0;
    }

    /// @return 0 if we have not found a new key recently, otherwise the value
    /// of the new key found. Clears the new key.
    uintptr_t new_key()
    {
        auto k = newKey_;
        newKey_ = 0;
        return k;
    }

    /// Defines how to print a given key. Must be called from the main executor.
    void set_key_description(uintptr_t key, string description)
    {
        auto it = perKeyCost_.begin();
        for (; it != perKeyCost_.end(); ++it)
        {
            if (it->key == key)
            {
                it->description = std::move(description);
                return;
            }
        }
        auto *kk = new KeyInfo;
        kk->key = key;
        kk->description = std::move(description);
        perKeyCost_.insert(it, kk);
    }

    /// Returns delta usage since last call by utilization key.
    /// @param output will be populated with data, utilization (number of ticks
    /// in this key since last invocation).
    uint32_t get_utilization_delta(
        std::vector<pair<unsigned, string *>> *output)
    {
        HASSERT(output);
        output->clear();
        for (auto it = perKeyCost_.begin(); it != perKeyCost_.end(); ++it)
        {
            volatile unsigned curr = it->rolling_count;
            unsigned diff = curr - it->last_count;
            it->last_count = curr;
            if (diff > 0)
            {
                output->emplace_back(diff, &it->description);
            }
        }
        uint32_t ret = countSinceUpdate_;
        countSinceUpdate_ = 0;
        return ret;
    }

private:
    friend void cpuload_tick(unsigned);
    /// Adds a value to the rolling average.
    /// @param busy false when idle
    /// @param key 0 if CPU is idle at this time, otherwise a key on what is
    /// taking time.
    inline void record_value(bool busy, uintptr_t key);

    /// Internal state for the rolling average (EWMA). This is a 0+24bit fixed
    /// point format, the top 8 bits are always 0 to allow overflow-less
    /// multiplication. 0x01000000 would be 1.0, 0x00ffffff is 0.99999...
    uint32_t avg_{0};

    /// Number of ticks we've seen since last updating the key info.
    uint32_t countSinceUpdate_{0};

    /// Temporary buffer that the interrupt can write unknown keys to.
    uintptr_t newKey_{0};

    struct KeyInfo : public QMember
    {
        /// Which cost key this entry belongs to
        uintptr_t key;
        /// textual description on how to print the cost key
        string description;
        /// what is the last printed cost offset
        uint32_t last_count{0};
        /// rolling count of cost offsets updated by the interrupt
        uint32_t rolling_count{0};
    };

    /// Collects cost information on a per-key basis.
    TypedQueue<KeyInfo> perKeyCost_;

    /// Streak of busy ticks.
    uint8_t consecutive_{0};
    /// Longest streak we've seen.
    uint8_t maxConsecutive_{0};
    /// Rolling window of the last 16 counts.
    uint16_t last16Bits_{0};
    /// Largest value we've seen of how busy we were over 16 counts.
    uint8_t peakOver16Counts_{0};
};

class CpuLoadLog : public StateFlowBase
{
public:
    CpuLoadLog(Service *service)
        : StateFlowBase(service)
    {
        start_flow(STATE(log_and_wait));
    }

private:
    Action log_and_wait()
    {
        auto *l = CpuLoad::instance();
        LOG(INFO, "Load: avg %3d max streak %d max of 16 %d", l->get_load(),
            l->get_max_consecutive(), l->get_peak_over_16_counts());
        l->clear_max_consecutive();
        l->clear_peak_over_16_counts();
        vector<pair<unsigned, string *>> per_task_ticks;
        unsigned c = l->get_utilization_delta(&per_task_ticks);
        std::sort(per_task_ticks.begin(), per_task_ticks.end(),
                  std::greater<decltype(per_task_ticks)::value_type>());
        string details;
        for (volatile auto it : per_task_ticks)
        {
            int perc = it.first * 1000 / c;
            details += StringPrintf(" | %d.%d:", perc / 10, perc % 10);
            details += *it.second;
        }
        log_output((char *)details.data(), details.size());
        auto k = l->new_key();
        if (k > 300)
        {
            char *name = pcTaskGetName((TaskHandle_t)k);
            l->set_key_description(k, name);
        }
        else
        {
            l->set_key_description(k, StringPrintf("irq-%u", k));
        }
        return sleep_and_call(&timer_, MSEC_TO_NSEC(2000), STATE(log_and_wait));
    }

    StateFlowTimer timer_{this};
};

#endif // _OS_CPULOAD_HXX_
<|endoftext|>


<commit_before>#include "poi_symbol_shape.hpp"

#include "../drape/texture_set_holder.hpp"
#include "../drape/attribute_provider.hpp"
#include "../drape/glstate.hpp"
#include "../drape/batcher.hpp"

#include "../drape/shader_def.hpp"

namespace df
{

PoiSymbolShape::PoiSymbolShape(m2::PointD const & mercatorPt, PoiSymbolViewParams const & params)
  : m_pt(mercatorPt)
  , m_params(params)
{
}

void PoiSymbolShape::Draw(RefPointer<Batcher> batcher, RefPointer<TextureSetHolder> textures) const
{
  TextureSetHolder::SymbolRegion region;
  textures->GetSymbolRegion(m_params.m_symbolName, region);

  GLState state(gpu::TEXTURING_PROGRAM, GLState::OverlayLayer);
  state.SetTextureSet(region.GetTextureNode().m_textureSet);

  state.SetBlending(Blending(true));

  m2::PointU pixelSize;
  region.GetPixelSize(pixelSize);
  m2::PointF halfSize(pixelSize.x / 2.0, pixelSize.y / 2.0);
  m2::RectF const & texRect = region.GetTexRect();
  float depth = m_params.m_depth;
  float texture = (float)region.GetTextureNode().m_textureOffset;

  float stream[]     =
  //   x       y       z      normal.x    normal.y        s              t          textureIndex
  { m_pt.x, m_pt.y, depth, -halfSize.x,  halfSize.y, texRect.minX(), texRect.maxY(), texture,
    m_pt.x, m_pt.y, depth, -halfSize.x, -halfSize.y, texRect.minX(), texRect.minY(), texture,
    m_pt.x, m_pt.y, depth,  halfSize.x,  halfSize.y, texRect.maxX(), texRect.maxY(), texture,
    m_pt.x, m_pt.y, depth,  halfSize.x, -halfSize.y, texRect.maxX(), texRect.minY(), texture};

  AttributeProvider provider(1, 4);
  BindingInfo info(3);

  BindingDecl & posDecl = info.GetBindingDecl(0);
  posDecl.m_attributeName = "a_position";
  posDecl.m_componentCount = 3;
  posDecl.m_componentType = gl_const::GLFloatType;
  posDecl.m_offset = 0;
  posDecl.m_stride = 8 * sizeof(float);

  BindingDecl & normalDecl = info.GetBindingDecl(1);
  normalDecl.m_attributeName = "a_normal";
  normalDecl.m_componentCount = 2;
  normalDecl.m_componentType = gl_const::GLFloatType;
  normalDecl.m_offset = 3 * sizeof(float);
  normalDecl.m_stride = 8 * sizeof(float);

  BindingDecl & texDecl = info.GetBindingDecl(2);
  texDecl.m_attributeName = "a_texCoords";
  texDecl.m_componentCount = 2;
  texDecl.m_componentType = gl_const::GLFloatType;
  texDecl.m_offset = (3 + 2) * sizeof(float);
  texDecl.m_stride = 8 * sizeof(float);

  OverlayHandle * handle = new SquareHandle(m_params.m_id,
                                           dp::Center,
                                           m_pt,
                                           pixelSize,
                                           m_params.m_depth);

  provider.InitStream(0, info, MakeStackRefPointer<void>(stream));
  batcher->InsertTriangleStrip(state, MakeStackRefPointer(&provider), MovePointer(handle));
}

} // namespace df
<commit_msg>rewritten to use the same shader like a symbol by path use<commit_after>#include "poi_symbol_shape.hpp"

#include "../drape/texture_set_holder.hpp"
#include "../drape/attribute_provider.hpp"
#include "../drape/glstate.hpp"
#include "../drape/batcher.hpp"

#include "../drape/shader_def.hpp"

namespace df
{

PoiSymbolShape::PoiSymbolShape(m2::PointD const & mercatorPt, PoiSymbolViewParams const & params)
  : m_pt(mercatorPt)
  , m_params(params)
{
}

void PoiSymbolShape::Draw(RefPointer<Batcher> batcher, RefPointer<TextureSetHolder> textures) const
{
  TextureSetHolder::SymbolRegion region;
  textures->GetSymbolRegion(m_params.m_symbolName, region);

  GLState state(gpu::TEXTURING_PROGRAM, GLState::OverlayLayer);
  state.SetTextureSet(region.GetTextureNode().m_textureSet);

  state.SetBlending(Blending(true));

  m2::PointU pixelSize;
  region.GetPixelSize(pixelSize);
  m2::PointF halfSize(pixelSize.x / 2.0, pixelSize.y / 2.0);
  m2::RectF const & texRect = region.GetTexRect();
  float depth = m_params.m_depth;
  float texture = (float)region.GetTextureNode().m_textureOffset;

  float positions[] =
  { m_pt.x, m_pt.y,
    m_pt.x, m_pt.y,
    m_pt.x, m_pt.y,
    m_pt.x, m_pt.y};

  float normals[] =
  { -halfSize.x,  halfSize.y,
    -halfSize.x, -halfSize.y,
    halfSize.x,  halfSize.y,
    halfSize.x, -halfSize.y};

  float uvs[] =
  { texRect.minX(), texRect.maxY(), texture, depth,
    texRect.minX(), texRect.minY(), texture, depth,
    texRect.maxX(), texRect.maxY(), texture, depth,
    texRect.maxX(), texRect.minY(), texture, depth};

  AttributeProvider provider(3, 4);
  {
    BindingInfo position(1, 1);
    BindingDecl & decl = position.GetBindingDecl(0);
    decl.m_attributeName = "a_position";
    decl.m_componentCount = 2;
    decl.m_componentType = gl_const::GLFloatType;
    decl.m_offset = 0;
    decl.m_stride = 0;
    provider.InitStream(0, position, MakeStackRefPointer<void>(positions));
  }
  {
    BindingInfo normal(1);
    BindingDecl & decl = normal.GetBindingDecl(0);
    decl.m_attributeName = "a_normal";
    decl.m_componentCount = 2;
    decl.m_componentType = gl_const::GLFloatType;
    decl.m_offset = 0;
    decl.m_stride = 0;
    provider.InitStream(1, normal, MakeStackRefPointer<void>(normals));
  }
  {
    BindingInfo texcoord(1);
    BindingDecl & decl = texcoord.GetBindingDecl(0);
    decl.m_attributeName = "a_texCoords";
    decl.m_componentCount = 4;
    decl.m_componentType = gl_const::GLFloatType;
    decl.m_offset = 0;
    decl.m_stride = 0;
    provider.InitStream(2, texcoord, MakeStackRefPointer<void>(uvs));
  }

  OverlayHandle * handle = new SquareHandle(m_params.m_id,
                                           dp::Center,
                                           m_pt,
                                           pixelSize,
                                           m_params.m_depth);

  batcher->InsertTriangleStrip(state, MakeStackRefPointer(&provider), MovePointer(handle));
}

} // namespace df
<|endoftext|>


<commit_before>/*
   Copyright (C) 2017 Alexandr Akulich <akulichalexander@gmail.com>

   This file is a part of TelegramQt library.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

 */

#include "Debug_p.hpp"

#include "IgnoredMessageNotification.hpp"
#include "MTProto/MessageHeader.hpp"
#include "TelegramNamespace.hpp"
#include "telegramqt_macros.h"

static const QByteArray c_spaces = QByteArray(40, ' ');
static constexpr int c_maxBytesPrintedLength = 42;

const char *getSpaces(int count)
{
    int position = c_spaces.count() - count;
    if (position < 0) {
        position = 0;
    }
    return c_spaces.constData() + position;
}

namespace Telegram {

namespace Debug {

int Spacer::m_spacing = 0;

Spacer::Spacer()
{
    m_spacing += m_step;
}

Spacer::~Spacer()
{
    m_spacing -= m_step;
}

const char *Spacer::innerSpaces()
{
    if (!m_hasInnerCalls) {
        m_hasInnerCalls = true;
    }
    return getSpaces(m_spacing);
}

const char *Spacer::outerSpaces()
{
    if (m_hasInnerCalls) {
        return getSpaces(m_spacing - m_step);
    } else {
        return " ";
    }
}

QByteArray printBytes(const QByteArray &bytes)
{
    if (bytes.size() > c_maxBytesPrintedLength) {
        static QString bytesTemplateString = QLatin1String("..(%1 bytes in total)..");
        static const int contextLength = c_maxBytesPrintedLength - bytesTemplateString.length();

        return bytes.left(contextLength).toHex()
                + bytesTemplateString.arg(bytes.size()).toLatin1()
                + bytes.right(contextLength).toHex();
    }
    return bytes.toHex();
}

} // Debug namespace

} // Telegram namespace

QDebug operator<<(QDebug d, const TLValue &v)
{
    d << v.toString();
    return d;
}

QDebug operator<<(QDebug d, const Telegram::Peer &peer)
{
    switch (peer.type()) {
    case Telegram::Peer::User:
        d << "Telegram::Peer(User, " << peer.id() << ")";
        break;
    case Telegram::Peer::Chat:
        d << "Telegram::Peer(Chat, " << peer.id() << ")";
        break;
    case Telegram::Peer::Channel:
        d << "Telegram::Peer(Channel, " << peer.id() << ")";
        break;
    default:
        d << "Telegram::Peer(Invalid:" << peer.type() << ", " << peer.id() << ")";
        break;
    }
    return d;
}

template <int Size>
QDebug operator<<(QDebug d, const TLNumber<Size> &n)
{
    d << QByteArray::fromRawData(n.data, n.size()).toHex();
    return d;
}

template QDebug operator<<(QDebug d, const TLNumber<128> &n);
template QDebug operator<<(QDebug d, const TLNumber<256> &n);

// Extra debug methods
QDebug operator<<(QDebug d, const Telegram::MTProto::FullMessageHeader &messageHeader)
{
    Telegram::Debug::Spacer spacer;
    d.noquote().nospace();
    d << TELEGRAMQT_HEX_SHOWBASE;
    d << "MessageHeader {";
    d << spacer.innerSpaces() << "salt: " << messageHeader.serverSalt << TELEGRAMQT_ENDL;
    d << spacer.innerSpaces() << "sessionId: " << messageHeader.sessionId << TELEGRAMQT_ENDL;
    d << spacer.innerSpaces() << "messageId: " << messageHeader.messageId << TELEGRAMQT_ENDL;
    d << spacer.innerSpaces() << "sequenceNumber: " << messageHeader.sequenceNumber << TELEGRAMQT_ENDL;
    d << spacer.innerSpaces() << "contentLength: " << messageHeader.contentLength << TELEGRAMQT_ENDL;
    d << "}";
    return d;
}

QDebug operator<<(QDebug d, const Telegram::MTProto::IgnoredMessageNotification &notification)
{
    Telegram::Debug::Spacer spacer;
    d.noquote().nospace();
    d << TELEGRAMQT_HEX_SHOWBASE;
    d << "IgnoredMessageNotification {";
    d << spacer.innerSpaces() << "messageId: " << notification.messageId << TELEGRAMQT_ENDL;
    d << spacer.innerSpaces() << "seqNo:" << notification.seqNo << TELEGRAMQT_ENDL;
    d << spacer.innerSpaces() << "errorCode: " << notification.errorCode << TELEGRAMQT_ENDL;
    d << "}";
    return d;
}
<commit_msg>Debug: Make the operator<<(Peer) output shorter<commit_after>/*
   Copyright (C) 2017 Alexandr Akulich <akulichalexander@gmail.com>

   This file is a part of TelegramQt library.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

 */

#include "Debug_p.hpp"

#include "IgnoredMessageNotification.hpp"
#include "MTProto/MessageHeader.hpp"
#include "TelegramNamespace.hpp"
#include "telegramqt_macros.h"

static const QByteArray c_spaces = QByteArray(40, ' ');
static constexpr int c_maxBytesPrintedLength = 42;

const char *getSpaces(int count)
{
    int position = c_spaces.count() - count;
    if (position < 0) {
        position = 0;
    }
    return c_spaces.constData() + position;
}

namespace Telegram {

namespace Debug {

int Spacer::m_spacing = 0;

Spacer::Spacer()
{
    m_spacing += m_step;
}

Spacer::~Spacer()
{
    m_spacing -= m_step;
}

const char *Spacer::innerSpaces()
{
    if (!m_hasInnerCalls) {
        m_hasInnerCalls = true;
    }
    return getSpaces(m_spacing);
}

const char *Spacer::outerSpaces()
{
    if (m_hasInnerCalls) {
        return getSpaces(m_spacing - m_step);
    } else {
        return " ";
    }
}

QByteArray printBytes(const QByteArray &bytes)
{
    if (bytes.size() > c_maxBytesPrintedLength) {
        static QString bytesTemplateString = QLatin1String("..(%1 bytes in total)..");
        static const int contextLength = c_maxBytesPrintedLength - bytesTemplateString.length();

        return bytes.left(contextLength).toHex()
                + bytesTemplateString.arg(bytes.size()).toLatin1()
                + bytes.right(contextLength).toHex();
    }
    return bytes.toHex();
}

} // Debug namespace

} // Telegram namespace

QDebug operator<<(QDebug d, const TLValue &v)
{
    d << v.toString();
    return d;
}

QDebug operator<<(QDebug d, const Telegram::Peer &peer)
{
    QDebugStateSaver saver(d);
    d.nospace();
    switch (peer.type()) {
    case Telegram::Peer::User:
        d << "Peer(User, " << peer.id() << ")";
        break;
    case Telegram::Peer::Chat:
        d << "Peer(Chat, " << peer.id() << ")";
        break;
    case Telegram::Peer::Channel:
        d << "Peer(Channel, " << peer.id() << ")";
        break;
    default:
        d << "Peer(Invalid:" << peer.type() << ", " << peer.id() << ")";
        break;
    }
    return d;
}

template <int Size>
QDebug operator<<(QDebug d, const TLNumber<Size> &n)
{
    d << QByteArray::fromRawData(n.data, n.size()).toHex();
    return d;
}

template QDebug operator<<(QDebug d, const TLNumber<128> &n);
template QDebug operator<<(QDebug d, const TLNumber<256> &n);

// Extra debug methods
QDebug operator<<(QDebug d, const Telegram::MTProto::FullMessageHeader &messageHeader)
{
    Telegram::Debug::Spacer spacer;
    d.noquote().nospace();
    d << TELEGRAMQT_HEX_SHOWBASE;
    d << "MessageHeader {";
    d << spacer.innerSpaces() << "salt: " << messageHeader.serverSalt << TELEGRAMQT_ENDL;
    d << spacer.innerSpaces() << "sessionId: " << messageHeader.sessionId << TELEGRAMQT_ENDL;
    d << spacer.innerSpaces() << "messageId: " << messageHeader.messageId << TELEGRAMQT_ENDL;
    d << spacer.innerSpaces() << "sequenceNumber: " << messageHeader.sequenceNumber << TELEGRAMQT_ENDL;
    d << spacer.innerSpaces() << "contentLength: " << messageHeader.contentLength << TELEGRAMQT_ENDL;
    d << "}";
    return d;
}

QDebug operator<<(QDebug d, const Telegram::MTProto::IgnoredMessageNotification &notification)
{
    Telegram::Debug::Spacer spacer;
    d.noquote().nospace();
    d << TELEGRAMQT_HEX_SHOWBASE;
    d << "IgnoredMessageNotification {";
    d << spacer.innerSpaces() << "messageId: " << notification.messageId << TELEGRAMQT_ENDL;
    d << spacer.innerSpaces() << "seqNo:" << notification.seqNo << TELEGRAMQT_ENDL;
    d << spacer.innerSpaces() << "errorCode: " << notification.errorCode << TELEGRAMQT_ENDL;
    d << "}";
    return d;
}
<|endoftext|>


<commit_before>#include "BindIndexInfo.h"
#include "Voxelization.h"
#include "Object.h"
#include "ResourceManager.h"
#include "EngineShaderFactory.hpp"

#include "MeshCamera.h"

using namespace Resource;
using namespace Core;
using namespace Math;
using namespace Rendering::Buffer;
using namespace Rendering::GI;
using namespace Rendering::Texture;
using namespace Rendering::Camera;
using namespace Rendering::Shader;
using namespace Rendering::Manager;
using namespace Rendering::View;
using namespace GPGPU::DirectCompute;

Voxelization::Voxelization()
:	_clearVoxelMapCS(nullptr),
	_voxelAlbedoMapAtlas(nullptr), _voxelNormalMapAtlas(nullptr), _voxelEmissionMapAtlas(nullptr)
{
}

Voxelization::~Voxelization()
{
	Destroy();

	SAFE_DELETE(_voxelAlbedoMapAtlas);
	SAFE_DELETE(_voxelNormalMapAtlas);
	SAFE_DELETE(_voxelEmissionMapAtlas);

	for(auto iter = _constBuffers.begin(); iter != _constBuffers.end(); ++iter)
		SAFE_DELETE(*iter);
	_constBuffers.clear();

	SAFE_DELETE(_clearVoxelMapCS);
}

void Voxelization::Initialize(const GlobalInfo& globalInfo)
{
	uint maxNumOfCascade = globalInfo.maxNumOfCascade;
	ASSERT_COND_MSG(maxNumOfCascade != 0, "Error, voxelization cascade num is zero.");

	auto Log2 = [](float v) -> float
	{
		return log(v) / log(2.0f);
	};

	uint dimension = 1 << globalInfo.voxelDimensionPow2;

	_voxelAlbedoMapAtlas = new AnisotropicVoxelMapAtlas;
	_voxelAlbedoMapAtlas->Initialize(dimension, maxNumOfCascade, DXGI_FORMAT_R8G8B8A8_TYPELESS, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R32_UINT, 1);

	_voxelNormalMapAtlas = new AnisotropicVoxelMapAtlas;
	_voxelNormalMapAtlas->Initialize(dimension, maxNumOfCascade, DXGI_FORMAT_R32_TYPELESS, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_UINT, 1);

	_voxelEmissionMapAtlas = new AnisotropicVoxelMapAtlas;
	_voxelEmissionMapAtlas->Initialize(dimension, maxNumOfCascade, DXGI_FORMAT_R8G8B8A8_TYPELESS, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R32_UINT, 1);

	// Setting Const Buffers
	{
		for(uint i=0; i<maxNumOfCascade; ++i)
		{
			ConstBuffer* infoConstBuffer = new ConstBuffer;
			infoConstBuffer->Initialize(sizeof(InfoCBData));

			_constBuffers.push_back(infoConstBuffer);
		}

//		_renderInputCBContainer.push_back( ShaderForm::InputConstBuffer(uint(ConstBufferBindIndex::Voxelization_InfoCB), nullptr, false, true, false, true) );
	}

	InitializeClearVoxelMap(dimension, maxNumOfCascade);
}

void Voxelization::InitializeClearVoxelMap(uint dimension, uint maxNumOfCascade)
{
	std::string filePath = "";
	{
		Factory::EngineFactory pathFind(nullptr);
		pathFind.FetchShaderFullPath(filePath, "ClearVoxelMaps");

		ASSERT_COND_MSG(filePath.empty() == false, "Error, File path is empty");
	}

	ShaderManager* shaderMgr = ResourceManager::SharedInstance()->GetShaderManager();
	ID3DBlob* blob = shaderMgr->CreateBlob(filePath, "cs", "ClearVoxelMapCS", false, nullptr);

	ComputeShader::ThreadGroup threadGroup;
	{
		auto ComputeThreadGroupSideLength = [](uint sideLength)
		{
			return (uint)((float)(sideLength + 8 - 1) / 8.0f);
		};

		threadGroup.x = ComputeThreadGroupSideLength(dimension * (uint)AnisotropicVoxelMapAtlas::Direction::Num);
		threadGroup.y = ComputeThreadGroupSideLength(dimension * maxNumOfCascade);
		threadGroup.z = ComputeThreadGroupSideLength(dimension);
	}

	_clearVoxelMapCS = new ComputeShader(threadGroup, blob);
	ASSERT_COND_MSG(_clearVoxelMapCS->Initialize(), "Error, Can't Init ClearVoxelMapCS");

	std::vector<ShaderForm::InputUnorderedAccessView> uavs;
	{
		ShaderForm::InputUnorderedAccessView uav;
		uav.bindIndex	= (uint)UAVBindIndex::VoxelMap_Albedo;
		uav.uav			= _voxelAlbedoMapAtlas->GetSourceMapUAV();
		uavs.push_back(uav);

		uav.bindIndex	= (uint)UAVBindIndex::VoxelMap_Normal;
		uav.uav			= _voxelNormalMapAtlas->GetSourceMapUAV();
		uavs.push_back(uav);

		uav.bindIndex	= (uint)UAVBindIndex::VoxelMap_Emission;
		uav.uav			= _voxelEmissionMapAtlas->GetSourceMapUAV();
		uavs.push_back(uav);
	}

	_clearVoxelMapCS->SetUAVs(uavs);
}

void Voxelization::Destroy()
{
	_voxelAlbedoMapAtlas->Destory();
	_voxelNormalMapAtlas->Destory();
	_voxelEmissionMapAtlas->Destory();

	for(auto iter = _constBuffers.begin(); iter != _constBuffers.end(); ++iter)
		(*iter)->Destory();

	_constBuffers.clear();
}

void Voxelization::ClearZeroVoxelMap(const Device::DirectX*& dx)
{
	_clearVoxelMapCS->Dispatch(dx->GetContext());
}

void Voxelization::Voxelize(const Device::DirectX*& dx,
							const MeshCamera*& camera, const RenderManager*& renderManager,
							const GlobalInfo& globalInfo,
							bool onlyStaticMesh)
{
	Math::Matrix camWorldMat;
	{
		const Transform* camTf = camera->GetOwner()->GetTransform();
		camTf->FetchWorldMatrix(camWorldMat);
	}

	if(onlyStaticMesh)
	{
		Matrix viewMat;
		CameraForm::GetViewMatrix(viewMat, camWorldMat);

		bool isDifferentViewMat = memcmp(&_prevStaticMeshVoxelizeViewMat, &viewMat, sizeof(Matrix)) != 0;
		if(isDifferentViewMat == false)
			return;

		_prevStaticMeshVoxelizeViewMat = viewMat;
	}

	ClearZeroVoxelMap(dx);
	Vector3 camWorldPos(camWorldMat._41, camWorldMat._42, camWorldMat._43);

	ID3D11DeviceContext* context = dx->GetContext();

	context->RSSetState(dx->GetRasterizerStateCWDisableCullingWithClip());
	context->OMSetDepthStencilState(dx->GetDepthStateLessEqual(), 0x00);

	float blendFactor[4] = {0.0f, 0.0f, 0.0f, 0.0f};
	context->OMSetBlendState(dx->GetBlendStateOpaque(), blendFactor, 0xffffffff);

	float dimension = float(1 << globalInfo.voxelDimensionPow2);

	D3D11_VIEWPORT viewport;
	{
		viewport.TopLeftX	= 0.0f;
		viewport.TopLeftY	= 0.0f;
		viewport.MinDepth	= 0.0f;
		viewport.MaxDepth	= 1.0f;
		viewport.Width		= dimension;
		viewport.Height		= dimension;
	}
	context->RSSetViewports(1, &viewport);

	ID3D11SamplerState* samplerState = dx->GetSamplerStateAnisotropic();
	context->PSSetSamplers((uint)SamplerStateBindIndex::DefaultSamplerState, 1, &samplerState);

	ID3D11UnorderedAccessView* uavs [] =
	{
		_voxelAlbedoMapAtlas->GetSourceMapUAV()->GetView(),
		_voxelNormalMapAtlas->GetSourceMapUAV()->GetView(),
		_voxelEmissionMapAtlas->GetSourceMapUAV()->GetView()
	};

	float clearColor[4] = {0.0f, 0.0f, 0.0f, 0.0f};
	context->ClearRenderTargetView(_voxelAlbedoMapAtlas->GetRenderTargetView(), clearColor);
	context->ClearRenderTargetView(_voxelNormalMapAtlas->GetRenderTargetView(), clearColor);
	context->ClearRenderTargetView(_voxelEmissionMapAtlas->GetRenderTargetView(), clearColor);

	context->OMSetRenderTargetsAndUnorderedAccessViews(0, nullptr, nullptr, 0, ARRAYSIZE(uavs), uavs, nullptr);

	uint maxCascade = globalInfo.maxNumOfCascade;
	for(uint currentCascade=0; currentCascade<maxCascade; ++currentCascade)
	{
		UpdateConstBuffer(dx, currentCascade, camWorldPos, globalInfo, dimension);

		// Render Voxel
		{
			ID3D11Buffer* buf = _constBuffers[currentCascade]->GetBuffer();
			context->GSSetConstantBuffers(uint(ConstBufferBindIndex::Voxelization_InfoCB), 1, &buf);
			context->PSSetConstantBuffers(uint(ConstBufferBindIndex::Voxelization_InfoCB), 1, &buf);

			const auto& opaqueMeshes = renderManager->GetOpaqueMeshes();
			MeshCamera::RenderMeshesUsingSortedMeshVectorByVB(dx, renderManager, opaqueMeshes, RenderType::Voxelization, nullptr);

			const auto& alphaTestMeshes = renderManager->GetAlphaTestMeshes();
			MeshCamera::RenderMeshesUsingSortedMeshVectorByVB(dx, renderManager, alphaTestMeshes, RenderType::Voxelization, nullptr);
		}
	}

	ID3D11Buffer* nullBuff = nullptr;
	context->GSSetConstantBuffers(uint(ConstBufferBindIndex::Voxelization_InfoCB), 1, &nullBuff);
	context->PSSetConstantBuffers(uint(ConstBufferBindIndex::Voxelization_InfoCB), 1, &nullBuff);

	ID3D11UnorderedAccessView* nullUAVs[] = {nullptr, nullptr, nullptr};
	context->OMSetRenderTargetsAndUnorderedAccessViews(0, nullptr, nullptr, 0, ARRAYSIZE(nullUAVs), nullUAVs, nullptr);

	ID3D11SamplerState* nullSampler = nullptr;
	context->PSSetSamplers((uint)SamplerStateBindIndex::DefaultSamplerState, 1, &nullSampler);

	context->RSSetState(nullptr);
}

void Voxelization::UpdateConstBuffer(const Device::DirectX*& dx, uint currentCascade,
									 const Vector3& camWorldPos, const GlobalInfo& globalInfo, float dimension)
{
	// Compute Voxelize Bound
	float worldSize;
	Vector3 bbMin, bbMax, bbMid;
	ComputeBound(&bbMin, &bbMid, &bbMax, &worldSize, currentCascade, camWorldPos, globalInfo.initWorldSize);

	InfoCBData currentVoxelizeInfo;
	currentVoxelizeInfo.currentCascade	= currentCascade;
	currentVoxelizeInfo.voxelizeMinPos	= bbMin;

	// Update View Proj ConstBuffer
	{
		Matrix voxelView;
		ComputeVoxelViewMatrix(voxelView, currentCascade, camWorldPos, globalInfo.initWorldSize);

		Matrix projMat;
		Matrix::OrthoLH(projMat, 2.0f, 2.0f, 1.0f, -1.0f);

		currentVoxelizeInfo.voxelView		= voxelView;
		currentVoxelizeInfo.voxelViewProj	= voxelView * projMat;
	}

	_constBuffers[currentCascade]->UpdateSubResource(dx->GetContext(), &currentVoxelizeInfo);
}

void Voxelization::ComputeVoxelViewMatrix(	Math::Matrix& outMat, uint currentCascade,
											const Math::Vector3& camWorldPos, float initVoxelizeSize)
{
	memset(&outMat, 0, sizeof(Math::Matrix));

	float worldSize = 0.0f;
	Vector3 centerPos;
	ComputeBound(nullptr, &centerPos, nullptr, &worldSize, currentCascade, camWorldPos, initVoxelizeSize);

	ASSERT_COND_MSG(worldSize != 0.0f, "Error, Voxelize Size is zero");

	outMat._11 = 2.0f / worldSize;
	outMat._22 = 2.0f / worldSize;
	outMat._33 = 2.0f / worldSize;

	outMat._41 = camWorldPos.x;
	outMat._42 = camWorldPos.y;
	outMat._43 = camWorldPos.z;
	outMat._44 = 1.0f;
}

void Voxelization::ComputeBound(
	Vector3* outMin, Vector3* outMid, Vector3* outMax, float* outWorldSize,
	uint currentCascade, const Vector3& camWorldPos, float initVoxelizeSize)
{
	float worldSize		= initVoxelizeSize * ( (float)( (currentCascade + 1) * (currentCascade + 1) ) );
	float halfWorldSize	= worldSize / 2.0f;

	Vector3 bbMin = camWorldPos - Vector3(halfWorldSize, halfWorldSize, halfWorldSize);
	Vector3 bbMax = bbMin + Vector3(worldSize, worldSize, worldSize);
	Vector3 bbMid = (bbMin + bbMax) / 2.0f;

	if(outMin)			(*outMin) = bbMin;
	if(outMid)			(*outMid) = bbMid;
	if(outMax)			(*outMax) = bbMax;
	if(outWorldSize)	(*outWorldSize) = worldSize;
}<commit_msg>Voxelization -   USE_ANISOTROPIC_VOXELIZATION 처리 추가<commit_after>#include "BindIndexInfo.h"
#include "Voxelization.h"
#include "Object.h"
#include "ResourceManager.h"
#include "EngineShaderFactory.hpp"

#include "MeshCamera.h"

using namespace Resource;
using namespace Core;
using namespace Math;
using namespace Rendering::Buffer;
using namespace Rendering::GI;
using namespace Rendering::Texture;
using namespace Rendering::Camera;
using namespace Rendering::Shader;
using namespace Rendering::Manager;
using namespace Rendering::View;
using namespace GPGPU::DirectCompute;

Voxelization::Voxelization()
:	_clearVoxelMapCS(nullptr),
	_voxelAlbedoMapAtlas(nullptr), _voxelNormalMapAtlas(nullptr), _voxelEmissionMapAtlas(nullptr)
{
}

Voxelization::~Voxelization()
{
	Destroy();

	SAFE_DELETE(_voxelAlbedoMapAtlas);
	SAFE_DELETE(_voxelNormalMapAtlas);
	SAFE_DELETE(_voxelEmissionMapAtlas);

	for(auto iter = _constBuffers.begin(); iter != _constBuffers.end(); ++iter)
		SAFE_DELETE(*iter);
	_constBuffers.clear();

	SAFE_DELETE(_clearVoxelMapCS);
}

void Voxelization::Initialize(const GlobalInfo& globalInfo)
{
	uint maxNumOfCascade = globalInfo.maxNumOfCascade;
	ASSERT_COND_MSG(maxNumOfCascade != 0, "Error, voxelization cascade num is zero.");

	auto Log2 = [](float v) -> float
	{
		return log(v) / log(2.0f);
	};

#if defined(USE_ANISOTROPIC_VOXELIZATION)
	bool isAnisotropic = true;
#else
	bool isAnisotropic = false;
#endif

	uint dimension = 1 << globalInfo.voxelDimensionPow2;

	_voxelAlbedoMapAtlas = new AnisotropicVoxelMapAtlas;
	_voxelAlbedoMapAtlas->Initialize(dimension, maxNumOfCascade, DXGI_FORMAT_R8G8B8A8_TYPELESS, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R32_UINT, 1, isAnisotropic);

	_voxelNormalMapAtlas = new AnisotropicVoxelMapAtlas;
	_voxelNormalMapAtlas->Initialize(dimension, maxNumOfCascade, DXGI_FORMAT_R32_TYPELESS, DXGI_FORMAT_R32_FLOAT, DXGI_FORMAT_R32_UINT, 1, isAnisotropic);

	_voxelEmissionMapAtlas = new AnisotropicVoxelMapAtlas;
	_voxelEmissionMapAtlas->Initialize(dimension, maxNumOfCascade, DXGI_FORMAT_R8G8B8A8_TYPELESS, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R32_UINT, 1, isAnisotropic);

	// Setting Const Buffers
	{
		for(uint i=0; i<maxNumOfCascade; ++i)
		{
			ConstBuffer* infoConstBuffer = new ConstBuffer;
			infoConstBuffer->Initialize(sizeof(InfoCBData));

			_constBuffers.push_back(infoConstBuffer);
		}

//		_renderInputCBContainer.push_back( ShaderForm::InputConstBuffer(uint(ConstBufferBindIndex::Voxelization_InfoCB), nullptr, false, true, false, true) );
	}

	InitializeClearVoxelMap(dimension, maxNumOfCascade);
}

void Voxelization::InitializeClearVoxelMap(uint dimension, uint maxNumOfCascade)
{
	std::string filePath = "";
	{
		Factory::EngineFactory pathFind(nullptr);
		pathFind.FetchShaderFullPath(filePath, "ClearVoxelMaps");

		ASSERT_COND_MSG(filePath.empty() == false, "Error, File path is empty");
	}

	ShaderManager* shaderMgr = ResourceManager::SharedInstance()->GetShaderManager();
	ID3DBlob* blob = shaderMgr->CreateBlob(filePath, "cs", "ClearVoxelMapCS", false, nullptr);

	ComputeShader::ThreadGroup threadGroup;
	{
		auto ComputeThreadGroupSideLength = [](uint sideLength)
		{
			return (uint)((float)(sideLength + 8 - 1) / 8.0f);
		};
#if defined(USE_ANISOTROPIC_VOXELIZATION)
		threadGroup.x = ComputeThreadGroupSideLength(dimension) * (uint)AnisotropicVoxelMapAtlas::Direction::Num;
#else
		threadGroup.x = ComputeThreadGroupSideLength(dimension);
#endif
		threadGroup.y = ComputeThreadGroupSideLength(dimension * maxNumOfCascade);
		threadGroup.z = ComputeThreadGroupSideLength(dimension);
	}

	_clearVoxelMapCS = new ComputeShader(threadGroup, blob);
	ASSERT_COND_MSG(_clearVoxelMapCS->Initialize(), "Error, Can't Init ClearVoxelMapCS");

	std::vector<ShaderForm::InputUnorderedAccessView> uavs;
	{
		ShaderForm::InputUnorderedAccessView uav;
		uav.bindIndex	= (uint)UAVBindIndex::VoxelMap_Albedo;
		uav.uav			= _voxelAlbedoMapAtlas->GetSourceMapUAV();
		uavs.push_back(uav);

		uav.bindIndex	= (uint)UAVBindIndex::VoxelMap_Normal;
		uav.uav			= _voxelNormalMapAtlas->GetSourceMapUAV();
		uavs.push_back(uav);

		uav.bindIndex	= (uint)UAVBindIndex::VoxelMap_Emission;
		uav.uav			= _voxelEmissionMapAtlas->GetSourceMapUAV();
		uavs.push_back(uav);
	}

	_clearVoxelMapCS->SetUAVs(uavs);
}

void Voxelization::Destroy()
{
	_voxelAlbedoMapAtlas->Destory();
	_voxelNormalMapAtlas->Destory();
	_voxelEmissionMapAtlas->Destory();

	for(auto iter = _constBuffers.begin(); iter != _constBuffers.end(); ++iter)
		(*iter)->Destory();

	_constBuffers.clear();
}

void Voxelization::ClearZeroVoxelMap(const Device::DirectX*& dx)
{
	_clearVoxelMapCS->Dispatch(dx->GetContext());
}

void Voxelization::Voxelize(const Device::DirectX*& dx,
							const MeshCamera*& camera, const RenderManager*& renderManager,
							const GlobalInfo& globalInfo,
							bool onlyStaticMesh)
{
	Math::Matrix camWorldMat;
	{
		const Transform* camTf = camera->GetOwner()->GetTransform();
		camTf->FetchWorldMatrix(camWorldMat);
	}

	if(onlyStaticMesh)
	{
		Matrix viewMat;
		CameraForm::GetViewMatrix(viewMat, camWorldMat);

		bool isDifferentViewMat = memcmp(&_prevStaticMeshVoxelizeViewMat, &viewMat, sizeof(Matrix)) != 0;
		if(isDifferentViewMat == false)
			return;

		_prevStaticMeshVoxelizeViewMat = viewMat;
	}

	ClearZeroVoxelMap(dx);
	Vector3 camWorldPos(camWorldMat._41, camWorldMat._42, camWorldMat._43);

	ID3D11DeviceContext* context = dx->GetContext();

	context->RSSetState(dx->GetRasterizerStateCWDisableCullingWithClip());
	context->OMSetDepthStencilState(dx->GetDepthStateLessEqual(), 0x00);

	float blendFactor[4] = {0.0f, 0.0f, 0.0f, 0.0f};
	context->OMSetBlendState(dx->GetBlendStateOpaque(), blendFactor, 0xffffffff);

	float dimension = float(1 << globalInfo.voxelDimensionPow2);

	D3D11_VIEWPORT viewport;
	{
		viewport.TopLeftX	= 0.0f;
		viewport.TopLeftY	= 0.0f;
		viewport.MinDepth	= 0.0f;
		viewport.MaxDepth	= 1.0f;
		viewport.Width		= dimension;
		viewport.Height		= dimension;
	}
	context->RSSetViewports(1, &viewport);

	ID3D11SamplerState* samplerState = dx->GetSamplerStateAnisotropic();
	context->PSSetSamplers((uint)SamplerStateBindIndex::DefaultSamplerState, 1, &samplerState);

	ID3D11UnorderedAccessView* uavs [] =
	{
		_voxelAlbedoMapAtlas->GetSourceMapUAV()->GetView(),
		_voxelNormalMapAtlas->GetSourceMapUAV()->GetView(),
		_voxelEmissionMapAtlas->GetSourceMapUAV()->GetView()
	};

	float clearColor[4] = {0.0f, 0.0f, 0.0f, 0.0f};
	context->ClearRenderTargetView(_voxelAlbedoMapAtlas->GetRenderTargetView(), clearColor);
	context->ClearRenderTargetView(_voxelNormalMapAtlas->GetRenderTargetView(), clearColor);
	context->ClearRenderTargetView(_voxelEmissionMapAtlas->GetRenderTargetView(), clearColor);

	context->OMSetRenderTargetsAndUnorderedAccessViews(0, nullptr, nullptr, 0, ARRAYSIZE(uavs), uavs, nullptr);

	uint maxCascade = globalInfo.maxNumOfCascade;
	for(uint currentCascade=0; currentCascade<maxCascade; ++currentCascade)
	{
		UpdateConstBuffer(dx, currentCascade, camWorldPos, globalInfo, dimension);

		// Render Voxel
		{
			ID3D11Buffer* buf = _constBuffers[currentCascade]->GetBuffer();
			context->GSSetConstantBuffers(uint(ConstBufferBindIndex::Voxelization_InfoCB), 1, &buf);
			context->PSSetConstantBuffers(uint(ConstBufferBindIndex::Voxelization_InfoCB), 1, &buf);

			const auto& opaqueMeshes = renderManager->GetOpaqueMeshes();
			MeshCamera::RenderMeshesUsingSortedMeshVectorByVB(dx, renderManager, opaqueMeshes, RenderType::Voxelization, nullptr);

			const auto& alphaTestMeshes = renderManager->GetAlphaTestMeshes();
			MeshCamera::RenderMeshesUsingSortedMeshVectorByVB(dx, renderManager, alphaTestMeshes, RenderType::Voxelization, nullptr);
		}
	}

	ID3D11Buffer* nullBuff = nullptr;
	context->GSSetConstantBuffers(uint(ConstBufferBindIndex::Voxelization_InfoCB), 1, &nullBuff);
	context->PSSetConstantBuffers(uint(ConstBufferBindIndex::Voxelization_InfoCB), 1, &nullBuff);

	ID3D11UnorderedAccessView* nullUAVs[] = {nullptr, nullptr, nullptr};
	context->OMSetRenderTargetsAndUnorderedAccessViews(0, nullptr, nullptr, 0, ARRAYSIZE(nullUAVs), nullUAVs, nullptr);

	ID3D11SamplerState* nullSampler = nullptr;
	context->PSSetSamplers((uint)SamplerStateBindIndex::DefaultSamplerState, 1, &nullSampler);

	context->RSSetState(nullptr);
}

void Voxelization::UpdateConstBuffer(const Device::DirectX*& dx, uint currentCascade,
									 const Vector3& camWorldPos, const GlobalInfo& globalInfo, float dimension)
{
	// Compute Voxelize Bound
	float worldSize;
	Vector3 bbMin, bbMax, bbMid;
	ComputeBound(&bbMin, &bbMid, &bbMax, &worldSize, currentCascade, camWorldPos, globalInfo.initWorldSize);

	InfoCBData currentVoxelizeInfo;
	currentVoxelizeInfo.currentCascade	= currentCascade;
	currentVoxelizeInfo.voxelizeMinPos	= bbMin;

	// Update View Proj ConstBuffer
	{
		Matrix voxelView;
		ComputeVoxelViewMatrix(voxelView, currentCascade, camWorldPos, globalInfo.initWorldSize);

		Matrix projMat;
		Matrix::OrthoLH(projMat, 2.0f, 2.0f, 1.0f, -1.0f);

		currentVoxelizeInfo.voxelView		= voxelView;
		currentVoxelizeInfo.voxelViewProj	= voxelView * projMat;
	}

	_constBuffers[currentCascade]->UpdateSubResource(dx->GetContext(), &currentVoxelizeInfo);
}

void Voxelization::ComputeVoxelViewMatrix(	Math::Matrix& outMat, uint currentCascade,
											const Math::Vector3& camWorldPos, float initVoxelizeSize)
{
	memset(&outMat, 0, sizeof(Math::Matrix));

	float worldSize = 0.0f;
	Vector3 centerPos;
	ComputeBound(nullptr, &centerPos, nullptr, &worldSize, currentCascade, camWorldPos, initVoxelizeSize);

	ASSERT_COND_MSG(worldSize != 0.0f, "Error, Voxelize Size is zero");

	outMat._11 = 2.0f / worldSize;
	outMat._22 = 2.0f / worldSize;
	outMat._33 = 2.0f / worldSize;

	outMat._41 = camWorldPos.x;
	outMat._42 = camWorldPos.y;
	outMat._43 = camWorldPos.z;
	outMat._44 = 1.0f;
}

void Voxelization::ComputeBound(
	Vector3* outMin, Vector3* outMid, Vector3* outMax, float* outWorldSize,
	uint currentCascade, const Vector3& camWorldPos, float initVoxelizeSize)
{
	float worldSize		= initVoxelizeSize * ( (float)( (currentCascade + 1) * (currentCascade + 1) ) );
	float halfWorldSize	= worldSize / 2.0f;

	Vector3 bbMin = camWorldPos - Vector3(halfWorldSize, halfWorldSize, halfWorldSize);
	Vector3 bbMax = bbMin + Vector3(worldSize, worldSize, worldSize);
	Vector3 bbMid = (bbMin + bbMax) / 2.0f;

	if(outMin)			(*outMin) = bbMin;
	if(outMid)			(*outMid) = bbMid;
	if(outMax)			(*outMax) = bbMax;
	if(outWorldSize)	(*outWorldSize) = worldSize;
}<|endoftext|>


<commit_before>/*
This file is part of Bohrium and copyright (c) 2012 the Bohrium
team <http://www.bh107.org>.

Bohrium is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3
of the License, or (at your option) any later version.

Bohrium is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the
GNU Lesser General Public License along with Bohrium.

If not, see <http://www.gnu.org/licenses/>.
*/

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <cassert>
#include <stdexcept>
#include <set>
#include <iostream>
#include <sigsegv.h>
#include <bh_mem_signal.h>
#include <bh_util.hpp>

using namespace std;

static pthread_mutex_t signal_mutex = PTHREAD_MUTEX_INITIALIZER;
static bool initialized = false;
static bool mem_warn = false;

struct Segment {
    //Start address of this memory segment
    const void *addr;
    //Size of memory segment in bytes
    uint64_t size;
    //Id to identify the memory segment when executing the callback function.
    const void *idx;
    //The callback function to call
    bh_mem_signal_callback_t callback;
    //sigsegv ticket of a registered memory range
    void *ticket;

    //Some constructors
    Segment() {};

    Segment(const void *addr) : addr(addr), size(1), idx(nullptr), callback(nullptr), ticket(nullptr) {};

    Segment(const void *addr, uint64_t size) : addr(addr), size(size), idx(nullptr),
                                               callback(nullptr), ticket(nullptr) {};

    Segment(const void *addr, uint64_t size, const void *idx) : addr(addr), size(size), idx(idx),
                                                                callback(nullptr), ticket(nullptr) {};

    void add_callback_and_ticket(bh_mem_signal_callback_t callback, void *ticket) {
        this->callback = callback;
        this->ticket = ticket;
    }

    bool operator<(const Segment &other) const {
        const uint64_t a_begin = (uint64_t) addr;
        const uint64_t b_begin = (uint64_t) other.addr;
        const uint64_t a_end = a_begin + size - 1;
        const uint64_t b_end = b_begin + other.size - 1;

        //When the two segments overlaps we return false such that
        //overlapping segments are identical in a set
        if ((a_begin <= b_end) and (a_end >= b_begin)) {
            return false;
        } else { //Else we simple compare the begin address
            return a_begin < b_begin;
        }
    }

    //Read begin and end memory address
    const void *addr_begin() const {
        return addr;
    }

    const void *addr_end() const {
        return (const void *) (((uint64_t) addr + size));
    }
};

//Pretty print of Segment
ostream &operator<<(ostream &out, const Segment &segment) {
    out << segment.idx << "{addr: " << segment.addr_begin() << " - "
        << segment.addr_end() << ", ticket: " << segment.ticket << "}";
    return out;
}

ostream &operator<<(ostream &out, const set<Segment> &segments) {
    out << "bh_mem_signal contains: " << endl;
    for (const Segment &seg: segments) {
        out << seg << endl;
    }
    return out;
}

// sigsegv boilerplate
static sigsegv_dispatcher dispatcher;
static int handler(void *fault_address, int serious) {
    // We only handle serious faults and not potential faults such as stack overflows
    if (serious == 1) {
        return sigsegv_dispatch(&dispatcher, fault_address);
    } else {
        return 0;
    }
}

// All registered memory segments
// NB: never insert overlapping memory segments into this set
static set<Segment> segments;

void bh_mem_signal_init(void) {
    mem_warn = getenv("BH_MEM_WARN") != nullptr;

    pthread_mutex_lock(&signal_mutex);
    if (!initialized) {
        sigsegv_init (&dispatcher);
        if (sigsegv_install_handler(&handler) == -1) {
            throw runtime_error("System cannot catch SIGSEGV");
        }
    }
    initialized = true;
    pthread_mutex_unlock(&signal_mutex);
}

void bh_mem_signal_shutdown(void) {
    pthread_mutex_lock(&signal_mutex);
    if (not segments.empty()) {
        if (mem_warn) {
            cout << "MEM_WARN: bh_mem_signal_shutdown() - not all attached memory segments are detached!" << endl;
            bh_mem_signal_pprint_db();
        }
    }
    if (initialized) {
        sigsegv_deinstall_handler();
    }
    pthread_mutex_unlock(&signal_mutex);
}

void bh_mem_signal_attach(void *idx, void *addr, uint64_t size, bh_mem_signal_callback_t callback) {
    pthread_mutex_lock(&signal_mutex);

    // Create new memory segment that we will attach
    Segment segment(addr, size, idx);

    // Let's check for double attachments
    if (util::exist(segments, segment)) {
        auto conflict = segments.find(Segment(addr, size));
        stringstream ss;
        ss << "mem_signal: Could not attach signal, memory segment (" \
           << segment.addr_begin() << " to " << segment.addr_end() \
           << ") is in conflict with already attached memory segment (" \
           << conflict->addr_begin() << " to " << conflict->addr_end() << ")" << endl;
        pthread_mutex_unlock(&signal_mutex);
        throw runtime_error(ss.str());
    }
    assert(((size_t) addr) % SIGSEGV_FAULT_ADDRESS_ALIGNMENT == 0);
    assert(size % SIGSEGV_FAULT_ADDRESS_ALIGNMENT == 0);
    // Let's register it in sigsegv and save it in the segment
    void *ticket = sigsegv_register(&dispatcher, addr, size, callback, idx);
    segment.add_callback_and_ticket(callback, ticket);

    // Finally, let's insert the new segment
    segments.insert(segment);
    pthread_mutex_unlock(&signal_mutex);
}

void bh_mem_signal_detach(const void *addr) {
    pthread_mutex_lock(&signal_mutex);
    auto it = segments.find(addr);
    if (it != segments.end()) {
        assert(it->ticket != nullptr);
        sigsegv_unregister(&dispatcher, it->ticket);
        segments.erase(it);
    }
    pthread_mutex_unlock(&signal_mutex);
}

int bh_mem_signal_exist(const void *addr) {
    int ret;
    pthread_mutex_lock(&signal_mutex);
    ret = util::exist(segments, addr);
    pthread_mutex_unlock(&signal_mutex);
    return ret;
}

void bh_mem_signal_pprint_db(void) {
    cout << segments << endl;
}
<commit_msg>older version does have SIGSEGV_FAULT_ADDRESS_ALIGNMENT<commit_after>/*
This file is part of Bohrium and copyright (c) 2012 the Bohrium
team <http://www.bh107.org>.

Bohrium is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3
of the License, or (at your option) any later version.

Bohrium is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the
GNU Lesser General Public License along with Bohrium.

If not, see <http://www.gnu.org/licenses/>.
*/

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <cassert>
#include <stdexcept>
#include <set>
#include <iostream>
#include <sigsegv.h>
#include <bh_mem_signal.h>
#include <bh_util.hpp>

using namespace std;

static pthread_mutex_t signal_mutex = PTHREAD_MUTEX_INITIALIZER;
static bool initialized = false;
static bool mem_warn = false;

struct Segment {
    //Start address of this memory segment
    const void *addr;
    //Size of memory segment in bytes
    uint64_t size;
    //Id to identify the memory segment when executing the callback function.
    const void *idx;
    //The callback function to call
    bh_mem_signal_callback_t callback;
    //sigsegv ticket of a registered memory range
    void *ticket;

    //Some constructors
    Segment() {};

    Segment(const void *addr) : addr(addr), size(1), idx(nullptr), callback(nullptr), ticket(nullptr) {};

    Segment(const void *addr, uint64_t size) : addr(addr), size(size), idx(nullptr),
                                               callback(nullptr), ticket(nullptr) {};

    Segment(const void *addr, uint64_t size, const void *idx) : addr(addr), size(size), idx(idx),
                                                                callback(nullptr), ticket(nullptr) {};

    void add_callback_and_ticket(bh_mem_signal_callback_t callback, void *ticket) {
        this->callback = callback;
        this->ticket = ticket;
    }

    bool operator<(const Segment &other) const {
        const uint64_t a_begin = (uint64_t) addr;
        const uint64_t b_begin = (uint64_t) other.addr;
        const uint64_t a_end = a_begin + size - 1;
        const uint64_t b_end = b_begin + other.size - 1;

        //When the two segments overlaps we return false such that
        //overlapping segments are identical in a set
        if ((a_begin <= b_end) and (a_end >= b_begin)) {
            return false;
        } else { //Else we simple compare the begin address
            return a_begin < b_begin;
        }
    }

    //Read begin and end memory address
    const void *addr_begin() const {
        return addr;
    }

    const void *addr_end() const {
        return (const void *) (((uint64_t) addr + size));
    }
};

//Pretty print of Segment
ostream &operator<<(ostream &out, const Segment &segment) {
    out << segment.idx << "{addr: " << segment.addr_begin() << " - "
        << segment.addr_end() << ", ticket: " << segment.ticket << "}";
    return out;
}

ostream &operator<<(ostream &out, const set<Segment> &segments) {
    out << "bh_mem_signal contains: " << endl;
    for (const Segment &seg: segments) {
        out << seg << endl;
    }
    return out;
}

// sigsegv boilerplate
static sigsegv_dispatcher dispatcher;
static int handler(void *fault_address, int serious) {
    // We only handle serious faults and not potential faults such as stack overflows
    if (serious == 1) {
        return sigsegv_dispatch(&dispatcher, fault_address);
    } else {
        return 0;
    }
}

// All registered memory segments
// NB: never insert overlapping memory segments into this set
static set<Segment> segments;

void bh_mem_signal_init(void) {
    mem_warn = getenv("BH_MEM_WARN") != nullptr;

    pthread_mutex_lock(&signal_mutex);
    if (!initialized) {
        sigsegv_init (&dispatcher);
        if (sigsegv_install_handler(&handler) == -1) {
            throw runtime_error("System cannot catch SIGSEGV");
        }
    }
    initialized = true;
    pthread_mutex_unlock(&signal_mutex);
}

void bh_mem_signal_shutdown(void) {
    pthread_mutex_lock(&signal_mutex);
    if (not segments.empty()) {
        if (mem_warn) {
            cout << "MEM_WARN: bh_mem_signal_shutdown() - not all attached memory segments are detached!" << endl;
            bh_mem_signal_pprint_db();
        }
    }
    if (initialized) {
        sigsegv_deinstall_handler();
    }
    pthread_mutex_unlock(&signal_mutex);
}

void bh_mem_signal_attach(void *idx, void *addr, uint64_t size, bh_mem_signal_callback_t callback) {
    pthread_mutex_lock(&signal_mutex);

    // Create new memory segment that we will attach
    Segment segment(addr, size, idx);

    // Let's check for double attachments
    if (util::exist(segments, segment)) {
        auto conflict = segments.find(Segment(addr, size));
        stringstream ss;
        ss << "mem_signal: Could not attach signal, memory segment (" \
           << segment.addr_begin() << " to " << segment.addr_end() \
           << ") is in conflict with already attached memory segment (" \
           << conflict->addr_begin() << " to " << conflict->addr_end() << ")" << endl;
        pthread_mutex_unlock(&signal_mutex);
        throw runtime_error(ss.str());
    }
#ifdef SIGSEGV_FAULT_ADDRESS_ALIGNMENT // SIGSEGV_FAULT_ADDRESS_ALIGNMENT isn't defined in older versions
    assert(((size_t) addr) % SIGSEGV_FAULT_ADDRESS_ALIGNMENT == 0);
    assert(size % SIGSEGV_FAULT_ADDRESS_ALIGNMENT == 0);
#endif
    // Let's register it in sigsegv and save it in the segment
    void *ticket = sigsegv_register(&dispatcher, addr, size, callback, idx);
    segment.add_callback_and_ticket(callback, ticket);

    // Finally, let's insert the new segment
    segments.insert(segment);
    pthread_mutex_unlock(&signal_mutex);
}

void bh_mem_signal_detach(const void *addr) {
    pthread_mutex_lock(&signal_mutex);
    auto it = segments.find(addr);
    if (it != segments.end()) {
        assert(it->ticket != nullptr);
        sigsegv_unregister(&dispatcher, it->ticket);
        segments.erase(it);
    }
    pthread_mutex_unlock(&signal_mutex);
}

int bh_mem_signal_exist(const void *addr) {
    int ret;
    pthread_mutex_lock(&signal_mutex);
    ret = util::exist(segments, addr);
    pthread_mutex_unlock(&signal_mutex);
    return ret;
}

void bh_mem_signal_pprint_db(void) {
    cout << segments << endl;
}
<|endoftext|>


<commit_before>/******************************************************************************/
/* Copyright (c) 2013 VectorChief (at github, bitbucket, sourceforge)         */
/* Distributed under the MIT software license, see the accompanying           */
/* file COPYING or http://www.opensource.org/licenses/mit-license.php         */
/******************************************************************************/

#include <string.h>

#include "rtgeom.h"
#include "system.h"

/******************************************************************************/
/********************************   MATRICES   ********************************/
/******************************************************************************/

/*
 * Identity matrix.
 */
rt_mat4 iden4 =
{
    1.0f,       0.0f,       0.0f,       0.0f,
    0.0f,       1.0f,       0.0f,       0.0f,
    0.0f,       0.0f,       1.0f,       0.0f,
    0.0f,       0.0f,       0.0f,       1.0f,
};

#if RT_DEBUG == 1
/*
 * Check if given address ranges overlap.
 */
static
rt_bool in_range(rt_real *p1, rt_cell n1, rt_real *p2, rt_cell n2)
{
    if ((p1 >= p2 && p1 < p2 + n2) || (p2 >= p1 && p2 < p1 + n1))
    {
        return RT_TRUE;
    }

    return RT_FALSE;
}
#endif /* RT_DEBUG */

/*
 * Multiply matrix by vector.
 */
rt_void matrix_mul_vector(rt_vec4 vp, rt_mat4 m1, rt_vec4 v1)
{
#if RT_DEBUG == 1
    if (in_range(vp, 4, m1[0], 16) || in_range(vp, 4, v1, 4))
    {
        throw rt_Exception("Attempting to multiply vectors in place");
    }
#endif /* RT_DEBUG */

    rt_cell i;

    for (i = 0; i < 4; i++)
    {
        vp[i] = m1[0][i] * v1[0] +
                m1[1][i] * v1[1] +
                m1[2][i] * v1[2] +
                m1[3][i] * v1[3];
    }
}

/*
 * Multiply matrix by matrix.
 */
rt_void matrix_mul_matrix(rt_mat4 mp, rt_mat4 m1, rt_mat4 m2)
{
#if RT_DEBUG == 1
    if (in_range(mp[0], 16, m1[0], 16) || in_range(mp[0], 16, m2[0], 16))
    {
        throw rt_Exception("Attempting to multiply matrices in place");
    }
#endif /* RT_DEBUG */

    rt_cell i;

    for (i = 0; i < 4; i++)
    {
        matrix_mul_vector(mp[i], m1, m2[i]);
    }
}

/*
 * Compute matrix from transform.
 */
rt_void matrix_from_transform(rt_mat4 mp, rt_TRANSFORM3D *t1)
{
    rt_mat4 mt;

    rt_real scl_x = t1->scl[RT_X];
    rt_real scl_y = t1->scl[RT_Y];
    rt_real scl_z = t1->scl[RT_Z];
    rt_mat4 sc =
    {
        scl_x,      0.0f,       0.0f,       0.0f,
        0.0f,       scl_y,      0.0f,       0.0f,
        0.0f,       0.0f,       scl_z,      0.0f,
        0.0f,       0.0f,       0.0f,       1.0f,
    };

    rt_real sin_x = RT_SINA(t1->rot[RT_X]);
    rt_real cos_x = RT_COSA(t1->rot[RT_X]);
    rt_mat4 rx =
    {
        1.0f,       0.0f,       0.0f,       0.0f,
        0.0f,      +cos_x,     +sin_x,      0.0f,
        0.0f,      -sin_x,     +cos_x,      0.0f,
        0.0f,       0.0f,       0.0f,       1.0f,
    };

    rt_real sin_y = RT_SINA(t1->rot[RT_Y]);
    rt_real cos_y = RT_COSA(t1->rot[RT_Y]);
    rt_mat4 ry =
    {
       +cos_y,      0.0f,      -sin_y,      0.0f,
        0.0f,       1.0f,       0.0f,       0.0f,
       +sin_y,      0.0f,      +cos_y,      0.0f,
        0.0f,       0.0f,       0.0f,       1.0f,
    };

    rt_real sin_z = RT_SINA(t1->rot[RT_Z]);
    rt_real cos_z = RT_COSA(t1->rot[RT_Z]);
    rt_mat4 rz =
    {
       +cos_z,     +sin_z,      0.0f,       0.0f,
       -sin_z,     +cos_z,      0.0f,       0.0f,
        0.0f,       0.0f,       1.0f,       0.0f,
        0.0f,       0.0f,       0.0f,       1.0f,
    };

    rt_real pos_x = t1->pos[RT_X];
    rt_real pos_y = t1->pos[RT_Y];
    rt_real pos_z = t1->pos[RT_Z];
    rt_mat4 ps =
    {
        1.0f,       0.0f,       0.0f,       0.0f,
        0.0f,       1.0f,       0.0f,       0.0f,
        0.0f,       0.0f,       1.0f,       0.0f,
        pos_x,      pos_y,      pos_z,      1.0f,
    };

    matrix_mul_matrix(mt, rx, sc);
    matrix_mul_matrix(mp, ry, mt);
    matrix_mul_matrix(mt, rz, mp);
    matrix_mul_matrix(mp, ps, mt);
}

/*
 * Compute inverse matrix.
 */
rt_void matrix_inverse(rt_mat4 mp, rt_mat4 m1)
{
    memset(mp, 0, sizeof(rt_real) * 16);

    rt_real A = m1[1][1] * m1[2][2] - m1[2][1] * m1[1][2];
    rt_real B = m1[2][1] * m1[0][2] - m1[0][1] * m1[2][2];
    rt_real C = m1[0][1] * m1[1][2] - m1[1][1] * m1[0][2];

    rt_real D = m1[2][0] * m1[1][2] - m1[1][0] * m1[2][2];
    rt_real E = m1[0][0] * m1[2][2] - m1[2][0] * m1[0][2];
    rt_real F = m1[0][2] * m1[1][0] - m1[0][0] * m1[1][2];

    rt_real G = m1[1][0] * m1[2][1] - m1[2][0] * m1[1][1];
    rt_real H = m1[2][0] * m1[0][1] - m1[0][0] * m1[2][1];
    rt_real K = m1[0][0] * m1[1][1] - m1[1][0] * m1[0][1];

    rt_real q = 1.0f / (m1[0][0] * A + m1[1][0] * B + m1[2][0] * C);

    mp[0][0] = A * q;
    mp[0][1] = B * q;
    mp[0][2] = C * q;

    mp[1][0] = D * q;
    mp[1][1] = E * q;
    mp[1][2] = F * q;

    mp[2][0] = G * q;
    mp[2][1] = H * q;
    mp[2][2] = K * q;

#if RT_DEBUG == 1
    rt_cell i, j, k = 0;

    rt_mat4 tm;

    matrix_mul_matrix(tm, mp, m1);

    for (i = 0; i < 3; i++)
    {
        for (j = 0; j < 3; j++)
        {
            if (RT_FABS(tm[i][j] - iden_m[i][j]) > 0.00001)
            {
                k = 1;
                break;
            }
        }
    }

    if (k == 0)
    {
        return;
    }

    RT_LOGE("Original matrix\n");

    for (i = 0; i < 4; i++)
    {
        for (j = 0; j < 4; j++)
        {
            RT_LOGE("%f ", m1[j][i]);
        }
        RT_LOGE("\n");
    }

    RT_LOGE("Inverted matrix\n");

    for (i = 0; i < 4; i++)
    {
        for (j = 0; j < 4; j++)
        {
            RT_LOGE("%f ", mp[j][i]);
        }
        RT_LOGE("\n");
    }

    throw rt_Exception("Inverted matrix mismatch detected.");
#endif /* RT_DEBUG */
}

/******************************************************************************/
/******************************************************************************/
/******************************************************************************/
<commit_msg>fix compilation in debug mode<commit_after>/******************************************************************************/
/* Copyright (c) 2013 VectorChief (at github, bitbucket, sourceforge)         */
/* Distributed under the MIT software license, see the accompanying           */
/* file COPYING or http://www.opensource.org/licenses/mit-license.php         */
/******************************************************************************/

#include <string.h>

#include "rtgeom.h"
#include "system.h"

/******************************************************************************/
/********************************   MATRICES   ********************************/
/******************************************************************************/

/*
 * Identity matrix.
 */
rt_mat4 iden4 =
{
    1.0f,       0.0f,       0.0f,       0.0f,
    0.0f,       1.0f,       0.0f,       0.0f,
    0.0f,       0.0f,       1.0f,       0.0f,
    0.0f,       0.0f,       0.0f,       1.0f,
};

#if RT_DEBUG == 1
/*
 * Check if given address ranges overlap.
 */
static
rt_bool in_range(rt_real *p1, rt_cell n1, rt_real *p2, rt_cell n2)
{
    if ((p1 >= p2 && p1 < p2 + n2) || (p2 >= p1 && p2 < p1 + n1))
    {
        return RT_TRUE;
    }

    return RT_FALSE;
}
#endif /* RT_DEBUG */

/*
 * Multiply matrix by vector.
 */
rt_void matrix_mul_vector(rt_vec4 vp, rt_mat4 m1, rt_vec4 v1)
{
#if RT_DEBUG == 1
    if (in_range(vp, 4, m1[0], 16) || in_range(vp, 4, v1, 4))
    {
        throw rt_Exception("Attempting to multiply vectors in place");
    }
#endif /* RT_DEBUG */

    rt_cell i;

    for (i = 0; i < 4; i++)
    {
        vp[i] = m1[0][i] * v1[0] +
                m1[1][i] * v1[1] +
                m1[2][i] * v1[2] +
                m1[3][i] * v1[3];
    }
}

/*
 * Multiply matrix by matrix.
 */
rt_void matrix_mul_matrix(rt_mat4 mp, rt_mat4 m1, rt_mat4 m2)
{
#if RT_DEBUG == 1
    if (in_range(mp[0], 16, m1[0], 16) || in_range(mp[0], 16, m2[0], 16))
    {
        throw rt_Exception("Attempting to multiply matrices in place");
    }
#endif /* RT_DEBUG */

    rt_cell i;

    for (i = 0; i < 4; i++)
    {
        matrix_mul_vector(mp[i], m1, m2[i]);
    }
}

/*
 * Compute matrix from transform.
 */
rt_void matrix_from_transform(rt_mat4 mp, rt_TRANSFORM3D *t1)
{
    rt_mat4 mt;

    rt_real scl_x = t1->scl[RT_X];
    rt_real scl_y = t1->scl[RT_Y];
    rt_real scl_z = t1->scl[RT_Z];
    rt_mat4 sc =
    {
        scl_x,      0.0f,       0.0f,       0.0f,
        0.0f,       scl_y,      0.0f,       0.0f,
        0.0f,       0.0f,       scl_z,      0.0f,
        0.0f,       0.0f,       0.0f,       1.0f,
    };

    rt_real sin_x = RT_SINA(t1->rot[RT_X]);
    rt_real cos_x = RT_COSA(t1->rot[RT_X]);
    rt_mat4 rx =
    {
        1.0f,       0.0f,       0.0f,       0.0f,
        0.0f,      +cos_x,     +sin_x,      0.0f,
        0.0f,      -sin_x,     +cos_x,      0.0f,
        0.0f,       0.0f,       0.0f,       1.0f,
    };

    rt_real sin_y = RT_SINA(t1->rot[RT_Y]);
    rt_real cos_y = RT_COSA(t1->rot[RT_Y]);
    rt_mat4 ry =
    {
       +cos_y,      0.0f,      -sin_y,      0.0f,
        0.0f,       1.0f,       0.0f,       0.0f,
       +sin_y,      0.0f,      +cos_y,      0.0f,
        0.0f,       0.0f,       0.0f,       1.0f,
    };

    rt_real sin_z = RT_SINA(t1->rot[RT_Z]);
    rt_real cos_z = RT_COSA(t1->rot[RT_Z]);
    rt_mat4 rz =
    {
       +cos_z,     +sin_z,      0.0f,       0.0f,
       -sin_z,     +cos_z,      0.0f,       0.0f,
        0.0f,       0.0f,       1.0f,       0.0f,
        0.0f,       0.0f,       0.0f,       1.0f,
    };

    rt_real pos_x = t1->pos[RT_X];
    rt_real pos_y = t1->pos[RT_Y];
    rt_real pos_z = t1->pos[RT_Z];
    rt_mat4 ps =
    {
        1.0f,       0.0f,       0.0f,       0.0f,
        0.0f,       1.0f,       0.0f,       0.0f,
        0.0f,       0.0f,       1.0f,       0.0f,
        pos_x,      pos_y,      pos_z,      1.0f,
    };

    matrix_mul_matrix(mt, rx, sc);
    matrix_mul_matrix(mp, ry, mt);
    matrix_mul_matrix(mt, rz, mp);
    matrix_mul_matrix(mp, ps, mt);
}

/*
 * Compute inverse matrix.
 */
rt_void matrix_inverse(rt_mat4 mp, rt_mat4 m1)
{
    memset(mp, 0, sizeof(rt_real) * 16);

    rt_real A = m1[1][1] * m1[2][2] - m1[2][1] * m1[1][2];
    rt_real B = m1[2][1] * m1[0][2] - m1[0][1] * m1[2][2];
    rt_real C = m1[0][1] * m1[1][2] - m1[1][1] * m1[0][2];

    rt_real D = m1[2][0] * m1[1][2] - m1[1][0] * m1[2][2];
    rt_real E = m1[0][0] * m1[2][2] - m1[2][0] * m1[0][2];
    rt_real F = m1[0][2] * m1[1][0] - m1[0][0] * m1[1][2];

    rt_real G = m1[1][0] * m1[2][1] - m1[2][0] * m1[1][1];
    rt_real H = m1[2][0] * m1[0][1] - m1[0][0] * m1[2][1];
    rt_real K = m1[0][0] * m1[1][1] - m1[1][0] * m1[0][1];

    rt_real q = 1.0f / (m1[0][0] * A + m1[1][0] * B + m1[2][0] * C);

    mp[0][0] = A * q;
    mp[0][1] = B * q;
    mp[0][2] = C * q;

    mp[1][0] = D * q;
    mp[1][1] = E * q;
    mp[1][2] = F * q;

    mp[2][0] = G * q;
    mp[2][1] = H * q;
    mp[2][2] = K * q;

#if RT_DEBUG == 1
    rt_cell i, j, k = 0;

    rt_mat4 tm;

    matrix_mul_matrix(tm, mp, m1);

    for (i = 0; i < 3; i++)
    {
        for (j = 0; j < 3; j++)
        {
            if (RT_FABS(tm[i][j] - iden4[i][j]) > 0.00001)
            {
                k = 1;
                break;
            }
        }
    }

    if (k == 0)
    {
        return;
    }

    RT_LOGE("Original matrix\n");

    for (i = 0; i < 4; i++)
    {
        for (j = 0; j < 4; j++)
        {
            RT_LOGE("%f ", m1[j][i]);
        }
        RT_LOGE("\n");
    }

    RT_LOGE("Inverted matrix\n");

    for (i = 0; i < 4; i++)
    {
        for (j = 0; j < 4; j++)
        {
            RT_LOGE("%f ", mp[j][i]);
        }
        RT_LOGE("\n");
    }

    throw rt_Exception("Inverted matrix mismatch detected.");
#endif /* RT_DEBUG */
}

/******************************************************************************/
/******************************************************************************/
/******************************************************************************/
<|endoftext|>


<commit_before>/*************************************************************************/
/*  dir_access.cpp                                                       */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                      https://godotengine.org                          */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */
/*                                                                       */
/* 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 "dir_access.h"

#include "core/config/project_settings.h"
#include "core/io/file_access.h"
#include "core/os/memory.h"
#include "core/os/os.h"

String DirAccess::_get_root_path() const {
	switch (_access_type) {
		case ACCESS_RESOURCES:
			return ProjectSettings::get_singleton()->get_resource_path();
		case ACCESS_USERDATA:
			return OS::get_singleton()->get_user_data_dir();
		default:
			return "";
	}
}

String DirAccess::_get_root_string() const {
	switch (_access_type) {
		case ACCESS_RESOURCES:
			return "res://";
		case ACCESS_USERDATA:
			return "user://";
		default:
			return "";
	}
}

int DirAccess::get_current_drive() {
	String path = get_current_dir().to_lower();
	for (int i = 0; i < get_drive_count(); i++) {
		String d = get_drive(i).to_lower();
		if (path.begins_with(d)) {
			return i;
		}
	}

	return 0;
}

bool DirAccess::drives_are_shortcuts() {
	return false;
}

static Error _erase_recursive(DirAccess *da) {
	List<String> dirs;
	List<String> files;

	da->list_dir_begin();
	String n = da->get_next();
	while (!n.is_empty()) {
		if (n != "." && n != "..") {
			if (da->current_is_dir()) {
				dirs.push_back(n);
			} else {
				files.push_back(n);
			}
		}

		n = da->get_next();
	}

	da->list_dir_end();

	for (const String &E : dirs) {
		Error err = da->change_dir(E);
		if (err == OK) {
			err = _erase_recursive(da);
			if (err) {
				da->change_dir("..");
				return err;
			}
			err = da->change_dir("..");
			if (err) {
				return err;
			}
			err = da->remove(da->get_current_dir().plus_file(E));
			if (err) {
				return err;
			}
		} else {
			return err;
		}
	}

	for (const String &E : files) {
		Error err = da->remove(da->get_current_dir().plus_file(E));
		if (err) {
			return err;
		}
	}

	return OK;
}

Error DirAccess::erase_contents_recursive() {
	return _erase_recursive(this);
}

Error DirAccess::make_dir_recursive(String p_dir) {
	if (p_dir.length() < 1) {
		return OK;
	}

	String full_dir;

	if (p_dir.is_relative_path()) {
		//append current
		full_dir = get_current_dir().plus_file(p_dir);

	} else {
		full_dir = p_dir;
	}

	full_dir = full_dir.replace("\\", "/");

	String base;

	if (full_dir.begins_with("res://")) {
		base = "res://";
	} else if (full_dir.begins_with("user://")) {
		base = "user://";
	} else if (full_dir.is_network_share_path()) {
		int pos = full_dir.find("/", 2);
		ERR_FAIL_COND_V(pos < 0, ERR_INVALID_PARAMETER);
		pos = full_dir.find("/", pos + 1);
		ERR_FAIL_COND_V(pos < 0, ERR_INVALID_PARAMETER);
		base = full_dir.substr(0, pos + 1);
	} else if (full_dir.begins_with("/")) {
		base = "/";
	} else if (full_dir.contains(":/")) {
		base = full_dir.substr(0, full_dir.find(":/") + 2);
	} else {
		ERR_FAIL_V(ERR_INVALID_PARAMETER);
	}

	full_dir = full_dir.replace_first(base, "").simplify_path();

	Vector<String> subdirs = full_dir.split("/");

	String curpath = base;
	for (int i = 0; i < subdirs.size(); i++) {
		curpath = curpath.plus_file(subdirs[i]);
		Error err = make_dir(curpath);
		if (err != OK && err != ERR_ALREADY_EXISTS) {
			ERR_FAIL_V_MSG(err, "Could not create directory: " + curpath);
		}
	}

	return OK;
}

DirAccess::AccessType DirAccess::get_access_type() const {
	return _access_type;
}

String DirAccess::fix_path(String p_path) const {
	switch (_access_type) {
		case ACCESS_RESOURCES: {
			if (ProjectSettings::get_singleton()) {
				if (p_path.begins_with("res://")) {
					String resource_path = ProjectSettings::get_singleton()->get_resource_path();
					if (!resource_path.is_empty()) {
						return p_path.replace_first("res:/", resource_path);
					}
					return p_path.replace_first("res://", "");
				}
			}

		} break;
		case ACCESS_USERDATA: {
			if (p_path.begins_with("user://")) {
				String data_dir = OS::get_singleton()->get_user_data_dir();
				if (!data_dir.is_empty()) {
					return p_path.replace_first("user:/", data_dir);
				}
				return p_path.replace_first("user://", "");
			}

		} break;
		case ACCESS_FILESYSTEM: {
			return p_path;
		} break;
		case ACCESS_MAX:
			break; // Can't happen, but silences warning
	}

	return p_path;
}

DirAccess::CreateFunc DirAccess::create_func[ACCESS_MAX] = { nullptr, nullptr, nullptr };

Ref<DirAccess> DirAccess::create_for_path(const String &p_path) {
	Ref<DirAccess> da;
	if (p_path.begins_with("res://")) {
		da = create(ACCESS_RESOURCES);
	} else if (p_path.begins_with("user://")) {
		da = create(ACCESS_USERDATA);
	} else {
		da = create(ACCESS_FILESYSTEM);
	}

	return da;
}

Ref<DirAccess> DirAccess::open(const String &p_path, Error *r_error) {
	Ref<DirAccess> da = create_for_path(p_path);
	ERR_FAIL_COND_V_MSG(da.is_null(), nullptr, "Cannot create DirAccess for path '" + p_path + "'.");
	Error err = da->change_dir(p_path);
	if (r_error) {
		*r_error = err;
	}
	if (err != OK) {
		return nullptr;
	}

	return da;
}

Ref<DirAccess> DirAccess::create(AccessType p_access) {
	Ref<DirAccess> da = create_func[p_access] ? create_func[p_access]() : nullptr;
	if (da.is_valid()) {
		da->_access_type = p_access;

		// for ACCESS_RESOURCES and ACCESS_FILESYSTEM, current_dir already defaults to where game was started
		// in case current directory is force changed elsewhere for ACCESS_RESOURCES
		if (p_access == ACCESS_RESOURCES) {
			da->change_dir("res://");
		} else if (p_access == ACCESS_USERDATA) {
			da->change_dir("user://");
		}
	}

	return da;
}

String DirAccess::get_full_path(const String &p_path, AccessType p_access) {
	Ref<DirAccess> d = DirAccess::create(p_access);
	if (d.is_null()) {
		return p_path;
	}

	d->change_dir(p_path);
	String full = d->get_current_dir();
	return full;
}

Error DirAccess::copy(String p_from, String p_to, int p_chmod_flags) {
	//printf("copy %s -> %s\n",p_from.ascii().get_data(),p_to.ascii().get_data());
	Error err;
	{
		Ref<FileAccess> fsrc = FileAccess::open(p_from, FileAccess::READ, &err);
		ERR_FAIL_COND_V_MSG(err != OK, err, "Failed to open " + p_from);

		Ref<FileAccess> fdst = FileAccess::open(p_to, FileAccess::WRITE, &err);
		ERR_FAIL_COND_V_MSG(err != OK, err, "Failed to open " + p_to);

		fsrc->seek_end(0);
		int size = fsrc->get_position();
		fsrc->seek(0);
		err = OK;
		while (size--) {
			if (fsrc->get_error() != OK) {
				err = fsrc->get_error();
				break;
			}
			if (fdst->get_error() != OK) {
				err = fdst->get_error();
				break;
			}

			fdst->store_8(fsrc->get_8());
		}
	}

	if (err == OK && p_chmod_flags != -1) {
		err = FileAccess::set_unix_permissions(p_to, p_chmod_flags);
		// If running on a platform with no chmod support (i.e., Windows), don't fail
		if (err == ERR_UNAVAILABLE) {
			err = OK;
		}
	}

	return err;
}

// Changes dir for the current scope, returning back to the original dir
// when scope exits
class DirChanger {
	DirAccess *da;
	String original_dir;

public:
	DirChanger(DirAccess *p_da, String p_dir) :
			da(p_da),
			original_dir(p_da->get_current_dir()) {
		p_da->change_dir(p_dir);
	}

	~DirChanger() {
		da->change_dir(original_dir);
	}
};

Error DirAccess::_copy_dir(Ref<DirAccess> &p_target_da, String p_to, int p_chmod_flags, bool p_copy_links) {
	List<String> dirs;

	String curdir = get_current_dir();
	list_dir_begin();
	String n = get_next();
	while (!n.is_empty()) {
		if (n != "." && n != "..") {
			if (p_copy_links && is_link(get_current_dir().plus_file(n))) {
				create_link(read_link(get_current_dir().plus_file(n)), p_to + n);
			} else if (current_is_dir()) {
				dirs.push_back(n);
			} else {
				const String &rel_path = n;
				if (!n.is_relative_path()) {
					list_dir_end();
					return ERR_BUG;
				}
				Error err = copy(get_current_dir().plus_file(n), p_to + rel_path, p_chmod_flags);
				if (err) {
					list_dir_end();
					return err;
				}
			}
		}

		n = get_next();
	}

	list_dir_end();

	for (const String &rel_path : dirs) {
		String target_dir = p_to + rel_path;
		if (!p_target_da->dir_exists(target_dir)) {
			Error err = p_target_da->make_dir(target_dir);
			ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot create directory '" + target_dir + "'.");
		}

		Error err = change_dir(rel_path);
		ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot change current directory to '" + rel_path + "'.");

		err = _copy_dir(p_target_da, p_to + rel_path + "/", p_chmod_flags, p_copy_links);
		if (err) {
			change_dir("..");
			ERR_FAIL_V_MSG(err, "Failed to copy recursively.");
		}
		err = change_dir("..");
		ERR_FAIL_COND_V_MSG(err != OK, err, "Failed to go back.");
	}

	return OK;
}

Error DirAccess::copy_dir(String p_from, String p_to, int p_chmod_flags, bool p_copy_links) {
	ERR_FAIL_COND_V_MSG(!dir_exists(p_from), ERR_FILE_NOT_FOUND, "Source directory doesn't exist.");

	Ref<DirAccess> target_da = DirAccess::create_for_path(p_to);
	ERR_FAIL_COND_V_MSG(target_da.is_null(), ERR_CANT_CREATE, "Cannot create DirAccess for path '" + p_to + "'.");

	if (!target_da->dir_exists(p_to)) {
		Error err = target_da->make_dir_recursive(p_to);
		ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot create directory '" + p_to + "'.");
	}

	if (!p_to.ends_with("/")) {
		p_to = p_to + "/";
	}

	DirChanger dir_changer(this, p_from);
	Error err = _copy_dir(target_da, p_to, p_chmod_flags, p_copy_links);

	return err;
}

bool DirAccess::exists(String p_dir) {
	Ref<DirAccess> da = DirAccess::create_for_path(p_dir);
	return da->change_dir(p_dir) == OK;
}
<commit_msg>Address slow copy performance when using the `FileAccessFilesystemJAndroid` implementation.<commit_after>/*************************************************************************/
/*  dir_access.cpp                                                       */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                      https://godotengine.org                          */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md).   */
/*                                                                       */
/* 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 "dir_access.h"

#include "core/config/project_settings.h"
#include "core/io/file_access.h"
#include "core/os/memory.h"
#include "core/os/os.h"
#include "core/templates/local_vector.h"

String DirAccess::_get_root_path() const {
	switch (_access_type) {
		case ACCESS_RESOURCES:
			return ProjectSettings::get_singleton()->get_resource_path();
		case ACCESS_USERDATA:
			return OS::get_singleton()->get_user_data_dir();
		default:
			return "";
	}
}

String DirAccess::_get_root_string() const {
	switch (_access_type) {
		case ACCESS_RESOURCES:
			return "res://";
		case ACCESS_USERDATA:
			return "user://";
		default:
			return "";
	}
}

int DirAccess::get_current_drive() {
	String path = get_current_dir().to_lower();
	for (int i = 0; i < get_drive_count(); i++) {
		String d = get_drive(i).to_lower();
		if (path.begins_with(d)) {
			return i;
		}
	}

	return 0;
}

bool DirAccess::drives_are_shortcuts() {
	return false;
}

static Error _erase_recursive(DirAccess *da) {
	List<String> dirs;
	List<String> files;

	da->list_dir_begin();
	String n = da->get_next();
	while (!n.is_empty()) {
		if (n != "." && n != "..") {
			if (da->current_is_dir()) {
				dirs.push_back(n);
			} else {
				files.push_back(n);
			}
		}

		n = da->get_next();
	}

	da->list_dir_end();

	for (const String &E : dirs) {
		Error err = da->change_dir(E);
		if (err == OK) {
			err = _erase_recursive(da);
			if (err) {
				da->change_dir("..");
				return err;
			}
			err = da->change_dir("..");
			if (err) {
				return err;
			}
			err = da->remove(da->get_current_dir().plus_file(E));
			if (err) {
				return err;
			}
		} else {
			return err;
		}
	}

	for (const String &E : files) {
		Error err = da->remove(da->get_current_dir().plus_file(E));
		if (err) {
			return err;
		}
	}

	return OK;
}

Error DirAccess::erase_contents_recursive() {
	return _erase_recursive(this);
}

Error DirAccess::make_dir_recursive(String p_dir) {
	if (p_dir.length() < 1) {
		return OK;
	}

	String full_dir;

	if (p_dir.is_relative_path()) {
		//append current
		full_dir = get_current_dir().plus_file(p_dir);

	} else {
		full_dir = p_dir;
	}

	full_dir = full_dir.replace("\\", "/");

	String base;

	if (full_dir.begins_with("res://")) {
		base = "res://";
	} else if (full_dir.begins_with("user://")) {
		base = "user://";
	} else if (full_dir.is_network_share_path()) {
		int pos = full_dir.find("/", 2);
		ERR_FAIL_COND_V(pos < 0, ERR_INVALID_PARAMETER);
		pos = full_dir.find("/", pos + 1);
		ERR_FAIL_COND_V(pos < 0, ERR_INVALID_PARAMETER);
		base = full_dir.substr(0, pos + 1);
	} else if (full_dir.begins_with("/")) {
		base = "/";
	} else if (full_dir.contains(":/")) {
		base = full_dir.substr(0, full_dir.find(":/") + 2);
	} else {
		ERR_FAIL_V(ERR_INVALID_PARAMETER);
	}

	full_dir = full_dir.replace_first(base, "").simplify_path();

	Vector<String> subdirs = full_dir.split("/");

	String curpath = base;
	for (int i = 0; i < subdirs.size(); i++) {
		curpath = curpath.plus_file(subdirs[i]);
		Error err = make_dir(curpath);
		if (err != OK && err != ERR_ALREADY_EXISTS) {
			ERR_FAIL_V_MSG(err, "Could not create directory: " + curpath);
		}
	}

	return OK;
}

DirAccess::AccessType DirAccess::get_access_type() const {
	return _access_type;
}

String DirAccess::fix_path(String p_path) const {
	switch (_access_type) {
		case ACCESS_RESOURCES: {
			if (ProjectSettings::get_singleton()) {
				if (p_path.begins_with("res://")) {
					String resource_path = ProjectSettings::get_singleton()->get_resource_path();
					if (!resource_path.is_empty()) {
						return p_path.replace_first("res:/", resource_path);
					}
					return p_path.replace_first("res://", "");
				}
			}

		} break;
		case ACCESS_USERDATA: {
			if (p_path.begins_with("user://")) {
				String data_dir = OS::get_singleton()->get_user_data_dir();
				if (!data_dir.is_empty()) {
					return p_path.replace_first("user:/", data_dir);
				}
				return p_path.replace_first("user://", "");
			}

		} break;
		case ACCESS_FILESYSTEM: {
			return p_path;
		} break;
		case ACCESS_MAX:
			break; // Can't happen, but silences warning
	}

	return p_path;
}

DirAccess::CreateFunc DirAccess::create_func[ACCESS_MAX] = { nullptr, nullptr, nullptr };

Ref<DirAccess> DirAccess::create_for_path(const String &p_path) {
	Ref<DirAccess> da;
	if (p_path.begins_with("res://")) {
		da = create(ACCESS_RESOURCES);
	} else if (p_path.begins_with("user://")) {
		da = create(ACCESS_USERDATA);
	} else {
		da = create(ACCESS_FILESYSTEM);
	}

	return da;
}

Ref<DirAccess> DirAccess::open(const String &p_path, Error *r_error) {
	Ref<DirAccess> da = create_for_path(p_path);
	ERR_FAIL_COND_V_MSG(da.is_null(), nullptr, "Cannot create DirAccess for path '" + p_path + "'.");
	Error err = da->change_dir(p_path);
	if (r_error) {
		*r_error = err;
	}
	if (err != OK) {
		return nullptr;
	}

	return da;
}

Ref<DirAccess> DirAccess::create(AccessType p_access) {
	Ref<DirAccess> da = create_func[p_access] ? create_func[p_access]() : nullptr;
	if (da.is_valid()) {
		da->_access_type = p_access;

		// for ACCESS_RESOURCES and ACCESS_FILESYSTEM, current_dir already defaults to where game was started
		// in case current directory is force changed elsewhere for ACCESS_RESOURCES
		if (p_access == ACCESS_RESOURCES) {
			da->change_dir("res://");
		} else if (p_access == ACCESS_USERDATA) {
			da->change_dir("user://");
		}
	}

	return da;
}

String DirAccess::get_full_path(const String &p_path, AccessType p_access) {
	Ref<DirAccess> d = DirAccess::create(p_access);
	if (d.is_null()) {
		return p_path;
	}

	d->change_dir(p_path);
	String full = d->get_current_dir();
	return full;
}

Error DirAccess::copy(String p_from, String p_to, int p_chmod_flags) {
	//printf("copy %s -> %s\n",p_from.ascii().get_data(),p_to.ascii().get_data());
	Error err;
	{
		Ref<FileAccess> fsrc = FileAccess::open(p_from, FileAccess::READ, &err);
		ERR_FAIL_COND_V_MSG(err != OK, err, "Failed to open " + p_from);

		Ref<FileAccess> fdst = FileAccess::open(p_to, FileAccess::WRITE, &err);
		ERR_FAIL_COND_V_MSG(err != OK, err, "Failed to open " + p_to);

		const size_t copy_buffer_limit = 65536; // 64 KB

		fsrc->seek_end(0);
		int size = fsrc->get_position();
		fsrc->seek(0);
		err = OK;
		size_t buffer_size = MIN(size * sizeof(uint8_t), copy_buffer_limit);
		LocalVector<uint8_t> buffer;
		buffer.resize(buffer_size);
		while (size > 0) {
			if (fsrc->get_error() != OK) {
				err = fsrc->get_error();
				break;
			}
			if (fdst->get_error() != OK) {
				err = fdst->get_error();
				break;
			}

			int bytes_read = fsrc->get_buffer(buffer.ptr(), buffer_size);
			if (bytes_read <= 0) {
				err = FAILED;
				break;
			}
			fdst->store_buffer(buffer.ptr(), bytes_read);

			size -= bytes_read;
		}
	}

	if (err == OK && p_chmod_flags != -1) {
		err = FileAccess::set_unix_permissions(p_to, p_chmod_flags);
		// If running on a platform with no chmod support (i.e., Windows), don't fail
		if (err == ERR_UNAVAILABLE) {
			err = OK;
		}
	}

	return err;
}

// Changes dir for the current scope, returning back to the original dir
// when scope exits
class DirChanger {
	DirAccess *da;
	String original_dir;

public:
	DirChanger(DirAccess *p_da, String p_dir) :
			da(p_da),
			original_dir(p_da->get_current_dir()) {
		p_da->change_dir(p_dir);
	}

	~DirChanger() {
		da->change_dir(original_dir);
	}
};

Error DirAccess::_copy_dir(Ref<DirAccess> &p_target_da, String p_to, int p_chmod_flags, bool p_copy_links) {
	List<String> dirs;

	String curdir = get_current_dir();
	list_dir_begin();
	String n = get_next();
	while (!n.is_empty()) {
		if (n != "." && n != "..") {
			if (p_copy_links && is_link(get_current_dir().plus_file(n))) {
				create_link(read_link(get_current_dir().plus_file(n)), p_to + n);
			} else if (current_is_dir()) {
				dirs.push_back(n);
			} else {
				const String &rel_path = n;
				if (!n.is_relative_path()) {
					list_dir_end();
					return ERR_BUG;
				}
				Error err = copy(get_current_dir().plus_file(n), p_to + rel_path, p_chmod_flags);
				if (err) {
					list_dir_end();
					return err;
				}
			}
		}

		n = get_next();
	}

	list_dir_end();

	for (const String &rel_path : dirs) {
		String target_dir = p_to + rel_path;
		if (!p_target_da->dir_exists(target_dir)) {
			Error err = p_target_da->make_dir(target_dir);
			ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot create directory '" + target_dir + "'.");
		}

		Error err = change_dir(rel_path);
		ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot change current directory to '" + rel_path + "'.");

		err = _copy_dir(p_target_da, p_to + rel_path + "/", p_chmod_flags, p_copy_links);
		if (err) {
			change_dir("..");
			ERR_FAIL_V_MSG(err, "Failed to copy recursively.");
		}
		err = change_dir("..");
		ERR_FAIL_COND_V_MSG(err != OK, err, "Failed to go back.");
	}

	return OK;
}

Error DirAccess::copy_dir(String p_from, String p_to, int p_chmod_flags, bool p_copy_links) {
	ERR_FAIL_COND_V_MSG(!dir_exists(p_from), ERR_FILE_NOT_FOUND, "Source directory doesn't exist.");

	Ref<DirAccess> target_da = DirAccess::create_for_path(p_to);
	ERR_FAIL_COND_V_MSG(target_da.is_null(), ERR_CANT_CREATE, "Cannot create DirAccess for path '" + p_to + "'.");

	if (!target_da->dir_exists(p_to)) {
		Error err = target_da->make_dir_recursive(p_to);
		ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot create directory '" + p_to + "'.");
	}

	if (!p_to.ends_with("/")) {
		p_to = p_to + "/";
	}

	DirChanger dir_changer(this, p_from);
	Error err = _copy_dir(target_da, p_to, p_chmod_flags, p_copy_links);

	return err;
}

bool DirAccess::exists(String p_dir) {
	Ref<DirAccess> da = DirAccess::create_for_path(p_dir);
	return da->change_dir(p_dir) == OK;
}
<|endoftext|>