text:
/*******************************************************************************
 *
 * MIT License
 *
 * Copyright (c) 2020 Advanced Micro Devices, Inc.
 *
 * 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.
 *
 *******************************************************************************/

#pragma once

#include <miopen/conv_algo_name.hpp>
#include <miopen/convolution.hpp>
#include <miopen/names.hpp>
#include <miopen/sqlite_db.hpp>
#include <miopen/tensor.hpp>

#include <boost/any.hpp>

namespace miopen {

std::string
EncodeDataTypesForKey(miopenDataType_t in, miopenDataType_t weights, miopenDataType_t out);

inline std::string GetDataTypeName(miopenDataType_t data_type)
{
    switch(data_type)
    {
    case miopenFloat: return "FP32";
    case miopenHalf: return "FP16";
    case miopenInt8: return "INT8";
    case miopenInt8x4: return "INT8x4";
    case miopenInt32: return "INT32";
    case miopenBFloat16: return "BF16";
    }

    return "Unknown(" + std::to_string(data_type) + ")";
}

template <class TElement>
constexpr auto GetDHW(int spatial_dims, const std::vector<TElement>& data)
{
    if(spatial_dims == 2)
        return std::make_tuple(0, data[0], data[1]);
    return std::make_tuple(data[0], data[1], data[2]);
}

template <class TElement>
constexpr TElement GetD3(int spatial_dims, const std::vector<TElement>& data)
{
    return std::get<0>(GetDHW(spatial_dims, data));
}

template <class TElement>
constexpr TElement GetH3(int spatial_dims, const std::vector<TElement>& data)
{
    return std::get<1>(GetDHW(spatial_dims, data));
}

template <class TElement>
constexpr TElement GetW3(int spatial_dims, const std::vector<TElement>& data)
{
    return std::get<2>(GetDHW(spatial_dims, data));
}

template <class TElement>
constexpr auto GetNCDHW(int spatial_dims, const std::vector<TElement>& data)
{
    if(spatial_dims == 3)
        return miopen::tien<5>(data, 1);
    else
        return std::make_tuple(data[0], data[1], static_cast<TElement>(1), data[2], data[3]);
}

template <class TElement>
constexpr TElement GetN5(int spatial_dims, const std::vector<TElement>& data)
{
    return std::get<0>(GetNCDHW(spatial_dims, data));
}

template <class TElement>
constexpr TElement GetC5(int spatial_dims, const std::vector<TElement>& data)
{
    return std::get<1>(GetNCDHW(spatial_dims, data));
}

template <class TElement>
constexpr TElement GetD5(int spatial_dims, const std::vector<TElement>& data)
{
    return std::get<2>(GetNCDHW(spatial_dims, data));
}

template <class TElement>
constexpr TElement GetH5(int spatial_dims, const std::vector<TElement>& data)
{
    return std::get<3>(GetNCDHW(spatial_dims, data));
}

template <class TElement>
constexpr TElement GetW5(int spatial_dims, const std::vector<TElement>& data)
{
    return std::get<4>(GetNCDHW(spatial_dims, data));
}

namespace conv {

struct ProblemDescription
#if MIOPEN_ENABLE_SQLITE
    : SQLiteSerializable<ProblemDescription>
#endif
{
    ProblemDescription() = default;

    ProblemDescription(const TensorDescriptor& in_,
                       const TensorDescriptor& weights_,
                       const TensorDescriptor& out_,
                       const ConvolutionDescriptor& conv_,
                       Direction direction_,
                       int bias_ = 0)
        : in(in_),
          weights(weights_),
          out(out_),
          conv(conv_),
          in_layout(ComputeInLayout()),
          weights_layout(ComputeWeightsLayout()),
          out_layout(ComputeOutLayout()),
          direction(direction_),
          bias(bias_)
    {
    }

    // Conv descriptor getters
    std::size_t GetSpatialDims() const { return conv.GetSpatialDimension(); }
    int GetPadD() const { return GetD3(GetSpatialDims(), conv.GetConvPads()); }
    int GetPadH() const { return GetH3(GetSpatialDims(), conv.GetConvPads()); }
    int GetPadW() const { return GetW3(GetSpatialDims(), conv.GetConvPads()); }
    int GetKernelStrideD() const { return GetD3(GetSpatialDims(), conv.GetConvStrides()); }
    int GetKernelStrideH() const { return GetH3(GetSpatialDims(), conv.GetConvStrides()); }
    int GetKernelStrideW() const { return GetW3(GetSpatialDims(), conv.GetConvStrides()); }
    int GetDilationD() const { return GetD3(GetSpatialDims(), conv.GetConvDilations()); }
    int GetDilationH() const { return GetH3(GetSpatialDims(), conv.GetConvDilations()); }
    int GetDilationW() const { return GetW3(GetSpatialDims(), conv.GetConvDilations()); }
    int GetGroupCount() const { return conv.GetGroupCount(); }

    // In getters
    miopenDataType_t GetInDataType() const { return in.GetType(); }
    std::size_t GetInBatchSize() const { return GetN5(GetSpatialDims(), in.GetLengths()); }
    std::size_t GetInChannels() const { return GetC5(GetSpatialDims(), in.GetLengths()); }
    std::size_t GetInDepth() const { return GetD5(GetSpatialDims(), in.GetLengths()); }
    std::size_t GetInHeight() const { return GetH5(GetSpatialDims(), in.GetLengths()); }
    std::size_t GetInWidth() const { return GetW5(GetSpatialDims(), in.GetLengths()); }
    std::size_t GetInBatchStride() const { return GetN5(GetSpatialDims(), in.GetStrides()); }
    std::size_t GetInChannelStride() const { return GetC5(GetSpatialDims(), in.GetStrides()); }
    std::size_t GetInStrideD() const { return GetD5(GetSpatialDims(), in.GetStrides()); }
    std::size_t GetInStrideH() const { return GetH5(GetSpatialDims(), in.GetStrides()); }
    std::size_t GetInStrideW() const { return GetW5(GetSpatialDims(), in.GetStrides()); }
    std::string GetInLayout() const { return in_layout; }
    std::string ComputeInLayout() const
    {
        if(GetSpatialDims() == 2)
        {
            return in.GetLayout("NCHW");
        }
        else
        {
            return in.GetLayout("NCDHW");
        }
    }
    std::size_t GetInElementSize() const { return GetTypeSize(GetInDataType()); }

    std::size_t GetInSize() const
    {
        // clang-format off
        return GetInBatchSize() * GetInChannels() * GetInDepth() * GetInHeight() *
            GetInWidth() * GetInElementSize();
        // clang-format on
    }

    // Out getters
    miopenDataType_t GetOutDataType() const { return out.GetType(); }
    std::size_t GetOutBatchSize() const { return GetN5(GetSpatialDims(), out.GetLengths()); }
    std::size_t GetOutChannels() const { return GetC5(GetSpatialDims(), out.GetLengths()); }
    std::size_t GetOutDepth() const { return GetD5(GetSpatialDims(), out.GetLengths()); }
    std::size_t GetOutHeight() const { return GetH5(GetSpatialDims(), out.GetLengths()); }
    std::size_t GetOutWidth() const { return GetW5(GetSpatialDims(), out.GetLengths()); }
    std::size_t GetOutBatchStride() const { return GetN5(GetSpatialDims(), out.GetStrides()); }
    std::size_t GetOutChannelStride() const { return GetC5(GetSpatialDims(), out.GetStrides()); }
    std::size_t GetOutStrideD() const { return GetD5(GetSpatialDims(), out.GetStrides()); }
    std::size_t GetOutStrideH() const { return GetH5(GetSpatialDims(), out.GetStrides()); }
    std::size_t GetOutStrideW() const { return GetW5(GetSpatialDims(), out.GetStrides()); }
    std::string GetOutLayout() const { return out_layout; }
    std::string ComputeOutLayout() const
    {
        if(GetSpatialDims() == 2)
        {
            return out.GetLayout("NCHW");
        }
        else
        {
            return out.GetLayout("NCDHW");
        }
    }
    std::size_t GetOutElementSize() const { return GetTypeSize(GetOutDataType()); }

    std::size_t GetOutSize() const
    {
        // clang-format off
        return GetOutBatchSize() * GetOutChannels() * GetOutDepth() * GetOutHeight() *
               GetOutWidth() * GetOutElementSize();
        // clang-format on
    }

    // Weights getters
    miopenDataType_t GetWeightsDataType() const { return weights.GetType(); }
    std::size_t GetWeightsDepth() const { return GetD5(GetSpatialDims(), weights.GetLengths()); }
    std::size_t GetWeightsHeight() const { return GetH5(GetSpatialDims(), weights.GetLengths()); }
    std::size_t GetWeightsWidth() const { return GetW5(GetSpatialDims(), weights.GetLengths()); }
    // std::size_t GetWeightsStrideD() const { return GetD5(GetSpatialDims(), weights.GetStrides());
    // }
    // std::size_t GetWeightsStrideH() const { return GetH5(GetSpatialDims(), weights.GetStrides());
    // }
    // std::size_t GetWeightsStrideW() const { return GetW5(GetSpatialDims(), weights.GetStrides());
    // }
    std::string GetWeightsLayout() const { return weights_layout; }
    std::string ComputeWeightsLayout() const
    {
        if(GetSpatialDims() == 2)
        {
            return weights.GetLayout("NCHW");
        }
        else
        {
            return weights.GetLayout("NCDHW");
        }
    }
    std::size_t GetWeightsElementSize() const { return GetTypeSize(GetWeightsDataType()); }

    std::size_t GetWeightsSize() const
    {
        // clang-format off
        return GetInChannels() * GetOutChannels() * GetWeightsDepth() * GetWeightsHeight() *
               GetWeightsWidth() * GetWeightsElementSize();
        // clang-format on
    }

    const TensorDescriptor& GetIn() const { return in; }
    const TensorDescriptor& GetWeights() const { return weights; }
    const TensorDescriptor& GetOut() const { return out; }
    const ConvolutionDescriptor& GetConv() const { return conv; }
    Direction GetDirection() const { return direction; }
    int GetBias() const { return bias; }

    std::size_t GetBaiasSize() const
    {
        return (GetBias() != 0) ? (GetOutChannels() * GetOutElementSize()) : 0;
    }

    std::size_t GetBackwardPadW() const { return GetWeightsWidth() - GetPadW() - 1; }
    std::size_t GetBackwardPadH() const { return GetWeightsHeight() - GetPadW() - 1; }

    bool IsAsymmetricPadH() const
    {
        return conv.paddingMode == miopenPaddingSame && (GetWeightsHeight() % 2) == 0;
    }
    bool IsAsymmetricPadW() const
    {
        return conv.paddingMode == miopenPaddingSame && (GetWeightsWidth() % 2) == 0;
    }

    bool Is2d() const { return GetSpatialDims() == 2; }

    bool IsFp32() const
    {
        return GetInDataType() == miopenFloat && GetWeightsDataType() == miopenFloat &&
               GetOutDataType() == miopenFloat;
    }
    bool IsFp16() const
    {
        return GetInDataType() == miopenHalf && GetWeightsDataType() == miopenHalf &&
               GetOutDataType() == miopenHalf;
    }
    bool IsBfp16() const
    {
        return GetInDataType() == miopenBFloat16 && GetWeightsDataType() == miopenBFloat16 &&
               GetOutDataType() == miopenBFloat16;
    }

    void BuildConfKey(std::string& conf_key) const;

    NetworkConfig BuildConfKey() const
    {
        std::string ret;
        BuildConfKey(ret);
        return NetworkConfig{ret};
    }

    void Serialize(std::ostream& stream) const;

    friend std::ostream& operator<<(std::ostream& os, const ProblemDescription& obj)
    {
        obj.Serialize(os);
        return os;
    }

    static std::string table_name() { return "config"; }
    template <class Self, class F>
    static void Visit(Self&& self, F f)
    {
        // Todo: shouldn't sqlitedb serialization support 3d convs?
        f(std::to_string(self.GetInChannels()), "in_channels");
        f(std::to_string(self.GetInHeight()), "in_h");
        f(std::to_string(self.GetInWidth()), "in_w");
        f(std::to_string(self.GetWeightsHeight()), "filter_h");
        f(std::to_string(self.GetWeightsWidth()), "filter_w");
        f(std::to_string(self.GetOutChannels()), "out_channels");
        f(std::to_string(self.GetInBatchSize()), "batchsize");
        f(std::to_string(self.GetPadH()), "pad_h");
        f(std::to_string(self.GetPadW()), "pad_w");
        f(std::to_string(self.GetKernelStrideH()), "conv_stride_1");
        f(std::to_string(self.GetKernelStrideW()), "conv_stride_0");
        f(std::to_string(self.GetDilationH()), "dilation_h");
        f(std::to_string(self.GetDilationW()), "dilation_w");
        f(std::to_string(self.GetBias()), "bias");
        f("'" + self.GetInLayout() + "'", "layout");
        std::string data_type = EncodeDataTypesForKey(
            self.GetInDataType(), self.GetWeightsDataType(), self.GetOutDataType());
        f("'" + data_type + "'", "data_type");

        switch(self.GetDirection())
        {
        case Direction::Forward: f("'F'", "direction"); break;
        case Direction::BackwardData: f("'B'", "direction"); break;
        case Direction::BackwardWeights: f("'W'", "direction"); break;
        }

        f(std::to_string(self.GetGroupCount()), "group_count");
    }

    private:
    TensorDescriptor in;
    TensorDescriptor weights;
    TensorDescriptor out;
    ConvolutionDescriptor conv;
    std::string in_layout;
    std::string weights_layout;
    std::string out_layout;
    Direction direction = Direction::Forward;
    int bias            = 0;
};

} // namespace conv
} // namespace miopen


