<commit_before>#include <osg/AlphaFunc>
#include <osg/Billboard>
#include <osg/BlendFunc>
#include <osg/Depth>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/GL2Extensions>
#include <osg/Material>
#include <osg/Math>
#include <osg/MatrixTransform>
#include <osg/PolygonOffset>
#include <osg/Program>
#include <osg/Projection>
#include <osg/Shader>
#include <osg/ShapeDrawable>
#include <osg/StateSet>
#include <osg/Switch>
#include <osg/Texture2D>
#include <osg/Uniform>

#include <osgDB/ReadFile>
#include <osgDB/FileUtils>

#include <osgUtil/IntersectVisitor>
#include <osgUtil/SmoothingVisitor>

#include <osgText/Text>

#include <osgViewer/Viewer>

#include <iostream>

// for the grid data..
#include "../osghangglide/terrain_coords.h"

osg::Node* createScene()
{
    osg::Group* scene = new osg::Group;

    unsigned int numColumns = 38;
    unsigned int numRows = 39;
    unsigned int r;
    unsigned int c;

    osg::Vec3 origin(0.0f,0.0f,0.0f);
    osg::Vec3 size(1000.0f,1000.0f,250.0f);
    osg::Vec3 scaleDown(1.0f/size.x(),1.0f/size.y(),1.0f/size.z());

    // ---------------------------------------
    // Set up a StateSet to texture the objects
    // ---------------------------------------
    osg::StateSet* stateset = new osg::StateSet();

    osg::Uniform* originUniform = new osg::Uniform("terrainOrigin",origin);
    stateset->addUniform(originUniform);

    osg::Uniform* sizeUniform = new osg::Uniform("terrainSize",size);
    stateset->addUniform(sizeUniform);

    osg::Uniform* scaleDownUniform = new osg::Uniform("terrainScaleDown",scaleDown);
    stateset->addUniform(scaleDownUniform);

    osg::Uniform* terrainTextureSampler = new osg::Uniform("terrainTexture",0);
    stateset->addUniform(terrainTextureSampler);

    osg::Uniform* baseTextureSampler = new osg::Uniform("baseTexture",1);
    stateset->addUniform(baseTextureSampler);

    osg::Uniform* treeTextureSampler = new osg::Uniform("treeTexture",1);
    stateset->addUniform(treeTextureSampler);

    // compute z range of z values of grid data so we can scale it.
    float min_z = FLT_MAX;
    float max_z = -FLT_MAX;
    for(r=0;r<numRows;++r)
    {
        for(c=0;c<numColumns;++c)
        {
            min_z = osg::minimum(min_z,vertex[r+c*numRows][2]);
            max_z = osg::maximum(max_z,vertex[r+c*numRows][2]);
        }
    }

    float scale_z = size.z()/(max_z-min_z);

    osg::Image* terrainImage = new osg::Image;
    terrainImage->allocateImage(numColumns,numRows,1,GL_LUMINANCE, GL_FLOAT);
    terrainImage->setInternalTextureFormat(GL_LUMINANCE_FLOAT32_ATI);
    for(r=0;r<numRows;++r)
    {
        for(c=0;c<numColumns;++c)
        {
            *((float*)(terrainImage->data(c,r))) = (vertex[r+c*numRows][2]-min_z)*scale_z;
        }
    }

    osg::Texture2D* terrainTexture = new osg::Texture2D;
    terrainTexture->setImage(terrainImage);
    terrainTexture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::NEAREST);
    terrainTexture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::NEAREST);
    terrainTexture->setResizeNonPowerOfTwoHint(false);
    stateset->setTextureAttributeAndModes(0,terrainTexture,osg::StateAttribute::ON);

    osg::Image* image = osgDB::readImageFile("Images/lz.rgb");
    if (image)
    {
        osg::Texture2D* texture = new osg::Texture2D;

        texture->setImage(image);
        stateset->setTextureAttributeAndModes(1,texture,osg::StateAttribute::ON);
    }

    {
        std::cout<<"Creating terrain...";

        osg::Geode* geode = new osg::Geode();
        geode->setStateSet( stateset );

        {
            osg::Program* program = new osg::Program;
            stateset->setAttribute(program);

#if 1
            // use inline shaders

            ///////////////////////////////////////////////////////////////////
            // vertex shader using just Vec4 coefficients
            char vertexShaderSource[] =
               "uniform float osg_FrameTime;\n"
               "uniform sampler2D terrainTexture;\n"
               "uniform vec3 terrainOrigin;\n"
               "uniform vec3 terrainScaleDown;\n"
               "\n"
               "varying vec2 texcoord;\n"
               "\n"
               "void main(void)\n"
               "{\n"
               "    texcoord = gl_Vertex.xy - terrainOrigin.xy;\n"
               "    texcoord.x *= terrainScaleDown.x;\n"
               "    texcoord.y *= terrainScaleDown.y;\n"
               "\n"
               "    vec4 position;\n"
               "    position.x = gl_Vertex.x;\n"
               "    position.y = gl_Vertex.y;\n"
               "    position.z = texture2D(terrainTexture, texcoord).r;\n"
               "    position.w = 1.0;\n"
               " \n"
               "    gl_Position     = gl_ModelViewProjectionMatrix * position;\n"
                "   gl_FrontColor = vec4(1.0,1.0,1.0,1.0);\n"
               "}\n";

            //////////////////////////////////////////////////////////////////
            // fragment shader
            //
            char fragmentShaderSource[] =
                "uniform sampler2D baseTexture; \n"
                "varying vec2 texcoord;\n"
                "\n"
                "void main(void) \n"
                "{\n"
                "    gl_FragColor = texture2D( baseTexture, texcoord); \n"
                "}\n";

            program->addShader(new osg::Shader(osg::Shader::VERTEX, vertexShaderSource));
            program->addShader(new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource));

#else

            // get shaders from source
            program->addShader(osg::Shader::readShaderFile(osg::Shader::VERTEX, osgDB::findDataFile("shaders/terrain.vert")));
            program->addShader(osg::Shader::readShaderFile(osg::Shader::FRAGMENT, osgDB::findDataFile("shaders/terrain.frag")));

#endif

            // get shaders from source
        }

        {
            osg::Geometry* geometry = new osg::Geometry;

            osg::Vec3Array& v = *(new osg::Vec3Array(numColumns*numRows));
            osg::Vec4ubArray& color = *(new osg::Vec4ubArray(1));

            color[0].set(255,255,255,255);

            float rowCoordDelta = size.y()/(float)(numRows-1);
            float columnCoordDelta = size.x()/(float)(numColumns-1);

            float rowTexDelta = 1.0f/(float)(numRows-1);
            float columnTexDelta = 1.0f/(float)(numColumns-1);

            osg::Vec3 pos = origin;
            osg::Vec2 tex(0.0f,0.0f);
            int vi=0;
            for(r=0;r<numRows;++r)
            {
                pos.x() = origin.x();
                tex.x() = 0.0f;
                for(c=0;c<numColumns;++c)
                {
                    v[vi].set(pos.x(),pos.y(),pos.z());
                    pos.x()+=columnCoordDelta;
                    tex.x()+=columnTexDelta;
                    ++vi;
                }
                pos.y() += rowCoordDelta;
                tex.y() += rowTexDelta;
            }

            geometry->setVertexArray(&v);
            geometry->setColorArray(&color);
            geometry->setColorBinding(osg::Geometry::BIND_OVERALL);

            for(r=0;r<numRows-1;++r)
            {
                osg::DrawElementsUShort& drawElements = *(new osg::DrawElementsUShort(GL_QUAD_STRIP,2*numColumns));
                geometry->addPrimitiveSet(&drawElements);
                int ei=0;
                for(c=0;c<numColumns;++c)
                {
                    drawElements[ei++] = (r+1)*numColumns+c;
                    drawElements[ei++] = (r)*numColumns+c;
                }
            }

            geometry->setInitialBound(osg::BoundingBox(origin, origin+size));

            geode->addDrawable(geometry);

            scene->addChild(geode);
        }
    }

    std::cout<<"done."<<std::endl;

    return scene;
}

#if 0
class TestSupportCallback : public osgProducer::OsgCameraGroup::RealizeCallback
{
    public:
        TestSupportCallback():_supported(true),_errorMessage() {}

        virtual void operator()( osgProducer::OsgCameraGroup&, osgProducer::OsgSceneHandler& sh, const Producer::RenderSurface& )
        {
            {
                OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);

                unsigned int contextID = sh.getSceneView()->getState()->getContextID();
                osg::GL2Extensions* gl2ext = osg::GL2Extensions::Get(contextID,true);
                if( gl2ext )
                {
                    if( !gl2ext->isGlslSupported() )
                    {
                        _supported = false;
                        _errorMessage = "ERROR: GLSL not supported by OpenGL driver.";
                    }

                    GLint numVertexTexUnits = 0;
                    glGetIntegerv( GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &numVertexTexUnits );
                    if( numVertexTexUnits <= 0 )
                    {
                        _supported = false;
                        _errorMessage = "ERROR: vertex texturing not supported by OpenGL driver.";
                    }
                }
            }

            sh.init();
        }

        OpenThreads::Mutex  _mutex;
        bool                _supported;
        std::string         _errorMessage;

};
#endif

int main(int, char **)
{
    // construct the viewer.
    osgViewer::Viewer viewer;

    osg::Node* node = createScene();

    // add model to viewer.
    viewer.setSceneData( node );

#if 0
    // register a test extension callback to be called when app realizes and gets a valid graphics context
    osg::ref_ptr<TestSupportCallback> testSupportCallback = new TestSupportCallback();
    viewer.setRealizeCallback(testSupportCallback.get());
    // create the windows and run the threads.
    viewer.realize();

    // exit if we don't have the extensions this example needs.
    if (!testSupportCallback->_supported)
    {
        osg::notify(osg::WARN)<<testSupportCallback->_errorMessage<<std::endl;

        exit(1);
    }
#else

    osg::notify(osg::NOTICE)<<"osgshaderterrain OpenGL support test not implemented yet"<<std::endl;

#endif

    return viewer.run();
}
<commit_msg>Implement a GraphicsOperation to test extension availability<commit_after>#include <osg/AlphaFunc>
#include <osg/Billboard>
#include <osg/BlendFunc>
#include <osg/Depth>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/GL2Extensions>
#include <osg/Material>
#include <osg/Math>
#include <osg/MatrixTransform>
#include <osg/PolygonOffset>
#include <osg/Program>
#include <osg/Projection>
#include <osg/Shader>
#include <osg/ShapeDrawable>
#include <osg/StateSet>
#include <osg/Switch>
#include <osg/Texture2D>
#include <osg/Uniform>