text:
/***************************************************************************
 * Copyright 1998-2018 by authors (see AUTHORS.txt)                        *
 *                                                                         *
 *   This file is part of LuxCoreRender.                                   *
 *                                                                         *
 * Licensed under the Apache License, Version 2.0 (the "License");         *
 * you may not use this file except in compliance with the License.        *
 * You may obtain a copy of the License at                                 *
 *                                                                         *
 *     http://www.apache.org/licenses/LICENSE-2.0                          *
 *                                                                         *
 * Unless required by applicable law or agreed to in writing, software     *
 * distributed under the License is distributed on an "AS IS" BASIS,       *
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.*
 * See the License for the specific language governing permissions and     *
 * limitations under the License.                                          *
 ***************************************************************************/

#include <math.h>

#include <boost/format.hpp>
#include <boost/filesystem.hpp>

#include "slg/samplers/sobol.h"
#include "slg/utils/pathdepthinfo.h"
#include "slg/engines/caches/photongi/photongicache.h"
#include "slg/engines/caches/photongi/tracephotonsthread.h"
#include "slg/engines/caches/photongi/tracevisibilitythread.h"

using namespace std;
using namespace luxrays;
using namespace slg;

//------------------------------------------------------------------------------
// PhotonGICache
//------------------------------------------------------------------------------