#include <osgDB/ReadFile>
#include <osgDB/FileUtils>

#include <osgUtil/IntersectVisitor>
#include <osgUtil/SmoothingVisitor>

#include <osgText/Text>

#include <osgViewer/Viewer>

#include <iostream>

// for the grid data..
#include "../osghangglide/terrain_coords.h"

osg::Node* createScene()
{
    osg::Group* scene = new osg::Group;

    unsigned int numColumns = 38;
    unsigned int numRows = 39;
    unsigned int r;
    unsigned int c;

    osg::Vec3 origin(0.0f,0.0f,0.0f);
    osg::Vec3 size(1000.0f,1000.0f,250.0f);
    osg::Vec3 scaleDown(1.0f/size.x(),1.0f/size.y(),1.0f/size.z());

    // ---------------------------------------
    // Set up a StateSet to texture the objects
    // ---------------------------------------
    osg::StateSet* stateset = new osg::StateSet();

    osg::Uniform* originUniform = new osg::Uniform("terrainOrigin",origin);
    stateset->addUniform(originUniform);

    osg::Uniform* sizeUniform = new osg::Uniform("terrainSize",size);
    stateset->addUniform(sizeUniform);

    osg::Uniform* scaleDownUniform = new osg::Uniform("terrainScaleDown",scaleDown);
    stateset->addUniform(scaleDownUniform);

    osg::Uniform* terrainTextureSampler = new osg::Uniform("terrainTexture",0);
    stateset->addUniform(terrainTextureSampler);

    osg::Uniform* baseTextureSampler = new osg::Uniform("baseTexture",1);
    stateset->addUniform(baseTextureSampler);

    osg::Uniform* treeTextureSampler = new osg::Uniform("treeTexture",1);
    stateset->addUniform(treeTextureSampler);

    // compute z range of z values of grid data so we can scale it.
    float min_z = FLT_MAX;
    float max_z = -FLT_MAX;
    for(r=0;r<numRows;++r)
    {
        for(c=0;c<numColumns;++c)
        {
            min_z = osg::minimum(min_z,vertex[r+c*numRows][2]);
            max_z = osg::maximum(max_z,vertex[r+c*numRows][2]);
        }
    }

    float scale_z = size.z()/(max_z-min_z);

    osg::Image* terrainImage = new osg::Image;
    terrainImage->allocateImage(numColumns,numRows,1,GL_LUMINANCE, GL_FLOAT);
    terrainImage->setInternalTextureFormat(GL_LUMINANCE_FLOAT32_ATI);
    for(r=0;r<numRows;++r)
    {
        for(c=0;c<numColumns;++c)
        {
            *((float*)(terrainImage->data(c,r))) = (vertex[r+c*numRows][2]-min_z)*scale_z;
        }
    }

    osg::Texture2D* terrainTexture = new osg::Texture2D;
    terrainTexture->setImage(terrainImage);
    terrainTexture->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::NEAREST);
    terrainTexture->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::NEAREST);
    terrainTexture->setResizeNonPowerOfTwoHint(false);
    stateset->setTextureAttributeAndModes(0,terrainTexture,osg::StateAttribute::ON);

    osg::Image* image = osgDB::readImageFile("Images/lz.rgb");
    if (image)
    {
        osg::Texture2D* texture = new osg::Texture2D;

        texture->setImage(image);
        stateset->setTextureAttributeAndModes(1,texture,osg::StateAttribute::ON);
    }

    {
        std::cout<<"Creating terrain...";

        osg::Geode* geode = new osg::Geode();
        geode->setStateSet( stateset );

        {
            osg::Program* program = new osg::Program;
            stateset->setAttribute(program);

#if 1
            // use inline shaders

            ///////////////////////////////////////////////////////////////////
            // vertex shader using just Vec4 coefficients
            char vertexShaderSource[] =
               "uniform float osg_FrameTime;\n"
               "uniform sampler2D terrainTexture;\n"
               "uniform vec3 terrainOrigin;\n"
               "uniform vec3 terrainScaleDown;\n"
               "\n"
               "varying vec2 texcoord;\n"
               "\n"
               "void main(void)\n"
               "{\n"
               "    texcoord = gl_Vertex.xy - terrainOrigin.xy;\n"
               "    texcoord.x *= terrainScaleDown.x;\n"
               "    texcoord.y *= terrainScaleDown.y;\n"
               "\n"
               "    vec4 position;\n"
               "    position.x = gl_Vertex.x;\n"
               "    position.y = gl_Vertex.y;\n"
               "    position.z = texture2D(terrainTexture, texcoord).r;\n"
               "    position.w = 1.0;\n"
               " \n"
               "    gl_Position     = gl_ModelViewProjectionMatrix * position;\n"
                "   gl_FrontColor = vec4(1.0,1.0,1.0,1.0);\n"
               "}\n";

            //////////////////////////////////////////////////////////////////
            // fragment shader
            //
            char fragmentShaderSource[] =
                "uniform sampler2D baseTexture; \n"
                "varying vec2 texcoord;\n"
                "\n"
                "void main(void) \n"
                "{\n"
                "    gl_FragColor = texture2D( baseTexture, texcoord); \n"
                "}\n";

            program->addShader(new osg::Shader(osg::Shader::VERTEX, vertexShaderSource));
            program->addShader(new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource));

#else

            // get shaders from source
            program->addShader(osg::Shader::readShaderFile(osg::Shader::VERTEX, osgDB::findDataFile("shaders/terrain.vert")));
            program->addShader(osg::Shader::readShaderFile(osg::Shader::FRAGMENT, osgDB::findDataFile("shaders/terrain.frag")));

#endif

            // get shaders from source
        }

        {
            osg::Geometry* geometry = new osg::Geometry;

            osg::Vec3Array& v = *(new osg::Vec3Array(numColumns*numRows));
            osg::Vec4ubArray& color = *(new osg::Vec4ubArray(1));

            color[0].set(255,255,255,255);

            float rowCoordDelta = size.y()/(float)(numRows-1);
            float columnCoordDelta = size.x()/(float)(numColumns-1);

            float rowTexDelta = 1.0f/(float)(numRows-1);
            float columnTexDelta = 1.0f/(float)(numColumns-1);

            osg::Vec3 pos = origin;
            osg::Vec2 tex(0.0f,0.0f);
            int vi=0;
            for(r=0;r<numRows;++r)
            {
                pos.x() = origin.x();
                tex.x() = 0.0f;
                for(c=0;c<numColumns;++c)
                {
                    v[vi].set(pos.x(),pos.y(),pos.z());
                    pos.x()+=columnCoordDelta;
                    tex.x()+=columnTexDelta;
                    ++vi;
                }
                pos.y() += rowCoordDelta;
                tex.y() += rowTexDelta;
            }

            geometry->setVertexArray(&v);
            geometry->setColorArray(&color);
            geometry->setColorBinding(osg::Geometry::BIND_OVERALL);

            for(r=0;r<numRows-1;++r)
            {
                osg::DrawElementsUShort& drawElements = *(new osg::DrawElementsUShort(GL_QUAD_STRIP,2*numColumns));
                geometry->addPrimitiveSet(&drawElements);
                int ei=0;
                for(c=0;c<numColumns;++c)
                {
                    drawElements[ei++] = (r+1)*numColumns+c;
                    drawElements[ei++] = (r)*numColumns+c;
                }
            }

            geometry->setInitialBound(osg::BoundingBox(origin, origin+size));

            geode->addDrawable(geometry);

            scene->addChild(geode);
        }
    }

    std::cout<<"done."<<std::endl;

    return scene;
}

class TestSupportOperation: public osg::GraphicsOperation
{
public:

    TestSupportOperation():
        osg::GraphicsOperation("TestSupportOperation",false),
        _supported(true),
        _errorMessage() {}

    virtual void operator () (osg::GraphicsContext* gc)
    {
        osg::notify(osg::NOTICE)<<"Not called"<<std::endl;

        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);

        unsigned int contextID = gc->getState()->getContextID();
        osg::GL2Extensions* gl2ext = osg::GL2Extensions::Get(contextID,true);
        if( gl2ext )
        {
            if( !gl2ext->isGlslSupported() )
            {
                _supported = false;
                _errorMessage = "ERROR: GLSL not supported by OpenGL driver.";
            }

            GLint numVertexTexUnits = 0;
            glGetIntegerv( GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &numVertexTexUnits );
            if( numVertexTexUnits <= 0 )
            {
                _supported = false;
                _errorMessage = "ERROR: vertex texturing not supported by OpenGL driver.";
            }
        }

        _supported = false;
        _errorMessage = "ERROR: Pllalalal.";

    }

    OpenThreads::Mutex  _mutex;
    bool                _supported;
    std::string         _errorMessage;
};

int main(int, char **)
{
    // construct the viewer.
    osgViewer::Viewer viewer;

    osg::Node* node = createScene();

    // add model to viewer.
    viewer.setSceneData( node );

    viewer.setUpViewAcrossAllScreens();

    osg::ref_ptr<TestSupportOperation> testSupportOperation = new TestSupportOperation;

    osgViewer::Viewer::Windows windows;
    viewer.getWindows(windows);
    for(osgViewer::Viewer::Windows::iterator itr = windows.begin();
        itr != windows.end();
        ++itr)
    {
        (*itr)->add(testSupportOperation.get());
    }

    // create the windows and run the threads.
    viewer.realize();

    if (!testSupportOperation->_supported)
    {
        osg::notify(osg::WARN)<<testSupportOperation->_errorMessage<<std::endl;

        return 1;
    }

    return viewer.run();
}
<|endoftext|>


<commit_before>#include <algorithm>
#include <map>
extern "C"
{
#include <wlr/types/wlr_surface.h>
#define static
#include <wlr/types/wlr_compositor.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_buffer.h>
#include <wlr/util/region.h>
#undef static
}

#include "surface-impl.hpp"
#include "subsurface.hpp"
#include "opengl.hpp"
#include "../core/core-impl.hpp"
#include "output.hpp"
#include "debug.hpp"
#include "render-manager.hpp"
#include "signal-definitions.hpp"

/****************************
 * surface_interface_t functions
 ****************************/
wf::surface_interface_t::surface_interface_t(surface_interface_t *parent)
{
    this->priv = std::make_unique<impl>();
    take_ref();
    this->priv->parent_surface = parent;

    if (parent)
    {
        set_output(parent->get_output());
        parent->priv->surface_children.insert(
            parent->priv->surface_children.begin(), this);
    }
}

wf::surface_interface_t::~surface_interface_t()
{
    if (priv->parent_surface)
    {
        auto& container = priv->parent_surface->priv->surface_children;
        auto it = std::remove(container.begin(), container.end(), this);
        container.erase(it, container.end());
    }

    for (auto c : priv->surface_children)
        c->priv->parent_surface = nullptr;
}

void wf::surface_interface_t::take_ref()
{
    ++priv->ref_cnt;
}

void wf::surface_interface_t::unref()
{
    --priv->ref_cnt;
    if (priv->ref_cnt <= 0)
        destruct();
}

wf::surface_interface_t *wf::surface_interface_t::get_main_surface()
{
    if (priv->parent_surface)
        return priv->parent_surface->get_main_surface();

    return this;
}

std::vector<wf::surface_iterator_t> wf::surface_interface_t::enumerate_surfaces(
    wf_point surface_origin)
{
    std::vector<wf::surface_iterator_t> result;
    for (auto& child : priv->surface_children)
    {
        if (child->is_mapped())
        {
            auto child_surfaces = child->enumerate_surfaces(
                child->get_offset() + surface_origin);

            result.insert(result.end(),
                child_surfaces.begin(), child_surfaces.end());
        }
    }

    if (is_mapped())
        result.push_back({this, surface_origin});

    return result;
}

wf::output_t *wf::surface_interface_t::get_output()
{
    return priv->output;
}

void wf::surface_interface_t::set_output(wf::output_t* output)
{
    priv->output = output;
    for (auto& c : priv->surface_children)
        c->set_output(output);
}

/* Static method */
int wf::surface_interface_t::impl::active_shrink_constraint = 0;

void wf::surface_interface_t::set_opaque_shrink_constraint(
    std::string constraint_name, int value)
{
    static std::map<std::string, int> shrink_constraints;

    shrink_constraints[constraint_name] = value;

    impl::active_shrink_constraint = 0;
    for (auto& constr : shrink_constraints)
    {
        impl::active_shrink_constraint =
            std::max(impl::active_shrink_constraint, constr.second);
    }
}

int wf::surface_interface_t::get_active_shrink_constraint()
{
    return impl::active_shrink_constraint;
}

void wf::surface_interface_t::destruct()
{
    delete this;
}

/****************************
 * surface_interface_t functions for surfaces which are
 * backed by a wlr_surface
 ****************************/
void wf::surface_interface_t::send_frame_done(const timespec& time)
{
    if (priv->wsurface)
        wlr_surface_send_frame_done(priv->wsurface, &time);
}

bool wf::surface_interface_t::accepts_input(int32_t sx, int32_t sy)
{
    if (!priv->wsurface)
        return false;

    return wlr_surface_point_accepts_input(priv->wsurface, sx, sy);
}

void wf::surface_interface_t::subtract_opaque(wf_region& region, int x, int y)
{
    if (!priv->wsurface)
        return;

    wf_region opaque{&priv->wsurface->opaque_region};
    opaque += wf_point{x, y};
    opaque *= get_output()->handle->scale;

    /* region scaling uses std::ceil/std::floor, so the resulting region
     * encompasses the opaque region. However, in the case of opaque region, we
     * don't want any pixels that aren't actually opaque. So in case of
     * different scales, we just shrink by 1 to compensate for the ceil/floor
     * discrepancy */
    int ceil_factor = 0;
    if (get_output()->handle->scale != (float)priv->wsurface->current.scale)
        ceil_factor = 1;

    opaque.expand_edges(-get_active_shrink_constraint() - ceil_factor);
    region ^= opaque;
}

wl_client* wf::surface_interface_t::get_client()
{
    if (priv->wsurface)
        return wl_resource_get_client(priv->wsurface->resource);

    return nullptr;
}

wf::wlr_surface_base_t::wlr_surface_base_t(surface_interface_t *self)
{
    _as_si = self;
    handle_new_subsurface = [&] (void* data)
    {
        auto sub = static_cast<wlr_subsurface*> (data);
        if (sub->data)
        {
            log_error("Creating the same subsurface twice!");
            return;
        }

        // parent isn't mapped yet
        if (!sub->parent->data)
            return;

        // will be deleted by destruct()
        new subsurface_implementation_t(sub, _as_si);
    };

    on_new_subsurface.set_callback(handle_new_subsurface);
    on_commit.set_callback([&] (void*) { commit(); });
}

wf::wlr_surface_base_t::~wlr_surface_base_t() {}

wf_point wf::wlr_surface_base_t::get_window_offset()
{
    return {0, 0};
}

bool wf::wlr_surface_base_t::_is_mapped() const
{
    return surface;
}

wf_surface_size_t wf::wlr_surface_base_t::_get_size() const
{
    if (!_is_mapped())
        return {0, 0};

    return {
        surface->current.width,
        surface->current.height,
    };
}

void wf::emit_map_state_change(wf::surface_interface_t *surface)
{
    std::string state = surface->is_mapped() ? "_surface_mapped" : "_surface_unmapped";

    _surface_map_state_changed_signal data;
    data.surface = surface;
    wf::get_core().emit_signal(state, &data);
}

void wf::wlr_surface_base_t::map(wlr_surface *surface)
{
    assert(!this->surface && surface);
    this->surface = surface;

    _as_si->priv->wsurface = surface;

    /* force surface_send_enter(), and also check whether parent surface
     * output hasn't changed while we were unmapped */
    wf::output_t *output = _as_si->priv->parent_surface ?
        _as_si->priv->parent_surface->get_output() : _as_si->get_output();
    _as_si->set_output(output);

    on_new_subsurface.connect(&surface->events.new_subsurface);
    on_commit.connect(&surface->events.commit);

    surface->data = _as_si;

    /* Handle subsurfaces which were created before this surface was mapped */
    wlr_subsurface *sub;
    wl_list_for_each(sub, &surface->subsurfaces, parent_link)
        handle_new_subsurface(sub);

    emit_map_state_change(_as_si);
}

void wf::wlr_surface_base_t::unmap()
{
    assert(this->surface);
    apply_surface_damage();
    damage_surface_box({.x = 0, .y = 0,
        .width = _get_size().width, .height = _get_size().height});

    this->surface->data = NULL;
    this->surface = nullptr;
    this->_as_si->priv->wsurface = nullptr;
    emit_map_state_change(_as_si);

    on_new_subsurface.disconnect();
    on_destroy.disconnect();
    on_commit.disconnect();
}

wlr_buffer* wf::wlr_surface_base_t::get_buffer()
{
    if (surface && wlr_surface_has_buffer(surface))
        return surface->buffer;

    return nullptr;
}

void wf::wlr_surface_base_t::damage_surface_region(
    const wf_region& dmg)
{
    for (const auto& rect : dmg)
        damage_surface_box(wlr_box_from_pixman_box(rect));
}

void wf::wlr_surface_base_t::damage_surface_box(const wlr_box& box)
{
    auto parent =
        dynamic_cast<wlr_surface_base_t*> (_as_si->priv->parent_surface);

    /* wlr_view_t overrides damage_surface_box and applies it to the output */
    if (parent && parent->_is_mapped())
    {
        wlr_box parent_box = box;
        parent_box.x += _as_si->get_offset().x;
        parent_box.y += _as_si->get_offset().y;
        parent->damage_surface_box(parent_box);
    }
}

void wf::wlr_surface_base_t::apply_surface_damage()
{
    if (!_as_si->get_output() || !_is_mapped())
        return;

    wf_region dmg;
    wlr_surface_get_effective_damage(surface, dmg.to_pixman());

    if (surface->current.scale != 1 ||
        surface->current.scale != _as_si->get_output()->handle->scale)
        dmg.expand_edges(1);

    damage_surface_region(dmg);
}

void wf::wlr_surface_base_t::commit()
{
    apply_surface_damage();
    if (_as_si->get_output())
    {
        /* we schedule redraw, because the surface might expect
         * a frame callback */
        _as_si->get_output()->render->schedule_redraw();
    }
}

void wf::wlr_surface_base_t::update_output(wf::output_t *old_output,
    wf::output_t *new_output)
{
    /* We should send send_leave only if the output is different from the last. */
    if (old_output && old_output != new_output && surface)
        wlr_surface_send_leave(surface, old_output->handle);

    if (new_output && surface)
        wlr_surface_send_enter(surface, new_output->handle);
}

void wf::wlr_surface_base_t::_wlr_render_box(
    const wf_framebuffer& fb, int x, int y, const wlr_box& scissor)
{
    if (!get_buffer())
        return;

    wlr_box geometry {x, y, surface->current.width, surface->current.height};
    geometry = fb.damage_box_from_geometry_box(geometry);

    float projection[9];
    wlr_matrix_projection(projection, fb.viewport_width, fb.viewport_height,
        (wl_output_transform)fb.wl_transform);

    float matrix[9];
    wlr_matrix_project_box(matrix, &geometry,
        wlr_output_transform_invert(surface->current.transform), 0, projection);

    OpenGL::render_begin(fb);
    auto sbox = scissor; wlr_renderer_scissor(wf::get_core().renderer, &sbox);
    wlr_render_texture_with_matrix(wf::get_core().renderer,
        get_buffer()->texture, matrix, 1.0);

#ifdef WAYFIRE_GRAPHICS_DEBUG
    float scissor_proj[9];
    wlr_matrix_projection(scissor_proj, fb.viewport_width, fb.viewport_height,
        WL_OUTPUT_TRANSFORM_NORMAL);

    float col[4] = {0, 0.2, 0, 0.5};
    wlr_render_rect(wf::get_core().renderer, &scissor, col, scissor_proj);
#endif

    OpenGL::render_end();
}