PhotonGICache::PhotonGICache() :
		scene(nullptr),
		visibilityParticlesKdTree(nullptr),
		radiancePhotonsBVH(nullptr) ,
		causticPhotonsBVH(nullptr) {
}

PhotonGICache::PhotonGICache(const Scene *scn, const PhotonGICacheParams &p) :
		scene(scn), params(p),
		visibilityParticlesKdTree(nullptr),
		radiancePhotonsBVH(nullptr) ,
		indirectPhotonTracedCount(0),
		causticPhotonsBVH(nullptr),
		causticPhotonTracedCount(0) {
}

PhotonGICache::~PhotonGICache() {
	delete visibilityParticlesKdTree;

	delete causticPhotonsBVH;
	delete radiancePhotonsBVH;
}

bool PhotonGICache::IsPhotonGIEnabled(const BSDF &bsdf) const {
	const BSDFEvent eventTypes = bsdf.GetEventTypes();

	if ((eventTypes & TRANSMIT) || (eventTypes & SPECULAR) ||
			((eventTypes & GLOSSY) && (bsdf.GetGlossiness() < params.indirect.glossinessUsageThreshold)))
		return false;
	else
		return bsdf.IsPhotonGIEnabled();
}

float PhotonGICache::GetIndirectUsageThreshold(const BSDFEvent lastBSDFEvent,
		const float lastGlossiness, const float u0) const {
	// Decide if the glossy surface is "nearly specular"

	if ((lastBSDFEvent & GLOSSY) && (lastGlossiness < params.indirect.glossinessUsageThreshold)) {
		// Disable the cache, the surface is "nearly specular"
		return numeric_limits<float>::infinity();
	} else {
		// Use a larger blend zone for glossy surface
		const float scale = (lastBSDFEvent & GLOSSY) ? 2.f : 1.f;

		// Enable the cache for diffuse or glossy "nearly diffuse" but only after
		// the threshold (before I brute force and cache between 0x and 1x the threshold)
		return scale * u0 * params.indirect.usageThresholdScale * params.indirect.lookUpRadius;
	}
}

bool PhotonGICache::IsDirectLightHitVisible(const bool causticCacheAlreadyUsed,
		const BSDFEvent lastBSDFEvent, const PathDepthInfo &depthInfo) const {
	// This is a specific check to cut fireflies created by some glossy or
	// specular bounce
	if (!(lastBSDFEvent & DIFFUSE) && (depthInfo.diffuseDepth > 0))
		return false;
	else if (!params.caustic.enabled)
		return true;
	else if ((!causticCacheAlreadyUsed || !(lastBSDFEvent & SPECULAR)) &&
			(params.debugType == PGIC_DEBUG_NONE))
		return true;
	else
		return false;
}

void PhotonGICache::TraceVisibilityParticles() {
	const size_t renderThreadCount = boost::thread::hardware_concurrency();
	vector<TraceVisibilityThread *> renderThreads(renderThreadCount, nullptr);
	SLG_LOG("PhotonGI trace visibility particles thread count: " << renderThreadCount);

	// Initialize the Octree where to store the visibility points
	//
	// I use an Octree because it can be built at runtime while I than switch
	// to a KdTree so I can lookup entries with any radius.
	PGCIOctree *particlesOctree = new PGCIOctree(visibilityParticles, scene->dataSet->GetBBox(),
			params.visibility.lookUpRadius, params.visibility.lookUpNormalAngle);
	boost::mutex particlesOctreeMutex;

	SobolSamplerSharedData visibilitySobolSharedData(131, nullptr);

	boost::atomic<u_int> globalVisibilityParticlesCount(0);
	u_int visibilityCacheLookUp = 0;
	u_int visibilityCacheHits = 0;
	bool visibilityWarmUp = true;

	// Create the visibility particles tracing threads
	for (size_t i = 0; i < renderThreadCount; ++i) {
		renderThreads[i] = new TraceVisibilityThread(*this, i,
				visibilitySobolSharedData,
				particlesOctree, particlesOctreeMutex,
				globalVisibilityParticlesCount,
				visibilityCacheLookUp, visibilityCacheHits,
				visibilityWarmUp);
	}

	// Start visibility particles tracing threads
	for (size_t i = 0; i < renderThreadCount; ++i)
		renderThreads[i]->Start();

	// Wait for the end of visibility particles tracing threads
	for (size_t i = 0; i < renderThreadCount; ++i) {
		renderThreads[i]->Join();

		delete renderThreads[i];
	}

	visibilityParticles.shrink_to_fit();
	SLG_LOG("PhotonGI visibility total entries: " << visibilityParticles.size());

	if (visibilityParticles.size() == 0) {
		// Something wrong, nothing in the scene is visible and/or cache enabled
		return;
	}

	// Free the Octree and build the KdTree
	delete particlesOctree;
	SLG_LOG("PhotonGI building visibility particles KdTree");
	visibilityParticlesKdTree = new PGICKdTree(&visibilityParticles);
}