void wf::wlr_surface_base_t::_simple_render(const wf_framebuffer& fb,
    int x, int y, const wf_region& damage)
{
    for (const auto& rect : damage)
    {
        auto box = wlr_box_from_pixman_box(rect);
        _wlr_render_box(fb, x, y, fb.framebuffer_box_from_damage_box(box));
    }
}

wf::wlr_child_surface_base_t::wlr_child_surface_base_t(
    surface_interface_t *parent, surface_interface_t *self) :
      wf::surface_interface_t(parent),
      wlr_surface_base_t(self)
{
}

wf::wlr_child_surface_base_t::~wlr_child_surface_base_t() { }
<commit_msg>surface: manually map "mapped" subsurfaces when they are created<commit_after>#include <algorithm>
#include <map>
extern "C"
{
#include <wlr/types/wlr_surface.h>
#define static
#include <wlr/types/wlr_compositor.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_buffer.h>
#include <wlr/util/region.h>
#undef static
}

#include "surface-impl.hpp"
#include "subsurface.hpp"
#include "opengl.hpp"
#include "../core/core-impl.hpp"
#include "output.hpp"
#include "debug.hpp"
#include "render-manager.hpp"
#include "signal-definitions.hpp"

/****************************
 * surface_interface_t functions
 ****************************/
wf::surface_interface_t::surface_interface_t(surface_interface_t *parent)
{
    this->priv = std::make_unique<impl>();
    take_ref();
    this->priv->parent_surface = parent;

    if (parent)
    {
        set_output(parent->get_output());
        parent->priv->surface_children.insert(
            parent->priv->surface_children.begin(), this);
    }
}

wf::surface_interface_t::~surface_interface_t()
{
    if (priv->parent_surface)
    {
        auto& container = priv->parent_surface->priv->surface_children;
        auto it = std::remove(container.begin(), container.end(), this);
        container.erase(it, container.end());
    }

    for (auto c : priv->surface_children)
        c->priv->parent_surface = nullptr;
}

void wf::surface_interface_t::take_ref()
{
    ++priv->ref_cnt;
}

void wf::surface_interface_t::unref()
{
    --priv->ref_cnt;
    if (priv->ref_cnt <= 0)
        destruct();
}

wf::surface_interface_t *wf::surface_interface_t::get_main_surface()
{
    if (priv->parent_surface)
        return priv->parent_surface->get_main_surface();

    return this;
}

std::vector<wf::surface_iterator_t> wf::surface_interface_t::enumerate_surfaces(
    wf_point surface_origin)
{
    std::vector<wf::surface_iterator_t> result;
    for (auto& child : priv->surface_children)
    {
        if (child->is_mapped())
        {
            auto child_surfaces = child->enumerate_surfaces(
                child->get_offset() + surface_origin);

            result.insert(result.end(),
                child_surfaces.begin(), child_surfaces.end());
        }
    }

    if (is_mapped())
        result.push_back({this, surface_origin});

    return result;
}

wf::output_t *wf::surface_interface_t::get_output()
{
    return priv->output;
}

void wf::surface_interface_t::set_output(wf::output_t* output)
{
    priv->output = output;
    for (auto& c : priv->surface_children)
        c->set_output(output);
}

/* Static method */
int wf::surface_interface_t::impl::active_shrink_constraint = 0;

void wf::surface_interface_t::set_opaque_shrink_constraint(
    std::string constraint_name, int value)
{
    static std::map<std::string, int> shrink_constraints;

    shrink_constraints[constraint_name] = value;

    impl::active_shrink_constraint = 0;
    for (auto& constr : shrink_constraints)
    {
        impl::active_shrink_constraint =
            std::max(impl::active_shrink_constraint, constr.second);
    }
}

int wf::surface_interface_t::get_active_shrink_constraint()
{
    return impl::active_shrink_constraint;
}

void wf::surface_interface_t::destruct()
{
    delete this;
}

/****************************
 * surface_interface_t functions for surfaces which are
 * backed by a wlr_surface
 ****************************/
void wf::surface_interface_t::send_frame_done(const timespec& time)
{
    if (priv->wsurface)
        wlr_surface_send_frame_done(priv->wsurface, &time);
}

bool wf::surface_interface_t::accepts_input(int32_t sx, int32_t sy)
{
    if (!priv->wsurface)
        return false;

    return wlr_surface_point_accepts_input(priv->wsurface, sx, sy);
}

void wf::surface_interface_t::subtract_opaque(wf_region& region, int x, int y)
{
    if (!priv->wsurface)
        return;

    wf_region opaque{&priv->wsurface->opaque_region};
    opaque += wf_point{x, y};
    opaque *= get_output()->handle->scale;

    /* region scaling uses std::ceil/std::floor, so the resulting region
     * encompasses the opaque region. However, in the case of opaque region, we
     * don't want any pixels that aren't actually opaque. So in case of
     * different scales, we just shrink by 1 to compensate for the ceil/floor
     * discrepancy */
    int ceil_factor = 0;
    if (get_output()->handle->scale != (float)priv->wsurface->current.scale)
        ceil_factor = 1;

    opaque.expand_edges(-get_active_shrink_constraint() - ceil_factor);
    region ^= opaque;
}

wl_client* wf::surface_interface_t::get_client()
{
    if (priv->wsurface)
        return wl_resource_get_client(priv->wsurface->resource);

    return nullptr;
}

wf::wlr_surface_base_t::wlr_surface_base_t(surface_interface_t *self)
{
    _as_si = self;
    handle_new_subsurface = [&] (void* data)
    {
        auto sub = static_cast<wlr_subsurface*> (data);
        if (sub->data)
        {
            log_error("Creating the same subsurface twice!");
            return;
        }

        // parent isn't mapped yet
        if (!sub->parent->data)
            return;

        // will be deleted by destruct()
        auto subsurface = new subsurface_implementation_t(sub, _as_si);
        if (sub->mapped)
            subsurface->map(sub->surface);
    };

    on_new_subsurface.set_callback(handle_new_subsurface);
    on_commit.set_callback([&] (void*) { commit(); });
}

wf::wlr_surface_base_t::~wlr_surface_base_t() {}

wf_point wf::wlr_surface_base_t::get_window_offset()
{
    return {0, 0};
}

bool wf::wlr_surface_base_t::_is_mapped() const
{
    return surface;
}

wf_surface_size_t wf::wlr_surface_base_t::_get_size() const
{
    if (!_is_mapped())
        return {0, 0};

    return {
        surface->current.width,
        surface->current.height,
    };
}

void wf::emit_map_state_change(wf::surface_interface_t *surface)
{
    std::string state = surface->is_mapped() ? "_surface_mapped" : "_surface_unmapped";

    _surface_map_state_changed_signal data;
    data.surface = surface;
    wf::get_core().emit_signal(state, &data);
}

void wf::wlr_surface_base_t::map(wlr_surface *surface)
{
    assert(!this->surface && surface);
    this->surface = surface;

    _as_si->priv->wsurface = surface;

    /* force surface_send_enter(), and also check whether parent surface
     * output hasn't changed while we were unmapped */
    wf::output_t *output = _as_si->priv->parent_surface ?
        _as_si->priv->parent_surface->get_output() : _as_si->get_output();
    _as_si->set_output(output);

    on_new_subsurface.connect(&surface->events.new_subsurface);
    on_commit.connect(&surface->events.commit);

    surface->data = _as_si;

    /* Handle subsurfaces which were created before this surface was mapped */
    wlr_subsurface *sub;
    wl_list_for_each(sub, &surface->subsurfaces, parent_link)
        handle_new_subsurface(sub);

    emit_map_state_change(_as_si);
}

void wf::wlr_surface_base_t::unmap()
{
    assert(this->surface);
    apply_surface_damage();
    damage_surface_box({.x = 0, .y = 0,
        .width = _get_size().width, .height = _get_size().height});

    this->surface->data = NULL;
    this->surface = nullptr;
    this->_as_si->priv->wsurface = nullptr;
    emit_map_state_change(_as_si);

    on_new_subsurface.disconnect();
    on_destroy.disconnect();
    on_commit.disconnect();
}

wlr_buffer* wf::wlr_surface_base_t::get_buffer()
{
    if (surface && wlr_surface_has_buffer(surface))
        return surface->buffer;

    return nullptr;
}

void wf::wlr_surface_base_t::damage_surface_region(
    const wf_region& dmg)
{
    for (const auto& rect : dmg)
        damage_surface_box(wlr_box_from_pixman_box(rect));
}

void wf::wlr_surface_base_t::damage_surface_box(const wlr_box& box)
{
    auto parent =
        dynamic_cast<wlr_surface_base_t*> (_as_si->priv->parent_surface);

    /* wlr_view_t overrides damage_surface_box and applies it to the output */
    if (parent && parent->_is_mapped())
    {
        wlr_box parent_box = box;
        parent_box.x += _as_si->get_offset().x;
        parent_box.y += _as_si->get_offset().y;
        parent->damage_surface_box(parent_box);
    }
}

void wf::wlr_surface_base_t::apply_surface_damage()
{
    if (!_as_si->get_output() || !_is_mapped())
        return;

    wf_region dmg;
    wlr_surface_get_effective_damage(surface, dmg.to_pixman());

    if (surface->current.scale != 1 ||
        surface->current.scale != _as_si->get_output()->handle->scale)
        dmg.expand_edges(1);

    damage_surface_region(dmg);
}

void wf::wlr_surface_base_t::commit()
{
    apply_surface_damage();
    if (_as_si->get_output())
    {
        /* we schedule redraw, because the surface might expect
         * a frame callback */
        _as_si->get_output()->render->schedule_redraw();
    }
}

void wf::wlr_surface_base_t::update_output(wf::output_t *old_output,
    wf::output_t *new_output)
{
    /* We should send send_leave only if the output is different from the last. */
    if (old_output && old_output != new_output && surface)
        wlr_surface_send_leave(surface, old_output->handle);

    if (new_output && surface)
        wlr_surface_send_enter(surface, new_output->handle);
}

void wf::wlr_surface_base_t::_wlr_render_box(
    const wf_framebuffer& fb, int x, int y, const wlr_box& scissor)
{
    if (!get_buffer())
        return;

    wlr_box geometry {x, y, surface->current.width, surface->current.height};
    geometry = fb.damage_box_from_geometry_box(geometry);

    float projection[9];
    wlr_matrix_projection(projection, fb.viewport_width, fb.viewport_height,
        (wl_output_transform)fb.wl_transform);

    float matrix[9];
    wlr_matrix_project_box(matrix, &geometry,
        wlr_output_transform_invert(surface->current.transform), 0, projection);

    OpenGL::render_begin(fb);
    auto sbox = scissor; wlr_renderer_scissor(wf::get_core().renderer, &sbox);
    wlr_render_texture_with_matrix(wf::get_core().renderer,
        get_buffer()->texture, matrix, 1.0);

#ifdef WAYFIRE_GRAPHICS_DEBUG
    float scissor_proj[9];
    wlr_matrix_projection(scissor_proj, fb.viewport_width, fb.viewport_height,
        WL_OUTPUT_TRANSFORM_NORMAL);

    float col[4] = {0, 0.2, 0, 0.5};
    wlr_render_rect(wf::get_core().renderer, &scissor, col, scissor_proj);
#endif

    OpenGL::render_end();
}

void wf::wlr_surface_base_t::_simple_render(const wf_framebuffer& fb,
    int x, int y, const wf_region& damage)
{
    for (const auto& rect : damage)
    {
        auto box = wlr_box_from_pixman_box(rect);
        _wlr_render_box(fb, x, y, fb.framebuffer_box_from_damage_box(box));
    }
}

wf::wlr_child_surface_base_t::wlr_child_surface_base_t(
    surface_interface_t *parent, surface_interface_t *self) :
      wf::surface_interface_t(parent),
      wlr_surface_base_t(self)
{
}

wf::wlr_child_surface_base_t::~wlr_child_surface_base_t() { }
<|endoftext|>


<commit_before>//
// Copyright (c) 2018 Rokas Kupstys
//
// 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 <regex>
#include <Urho3D/Core/CoreEvents.h>
#include <Urho3D/Core/WorkQueue.h>
#include <Urho3D/Engine/EngineEvents.h>
#include <Urho3D/IO/FileSystem.h>
#include <Urho3D/IO/Log.h>
#include <Urho3D/Resource/ResourceCache.h>
#include "Editor.h"
#include "AssetConverter.h"
#include "ImportAssimp.h"

namespace Urho3D
{

AssetConverter::AssetConverter(Context* context)
    : Object(context)
{
    assetImporters_.Push(SharedPtr<ImportAssimp>(new ImportAssimp(context_)));

    SubscribeToEvent(E_ENDFRAME, std::bind(&AssetConverter::DispatchChangedAssets, this));
    SubscribeToEvent(E_CONSOLECOMMAND, std::bind(&AssetConverter::OnConsoleCommand, this, _2));
}

AssetConverter::~AssetConverter()
{
    for (auto& watcher : watchers_)
        watcher->StopWatching();
}

void AssetConverter::AddAssetDirectory(const String& path)
{
    SharedPtr<FileWatcher> watcher(new FileWatcher(context_));
    watcher->StartWatching(path, true);
    watchers_.Push(watcher);
}

void AssetConverter::RemoveAssetDirectory(const String& path)
{
    String realPath = AddTrailingSlash(path);
    for (auto it = watchers_.Begin(); it != watchers_.End();)
    {
        if ((*it)->GetPath() == realPath)
        {
            (*it)->StopWatching();
            it = watchers_.Erase(it);
        }
        else
            ++it;
    }
}

void AssetConverter::SetCachePath(const String& cachePath)
{
    GetFileSystem()->CreateDirsRecursive(cachePath);
    cachePath_ = cachePath;
}

String AssetConverter::GetCachePath() const
{
    return cachePath_;
}

void AssetConverter::VerifyCacheAsync()
{
    GetWorkQueue()->AddWorkItem([=]() {
        for (const auto& watcher : watchers_)
        {
            Vector<String> files;
            GetFileSystem()->ScanDir(files, watcher->GetPath(), "*", SCAN_FILES, true);

            for (const auto& file : files)
                ConvertAsset(file);
        }
    });
}

void AssetConverter::ConvertAssetAsync(const String& resourceName)
{
    GetWorkQueue()->AddWorkItem(std::bind(&AssetConverter::ConvertAsset, this, resourceName));
}

bool AssetConverter::ConvertAsset(const String& resourceName)
{
    if (!IsCacheOutOfDate(resourceName))
        return true;

    // Ensure that no resources are left over from previous version
    GetFileSystem()->RemoveDir(cachePath_ + resourceName, true);
    String resourceFileName = GetCache()->GetResourceFileName(resourceName);
    bool convertedAny = false;

    for (auto& importer : assetImporters_)
    {
        if (importer->Accepts(resourceFileName))
        {
            if (importer->Convert(resourceFileName))
                convertedAny = true;
        }
    }

    auto convertedAssets = GetCacheAssets(resourceName);
    if (!convertedAssets.Empty())
    {
        unsigned mtime = GetFileSystem()->GetLastModifiedTime(resourceFileName);
        for (const auto& path : GetCacheAssets(resourceName))
        {
            GetFileSystem()->SetLastModifiedTime(path, mtime);
            URHO3D_LOGINFOF("Imported %s", path.CString());
        }
    }

    return convertedAny;
}

void AssetConverter::DispatchChangedAssets()
{
    if (checkTimer_.GetMSec(false) < 3000)
        return;
    checkTimer_.Reset();

    for (auto& watcher : watchers_)
    {
        String path;
        while (watcher->GetNextChange(path))
            ConvertAssetAsync(path);
    }
}

bool AssetConverter::IsCacheOutOfDate(const String& resourceName)
{
    unsigned mtime = GetFileSystem()->GetLastModifiedTime(GetCache()->GetResourceFileName(resourceName));

    auto files = GetCacheAssets(resourceName);
    for (const auto& path : files)
    {
        if (GetFileSystem()->GetLastModifiedTime(path) != mtime)
            return true;
    }

    return files.Empty();
}

Vector<String> AssetConverter::GetCacheAssets(const String& resourceName)
{
    Vector<String> files;
    String assetCacheDirectory = cachePath_ + resourceName;
    if (GetFileSystem()->DirExists(assetCacheDirectory))
        GetFileSystem()->ScanDir(files, assetCacheDirectory, "", SCAN_FILES, true);
    for (auto& fileName : files)
        fileName = AddTrailingSlash(assetCacheDirectory) + fileName;
    return files;
}

void AssetConverter::OnConsoleCommand(VariantMap& args)
{
    using namespace ConsoleCommand;
    if (args[P_COMMAND].GetString() == "cache.sync")
        VerifyCacheAsync();
}

}
<commit_msg>Editor: Safer variable captures in asset conversion.<commit_after>//
// Copyright (c) 2018 Rokas Kupstys
//
// 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 <regex>
#include <Urho3D/Core/CoreEvents.h>
#include <Urho3D/Core/WorkQueue.h>
#include <Urho3D/Engine/EngineEvents.h>
#include <Urho3D/IO/FileSystem.h>
#include <Urho3D/IO/Log.h>
#include <Urho3D/Resource/ResourceCache.h>
#include "Editor.h"
#include "AssetConverter.h"
#include "ImportAssimp.h"

namespace Urho3D
{

AssetConverter::AssetConverter(Context* context)
    : Object(context)
{
    assetImporters_.Push(SharedPtr<ImportAssimp>(new ImportAssimp(context_)));

    SubscribeToEvent(E_ENDFRAME, std::bind(&AssetConverter::DispatchChangedAssets, this));
    SubscribeToEvent(E_CONSOLECOMMAND, std::bind(&AssetConverter::OnConsoleCommand, this, _2));
}

AssetConverter::~AssetConverter()
{
    for (auto& watcher : watchers_)
        watcher->StopWatching();
}

void AssetConverter::AddAssetDirectory(const String& path)
{
    SharedPtr<FileWatcher> watcher(new FileWatcher(context_));
    watcher->StartWatching(path, true);
    watchers_.Push(watcher);
}

void AssetConverter::RemoveAssetDirectory(const String& path)
{
    String realPath = AddTrailingSlash(path);
    for (auto it = watchers_.Begin(); it != watchers_.End();)
    {
        if ((*it)->GetPath() == realPath)
        {
            (*it)->StopWatching();
            it = watchers_.Erase(it);
        }
        else
            ++it;
    }
}

void AssetConverter::SetCachePath(const String& cachePath)
{
    GetFileSystem()->CreateDirsRecursive(cachePath);
    cachePath_ = cachePath;
}

String AssetConverter::GetCachePath() const
{
    return cachePath_;
}

void AssetConverter::VerifyCacheAsync()
{
    StringVector paths;
    for (const auto& watcher : watchers_)
        paths.Push(watcher->GetPath());

    GetWorkQueue()->AddWorkItem([this, paths]() {
        for (const auto& path : paths)
        {
            Vector<String> files;
            GetFileSystem()->ScanDir(files, path, "*", SCAN_FILES, true);

            for (const auto& file : files)
                ConvertAsset(file);
        }
    });
}

void AssetConverter::ConvertAssetAsync(const String& resourceName)
{
    GetWorkQueue()->AddWorkItem([this, resourceName]() { ConvertAsset(resourceName); });
}

bool AssetConverter::ConvertAsset(const String& resourceName)
{
    if (!IsCacheOutOfDate(resourceName))
        return true;

    // Ensure that no resources are left over from previous version
    GetFileSystem()->RemoveDir(cachePath_ + resourceName, true);
    String resourceFileName = GetCache()->GetResourceFileName(resourceName);
    bool convertedAny = false;

    for (auto& importer : assetImporters_)
    {
        if (importer->Accepts(resourceFileName))
        {
            if (importer->Convert(resourceFileName))
                convertedAny = true;
        }
    }

    auto convertedAssets = GetCacheAssets(resourceName);
    if (!convertedAssets.Empty())
    {
        unsigned mtime = GetFileSystem()->GetLastModifiedTime(resourceFileName);
        for (const auto& path : GetCacheAssets(resourceName))
        {
            GetFileSystem()->SetLastModifiedTime(path, mtime);
            URHO3D_LOGINFOF("Imported %s", path.CString());
        }
    }

    return convertedAny;
}

void AssetConverter::DispatchChangedAssets()
{
    if (checkTimer_.GetMSec(false) < 3000)
        return;
    checkTimer_.Reset();

    for (auto& watcher : watchers_)
    {
        String path;
        while (watcher->GetNextChange(path))
            ConvertAssetAsync(path);
    }
}

bool AssetConverter::IsCacheOutOfDate(const String& resourceName)
{
    unsigned mtime = GetFileSystem()->GetLastModifiedTime(GetCache()->GetResourceFileName(resourceName));

    auto files = GetCacheAssets(resourceName);
    for (const auto& path : files)
    {
        if (GetFileSystem()->GetLastModifiedTime(path) != mtime)
            return true;
    }

    return files.Empty();
}

Vector<String> AssetConverter::GetCacheAssets(const String& resourceName)
{
    Vector<String> files;
    String assetCacheDirectory = cachePath_ + resourceName;
    if (GetFileSystem()->DirExists(assetCacheDirectory))
        GetFileSystem()->ScanDir(files, assetCacheDirectory, "", SCAN_FILES, true);
    for (auto& fileName : files)
        fileName = AddTrailingSlash(assetCacheDirectory) + fileName;
    return files;
}

void AssetConverter::OnConsoleCommand(VariantMap& args)
{
    using namespace ConsoleCommand;
    if (args[P_COMMAND].GetString() == "cache.sync")
        VerifyCacheAsync();
}

}
<|endoftext|>