void PhotonGICache::TracePhotons(const u_int photonTracedCount,
		boost::atomic<u_int> &globalIndirectPhotonsTraced, boost::atomic<u_int> &globalCausticPhotonsTraced,
		boost::atomic<u_int> &globalIndirectSize, boost::atomic<u_int> &globalCausticSize) {
	const size_t renderThreadCount = boost::thread::hardware_concurrency();
	vector<TracePhotonsThread *> renderThreads(renderThreadCount, nullptr);

	boost::atomic<u_int> globalPhotonsCounter(0);

	// Create the photon tracing threads
	for (size_t i = 0; i < renderThreadCount; ++i) {
		renderThreads[i] = new TracePhotonsThread(*this, i, photonTracedCount,
				globalPhotonsCounter, globalIndirectPhotonsTraced,
				globalCausticPhotonsTraced, globalIndirectSize,
				globalCausticSize);
	}

	// Start photon tracing threads
	for (size_t i = 0; i < renderThreadCount; ++i)
		renderThreads[i]->Start();

	// Wait for the end of photon tracing threads
	u_int indirectPhotonStored = 0;
	u_int causticPhotonStored = 0;
	for (size_t i = 0; i < renderThreadCount; ++i) {
		renderThreads[i]->Join();

		// Copy all photons
		for (auto const &p : renderThreads[i]->indirectPhotons) {
			VisibilityParticle &vp = visibilityParticles[p.visibilityParticelIndex];

			vp.alphaAccumulated += p.alpha;
		}
		indirectPhotonStored += renderThreads[i]->indirectPhotons.size();

		causticPhotons.insert(causticPhotons.end(), renderThreads[i]->causticPhotons.begin(),
				renderThreads[i]->causticPhotons.end());
		causticPhotonStored += renderThreads[i]->causticPhotons.size();

		delete renderThreads[i];
	}

	indirectPhotonTracedCount = globalIndirectPhotonsTraced;
	causticPhotonTracedCount = globalCausticPhotonsTraced;

	SLG_LOG("PhotonGI additional indirect photon stored: " << indirectPhotonStored);
	SLG_LOG("PhotonGI additional caustic photon stored: " << causticPhotonStored);
	// photonReacedCount isn't exactly but it is quite near
	SLG_LOG("PhotonGI total photon traced: " << Max(indirectPhotonTracedCount, indirectPhotonTracedCount));
}

void PhotonGICache::TracePhotons() {
	SLG_LOG("PhotonGI trace photons thread count: " << boost::thread::hardware_concurrency());

	boost::atomic<u_int> globalIndirectPhotonsTraced(0);
	boost::atomic<u_int> globalCausticPhotonsTraced(0);
	boost::atomic<u_int> globalIndirectSize(0);
	boost::atomic<u_int> globalCausticSize(0);

	indirectPhotonTracedCount = 0;
	causticPhotonTracedCount = 0;

	if (params.indirect.enabled && (params.indirect.maxSize == 0)) {
		// Automatic indirect cache convergence test is required

		const u_int photonTracedStep = 2000000;
		u_int photonTracedCount = 0;
		vector<Spectrum> lastAlpha(visibilityParticles.size());
		vector<Spectrum> currentAlpha(visibilityParticles.size());
		while(photonTracedCount < params.photon.maxTracedCount) {
			//------------------------------------------------------------------
			// Trace additional photons
			//------------------------------------------------------------------

			TracePhotons(photonTracedStep,
				globalIndirectPhotonsTraced, globalCausticPhotonsTraced,
				globalIndirectSize, globalCausticSize);
			photonTracedCount += photonTracedStep;

			//------------------------------------------------------------------
			// Check the convergence if it is not the first step
			//------------------------------------------------------------------

			if (photonTracedCount > photonTracedStep) {
				// Compute current alpha

				for (u_int i = 0; i < visibilityParticles.size(); ++i) {
					const VisibilityParticle vp = visibilityParticles[i];

					currentAlpha[i] = vp.ComputeRadiance(params.indirect.lookUpRadius2, indirectPhotonTracedCount);
				}

				// Filter outgoing radiance

				if (params.indirect.filterRadiusScale > 0.f) {
					vector<Spectrum> filteredCurrentAlpha(visibilityParticles.size());
					FilterVisibilityParticlesRadiance(currentAlpha, filteredCurrentAlpha);

					currentAlpha = filteredCurrentAlpha;
				}

				// Compute the scale for an auto-linear-like tone mapping of values

				float Y = 0.f;
				for (u_int i = 0; i < visibilityParticles.size(); ++i)
					Y += currentAlpha[i].Y();
				Y /= visibilityParticles.size();

				const float alphaScale = (Y > 0.f) ? (1.25f / Y * powf(118.f / 255.f, 2.2f)) : 1.f;
				for (u_int i = 0; i < visibilityParticles.size(); ++i)
					currentAlpha[i] *= alphaScale;

				// Look for the max. error

				float maxError = 0.f;
				for (u_int i = 0; i < visibilityParticles.size(); ++i) {
					if (!currentAlpha[i].Black()) {
						const float currentError =  (currentAlpha[i] - lastAlpha[i]).Abs().Max();

						maxError = Max(maxError, currentError);
					}

					// Update last alpha cache entries
					lastAlpha[i] = currentAlpha[i];
				}

				SLG_LOG(boost::format("PhotonGI estimated current indirect photon error: %.2f%%") % (100.f * maxError));

				// If the error is under the threshold, stop tracing photons
				if (maxError < params.indirect.haltThreshold)
					break;
			} else {
				// Update last alpha cache entries

				for (u_int i = 0; i < visibilityParticles.size(); ++i) {
					const VisibilityParticle vp = visibilityParticles[i];

					lastAlpha[i] = vp.ComputeRadiance(params.indirect.lookUpRadius2, indirectPhotonTracedCount);
				}
			}
		}
	} else {
		// Just trace the asked amount of photon paths
		TracePhotons(params.photon.maxTracedCount,
				globalIndirectPhotonsTraced, globalCausticPhotonsTraced,
				globalIndirectSize, globalCausticSize);
	}

	causticPhotons.shrink_to_fit();
}