<commit_before>/*******************************************************************************
 * c7a/net/group.hpp
 *
 * net::Group is a collection of NetConnections providing simple MPI-like
 * collectives and point-to-point communication.
 *
 * Part of Project c7a.
 *
 * Copyright (C) 2015 Timo Bingmann <tb@panthema.net>
 *
 * This file has no license. Only Chuck Norris can compile it.
 ******************************************************************************/

#pragma once
#ifndef C7A_NET_GROUP_HEADER
#define C7A_NET_GROUP_HEADER

#include <c7a/net/endpoint.hpp>
#include <c7a/net/connection.hpp>
#include <c7a/common/functional.hpp>
#include <c7a/common/logger.hpp>

#include <algorithm>
#include <cassert>
#include <cstring>
#include <map>
#include <string>
#include <thread>
#include <utility>
#include <vector>

namespace c7a {
namespace net {

//! \addtogroup net Network Communication
//! \{

typedef unsigned int ClientId;

//TODO(ej) Cleanup the Group. Make to a sole collection holding a bunch of connections.
//Move everything else into appropriate channel.

/*!
 * Collection of NetConnections to workers, allows point-to-point client
 * communication and simple collectives like MPI.
 */
class Group
{
    static const bool debug = false;

public:
    //! \name Construction and Initialization
    //! \{

    //! Construct a mock Group using a complete graph of local stream sockets
    //! for testing, and starts a thread for each client, which gets passed the
    //! Group object. This is ideal for testing network communication
    //! protocols. See tests/net/test-net-group.cpp for examples.
    //! @param num_clients The number of clients to spawn.
    //! @param thread_function The function to execute for each client.
    static void ExecuteLocalMock(
        size_t num_clients,
        const std::function<void(Group*)>& thread_function);

    //! Default empty constructor, must be Initialize()d later.
    Group()
    { }

    //! Initialize a real Group for construction from the NetManager.
    void Initialize(ClientId my_rank, size_t group_size) {
        assert(my_rank_ == -1u);
        my_rank_ = my_rank;
        connections_.resize(group_size);
    }

    //! Initializing constructor, used by tests for creating Groups.
    Group(ClientId my_rank, size_t group_size) {
        Initialize(my_rank, group_size);
    }

    //! \}

    //! non-copyable: delete copy-constructor
    Group(const Group&) = delete;
    //! non-copyable: delete assignment operator
    Group& operator = (const Group&) = delete;

    //! \name Status und Access to NetConnections
    //! \{

    //! Return Connection to client id.
    Connection & connection(ClientId id) {
        if (id >= connections_.size())
            throw Exception("Group::Connection() requested "
                            "invalid client id " + std::to_string(id));

        if (id == my_rank_)
            throw Exception("Group::Connection() requested "
                            "connection to self.");

        return connections_[id];
    }       //! Return Connection to client id.

    /**
     * @brief Assigns a connection to this net group.
     * @details This method swaps the net connection to memory managed by this group.
     *          The reference given to that method will be invalid afterwards.
     *
     * @param connection The connection to assign.
     * @return A ref to the assigned connection, which is always valid, but might be different from the
     * inut connection.
     */
    Connection & AssignConnection(Connection& connection) {
        if (connection.peer_id() >= connections_.size())
            throw Exception("Group::GetClient() requested "
                            "invalid client id "
                            + std::to_string(connection.peer_id()));

        connections_[connection.peer_id()] = std::move(connection);

        return connections_[connection.peer_id()];
    }

    //! Return number of connections in this group.
    size_t Size() const {
        return connections_.size();
    }

    //! Return my rank in the connection group
    size_t MyRank() const {
        return my_rank_;
    }

    //! Closes all client connections
    void Close() {
        if (listener_.IsValid())
            listener_.Close();

        for (size_t i = 0; i != connections_.size(); ++i)
        {
            if (i == my_rank_) continue;

            if (connections_[i].IsValid())
                connections_[i].Close();
        }

        connections_.clear();
        my_rank_ = -1u;
    }

    //! Closes all client connections
    ~Group() {
        Close();
    }

    //! \}

    //! \name Richer ReceiveFromAny Functions
    //! \{

    /*!
     * Receive a fixed-length integral type from any worker into out_value, puts
     * worker id in *src.
     *
     * @param out_src The id of the client the data was received from.
     * @param out_value The received value.
     */
    template <typename T>
    void ReceiveFromAny(ClientId* out_src, T* out_value) {
        fd_set fd_set;
        int max_fd = 0;

        FD_ZERO(&fd_set);

        // TODO(ej) use NetDispatcher here?
        // add file descriptor to read set for poll TODO(ts): make this faster
        // (somewhen)

        sLOG0 << "--- Group::ReceiveFromAny() - select():";

        for (size_t i = 0; i != connections_.size(); ++i)
        {
            if (i == my_rank_) continue;

            int fd = connections_[i].GetSocket().fd();
            FD_SET(fd, &fd_set);
            max_fd = std::max(max_fd, fd);
            sLOG0 << "select from fd=" << fd;
        }

        int retval = select(max_fd + 1, &fd_set, NULL, NULL, NULL);

        if (retval < 0) {
            perror("select()");
            abort();
        }
        else if (retval == 0) {
            perror("select() TIMEOUT");
            abort();
        }

        for (size_t i = 0; i < connections_.size(); i++)
        {
            if (i == my_rank_) continue;

            int fd = connections_[i].GetSocket().fd();

            if (FD_ISSET(fd, &fd_set))
            {
                sLOG << "select() readable fd" << fd;

                *out_src = i;
                return connections_[i].Receive<T>(out_value);
            }
        }

        sLOG << "Select() returned but no fd was readable.";

        return ReceiveFromAny<T>(out_src, out_value);
    }

    /*!
     * Receives a string message from any worker into out_data, which will be
     * resized as needed, puts worker id in *src.
     *
     * @param out_src The id of the worker the string was received from.
     * @param out_data The string received from the worker.
     */
    void ReceiveStringFromAny(ClientId* out_src, std::string* out_data) {
        fd_set fd_set;
        int max_fd = 0;

        FD_ZERO(&fd_set);

        // add file descriptor to read set for poll TODO(ts): make this faster
        // (somewhen)

        sLOG0 << "--- Group::ReceiveFromAny() - select():";

        for (size_t i = 0; i != connections_.size(); ++i)
        {
            if (i == my_rank_) continue;

            int fd = connections_[i].GetSocket().fd();
            FD_SET(fd, &fd_set);
            max_fd = std::max(max_fd, fd);
            sLOG0 << "select from fd=" << fd;
        }

        int retval = select(max_fd + 1, &fd_set, NULL, NULL, NULL);

        if (retval < 0) {
            perror("select()");
            abort();
        }
        else if (retval == 0) {
            perror("select() TIMEOUT");
            abort();
        }

        for (size_t i = 0; i < connections_.size(); i++)
        {
            if (i == my_rank_) continue;

            int fd = connections_[i].GetSocket().fd();

            if (FD_ISSET(fd, &fd_set))
            {
                sLOG << my_rank_ << "- select() readable fd" << fd << "id" << i;

                *out_src = i;
                return connections_[i].ReceiveString(out_data);
            }
        }

        sLOG << my_rank_ << " - Select() returned but no fd was readable.";

        return ReceiveStringFromAny(out_src, out_data);
    }

    /**
     * @brief Sends a string to a worker.
     * @details Sends a string to a worker.
     *
     * @param dest The worker to send the string to.
     * @param data The string to send.
     */
    void SendStringTo(ClientId dest, const std::string& data) {
        this->connection(dest).SendString(data);
    }

    /**
     * @brief Receives a string from the given worker.
     * @details Receives a string from the given worker.
     *
     * @param src The worker to receive the string from.
     * @param data A pointer to the string where the received string should be stored.
     */
    void ReceiveStringFrom(ClientId src, std::string* data) {
        this->connection(src).ReceiveString(data);
    }