void PhotonGICache::FilterVisibilityParticlesRadiance(const vector<Spectrum> &radianceValues,
			vector<Spectrum> &filteredRadianceValues) const {
	const float lookUpRadius2 = Sqr(params.indirect.filterRadiusScale * params.indirect.lookUpRadius);
	const float lookUpCosNormalAngle = cosf(Radians(params.indirect.lookUpNormalAngle));

	#pragma omp parallel for
	for (
			// Visual C++ 2013 supports only OpenMP 2.5
#if _OPENMP >= 200805
			unsigned
#endif
			int index = 0; index < visibilityParticles.size(); ++index) {
		// Look for all near particles

		vector<u_int> nearParticleIndices;
		const VisibilityParticle &vp = visibilityParticles[index];
		// I can use visibilityParticlesKdTree to get radiance photons indices
		// because there is a one on one correspondence
		visibilityParticlesKdTree->GetAllNearEntries(nearParticleIndices,
				vp.p, vp.n, vp.isVolume,
				lookUpRadius2, lookUpCosNormalAngle);

		if (nearParticleIndices.size() > 0) {
			Spectrum &v = filteredRadianceValues[index];
			for (auto nearIndex : nearParticleIndices)
				v += radianceValues[nearIndex];

			v /= nearParticleIndices.size();
		}
	}
}

void PhotonGICache::CreateRadiancePhotons() {
	//--------------------------------------------------------------------------
	// Compute the outgoing radiance for each visibility entry
	//--------------------------------------------------------------------------

	vector<Spectrum> outgoingRadianceValues(visibilityParticles.size());

	for (u_int index = 0 ; index < visibilityParticles.size(); ++index) {
		const VisibilityParticle &vp = visibilityParticles[index];

		outgoingRadianceValues[index] = vp.ComputeRadiance(params.indirect.lookUpRadius2, indirectPhotonTracedCount);
		assert (outgoingRadianceValues[index].IsValid());
	}

	//--------------------------------------------------------------------------
	// Filter outgoing radiance
	//--------------------------------------------------------------------------

	if (params.indirect.filterRadiusScale > 0.f) {
		SLG_LOG("PhotonGI filtering radiance photons");

		vector<Spectrum> filteredOutgoingRadianceValues(visibilityParticles.size());
		FilterVisibilityParticlesRadiance(outgoingRadianceValues, filteredOutgoingRadianceValues);

		outgoingRadianceValues = filteredOutgoingRadianceValues;
	}

	//--------------------------------------------------------------------------
	// Create a radiance map entry for each visibility entry
	//--------------------------------------------------------------------------

	for (u_int index = 0 ; index < visibilityParticles.size(); ++index) {
		if (!outgoingRadianceValues[index].Black()) {
			const VisibilityParticle &vp = visibilityParticles[index];

			radiancePhotons.push_back(RadiancePhoton(vp.p,
					vp.n, outgoingRadianceValues[index], vp.isVolume));
		}
	}
	radiancePhotons.shrink_to_fit();

	SLG_LOG("PhotonGI total radiance photon stored: " << radiancePhotons.size());
}

void PhotonGICache::MergeCausticPhotons() {
	// Build a BVH in order to make fast look ups. Note the numeric_limits<u_int>::max()
	// to get all of them.
	PGICPhotonBvh *photonsBVH = new PGICPhotonBvh(&causticPhotons, numeric_limits<u_int>::max(),
			params.caustic.lookUpRadius * params.caustic.mergeRadiusScale,
			params.caustic.lookUpNormalAngle);

	// Merge all "similar" photons

	vector<bool> usedPhotons(causticPhotons.size(), false);
	vector<NearPhoton> entries;
	float maxDistance2;
	vector<Photon> mergedCausticPhotons;
	const float normalCosAngle = cosf(Radians(params.caustic.lookUpNormalAngle));
	double lastPrintTime = WallClockTime();

	for (u_int i = 0; i < causticPhotons.size(); ++i) {
		// Check if I have already used this photon
		if (usedPhotons[i])
			continue;

		const Photon &photon = causticPhotons[i];

		// Look for near photons
		entries.clear();
		photonsBVH->GetAllNearEntries(entries, photon.p, photon.landingSurfaceNormal,
				photon.isVolume, maxDistance2);

		// I must find the same photon at least
		assert (entries.size() >= 1);

		Photon newPhoton(photon.p, photon.d, Spectrum(), photon.landingSurfaceNormal, photon.isVolume);
		for (u_int j = 0; j < entries.size(); ++j) {
			const u_int entryIndex = entries[j].photonIndex;

			// Check if I have already used this photon
			if (usedPhotons[entryIndex])
				continue;

			// Check if the direction vector is near "enough"
			if (Dot(causticPhotons[entryIndex].d, photon.d) <= normalCosAngle)
				continue;

			// Accumulate all photon alpha
			newPhoton.alpha += causticPhotons[entryIndex].alpha;
			// Accumulate all photon directions
			newPhoton.d += causticPhotons[entryIndex].d;

			// Mark the photon as used
			usedPhotons[entries[j].photonIndex] = true;
		}
		newPhoton.d = Normalize(newPhoton.d);

		// Add the new merged photon
		mergedCausticPhotons.push_back(newPhoton);

		const double now = WallClockTime();
		if (now - lastPrintTime > 2.0) {
			SLG_LOG(boost::format("PhotonGI caustic photons merged: %d/%d [%.1f%%]") %
					i % causticPhotons.size() %
					((100.0 * i) / causticPhotons.size()));
			lastPrintTime = now;
		}
	}

	delete photonsBVH;

	SLG_LOG(boost::format("PhotonGI merged caustics photons: %d => %d [%.1f%%]") %
			causticPhotons.size() % mergedCausticPhotons.size() %
			(100.0 * mergedCausticPhotons.size() / causticPhotons.size()));

	causticPhotons = mergedCausticPhotons;
	mergedCausticPhotons.shrink_to_fit();
}