    /**
     * @brief Sends a fixed lentgh type to the given worker.
     * @details Sends a fixed lentgh type to the given worker.
     *
     * @param dest The worker to send the data to.
     * @param data The data to send.
     */
    template <typename T>
    void SendTo(ClientId dest, const T& data) {
        this->connection(dest).Send(data);
    }

    /**
     * @brief Receives a fixed length type from the given worker.
     * @details Receives a fixed length type from the given worker.
     *
     * @param src The worker to receive the fixed length type from.
     * @param data A pointer to the location where the received data should be stored.
     */
    template <typename T>
    void ReceiveFrom(ClientId src, T* data) {
        this->connection(src).Receive(data);
    }

    /**
     * @brief Broadcasts a string to all workers.
     * @details Broadcasts a string to all workers.
     *
     * @param data The string to broadcast.
     */
    void BroadcastString(const std::string& data) {
        for (size_t i = 0; i < connections_.size(); i++)
        {
            if (i == my_rank_) continue;
            SendStringTo(i, data);
        }
    }

    /**
     * @brief Broadcasts a fixed length type to all workers.
     * @details Broadcasts a fixed length type to all workers.
     *
     * @param data The data to broadcast.
     */
    template <typename T>
    void BroadcastString(const T& data) {
        for (size_t i = 0; i < connections_.size(); i++)
        {
            if (i == my_rank_) continue;
            SendStringTo(i, data);
        }
    }

    //! \}

    //! \name Collective Operations
    //! \{

    //TODO(rh) Please migrate into a different class, since Group is an abstract concept that bundles connections.

    template <typename T, typename BinarySumOp = common::SumOp<T> >
    void AllReduce(T& value, BinarySumOp sumOp = BinarySumOp());

    template <typename T, typename BinarySumOp = common::SumOp<T> >
    void PrefixSum(T& value, BinarySumOp sumOp = BinarySumOp());

    template <typename T, typename BinarySumOp = common::SumOp<T> >
    void ReduceToRoot(T& value, BinarySumOp sumOp = BinarySumOp());

    template <typename T>
    void Broadcast(T& value);

    //! \}

private:
    //! The client id of this object in the Group.
    ClientId my_rank_ = -1;

    //! Connections to all other clients in the Group.
    std::vector<Connection> connections_;

    //! Socket on which to listen for incoming connections.
    Connection listener_;
};

template <typename T, typename BinarySumOp>
void Group::PrefixSum(T& value, BinarySumOp sumOp) {
    // The total sum in the current hypercube. This is stored, because later,
    // bigger hypercubes need this value.
    T total_sum = value;

    for (size_t d = 1; d < Size(); d <<= 1)
    {
        // Send total sum of this hypercube to worker with id = id XOR d
        if ((MyRank() ^ d) < Size()) {
            Connection(MyRank() ^ d).Send(total_sum);
            sLOG << "PREFIX_SUM: Worker" << MyRank() << ": Sending" << total_sum
                 << "to worker" << (MyRank() ^ d);
        }

        // Receive total sum of smaller hypercube from worker with id = id XOR d
        T recv_data;
        if ((MyRank() ^ d) < Size()) {
            Connection(MyRank() ^ d).Receive(&recv_data);
            total_sum = sumOp(total_sum, recv_data);
            // Variable 'value' represents the prefix sum of this worker
            if (MyRank() & d)
                value = sumOp(value, recv_data);
            sLOG << "PREFIX_SUM: Worker" << MyRank() << ": Received" << recv_data
                 << "from worker" << (MyRank() ^ d)
                 << "value =" << value;
        }
    }

    sLOG << "PREFIX_SUM: Worker" << MyRank()
         << ": value after prefix sum =" << value;
}

//! Perform a binomial tree reduce to the worker with index 0
template <typename T, typename BinarySumOp>
void Group::ReduceToRoot(T& value, BinarySumOp sumOp) {
    bool active = true;
    for (size_t d = 1; d < Size(); d <<= 1) {
        if (active) {
            if (MyRank() & d) {
                connection(MyRank() - d).Send(value);
                active = false;
            }
            else if (MyRank() + d < Size()) {
                T recv_data;
                connection(MyRank() + d).Receive(&recv_data);
                value = sumOp(value, recv_data);
            }
        }
    }
}

//! Binomial-broadcasts the value of the worker with index 0 to all the others
template <typename T>
void Group::Broadcast(T& value) {
    if (MyRank() > 0) {
        ClientId from;
        ReceiveFromAny(&from, &value);
    }
    for (size_t d = 1, i = 0; ((MyRank() >> i) & 1) == 0 && d < Size(); d <<= 1, ++i) {
        if (MyRank() + d < Size()) {
            connection(MyRank() + d).Send(value);
        }
    }
}

//! Perform an All-Reduce on the workers by aggregating all values and sending
//! them backto all workers
template <typename T, typename BinarySumOp>
void Group::AllReduce(T& value, BinarySumOp sum_op) {
    ReduceToRoot(value, sum_op);
    Broadcast(value);
}

//! \}

} // namespace net
} // namespace c7a

#endif // !C7A_NET_GROUP_HEADER

/******************************************************************************/
<commit_msg>Fixing connection() calls.<commit_after>/*******************************************************************************
 * c7a/net/group.hpp
 *
 * net::Group is a collection of NetConnections providing simple MPI-like
 * collectives and point-to-point communication.
 *
 * Part of Project c7a.
 *
 * Copyright (C) 2015 Timo Bingmann <tb@panthema.net>
 *
 * This file has no license. Only Chuck Norris can compile it.
 ******************************************************************************/

#pragma once
#ifndef C7A_NET_GROUP_HEADER
#define C7A_NET_GROUP_HEADER

#include <c7a/net/endpoint.hpp>
#include <c7a/net/connection.hpp>
#include <c7a/common/functional.hpp>
#include <c7a/common/logger.hpp>

#include <algorithm>
#include <cassert>
#include <cstring>
#include <map>
#include <string>
#include <thread>
#include <utility>
#include <vector>

namespace c7a {
namespace net {

//! \addtogroup net Network Communication
//! \{

typedef unsigned int ClientId;

//TODO(ej) Cleanup the Group. Make to a sole collection holding a bunch of connections.
//Move everything else into appropriate channel.

/*!
 * Collection of NetConnections to workers, allows point-to-point client
 * communication and simple collectives like MPI.
 */
class Group
{
    static const bool debug = false;

public:
    //! \name Construction and Initialization
    //! \{

    //! Construct a mock Group using a complete graph of local stream sockets
    //! for testing, and starts a thread for each client, which gets passed the
    //! Group object. This is ideal for testing network communication
    //! protocols. See tests/net/test-net-group.cpp for examples.
    //! @param num_clients The number of clients to spawn.
    //! @param thread_function The function to execute for each client.
    static void ExecuteLocalMock(
        size_t num_clients,
        const std::function<void(Group*)>& thread_function);

    //! Default empty constructor, must be Initialize()d later.
    Group()
    { }

    //! Initialize a real Group for construction from the NetManager.
    void Initialize(ClientId my_rank, size_t group_size) {
        assert(my_rank_ == -1u);
        my_rank_ = my_rank;
        connections_.resize(group_size);
    }

    //! Initializing constructor, used by tests for creating Groups.
    Group(ClientId my_rank, size_t group_size) {
        Initialize(my_rank, group_size);
    }

    //! \}

    //! non-copyable: delete copy-constructor
    Group(const Group&) = delete;
    //! non-copyable: delete assignment operator
    Group& operator = (const Group&) = delete;

    //! \name Status und Access to NetConnections
    //! \{

    //! Return Connection to client id.
    Connection & connection(ClientId id) {
        if (id >= connections_.size())
            throw Exception("Group::Connection() requested "
                            "invalid client id " + std::to_string(id));

        if (id == my_rank_)
            throw Exception("Group::Connection() requested "
                            "connection to self.");

        return connections_[id];
    }       //! Return Connection to client id.

    /**
     * @brief Assigns a connection to this net group.
     * @details This method swaps the net connection to memory managed by this group.
     *          The reference given to that method will be invalid afterwards.
     *
     * @param connection The connection to assign.
     * @return A ref to the assigned connection, which is always valid, but might be different from the
     * inut connection.
     */
    Connection & AssignConnection(Connection& connection) {
        if (connection.peer_id() >= connections_.size())
            throw Exception("Group::GetClient() requested "
                            "invalid client id "
                            + std::to_string(connection.peer_id()));

        connections_[connection.peer_id()] = std::move(connection);

        return connections_[connection.peer_id()];
    }

    //! Return number of connections in this group.
    size_t Size() const {
        return connections_.size();
    }

    //! Return my rank in the connection group
    size_t MyRank() const {
        return my_rank_;
    }

    //! Closes all client connections
    void Close() {
        if (listener_.IsValid())
            listener_.Close();

        for (size_t i = 0; i != connections_.size(); ++i)
        {
            if (i == my_rank_) continue;

            if (connections_[i].IsValid())
                connections_[i].Close();
        }

        connections_.clear();
        my_rank_ = -1u;
    }

    //! Closes all client connections
    ~Group() {
        Close();
    }

    //! \}

    //! \name Richer ReceiveFromAny Functions
    //! \{

    /*!
     * Receive a fixed-length integral type from any worker into out_value, puts
     * worker id in *src.
     *
     * @param out_src The id of the client the data was received from.
     * @param out_value The received value.
     */
    template <typename T>
    void ReceiveFromAny(ClientId* out_src, T* out_value) {
        fd_set fd_set;
        int max_fd = 0;

        FD_ZERO(&fd_set);

        // TODO(ej) use NetDispatcher here?
        // add file descriptor to read set for poll TODO(ts): make this faster
        // (somewhen)

        sLOG0 << "--- Group::ReceiveFromAny() - select():";

        for (size_t i = 0; i != connections_.size(); ++i)
        {
            if (i == my_rank_) continue;

            int fd = connections_[i].GetSocket().fd();
            FD_SET(fd, &fd_set);
            max_fd = std::max(max_fd, fd);
            sLOG0 << "select from fd=" << fd;
        }

        int retval = select(max_fd + 1, &fd_set, NULL, NULL, NULL);

        if (retval < 0) {
            perror("select()");
            abort();
        }
        else if (retval == 0) {
            perror("select() TIMEOUT");
            abort();
        }

        for (size_t i = 0; i < connections_.size(); i++)
        {
            if (i == my_rank_) continue;

            int fd = connections_[i].GetSocket().fd();

            if (FD_ISSET(fd, &fd_set))
            {
                sLOG << "select() readable fd" << fd;

                *out_src = i;
                return connections_[i].Receive<T>(out_value);
            }
        }

        sLOG << "Select() returned but no fd was readable.";

        return ReceiveFromAny<T>(out_src, out_value);
    }

    /*!
     * Receives a string message from any worker into out_data, which will be
     * resized as needed, puts worker id in *src.
     *
     * @param out_src The id of the worker the string was received from.
     * @param out_data The string received from the worker.
     */
    void ReceiveStringFromAny(ClientId* out_src, std::string* out_data) {
        fd_set fd_set;
        int max_fd = 0;

        FD_ZERO(&fd_set);

        // add file descriptor to read set for poll TODO(ts): make this faster
        // (somewhen)

        sLOG0 << "--- Group::ReceiveFromAny() - select():";

        for (size_t i = 0; i != connections_.size(); ++i)
        {
            if (i == my_rank_) continue;

            int fd = connections_[i].GetSocket().fd();
            FD_SET(fd, &fd_set);
            max_fd = std::max(max_fd, fd);
            sLOG0 << "select from fd=" << fd;
        }

        int retval = select(max_fd + 1, &fd_set, NULL, NULL, NULL);

        if (retval < 0) {
            perror("select()");
            abort();
        }
        else if (retval == 0) {
            perror("select() TIMEOUT");
            abort();
        }

        for (size_t i = 0; i < connections_.size(); i++)
        {
            if (i == my_rank_) continue;

            int fd = connections_[i].GetSocket().fd();

            if (FD_ISSET(fd, &fd_set))
            {
                sLOG << my_rank_ << "- select() readable fd" << fd << "id" << i;

                *out_src = i;
                return connections_[i].ReceiveString(out_data);
            }
        }

        sLOG << my_rank_ << " - Select() returned but no fd was readable.";

        return ReceiveStringFromAny(out_src, out_data);
    }

    /**
     * @brief Sends a string to a worker.
     * @details Sends a string to a worker.
     *
     * @param dest The worker to send the string to.
     * @param data The string to send.
     */
    void SendStringTo(ClientId dest, const std::string& data) {
        this->connection(dest).SendString(data);
    }

    /**
     * @brief Receives a string from the given worker.
     * @details Receives a string from the given worker.
     *
     * @param src The worker to receive the string from.
     * @param data A pointer to the string where the received string should be stored.
     */
    void ReceiveStringFrom(ClientId src, std::string* data) {
        this->connection(src).ReceiveString(data);
    }

    /**
     * @brief Sends a fixed lentgh type to the given worker.
     * @details Sends a fixed lentgh type to the given worker.
     *
     * @param dest The worker to send the data to.
     * @param data The data to send.
     */
    template <typename T>
    void SendTo(ClientId dest, const T& data) {
        this->connection(dest).Send(data);
    }

    /**
     * @brief Receives a fixed length type from the given worker.
     * @details Receives a fixed length type from the given worker.
     *
     * @param src The worker to receive the fixed length type from.
     * @param data A pointer to the location where the received data should be stored.
     */
    template <typename T>
    void ReceiveFrom(ClientId src, T* data) {
        this->connection(src).Receive(data);
    }

    /**
     * @brief Broadcasts a string to all workers.
     * @details Broadcasts a string to all workers.
     *
     * @param data The string to broadcast.
     */
    void BroadcastString(const std::string& data) {
        for (size_t i = 0; i < connections_.size(); i++)
        {
            if (i == my_rank_) continue;
            SendStringTo(i, data);
        }
    }

    /**
     * @brief Broadcasts a fixed length type to all workers.
     * @details Broadcasts a fixed length type to all workers.
     *
     * @param data The data to broadcast.
     */
    template <typename T>
    void BroadcastString(const T& data) {
        for (size_t i = 0; i < connections_.size(); i++)
        {
            if (i == my_rank_) continue;
            SendStringTo(i, data);
        }
    }

    //! \}

    //! \name Collective Operations
    //! \{

    //TODO(rh) Please migrate into a different class, since Group is an abstract concept that bundles connections.

    template <typename T, typename BinarySumOp = common::SumOp<T> >
    void AllReduce(T& value, BinarySumOp sumOp = BinarySumOp());

    template <typename T, typename BinarySumOp = common::SumOp<T> >
    void PrefixSum(T& value, BinarySumOp sumOp = BinarySumOp());

    template <typename T, typename BinarySumOp = common::SumOp<T> >
    void ReduceToRoot(T& value, BinarySumOp sumOp = BinarySumOp());

    template <typename T>
    void Broadcast(T& value);

    //! \}

private:
    //! The client id of this object in the Group.
    ClientId my_rank_ = -1;

    //! Connections to all other clients in the Group.
    std::vector<Connection> connections_;

    //! Socket on which to listen for incoming connections.
    Connection listener_;
};

template <typename T, typename BinarySumOp>
void Group::PrefixSum(T& value, BinarySumOp sumOp) {
    // The total sum in the current hypercube. This is stored, because later,
    // bigger hypercubes need this value.
    T total_sum = value;

    for (size_t d = 1; d < Size(); d <<= 1)
    {
        // Send total sum of this hypercube to worker with id = id XOR d
        if ((MyRank() ^ d) < Size()) {
            connection(MyRank() ^ d).Send(total_sum);
            sLOG << "PREFIX_SUM: Worker" << MyRank() << ": Sending" << total_sum
                 << "to worker" << (MyRank() ^ d);
        }

        // Receive total sum of smaller hypercube from worker with id = id XOR d
        T recv_data;
        if ((MyRank() ^ d) < Size()) {
            connection(MyRank() ^ d).Receive(&recv_data);
            total_sum = sumOp(total_sum, recv_data);
            // Variable 'value' represents the prefix sum of this worker
            if (MyRank() & d)
                value = sumOp(value, recv_data);
            sLOG << "PREFIX_SUM: Worker" << MyRank() << ": Received" << recv_data
                 << "from worker" << (MyRank() ^ d)
                 << "value =" << value;
        }
    }

    sLOG << "PREFIX_SUM: Worker" << MyRank()
         << ": value after prefix sum =" << value;
}

//! Perform a binomial tree reduce to the worker with index 0
template <typename T, typename BinarySumOp>
void Group::ReduceToRoot(T& value, BinarySumOp sumOp) {
    bool active = true;
    for (size_t d = 1; d < Size(); d <<= 1) {
        if (active) {
            if (MyRank() & d) {
                connection(MyRank() - d).Send(value);
                active = false;
            }
            else if (MyRank() + d < Size()) {
                T recv_data;
                connection(MyRank() + d).Receive(&recv_data);
                value = sumOp(value, recv_data);
            }
        }
    }
}

//! Binomial-broadcasts the value of the worker with index 0 to all the others
template <typename T>
void Group::Broadcast(T& value) {
    if (MyRank() > 0) {
        ClientId from;
        ReceiveFromAny(&from, &value);
    }
    for (size_t d = 1, i = 0; ((MyRank() >> i) & 1) == 0 && d < Size(); d <<= 1, ++i) {
        if (MyRank() + d < Size()) {
            connection(MyRank() + d).Send(value);
        }
    }
}

//! Perform an All-Reduce on the workers by aggregating all values and sending
//! them backto all workers
template <typename T, typename BinarySumOp>
void Group::AllReduce(T& value, BinarySumOp sum_op) {
    ReduceToRoot(value, sum_op);
    Broadcast(value);
}

//! \}

} // namespace net
} // namespace c7a

#endif // !C7A_NET_GROUP_HEADER

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


<commit_before>using namespace GeFiCa;
// calculate and save fields of an Inverted Coaxial Point-Contact (ICPC) HPGe
void calculateFields(const char *output="ICPC.root")
{
   SquarePointContact detector;
   detector.Bias[0]=70; // bias on point contact
   detector.Bias[1]=0; // ground outer contact

   detector.Width=1.8*cm; detector.Height=1.0*cm; detector.Length=1.8*cm;

   detector.PointContactW=0.6*mm; detector.PointContactH=0.1*mm;
   detector.PointContactL=0.6*mm;

   detector.TaperW=0.3*cm;
   //detector.WrapAroundW=0.3*cm;

   detector.BottomImpurity=4e9/cm3; detector.TopImpurity=4e9/cm3;

   int nx=50;
   int ny=50; // precision: 0.1 mm
   int nz=50; // precision: 0.1 mm
   XYZ grid(nx,ny,nz);
   grid.SetupWith(detector);
   grid.RelaxationFactor=1.84;
   grid.SuccessiveOverRelax();

   TFile file(output,"recreate");
   detector.Write();
   grid.Write();
   file.Close();
}
<commit_msg>update<commit_after>using namespace GeFiCa;
// calculate and save fields of an Inverted Coaxial Point-Contact (ICPC) HPGe
void calculateFields(const char *output="ICPC.root")
{
   SquarePointContact detector;
   detector.Bias[0]=0; // bias on point contact
   detector.Bias[1]=70; // ground outer contact

   detector.Width=1.8*cm; detector.Height=1.0*cm; detector.Length=1.8*cm;

   detector.PointContactW=0.6*mm; detector.PointContactH=0.1*mm;
   detector.PointContactL=0.6*mm;

   detector.TaperW=0.3*cm;
   //detector.WrapAroundW=0.3*cm;

   detector.BottomImpurity=4e9/cm3; detector.TopImpurity=4e9/cm3;

   int nx=50;
   int ny=50; // precision: 0.1 mm
   int nz=100; // precision: 0.1 mm
   XYZ grid(nx,ny,nz);
   grid.SetupWith(detector);
   grid.RelaxationFactor=1.94;
   grid.SuccessiveOverRelax();

   TFile file(output,"recreate");
   detector.Write();
   grid.Write();
   file.Close();
}
<|endoftext|>