void PhotonGICache::Preprocess() {
	if (params.persistent.fileName != "") {
		// Check if the file already exist
		if (boost::filesystem::exists(params.persistent.fileName)) {
			// Load the cache from the file
			LoadPersistentCache(params.persistent.fileName);

			return;
		}

		// The file doesn't exist so I have to go trough normal pre-processing
	}

	//--------------------------------------------------------------------------
	// Evaluate best radius if required
	//--------------------------------------------------------------------------

	if (params.indirect.enabled && (params.indirect.lookUpRadius == 0.f)) {
		params.indirect.lookUpRadius = EvaluateBestRadius();
		SLG_LOG("PhotonGI best indirect cache radius: " << params.indirect.lookUpRadius);
	}

	//--------------------------------------------------------------------------
	// Initialize all parameters
	//--------------------------------------------------------------------------

	if (!params.indirect.enabled)
		params.indirect.maxSize = 0;

	if (!params.caustic.enabled)
		params.caustic.maxSize = 0;

	if (params.indirect.enabled) {
		if (params.caustic.enabled) {
			params.visibility.lookUpRadius = Max(params.indirect.lookUpRadius, params.caustic.lookUpRadius);
			params.visibility.lookUpNormalAngle = Max(params.indirect.lookUpNormalAngle, params.caustic.lookUpNormalAngle);
		} else {
			params.visibility.lookUpRadius = params.indirect.lookUpRadius;
			params.visibility.lookUpNormalAngle = params.indirect.lookUpNormalAngle;
		}
	} else {
		if (params.caustic.enabled) {
			params.visibility.lookUpRadius = params.caustic.lookUpRadius;
			params.visibility.lookUpNormalAngle = params.caustic.lookUpNormalAngle;
		} else
			throw runtime_error("Indirect and/or caustic cache must be enabled in PhotonGI");
	}
	params.visibility.lookUpNormalCosAngle = cosf(Radians(params.visibility.lookUpNormalAngle));

	params.visibility.lookUpRadius2 = params.visibility.lookUpRadius * params.visibility.lookUpRadius;
	params.indirect.lookUpRadius2 = params.indirect.lookUpRadius * params.indirect.lookUpRadius;
	params.caustic.lookUpRadius2 = params.caustic.lookUpRadius * params.caustic.lookUpRadius;

	//--------------------------------------------------------------------------
	// Trace visibility particles
	//--------------------------------------------------------------------------

	TraceVisibilityParticles();
	if (visibilityParticles.size() == 0) {
		SLG_LOG("PhotonGI WARNING: nothing is visible and/or cache enabled.");
		return;
	}

	//--------------------------------------------------------------------------
	// Fill all photon vectors
	//--------------------------------------------------------------------------

	TracePhotons();

	//--------------------------------------------------------------------------
	// Radiance photon map
	//--------------------------------------------------------------------------

	if (params.indirect.enabled) {
		SLG_LOG("PhotonGI building radiance photon data");
		CreateRadiancePhotons();

		if (radiancePhotons.size() > 0) {
			SLG_LOG("PhotonGI building radiance photons BVH");
			radiancePhotonsBVH = new PGICRadiancePhotonBvh(&radiancePhotons,
					params.indirect.lookUpRadius, params.indirect.lookUpNormalAngle);
		}
	}

	//--------------------------------------------------------------------------
	// Caustic photon map
	//--------------------------------------------------------------------------

	if ((causticPhotons.size() > 0) && params.caustic.enabled) {
		if (params.caustic.mergeRadiusScale > 0.f) {
			SLG_LOG("PhotonGI merging caustic photons BVH");
			MergeCausticPhotons();
		}

		SLG_LOG("PhotonGI building caustic photons BVH");
		causticPhotonsBVH = new PGICPhotonBvh(&causticPhotons, params.caustic.lookUpMaxCount,
				params.caustic.lookUpRadius, params.caustic.lookUpNormalAngle);
	}

	//--------------------------------------------------------------------------
	// Free visibility map
	//--------------------------------------------------------------------------

	delete visibilityParticlesKdTree;
	visibilityParticlesKdTree = nullptr;
	visibilityParticles.clear();
	visibilityParticles.shrink_to_fit();

	//--------------------------------------------------------------------------
	// Print some statistics about memory usage
	//--------------------------------------------------------------------------

	size_t totalMemUsage = 0;

	if (causticPhotonsBVH) {
		SLG_LOG("PhotonGI caustic cache photons memory usage: " << ToMemString(causticPhotons.size() * sizeof(Photon)));
		SLG_LOG("PhotonGI caustic cache BVH memory usage: " << ToMemString(causticPhotonsBVH->GetMemoryUsage()));

		totalMemUsage += causticPhotons.size() * sizeof(Photon) + causticPhotonsBVH->GetMemoryUsage();
	}

	if (radiancePhotonsBVH) {
		SLG_LOG("PhotonGI indirect cache photons memory usage: " << ToMemString(radiancePhotons.size() * sizeof(RadiancePhoton)));
		SLG_LOG("PhotonGI indirect cache BVH memory usage: " << ToMemString(radiancePhotonsBVH->GetMemoryUsage()));

		totalMemUsage += radiancePhotons.size() * sizeof(Photon) + radiancePhotonsBVH->GetMemoryUsage();
	}

	SLG_LOG("PhotonGI total memory usage: " << ToMemString(totalMemUsage));

	//--------------------------------------------------------------------------
	// Check if I have to save the persistent cache
	//--------------------------------------------------------------------------

	if (params.persistent.fileName != "")
		SavePersistentCache(params.persistent.fileName);
}

// Simpson filter from PBRT v2. Filter the photons according their
// distance, giving more weight to the nearest.
static inline float SimpsonKernel(const Point &p1, const Point &p2,
		const float maxDist2) {
	const float dist2 = DistanceSquared(p1, p2);

	// The distance between p1 and p2 is supposed to be < maxDist2
	assert (dist2 <= maxDist2);
    const float s = (1.f - dist2 / maxDist2);

    return 3.f * INV_PI * s * s;
}

Spectrum PhotonGICache::ProcessCacheEntries(const vector<NearPhoton> &entries,
		const float maxDistance2,
		const vector<Photon> &photons, const u_int photonTracedCount,
		const BSDF &bsdf) const {
	Spectrum result;

	if (entries.size() > 0) {
		if (bsdf.GetMaterialType() == MaterialType::MATTE) {
			// A fast path for matte material

			for (auto const &nearPhoton : entries) {
				const Photon &photon = photons[nearPhoton.photonIndex];

				// Using a Simpson filter here
				result += SimpsonKernel(bsdf.hitPoint.p, photon.p, maxDistance2) *
						photon.alpha;
			}

			result *= bsdf.EvaluateTotal() * INV_PI;
		} else {
			// Generic path

			BSDFEvent event;
			for (auto const &nearPhoton : entries) {
				const Photon &photon = photons[nearPhoton.photonIndex];

				// bsdf.Evaluate() multiplies the result by AbsDot(bsdf.hitPoint.shadeN, -photon->d)
				// so I have to cancel that factor. It is already included in photon density
				// estimation.
				const Spectrum bsdfEval = bsdf.Evaluate(-photon.d, &event, nullptr, nullptr) / AbsDot(bsdf.hitPoint.shadeN, -photon.d);

				// Using a Simpson filter here
				result += SimpsonKernel(bsdf.hitPoint.p, photon.p, maxDistance2) *
						bsdfEval * photon.alpha;
			}
		}

		result /= photonTracedCount * maxDistance2;
	}

	return result;
}

Spectrum PhotonGICache::GetIndirectRadiance(const BSDF &bsdf) const {
	assert (IsPhotonGIEnabled(bsdf));

	Spectrum result;
	if (radiancePhotonsBVH) {
		// Flip the normal if required
		const Normal n = (bsdf.hitPoint.intoObject ? 1.f: -1.f) * bsdf.hitPoint.geometryN;
		const RadiancePhoton *radiancePhoton = radiancePhotonsBVH->GetNearestEntry(bsdf.hitPoint.p, n, bsdf.IsVolume());

		if (radiancePhoton) {
			result = radiancePhoton->outgoingRadiance;

			assert (result.IsValid());
			assert (DistanceSquared(radiancePhoton->p, bsdf.hitPoint.p) < radiancePhotonsBVH->GetEntryRadius());
			assert (bsdf.IsVolume() == radiancePhoton->isVolume);
			assert (radiancePhoton->isVolume || (Dot(radiancePhoton->n, n) > radiancePhotonsBVH->GetEntryNormalCosAngle()));
		}
	}

	return result;
}

Spectrum PhotonGICache::GetCausticRadiance(const BSDF &bsdf) const {
	assert (IsPhotonGIEnabled(bsdf));

	Spectrum result;
	if (causticPhotonsBVH) {
		vector<NearPhoton> entries;
		entries.reserve(causticPhotonsBVH->GetEntryMaxLookUpCount());

		// Flip the normal if required
		const Normal n = (bsdf.hitPoint.intoObject ? 1.f: -1.f) * bsdf.hitPoint.geometryN;
		float maxDistance2;
		causticPhotonsBVH->GetAllNearEntries(entries, bsdf.hitPoint.p, n, bsdf.IsVolume(),
				maxDistance2);

		result = ProcessCacheEntries(entries, maxDistance2, causticPhotons, causticPhotonTracedCount, bsdf);

		assert (result.IsValid());
	}

	return result;
}

PhotonGISamplerType PhotonGICache::String2SamplerType(const string &type) {
	if (type == "RANDOM")
		return PhotonGISamplerType::PGIC_SAMPLER_RANDOM;
	else if (type == "METROPOLIS")
		return PhotonGISamplerType::PGIC_SAMPLER_METROPOLIS;
	else
		throw runtime_error("Unknown PhotonGI cache debug type: " + type);
}

string PhotonGICache::SamplerType2String(const PhotonGISamplerType type) {
	switch (type) {
		case PhotonGISamplerType::PGIC_SAMPLER_RANDOM:
			return "RANDOM";
		case PhotonGISamplerType::PGIC_SAMPLER_METROPOLIS:
			return "METROPOLIS";
		default:
			throw runtime_error("Unsupported wrap type in PhotonGICache::SamplerType2String(): " + ToString(type));
	}
}

PhotonGIDebugType PhotonGICache::String2DebugType(const string &type) {
	if (type == "none")
		return PhotonGIDebugType::PGIC_DEBUG_NONE;
	else if (type == "showindirect")
		return PhotonGIDebugType::PGIC_DEBUG_SHOWINDIRECT;
	else if (type == "showcaustic")
		return PhotonGIDebugType::PGIC_DEBUG_SHOWCAUSTIC;
	else if (type == "showindirectpathmix")
		return PhotonGIDebugType::PGIC_DEBUG_SHOWINDIRECTPATHMIX;
	else
		throw runtime_error("Unknown PhotonGI cache debug type: " + type);
}

string PhotonGICache::DebugType2String(const PhotonGIDebugType type) {
	switch (type) {
		case PhotonGIDebugType::PGIC_DEBUG_NONE:
			return "none";
		case PhotonGIDebugType::PGIC_DEBUG_SHOWINDIRECT:
			return "showindirect";
		case PhotonGIDebugType::PGIC_DEBUG_SHOWCAUSTIC:
			return "showcaustic";
		case PhotonGIDebugType::PGIC_DEBUG_SHOWINDIRECTPATHMIX:
			return "showindirectpathmix";
		default:
			throw runtime_error("Unsupported wrap type in PhotonGICache::DebugType2String(): " + ToString(type));
	}
}

ext: cpp

max_stars_repo_name: bartoszek/LuxCore

max_issues_repo_name: MetaRabbit/LuxCore

max_forks_repo_name: MetaRabbit/LuxCore


text:
// Copyright (c) 2016-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <wallet/test/wallet_test_fixture.h>

#include <consensus/merkle.h>
#include <injector.h>
#include <key_io.h>
#include <rpc/server.h>
#include <test/test_unite_mocks.h>
#include <validation.h>
#include <wallet/db.h>
#include <wallet/rpcvalidator.h>
#include <wallet/rpcwalletext.h>
#include <boost/test/unit_test.hpp>

WalletTestingSetup::WalletTestingSetup(const std::string& chainName, UnitEInjectorConfiguration config)
    : WalletTestingSetup([](Settings& s){}, chainName, config) {}

WalletTestingSetup::WalletTestingSetup(
    std::function<void(Settings&)> f,
    const std::string& chainName,
    UnitEInjectorConfiguration config)
    : TestingSetup(chainName, config)
{
    bool fFirstRun;

    stake_validator_mock.mock_IsStakeMature.SetResult(true);

    f(settings);
    esperanza::WalletExtensionDeps deps(&settings, &stake_validator_mock);

    m_wallet.reset(new CWallet("mock", WalletDatabase::CreateMock(), deps));
    m_wallet->LoadWallet(fFirstRun);
    RegisterValidationInterface(m_wallet.get());
    AddWallet(m_wallet);

    RegisterWalletRPCCommands(tableRPC);
    RegisterValidatorRPCCommands(tableRPC);
    RegisterWalletextRPCCommands(tableRPC);
}

WalletTestingSetup::~WalletTestingSetup()
{
    RemoveWallet(m_wallet);
    UnregisterValidationInterface(m_wallet.get());
}

TestChain100Setup::TestChain100Setup(UnitEInjectorConfiguration config)
    : WalletTestingSetup(CBaseChainParams::REGTEST, config),
      m_block_builder(proposer::BlockBuilder::New(&settings)),
      m_active_chain(staking::ActiveChain::New()),
      m_behavior(blockchain::Behavior::NewForNetwork(blockchain::Network::regtest))
{

  coinbaseKey = DecodeSecret("cQTjnbHifWGuMhm9cRgQ23ip5KntTMfj3zwo6iQyxMVxSfJyptqL");
  assert(coinbaseKey.IsValid());
  {
    LOCK(m_wallet->cs_wallet);
    assert(m_wallet->AddKey(coinbaseKey));
  }

  WalletRescanReserver reserver(m_wallet.get());
  reserver.reserve();
  m_wallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver);

  // Generate a 100-block chain:
  GetComponent<Settings>()->stake_split_threshold = 0; // reset to 0
  CScript script_pubkey = GetScriptForDestination(WitnessV0KeyHash(coinbaseKey.GetPubKey().GetID()));
  for (int i = 0; i < COINBASE_MATURITY; i++)
  {
    std::vector<CMutableTransaction> noTxns;
    CBlock b = CreateAndProcessBlock(noTxns, script_pubkey);
    m_coinbase_txns.push_back(*b.vtx[0]);
    auto x = b.GetHash().ToString();
    assert(!x.empty());
  }
}

std::shared_ptr<const CBlock> TestChain100Setup::CreateBlock(
    const std::vector<CMutableTransaction>& txns,
    const CScript& coinbase_script,
    boost::optional<staking::Coin> stake)
{
  esperanza::WalletExtension &wallet_ext = m_wallet->GetWalletExtension();

  if (!stake) {
    LOCK2(m_active_chain->GetLock(), wallet_ext.GetLock());
    staking::CoinSet coin_set = wallet_ext.GetStakeableCoins();
    BOOST_REQUIRE(!coin_set.empty());
    stake.emplace(*coin_set.begin());
  }

  const CAmount fees = 0;
  uint256 snapshot_hash;
  {
    LOCK(m_active_chain->GetLock());
    snapshot_hash = m_active_chain->ComputeSnapshotHash();
  }

  std::vector<CTransactionRef> tx_refs;
  for (const CMutableTransaction& tx : txns) {
    tx_refs.push_back(MakeTransactionRef(tx));
  }

  const CBlockIndex *tip = m_active_chain->GetTip();
  blockchain::Height tip_height = tip->nHeight;

  proposer::EligibleCoin coin = {
      *stake,
      uint256(),
      m_behavior->CalculateBlockReward(tip_height),
      static_cast<blockchain::Height>(tip_height+1),
      static_cast<blockchain::Time>(std::max(tip->GetMedianTimePast()+1, GetTime())),
      tip->nBits
  };

  std::shared_ptr<const CBlock> block = m_block_builder->BuildBlock(
      *m_active_chain->GetTip(),
      snapshot_hash,
      coin,
      {},
      tx_refs,
      fees,
      coinbase_script,
      wallet_ext);

  return block;
}

bool TestChain100Setup::ProcessBlock(const std::shared_ptr<const CBlock> block) {
  const bool was_processed = ProcessNewBlock(Params(), block, true, nullptr);

  SyncWithValidationInterfaceQueue(); // To prevent Wallet::ConnectBlock from running concurrently

  return was_processed;
}

CBlock TestChain100Setup::CreateAndProcessBlock(const std::vector<CMutableTransaction>& txns,
                                                const CScript& coinbase_script,
                                                const boost::optional<staking::Coin> stake,
                                                bool *processed) {
  std::shared_ptr<const CBlock> block = CreateBlock(txns, coinbase_script, stake);

  const bool was_processed = ProcessBlock(block);
  BOOST_REQUIRE(processed != nullptr || was_processed);
  if (processed) {
    *processed = was_processed;
  }

  return *block;
}

TestChain100Setup::~TestChain100Setup()
{
}

max_stars_repo_name: dtr-org/unit-e


text:
/**
 *  @file oglplus/config/compiler.hpp
 *  @brief Configuration options based on compiler capabilities
 *
 *  @author Matus Chochlik
 *
 *  Copyright 2010-2019 Matus Chochlik. Distributed under the Boost
 *  Software License, Version 1.0. (See accompanying file
 *  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
 */

#pragma once
#ifndef OGLPLUS_CONFIG_COMPILER_1107121519_HPP
#define OGLPLUS_CONFIG_COMPILER_1107121519_HPP

#ifndef OGLPLUS_NO_SITE_CONFIG
#include <oglplus/config/site.hpp>
#endif

#ifndef OGLPLUS_USE_BOOST_CONFIG
#define OGLPLUS_USE_BOOST_CONFIG 0
#endif

#if OGLPLUS_USE_BOOST_CONFIG
#include <boost/config.hpp>
#endif

// ------- C++XY feature availability detection -------

#ifndef OGLPLUS_NO_VARIADIC_MACROS
#if defined(BOOST_NO_CXX11_VARIADIC_MACROS) || defined(BOOST_NO_VARIADIC_MACROS)
#define OGLPLUS_NO_VARIADIC_MACROS 1
#else
#define OGLPLUS_NO_VARIADIC_MACROS 0
#endif
#endif

#ifndef OGLPLUS_NO_FUNCTION_TEMPLATE_DEFAULT_ARGS
#if defined(BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS) || \
  defined(BOOST_NO_FUNCTION_TEMPLATE_DEFAULT_ARGS)
#define OGLPLUS_NO_FUNCTION_TEMPLATE_DEFAULT_ARGS 1
#else
#define OGLPLUS_NO_FUNCTION_TEMPLATE_DEFAULT_ARGS 0
#endif
#endif

#ifndef OGLPLUS_NO_GENERALIZED_ATTRIBUTES
#define OGLPLUS_NO_GENERALIZED_ATTRIBUTES 1
#endif

// ------- C++XY feature availability detection -------

#if !OGLPLUS_NO_GENERALIZED_ATTRIBUTES
#define OGLPLUS_NORETURN [[noreturn]]
#if defined(__GNUC__)
#if __GNUC_PREREQ(7, 0)
#define OGLPLUS_FALLTHROUGH __attribute__((fallthrough));
#endif
#elif defined(__clang__)
#define OGLPLUS_FALLTHROUGH [[clang::fallthrough]];
#endif
#else
#define OGLPLUS_NORETURN
#endif

#ifndef OGLPLUS_FALLTHROUGH
#define OGLPLUS_FALLTHROUGH
#endif

// -------- disable certain warnings ---------

#ifdef _MSC_VER
#pragma warning(disable : 4396)
#endif //_MSC_VER

// -------- helper definitions ---------

#ifndef OGLPLUS_DOCUMENTATION_ONLY
#define OGLPLUS_DOCUMENTATION_ONLY 0
#endif

// helper macro that marks unused parameters
// so that the compiler does not complain
#define OGLPLUS_FAKE_USE(X) (void)X

#endif // include guard

ext: hpp

max_stars_repo_name: matus-chochlik/oglplus

max_issues_repo_name: matus-chochlik/oglplus

max_forks_repo_name: matus-chochlik/oglplus