<commit_before>
//
// This source file is part of appleseed.
// Visit http://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2015-2016 Francois Beaune, The appleseedhq Organization
//
// 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.
//

// Interface header.
#include "appleseedrenderer.h"

// appleseed-max headers.
#include "renderer/appleseedrendererparamdlg.h"
#include "renderer/datachunks.h"
#include "renderer/projectbuilder.h"
#include "renderer/renderercontroller.h"
#include "renderer/tilecallback.h"
#include "utilities.h"
#include "version.h"

// appleseed.renderer headers.
#include "renderer/api/frame.h"
#include "renderer/api/project.h"
#include "renderer/api/rendering.h"

// appleseed.foundation headers.
#include "foundation/image/canvasproperties.h"
#include "foundation/image/image.h"
#include "foundation/platform/types.h"
#include "foundation/utility/autoreleaseptr.h"

// 3ds Max headers.
#include <assert1.h>
#include <bitmap.h>

// Standard headers.
#include <cstddef>

namespace asf = foundation;
namespace asr = renderer;

namespace
{
    const Class_ID AppleseedRendererClassId(0x72651b24, 0x5da32e1d);
    const TCHAR* AppleseedRendererClassName = _T("appleseed Renderer");
}

AppleseedRendererClassDesc g_appleseed_renderer_classdesc;

//
// AppleseedRenderer class implementation.
//

AppleseedRenderer::AppleseedRenderer()
  : m_settings(RendererSettings::defaults())
{
    clear();
}

Class_ID AppleseedRenderer::ClassID()
{
    return AppleseedRendererClassId;
}

void AppleseedRenderer::GetClassName(MSTR& s)
{
    s = AppleseedRendererClassName;
}

void AppleseedRenderer::DeleteThis()
{
    delete this;
}

RefResult AppleseedRenderer::NotifyRefChanged(
    const Interval&         changeInt,
    RefTargetHandle         hTarget,
    PartID&                 partID,
    RefMessage              message,
    BOOL                    propagate)
{
    return REF_SUCCEED;
}

int AppleseedRenderer::Open(
    INode*                  scene,
    INode*                  view_node,
    ViewParams*             view_params,
    RendParams&             rend_params,
    HWND                    hwnd,
    DefaultLight*           default_lights,
    int                     default_light_count,
    RendProgressCallback*   progress_cb)
{
    SuspendAll suspend(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);

    m_scene = scene;
    m_view_node = view_node;
    if (view_params)
        m_view_params = *view_params;
    m_rend_params = rend_params;

    // Copy the default lights as the 'default_lights' pointer is no longer valid in Render().
    m_default_lights.clear();
    m_default_lights.reserve(default_light_count);
    for (int i = 0; i < default_light_count; ++i)
        m_default_lights.push_back(default_lights[i]);

    return 1;   // success
}

namespace
{
    void get_view_params_from_view_node(
        ViewParams&             view_params,
        INode*                  view_node,
        const TimeValue         time)
    {
        const ObjectState& os = view_node->EvalWorldState(time);
        switch (os.obj->SuperClassID())
        {
          case CAMERA_CLASS_ID:
            {
                CameraObject* cam = static_cast<CameraObject*>(os.obj);

                Interval validity_interval;
                validity_interval.SetInfinite();

                Matrix3 cam_to_world = view_node->GetObjTMAfterWSM(time, &validity_interval);
                cam_to_world.NoScale();

                view_params.affineTM = Inverse(cam_to_world);

                CameraState cam_state;
                cam->EvalCameraState(time, validity_interval, &cam_state);

                view_params.projType = PROJ_PERSPECTIVE;
                view_params.fov = cam_state.fov;

                if (cam_state.manualClip)
                {
                    view_params.hither = cam_state.hither;
                    view_params.yon = cam_state.yon;
                }
                else
                {
                    view_params.hither = 0.001f;
                    view_params.yon = 1.0e38f;
                }
            }
            break;

          case LIGHT_CLASS_ID:
            {
                DbgAssert(!"Not implemented yet.");
            }
            break;

          default:
            DbgAssert(!"Unexpected super class ID for camera.");
        }
    }

    class RenderBeginProc
      : public RefEnumProc
    {
      public:
        explicit RenderBeginProc(const TimeValue time)
          : m_time(time)
        {
        }

        virtual int proc(ReferenceMaker* rm) override
        {
            rm->RenderBegin(m_time);
            return REF_ENUM_CONTINUE;
        }

      private:
        const TimeValue m_time;
    };

    class RenderEndProc
      : public RefEnumProc
    {
      public:
        explicit RenderEndProc(const TimeValue time)
          : m_time(time)
        {
        }

        virtual int proc(ReferenceMaker* rm) override
        {
            rm->RenderEnd(m_time);
            return REF_ENUM_CONTINUE;
        }

      private:
        const TimeValue m_time;
    };

    void render_begin(
        std::vector<INode*>&    nodes,
        const TimeValue         time)
    {
        RenderBeginProc proc(time);
        proc.BeginEnumeration();

        for (auto node : nodes)
            node->EnumRefHierarchy(proc);

        proc.EndEnumeration();
    }

    void render_end(
        std::vector<INode*>&    nodes,
        const TimeValue         time)
    {
        RenderEndProc proc(time);
        proc.BeginEnumeration();

        for (auto node : nodes)
            node->EnumRefHierarchy(proc);

        proc.EndEnumeration();
    }

    void render(
        asr::Project&           project,
        const RendererSettings& settings,
        Bitmap*                 bitmap,
        RendProgressCallback*   progress_cb)
    {
        // Number of rendered tiles, shared counter accessed atomically.
        volatile asf::uint32 rendered_tile_count = 0;

        // Create the renderer controller.
        const size_t total_tile_count =
              static_cast<size_t>(settings.m_passes)
            * project.get_frame()->image().properties().m_tile_count;
        RendererController renderer_controller(
            progress_cb,
            &rendered_tile_count,
            total_tile_count);

        // Create the tile callback.
        TileCallback tile_callback(bitmap, &rendered_tile_count);

        // Create the master renderer.
        std::auto_ptr<asr::MasterRenderer> renderer(
            new asr::MasterRenderer(
                project,
                project.configurations().get_by_name("final")->get_inherited_parameters(),
                &renderer_controller,
                &tile_callback));

        // Render the frame.
        renderer->render();

        // Make sure the master renderer is deleted before the project.
    }
}

int AppleseedRenderer::Render(
    TimeValue               time,
    Bitmap*                 bitmap,
    FrameRendParams&        frame_rend_params,
    HWND                    hwnd,
    RendProgressCallback*   progress_cb,
    ViewParams*             view_params)
{
    SuspendAll suspend(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);

    m_time = time;

    if (view_params)
        m_view_params = *view_params;

    if (m_view_node)
        get_view_params_from_view_node(m_view_params, m_view_node, time);

    // Retrieve and tweak renderer settings.
    RendererSettings renderer_settings = m_settings;
    if (m_rend_params.inMtlEdit)
    {
        renderer_settings.m_pixel_samples = m_rend_params.mtlEditAA ? 32 : 4;
        renderer_settings.m_passes = 1;
        renderer_settings.m_gi = false;
        renderer_settings.m_background_emits_light = false;
    }

    // Collect the entities we're interested in.
    if (progress_cb)
        progress_cb->SetTitle(_T("Collecting Entities..."));
    MaxSceneEntityCollector collector(m_entities);
    collector.collect(m_scene);

    // Call RenderBegin() on all object instances.
    render_begin(m_entities.m_objects, m_time);

    // Build the project.
    if (progress_cb)
        progress_cb->SetTitle(_T("Building Project..."));
    asf::auto_release_ptr<asr::Project> project(
        build_project(
            m_entities,
            m_default_lights,
            m_view_params,
            m_rend_params,
            frame_rend_params,
            renderer_settings,
            bitmap,
            time));

    if (m_rend_params.inMtlEdit)
    {
        // Render the project.
        if (progress_cb)
            progress_cb->SetTitle(_T("Rendering..."));
        render(project.ref(), m_settings, bitmap, progress_cb);
    }
    else
    {
        // Write the project to disk.
        if (m_settings.m_output_mode == RendererSettings::OutputMode::SaveProjectOnly ||
            m_settings.m_output_mode == RendererSettings::OutputMode::SaveProjectAndRender)
        {
            if (progress_cb)
                progress_cb->SetTitle(_T("Writing Project To Disk..."));
            asr::ProjectFileWriter::write(
                project.ref(),
                wide_to_utf8(m_settings.m_project_file_path).c_str());
        }

        // Render the project.
        if (m_settings.m_output_mode == RendererSettings::OutputMode::RenderOnly ||
            m_settings.m_output_mode == RendererSettings::OutputMode::SaveProjectAndRender)
        {
            if (progress_cb)
                progress_cb->SetTitle(_T("Rendering..."));
            render(project.ref(), m_settings, bitmap, progress_cb);
        }
    }

    if (progress_cb)
        progress_cb->SetTitle(_T("Done."));

    // Success.
    return 1;
}

void AppleseedRenderer::Close(
    HWND                    hwnd,
    RendProgressCallback*   progress_cb)
{
    // Call RenderEnd() on all object instances.
    render_end(m_entities.m_objects, m_time);

    clear();
}

RendParamDlg* AppleseedRenderer::CreateParamDialog(
    IRendParams*            rend_params,
    BOOL                    in_progress)
{
    return
        new AppleseedRendererParamDlg(
            rend_params,
            in_progress,
            m_settings);
}

void AppleseedRenderer::ResetParams()
{
    clear();
}

IOResult AppleseedRenderer::Save(ISave* isave)
{
    bool success = true;

    isave->BeginChunk(ChunkFileFormatVersion);
    success &= write(isave, FileFormatVersion);
    isave->EndChunk();

    isave->BeginChunk(ChunkSettings);
    success &= m_settings.save(isave);
    isave->EndChunk();

    return success ? IO_OK : IO_ERROR;
}

IOResult AppleseedRenderer::Load(ILoad* iload)
{
    IOResult result = IO_OK;

    while (true)
    {
        result = iload->OpenChunk();
        if (result == IO_END)
            return IO_OK;
        if (result != IO_OK)
            break;

        switch (iload->CurChunkID())
        {
          case ChunkFileFormatVersion:
            {
                USHORT version;
                result = read<USHORT>(iload, &version);
            }
            break;

          case ChunkSettings:
            result = m_settings.load(iload);
            break;
        }

        if (result != IO_OK)
            break;

        result = iload->CloseChunk();
        if (result != IO_OK)
            break;
    }

    return result;
}

void AppleseedRenderer::clear()
{
    m_scene = nullptr;
    m_view_node = nullptr;
    m_default_lights.clear();
    m_time = 0;
    m_entities.clear();
}

//
// AppleseedRendererClassDesc class implementation.
//

int AppleseedRendererClassDesc::IsPublic()
{
    return TRUE;
}

void* AppleseedRendererClassDesc::Create(BOOL loading)
{
    return new AppleseedRenderer();
}

const MCHAR* AppleseedRendererClassDesc::ClassName()
{
    return AppleseedRendererClassName;
}

SClass_ID AppleseedRendererClassDesc::SuperClassID()
{
    return RENDERER_CLASS_ID;
}

Class_ID AppleseedRendererClassDesc::ClassID()
{
    return AppleseedRendererClassId;
}

const MCHAR* AppleseedRendererClassDesc::Category()
{
    return _T("");
}

const MCHAR* AppleseedRendererClassDesc::InternalName()
{
    // Parsable name used by MAXScript.
    return _T("appleseed_renderer");
}
<commit_msg>Fix scene accumulation bug when rendering animations<commit_after>
//
// This source file is part of appleseed.
// Visit http://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2015-2016 Francois Beaune, The appleseedhq Organization
//
// 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.
//

// Interface header.
#include "appleseedrenderer.h"

// appleseed-max headers.
#include "renderer/appleseedrendererparamdlg.h"
#include "renderer/datachunks.h"
#include "renderer/projectbuilder.h"
#include "renderer/renderercontroller.h"
#include "renderer/tilecallback.h"
#include "utilities.h"
#include "version.h"

// appleseed.renderer headers.
#include "renderer/api/frame.h"
#include "renderer/api/project.h"
#include "renderer/api/rendering.h"

// appleseed.foundation headers.
#include "foundation/image/canvasproperties.h"
#include "foundation/image/image.h"
#include "foundation/platform/types.h"
#include "foundation/utility/autoreleaseptr.h"

// 3ds Max headers.
#include <assert1.h>
#include <bitmap.h>

// Standard headers.
#include <cstddef>

namespace asf = foundation;
namespace asr = renderer;

namespace
{
    const Class_ID AppleseedRendererClassId(0x72651b24, 0x5da32e1d);
    const TCHAR* AppleseedRendererClassName = _T("appleseed Renderer");
}

AppleseedRendererClassDesc g_appleseed_renderer_classdesc;

//
// AppleseedRenderer class implementation.
//

AppleseedRenderer::AppleseedRenderer()
  : m_settings(RendererSettings::defaults())
{
    clear();
}

Class_ID AppleseedRenderer::ClassID()
{
    return AppleseedRendererClassId;
}

void AppleseedRenderer::GetClassName(MSTR& s)
{
    s = AppleseedRendererClassName;
}

void AppleseedRenderer::DeleteThis()
{
    delete this;
}

RefResult AppleseedRenderer::NotifyRefChanged(
    const Interval&         changeInt,
    RefTargetHandle         hTarget,
    PartID&                 partID,
    RefMessage              message,
    BOOL                    propagate)
{
    return REF_SUCCEED;
}

int AppleseedRenderer::Open(
    INode*                  scene,
    INode*                  view_node,
    ViewParams*             view_params,
    RendParams&             rend_params,
    HWND                    hwnd,
    DefaultLight*           default_lights,
    int                     default_light_count,
    RendProgressCallback*   progress_cb)
{
    SuspendAll suspend(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);

    m_scene = scene;
    m_view_node = view_node;
    if (view_params)
        m_view_params = *view_params;
    m_rend_params = rend_params;

    // Copy the default lights as the 'default_lights' pointer is no longer valid in Render().
    m_default_lights.clear();
    m_default_lights.reserve(default_light_count);
    for (int i = 0; i < default_light_count; ++i)
        m_default_lights.push_back(default_lights[i]);

    return 1;   // success
}

namespace
{
    void get_view_params_from_view_node(
        ViewParams&             view_params,
        INode*                  view_node,
        const TimeValue         time)
    {
        const ObjectState& os = view_node->EvalWorldState(time);
        switch (os.obj->SuperClassID())
        {
          case CAMERA_CLASS_ID:
            {
                CameraObject* cam = static_cast<CameraObject*>(os.obj);

                Interval validity_interval;
                validity_interval.SetInfinite();

                Matrix3 cam_to_world = view_node->GetObjTMAfterWSM(time, &validity_interval);
                cam_to_world.NoScale();

                view_params.affineTM = Inverse(cam_to_world);

                CameraState cam_state;
                cam->EvalCameraState(time, validity_interval, &cam_state);

                view_params.projType = PROJ_PERSPECTIVE;
                view_params.fov = cam_state.fov;

                if (cam_state.manualClip)
                {
                    view_params.hither = cam_state.hither;
                    view_params.yon = cam_state.yon;
                }
                else
                {
                    view_params.hither = 0.001f;
                    view_params.yon = 1.0e38f;
                }
            }
            break;

          case LIGHT_CLASS_ID:
            {
                DbgAssert(!"Not implemented yet.");
            }
            break;

          default:
            DbgAssert(!"Unexpected super class ID for camera.");
        }
    }

    class RenderBeginProc
      : public RefEnumProc
    {
      public:
        explicit RenderBeginProc(const TimeValue time)
          : m_time(time)
        {
        }

        virtual int proc(ReferenceMaker* rm) override
        {
            rm->RenderBegin(m_time);
            return REF_ENUM_CONTINUE;
        }

      private:
        const TimeValue m_time;
    };

    class RenderEndProc
      : public RefEnumProc
    {
      public:
        explicit RenderEndProc(const TimeValue time)
          : m_time(time)
        {
        }

        virtual int proc(ReferenceMaker* rm) override
        {
            rm->RenderEnd(m_time);
            return REF_ENUM_CONTINUE;
        }

      private:
        const TimeValue m_time;
    };

    void render_begin(
        std::vector<INode*>&    nodes,
        const TimeValue         time)
    {
        RenderBeginProc proc(time);
        proc.BeginEnumeration();

        for (auto node : nodes)
            node->EnumRefHierarchy(proc);

        proc.EndEnumeration();
    }

    void render_end(
        std::vector<INode*>&    nodes,
        const TimeValue         time)
    {
        RenderEndProc proc(time);
        proc.BeginEnumeration();

        for (auto node : nodes)
            node->EnumRefHierarchy(proc);

        proc.EndEnumeration();
    }

    void render(
        asr::Project&           project,
        const RendererSettings& settings,
        Bitmap*                 bitmap,
        RendProgressCallback*   progress_cb)
    {
        // Number of rendered tiles, shared counter accessed atomically.
        volatile asf::uint32 rendered_tile_count = 0;

        // Create the renderer controller.
        const size_t total_tile_count =
              static_cast<size_t>(settings.m_passes)
            * project.get_frame()->image().properties().m_tile_count;
        RendererController renderer_controller(
            progress_cb,
            &rendered_tile_count,
            total_tile_count);

        // Create the tile callback.
        TileCallback tile_callback(bitmap, &rendered_tile_count);

        // Create the master renderer.
        std::auto_ptr<asr::MasterRenderer> renderer(
            new asr::MasterRenderer(
                project,
                project.configurations().get_by_name("final")->get_inherited_parameters(),
                &renderer_controller,
                &tile_callback));

        // Render the frame.
        renderer->render();

        // Make sure the master renderer is deleted before the project.
    }
}

int AppleseedRenderer::Render(
    TimeValue               time,
    Bitmap*                 bitmap,
    FrameRendParams&        frame_rend_params,
    HWND                    hwnd,
    RendProgressCallback*   progress_cb,
    ViewParams*             view_params)
{
    SuspendAll suspend(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);

    m_time = time;

    if (view_params)
        m_view_params = *view_params;

    if (m_view_node)
        get_view_params_from_view_node(m_view_params, m_view_node, time);

    // Retrieve and tweak renderer settings.
    RendererSettings renderer_settings = m_settings;
    if (m_rend_params.inMtlEdit)
    {
        renderer_settings.m_pixel_samples = m_rend_params.mtlEditAA ? 32 : 4;
        renderer_settings.m_passes = 1;
        renderer_settings.m_gi = false;
        renderer_settings.m_background_emits_light = false;
    }

    // Collect the entities we're interested in.
    if (progress_cb)
        progress_cb->SetTitle(_T("Collecting Entities..."));
    m_entities.clear();
    MaxSceneEntityCollector collector(m_entities);
    collector.collect(m_scene);

    // Call RenderBegin() on all object instances.
    render_begin(m_entities.m_objects, m_time);

    // Build the project.
    if (progress_cb)
        progress_cb->SetTitle(_T("Building Project..."));
    asf::auto_release_ptr<asr::Project> project(
        build_project(
            m_entities,
            m_default_lights,
            m_view_params,
            m_rend_params,
            frame_rend_params,
            renderer_settings,
            bitmap,
            time));

    if (m_rend_params.inMtlEdit)
    {
        // Render the project.
        if (progress_cb)
            progress_cb->SetTitle(_T("Rendering..."));
        render(project.ref(), m_settings, bitmap, progress_cb);
    }
    else
    {
        // Write the project to disk.
        if (m_settings.m_output_mode == RendererSettings::OutputMode::SaveProjectOnly ||
            m_settings.m_output_mode == RendererSettings::OutputMode::SaveProjectAndRender)
        {
            if (progress_cb)
                progress_cb->SetTitle(_T("Writing Project To Disk..."));
            asr::ProjectFileWriter::write(
                project.ref(),
                wide_to_utf8(m_settings.m_project_file_path).c_str());
        }

        // Render the project.
        if (m_settings.m_output_mode == RendererSettings::OutputMode::RenderOnly ||
            m_settings.m_output_mode == RendererSettings::OutputMode::SaveProjectAndRender)
        {
            if (progress_cb)
                progress_cb->SetTitle(_T("Rendering..."));
            render(project.ref(), m_settings, bitmap, progress_cb);
        }
    }

    if (progress_cb)
        progress_cb->SetTitle(_T("Done."));

    // Success.
    return 1;
}

void AppleseedRenderer::Close(
    HWND                    hwnd,
    RendProgressCallback*   progress_cb)
{
    // Call RenderEnd() on all object instances.
    render_end(m_entities.m_objects, m_time);

    clear();
}

RendParamDlg* AppleseedRenderer::CreateParamDialog(
    IRendParams*            rend_params,
    BOOL                    in_progress)
{
    return
        new AppleseedRendererParamDlg(
            rend_params,
            in_progress,
            m_settings);
}

void AppleseedRenderer::ResetParams()
{
    clear();
}

IOResult AppleseedRenderer::Save(ISave* isave)
{
    bool success = true;

    isave->BeginChunk(ChunkFileFormatVersion);
    success &= write(isave, FileFormatVersion);
    isave->EndChunk();

    isave->BeginChunk(ChunkSettings);
    success &= m_settings.save(isave);
    isave->EndChunk();

    return success ? IO_OK : IO_ERROR;
}

IOResult AppleseedRenderer::Load(ILoad* iload)
{
    IOResult result = IO_OK;

    while (true)
    {
        result = iload->OpenChunk();
        if (result == IO_END)
            return IO_OK;
        if (result != IO_OK)
            break;

        switch (iload->CurChunkID())
        {
          case ChunkFileFormatVersion:
            {
                USHORT version;
                result = read<USHORT>(iload, &version);
            }
            break;

          case ChunkSettings:
            result = m_settings.load(iload);
            break;
        }

        if (result != IO_OK)
            break;

        result = iload->CloseChunk();
        if (result != IO_OK)
            break;
    }

    return result;
}

void AppleseedRenderer::clear()
{
    m_scene = nullptr;
    m_view_node = nullptr;
    m_default_lights.clear();
    m_time = 0;
    m_entities.clear();
}

//
// AppleseedRendererClassDesc class implementation.
//

int AppleseedRendererClassDesc::IsPublic()
{
    return TRUE;
}

void* AppleseedRendererClassDesc::Create(BOOL loading)
{
    return new AppleseedRenderer();
}

const MCHAR* AppleseedRendererClassDesc::ClassName()
{
    return AppleseedRendererClassName;
}

SClass_ID AppleseedRendererClassDesc::SuperClassID()
{
    return RENDERER_CLASS_ID;
}

Class_ID AppleseedRendererClassDesc::ClassID()
{
    return AppleseedRendererClassId;
}

const MCHAR* AppleseedRendererClassDesc::Category()
{
    return _T("");
}

const MCHAR* AppleseedRendererClassDesc::InternalName()
{
    // Parsable name used by MAXScript.
    return _T("appleseed_renderer");
}
<|endoftext|>


<commit_before>/* Copyright 2020 Google Inc. All Rights Reserved.

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 "tensorflow_serving/servables/tensorflow/tflite_interpreter_pool.h"

#include <limits>
#include <memory>
#include <vector>

#include "tensorflow/core/platform/errors.h"
#include "tensorflow/lite/kernels/parse_example/parse_example.h"
#include "tensorflow/lite/kernels/register.h"

namespace tensorflow {
namespace serving {
namespace internal {

TfLiteInterpreterWrapper::TfLiteInterpreterWrapper(
    std::unique_ptr<tflite::Interpreter> interpreter)
    :
#ifdef TFLITE_PROFILE
      interpreter_(std::move(interpreter)),
      fixed_batch_size_(fixed_batch_size),
      max_num_entries_(TFLITE_PROFILE_EVENTS),
      profiler_(max_num_entries_)
#else
      interpreter_(std::move(interpreter))
#endif
{
#ifdef TFLITE_PROFILE
  interpreter_->SetProfiler(&profiler_);
#endif
  for (const int& idx : interpreter_->inputs()) {
    const auto* tflite_tensor = interpreter_->tensor(idx);
    if (tflite_tensor->type == kTfLiteString) {
      tensor_buffer_.emplace(idx,
                             std::unique_ptr<char>(tflite_tensor->data.raw));
      tensor_buffer_max_bytes_[idx] = 0;
    }
  }
}

tensorflow::Status TfLiteInterpreterWrapper::SetStringData(
    const gtl::ArraySlice<tensorflow::tstring>& batch,
    TfLiteTensor* tflite_tensor, int tensor_index) {
  // Format of the buffer for tflite:
  //   [0] number of strings (int32_t)
  //   [4] offset of each string (int32_t)
  //   [sizeof(int32_t) * (num_strings + 1)]] total size of strings
  //   [sizeof(int32_t) * (num_strings + 2)] batch.data()
  int32_t num_strings = batch.size();
  offset_.clear();
  size_t total_size = 0;
  offset_.push_back(static_cast<int32_t>(total_size));
  for (int i = 0; i < num_strings; i++) {
    total_size += batch[i].size();
    offset_.push_back(static_cast<int32_t>(total_size));
  }
  size_t required_bytes = total_size + sizeof(int32_t) * (num_strings + 2);
  if (tensor_buffer_.find(tensor_index) == tensor_buffer_.end()) {
    return errors::Internal("Tensor input for index not found: ", tensor_index);
  }
  if (tensor_buffer_max_bytes_[tensor_index] > 0 &&
      required_bytes > tensor_buffer_max_bytes_[tensor_index]) {
    tensor_buffer_max_bytes_[tensor_index] = 0;
  }

  if (tensor_buffer_max_bytes_[tensor_index] == 0) {
    tensor_buffer_[tensor_index].reset(
        reinterpret_cast<char*>(malloc(required_bytes)));
    tensor_buffer_max_bytes_[tensor_index] = required_bytes;
  } else {
    tensor_buffer_[tensor_index].reset(tflite_tensor->data.raw);
  }
  memcpy(tensor_buffer_[tensor_index].get(), &num_strings, sizeof(int32_t));
  int32_t start = sizeof(int32_t) * (num_strings + 2);
  for (size_t i = 0; i < offset_.size(); i++) {
    size_t size_offset_i = start + offset_[i];
    if (size_offset_i > std::numeric_limits<int32_t>::max()) {
      return errors::Internal("Invalid size, string input too large:",
                              size_offset_i);
    }
    int32_t offset_i = static_cast<int32_t>(size_offset_i);
    memcpy(tensor_buffer_[tensor_index].get() + sizeof(int32_t) * (i + 1),
           &offset_i, sizeof(int32_t));
  }
  for (int i = 0; i < num_strings; i++) {
    memcpy(tensor_buffer_[tensor_index].get() + start, batch[i].data(),
           batch[i].size());
    start += batch[i].size();
  }

  // tflite_tensor will take ownership of the pointer.
  tflite_tensor->data.raw = tensor_buffer_[tensor_index].release();
  tflite_tensor->bytes = required_bytes;
  tflite_tensor->allocation_type = kTfLiteDynamic;
  return Status::OK();
}

TfLiteStatus TfLiteInterpreterWrapper::Invoke() {
#ifdef TFLITE_PROFILE
  if (invocation_count_ > 0) {
    profiler_.Reset();
    profiler_.StartProfiling();
  }
#endif
  auto status = interpreter_->Invoke();
#ifdef TFLITE_PROFILE
  if (invocation_count_ > 0) {
    profiler_.StopProfiling();
    auto profile_events = profiler_.GetProfileEvents();
    run_summarizer_.ProcessProfiles(profile_events, *interpreter_);
  }
  if (invocation_count_++ >= MAX_PROFILE_EVENTS) {
    WriteProfileData();
    run_summarizer_.Clear();
    invocation_count_ = 0;
  }
#endif
  return status;
}

tensorflow::Status TfLiteInterpreterPool::CreateTfLiteInterpreterPool(
    const tflite::FlatBufferModel& model, bool run_in_caller,
    bool use_batch_parallelism, int batch_pool_size, int id,
    const tensorflow::SessionOptions& options,
    std::unique_ptr<TfLiteInterpreterPool>& pool) {
  // If can't use_batch_parallelism or pool size is 1,
  // just use 1 interpreter and run in caller.
  if (!use_batch_parallelism || batch_pool_size == 1) {
    run_in_caller = true;
    batch_pool_size = 1;
    use_batch_parallelism = false;
  }
  std::unique_ptr<tensorflow::thread::ThreadPool> thread_pool;
  int num_interpreters = run_in_caller ? 1 : 0;
  if (!run_in_caller || use_batch_parallelism) {
    thread_pool.reset(new tensorflow::thread::ThreadPool(
        options.env, tensorflow::ThreadOptions(),
        absl::StrCat(kTfLiteThreadPoolName, id), batch_pool_size, false,
        nullptr));
    num_interpreters += thread_pool->NumThreads();
  }

  // TODO(b/140959776): Add support for non-builtin ops (flex or custom ops).
  tflite::ops::builtin::BuiltinOpResolver resolver;
  tflite::ops::custom::AddParseExampleOp(&resolver);
  int fixed_batch_size = 1;
  if (use_batch_parallelism) {
    if (num_interpreters < 1) {
      return errors::InvalidArgument(
          "CreateTfLiteInterpreterPool requested ",
          "invalid number of interpreters: ", num_interpreters);
    }
    fixed_batch_size =
        (kInitialBatchSize + num_interpreters - 1) / num_interpreters;
  }
  std::vector<std::unique_ptr<TfLiteInterpreterWrapper>> interpreters;

  for (int i = 0; i < num_interpreters; i++) {
    std::unique_ptr<tflite::Interpreter> interpreter;
    if (tflite::InterpreterBuilder(model, resolver)(
            &interpreter, /*num_threads=*/1) != kTfLiteOk) {
      return errors::Internal(
          "Failed to create a TFLite interpreter with the given model");
    }
    const int idx = interpreter->inputs()[0];
    const auto* tensor = interpreter->tensor(idx);
    if (tensor->type == kTfLiteString) {
      interpreter->ResizeInputTensor(idx, {fixed_batch_size});
    }
    if (interpreter->AllocateTensors() != kTfLiteOk) {
      return errors::Internal("Failed to allocate tensors");
    }
    interpreters.push_back(
        std::make_unique<TfLiteInterpreterWrapper>(std::move(interpreter)));
    interpreters.back()->SetMiniBatchSize(fixed_batch_size);
  }
  pool.reset(new TfLiteInterpreterPool(
      id, std::move(interpreters), std::move(thread_pool), num_interpreters,
      fixed_batch_size, use_batch_parallelism));
  return tensorflow::Status::OK();
}

TfLiteInterpreterPool::TfLiteInterpreterPool(
    int id, std::vector<std::unique_ptr<TfLiteInterpreterWrapper>> interpreters,
    std::unique_ptr<tensorflow::thread::ThreadPool> thread_pool,
    int num_interpreters, int fixed_batch_size, bool use_batch_parallelism)
    : id_(id),
      interpreters_(std::move(interpreters)),
      thread_pool_(std::move(thread_pool)),
      num_interpreters_(num_interpreters),
      fixed_batch_size_(fixed_batch_size),
      use_batch_parallelism_(use_batch_parallelism) {}

std::unique_ptr<TfLiteInterpreterWrapper>&
TfLiteInterpreterPool::GetInterpreter(int interpreter_idx) {
  return interpreters_[interpreter_idx];
}

namespace {

bool IsBatchParallelizable(const tflite::Model* model) {
  auto tflite_model = absl::make_unique<tflite::ModelT>();
  model->UnPackTo(tflite_model.get(), nullptr);
  if (tflite_model->subgraphs.empty()) {
    return false;
  }
  const auto& subgraph = tflite_model->subgraphs[0];
  if (subgraph->inputs.size() != 1) {
    return false;
  }
  int input_tensor_id = subgraph->inputs[0];
  const std::vector<string> supported_ops = {"ParseExample", "ParseExampleV2"};
  for (size_t op_idx = 0; op_idx < subgraph->operators.size(); op_idx++) {
    tflite::OperatorT* op = subgraph->operators[op_idx].get();
    if (std::find(op->inputs.begin(), op->inputs.end(), input_tensor_id) !=
        op->inputs.end()) {
      const std::string& custom_code =
          tflite_model->operator_codes[op->opcode_index]->custom_code;
      return std::find(supported_ops.begin(), supported_ops.end(),
                       custom_code) != supported_ops.end();
    }
  }
  return false;
}

}  // namespace

tensorflow::Status TfLiteSessionPool::CreateTfLiteSessionPool(
    const tflite::FlatBufferModel* model,
    const tensorflow::SessionOptions& options, bool run_in_caller,
    int pool_size, int batch_pool_size,
    std::unique_ptr<TfLiteSessionPool>& tflite_session_pool) {
  bool use_batch_parallelism = IsBatchParallelizable(model->GetModel());
  std::vector<std::unique_ptr<TfLiteInterpreterPool>> pools;
  for (int i = 0; i < pool_size; i++) {
    std::unique_ptr<TfLiteInterpreterPool> pool;
    TF_RETURN_IF_ERROR(TfLiteInterpreterPool::CreateTfLiteInterpreterPool(
        *model, run_in_caller, use_batch_parallelism, batch_pool_size, i,
        options, pool));
    pools.push_back(std::move(pool));
  }
  tflite_session_pool.reset(new TfLiteSessionPool(std::move(pools)));
  return tensorflow::Status::OK();
}

}  // namespace internal
}  // namespace serving
}  // namespace tensorflow
<commit_msg>Fix memory leak from allocating input tensors<commit_after>/* Copyright 2020 Google Inc. All Rights Reserved.

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 "tensorflow_serving/servables/tensorflow/tflite_interpreter_pool.h"

#include <limits>
#include <memory>
#include <vector>

#include "tensorflow/core/platform/errors.h"
#include "tensorflow/lite/kernels/parse_example/parse_example.h"
#include "tensorflow/lite/kernels/register.h"

namespace tensorflow {
namespace serving {
namespace internal {

TfLiteInterpreterWrapper::TfLiteInterpreterWrapper(
    std::unique_ptr<tflite::Interpreter> interpreter)
    :
#ifdef TFLITE_PROFILE
      interpreter_(std::move(interpreter)),
      fixed_batch_size_(fixed_batch_size),
      max_num_entries_(TFLITE_PROFILE_EVENTS),
      profiler_(max_num_entries_)
#else
      interpreter_(std::move(interpreter))
#endif
{
#ifdef TFLITE_PROFILE
  interpreter_->SetProfiler(&profiler_);
#endif
  for (const int& idx : interpreter_->inputs()) {
    const auto* tflite_tensor = interpreter_->tensor(idx);
    if (tflite_tensor->type == kTfLiteString) {
      tensor_buffer_.emplace(idx,
                             std::unique_ptr<char>(tflite_tensor->data.raw));
      tensor_buffer_max_bytes_[idx] = 0;
    }
  }
}

tensorflow::Status TfLiteInterpreterWrapper::SetStringData(
    const gtl::ArraySlice<tensorflow::tstring>& batch,
    TfLiteTensor* tflite_tensor, int tensor_index) {
  // Format of the buffer for tflite:
  //   [0] number of strings (int32_t)
  //   [4] offset of each string (int32_t)
  //   [sizeof(int32_t) * (num_strings + 1)]] total size of strings
  //   [sizeof(int32_t) * (num_strings + 2)] batch.data()
  int32_t num_strings = batch.size();
  offset_.clear();
  size_t total_size = 0;
  offset_.push_back(static_cast<int32_t>(total_size));
  for (int i = 0; i < num_strings; i++) {
    total_size += batch[i].size();
    offset_.push_back(static_cast<int32_t>(total_size));
  }
  size_t required_bytes = total_size + sizeof(int32_t) * (num_strings + 2);
  if (tensor_buffer_.find(tensor_index) == tensor_buffer_.end()) {
    return errors::Internal("Tensor input for index not found: ", tensor_index);
  }
  if (required_bytes > tensor_buffer_max_bytes_[tensor_index]) {
    if (tflite_tensor->data.raw) {
      free(tflite_tensor->data.raw);
    }
    tflite_tensor->data.raw = reinterpret_cast<char*>(malloc(required_bytes));
    tensor_buffer_max_bytes_[tensor_index] = required_bytes;
  }
  tensor_buffer_[tensor_index].reset(tflite_tensor->data.raw);
  memcpy(tensor_buffer_[tensor_index].get(), &num_strings, sizeof(int32_t));
  int32_t start = sizeof(int32_t) * (num_strings + 2);
  for (size_t i = 0; i < offset_.size(); i++) {
    size_t size_offset_i = start + offset_[i];
    if (size_offset_i > std::numeric_limits<int32_t>::max()) {
      return errors::Internal("Invalid size, string input too large:",
                              size_offset_i);
    }
    int32_t offset_i = static_cast<int32_t>(size_offset_i);
    memcpy(tensor_buffer_[tensor_index].get() + sizeof(int32_t) * (i + 1),
           &offset_i, sizeof(int32_t));
  }
  for (int i = 0; i < num_strings; i++) {
    memcpy(tensor_buffer_[tensor_index].get() + start, batch[i].data(),
           batch[i].size());
    start += batch[i].size();
  }

  // tflite_tensor will take ownership of the pointer.
  tflite_tensor->data.raw = tensor_buffer_[tensor_index].release();
  tflite_tensor->bytes = required_bytes;
  tflite_tensor->allocation_type = kTfLiteDynamic;
  return Status::OK();
}

TfLiteStatus TfLiteInterpreterWrapper::Invoke() {
#ifdef TFLITE_PROFILE
  if (invocation_count_ > 0) {
    profiler_.Reset();
    profiler_.StartProfiling();
  }
#endif
  auto status = interpreter_->Invoke();
#ifdef TFLITE_PROFILE
  if (invocation_count_ > 0) {
    profiler_.StopProfiling();
    auto profile_events = profiler_.GetProfileEvents();
    run_summarizer_.ProcessProfiles(profile_events, *interpreter_);
  }
  if (invocation_count_++ >= MAX_PROFILE_EVENTS) {
    WriteProfileData();
    run_summarizer_.Clear();
    invocation_count_ = 0;
  }
#endif
  return status;
}

tensorflow::Status TfLiteInterpreterPool::CreateTfLiteInterpreterPool(
    const tflite::FlatBufferModel& model, bool run_in_caller,
    bool use_batch_parallelism, int batch_pool_size, int id,
    const tensorflow::SessionOptions& options,
    std::unique_ptr<TfLiteInterpreterPool>& pool) {
  // If can't use_batch_parallelism or pool size is 1,
  // just use 1 interpreter and run in caller.
  if (!use_batch_parallelism || batch_pool_size == 1) {
    run_in_caller = true;
    batch_pool_size = 1;
    use_batch_parallelism = false;
  }
  std::unique_ptr<tensorflow::thread::ThreadPool> thread_pool;
  int num_interpreters = run_in_caller ? 1 : 0;
  if (!run_in_caller || use_batch_parallelism) {
    thread_pool.reset(new tensorflow::thread::ThreadPool(
        options.env, tensorflow::ThreadOptions(),
        absl::StrCat(kTfLiteThreadPoolName, id), batch_pool_size, false,
        nullptr));
    num_interpreters += thread_pool->NumThreads();
  }

  // TODO(b/140959776): Add support for non-builtin ops (flex or custom ops).
  tflite::ops::builtin::BuiltinOpResolver resolver;
  tflite::ops::custom::AddParseExampleOp(&resolver);
  int fixed_batch_size = 1;
  if (use_batch_parallelism) {
    if (num_interpreters < 1) {
      return errors::InvalidArgument(
          "CreateTfLiteInterpreterPool requested ",
          "invalid number of interpreters: ", num_interpreters);
    }
    fixed_batch_size =
        (kInitialBatchSize + num_interpreters - 1) / num_interpreters;
  }
  std::vector<std::unique_ptr<TfLiteInterpreterWrapper>> interpreters;

  for (int i = 0; i < num_interpreters; i++) {
    std::unique_ptr<tflite::Interpreter> interpreter;
    if (tflite::InterpreterBuilder(model, resolver)(
            &interpreter, /*num_threads=*/1) != kTfLiteOk) {
      return errors::Internal(
          "Failed to create a TFLite interpreter with the given model");
    }
    const int idx = interpreter->inputs()[0];
    const auto* tensor = interpreter->tensor(idx);
    if (tensor->type == kTfLiteString) {
      interpreter->ResizeInputTensor(idx, {fixed_batch_size});
    }
    if (interpreter->AllocateTensors() != kTfLiteOk) {
      return errors::Internal("Failed to allocate tensors");
    }
    interpreters.push_back(
        std::make_unique<TfLiteInterpreterWrapper>(std::move(interpreter)));
    interpreters.back()->SetMiniBatchSize(fixed_batch_size);
  }
  pool.reset(new TfLiteInterpreterPool(
      id, std::move(interpreters), std::move(thread_pool), num_interpreters,
      fixed_batch_size, use_batch_parallelism));
  return tensorflow::Status::OK();
}

TfLiteInterpreterPool::TfLiteInterpreterPool(
    int id, std::vector<std::unique_ptr<TfLiteInterpreterWrapper>> interpreters,
    std::unique_ptr<tensorflow::thread::ThreadPool> thread_pool,
    int num_interpreters, int fixed_batch_size, bool use_batch_parallelism)
    : id_(id),
      interpreters_(std::move(interpreters)),
      thread_pool_(std::move(thread_pool)),
      num_interpreters_(num_interpreters),
      fixed_batch_size_(fixed_batch_size),
      use_batch_parallelism_(use_batch_parallelism) {}

std::unique_ptr<TfLiteInterpreterWrapper>&
TfLiteInterpreterPool::GetInterpreter(int interpreter_idx) {
  return interpreters_[interpreter_idx];
}

namespace {

bool IsBatchParallelizable(const tflite::Model* model) {
  auto tflite_model = absl::make_unique<tflite::ModelT>();
  model->UnPackTo(tflite_model.get(), nullptr);
  if (tflite_model->subgraphs.empty()) {
    return false;
  }
  const auto& subgraph = tflite_model->subgraphs[0];
  if (subgraph->inputs.size() != 1) {
    return false;
  }
  int input_tensor_id = subgraph->inputs[0];
  const std::vector<string> supported_ops = {"ParseExample", "ParseExampleV2"};
  for (size_t op_idx = 0; op_idx < subgraph->operators.size(); op_idx++) {
    tflite::OperatorT* op = subgraph->operators[op_idx].get();
    if (std::find(op->inputs.begin(), op->inputs.end(), input_tensor_id) !=
        op->inputs.end()) {
      const std::string& custom_code =
          tflite_model->operator_codes[op->opcode_index]->custom_code;
      return std::find(supported_ops.begin(), supported_ops.end(),
                       custom_code) != supported_ops.end();
    }
  }
  return false;
}

}  // namespace

tensorflow::Status TfLiteSessionPool::CreateTfLiteSessionPool(
    const tflite::FlatBufferModel* model,
    const tensorflow::SessionOptions& options, bool run_in_caller,
    int pool_size, int batch_pool_size,
    std::unique_ptr<TfLiteSessionPool>& tflite_session_pool) {
  bool use_batch_parallelism = IsBatchParallelizable(model->GetModel());
  std::vector<std::unique_ptr<TfLiteInterpreterPool>> pools;
  for (int i = 0; i < pool_size; i++) {
    std::unique_ptr<TfLiteInterpreterPool> pool;
    TF_RETURN_IF_ERROR(TfLiteInterpreterPool::CreateTfLiteInterpreterPool(
        *model, run_in_caller, use_batch_parallelism, batch_pool_size, i,
        options, pool));
    pools.push_back(std::move(pool));
  }
  tflite_session_pool.reset(new TfLiteSessionPool(std::move(pools)));
  return tensorflow::Status::OK();
}

}  // namespace internal
}  // namespace serving
}  // namespace tensorflow
<|endoftext|>


<commit_before>#include "self_youhua.h"

self_youhua::self_youhua()
{

}

<commit_msg>开始自优化<commit_after>#include "self_youhua.h"

self_youhua::self_youhua()
{

}
self_youhua::initUser()
{
    userList.clear();
    for (int i=0;i<19;i++)
    {

    }
}

<|endoftext|>


<commit_before>#include <QtWidgets/QGraphicsSceneMouseEvent>
#include <QtWidgets/QStyleOptionGraphicsItem>

#include "wallItem.h"

#include "../../../../../qrkernel/settingsManager.h"
#include "d2ModelScene.h"

using namespace qReal::interpreters::robots;
using namespace details::d2Model;
using namespace graphicsUtils;

WallItem::WallItem(QPointF const &begin, QPointF const &end)
	: LineItem(begin, end)
	, mDragged(false)
	, mImage(":/icons/2d_wall.png")
	, mOldX1(0)
	, mOldY1(0)
{
	setPrivateData();
	setAcceptDrops(true);
}

void WallItem::setPrivateData()
{
	setZValue(1);

	mPen.setWidth(10);
	mPen.setStyle(Qt::NoPen);
	mBrush.setStyle(Qt::SolidPattern);
	mBrush.setTextureImage(mImage);
	mSerializeName = "wall";
}

QPointF WallItem::begin()
{
	return QPointF(mX1, mY1) + scenePos();
}

QPointF WallItem::end()
{
	return QPointF(mX2, mY2) + scenePos();
}

void WallItem::drawItem(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
	Q_UNUSED(option);
	Q_UNUSED(widget);
	painter->drawPath(shape());
}

void WallItem::drawExtractionForItem(QPainter *painter)
{
	if (!isSelected()) {
		return;
	}

	painter->setPen(QPen(Qt::green));
	mLineImpl.drawExtractionForItem(painter, mX1, mY1, mX2, mY2, drift);
	mLineImpl.drawFieldForResizeItem(painter, resizeDrift, mX1, mY1, mX2, mY2);
}

void WallItem::mousePressEvent(QGraphicsSceneMouseEvent * event)
{
	AbstractItem::mousePressEvent(event);
	mDragged = (flags() & ItemIsMovable) || mOverlappedWithRobot;
	mOldX1 = qAbs(mX1 - event->scenePos().x());
	mOldY1 = qAbs(mY1 - event->scenePos().y());
}

void WallItem::mouseMoveEvent(QGraphicsSceneMouseEvent * event)
{
	QPointF const oldPos = pos();
	if (SettingsManager::value("2dShowGrid").toBool() && mDragged && ((flags() & ItemIsMovable) || mOverlappedWithRobot)){
		QPointF const pos = event->scenePos();
		qreal const deltaX = (mX1 - mX2);
		qreal const deltaY = (mY1 - mY2);
		mX1 = pos.x() - mOldX1;
		mY1 = pos.y() - mOldY1;
		this->reshapeBeginWithGrid(SettingsManager::value("2dGridCellSize").toInt());
		this->setDraggedEndWithGrid(deltaX, deltaY);
	} else {
		QGraphicsItem::mouseMoveEvent(event);
	}
	// Items under cursor cannot be dragged when adding new item,
	// but it mustn`t confuse the case when item is unmovable
	// because overapped with robot
	if (mDragged && ((flags() & ItemIsMovable) || mOverlappedWithRobot)) {
		emit wallDragged(this, realShape(), oldPos);
	}
	event->accept();
}

bool WallItem::isDragged()
{
	return mDragged;
}

void WallItem::mouseReleaseEvent(QGraphicsSceneMouseEvent * event)
{
	QGraphicsItem::mouseReleaseEvent(event);
	mDragged = false;
}

QDomElement WallItem::serialize(QDomDocument &document, QPoint const &topLeftPicture)
{
	QDomElement wallNode = document.createElement(mSerializeName);
	wallNode.setAttribute("begin", QString::number(mX1 + scenePos().x() - topLeftPicture.x())
			+ ":" + QString::number(mY1 + scenePos().y() - topLeftPicture.y()));
	wallNode.setAttribute("end", QString::number(mX2 + scenePos().x() - topLeftPicture.x())
			+ ":" + QString::number(mY2 + scenePos().y() - topLeftPicture.y()));
	return wallNode;
}

void WallItem::deserializePenBrush(QDomElement const &element)
{
	Q_UNUSED(element)
	setPrivateData();
}

void WallItem::onOverlappedWithRobot(bool overlapped)
{
	mOverlappedWithRobot = overlapped;
}
<commit_msg>Little fix in drag<commit_after>#include <QtWidgets/QGraphicsSceneMouseEvent>
#include <QtWidgets/QStyleOptionGraphicsItem>

#include "wallItem.h"

#include "../../../../../qrkernel/settingsManager.h"
#include "d2ModelScene.h"

using namespace qReal::interpreters::robots;
using namespace details::d2Model;
using namespace graphicsUtils;

WallItem::WallItem(QPointF const &begin, QPointF const &end)
	: LineItem(begin, end)
	, mDragged(false)
	, mImage(":/icons/2d_wall.png")
	, mOldX1(0)
	, mOldY1(0)
{
	setPrivateData();
	setAcceptDrops(true);
}

void WallItem::setPrivateData()
{
	setZValue(1);

	mPen.setWidth(10);
	mPen.setStyle(Qt::NoPen);
	mBrush.setStyle(Qt::SolidPattern);
	mBrush.setTextureImage(mImage);
	mSerializeName = "wall";
}

QPointF WallItem::begin()
{
	return QPointF(mX1, mY1) + scenePos();
}

QPointF WallItem::end()
{
	return QPointF(mX2, mY2) + scenePos();
}

void WallItem::drawItem(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
{
	Q_UNUSED(option);
	Q_UNUSED(widget);
	painter->drawPath(shape());
}

void WallItem::drawExtractionForItem(QPainter *painter)
{
	if (!isSelected()) {
		return;
	}

	painter->setPen(QPen(Qt::green));
	mLineImpl.drawExtractionForItem(painter, mX1, mY1, mX2, mY2, drift);
	mLineImpl.drawFieldForResizeItem(painter, resizeDrift, mX1, mY1, mX2, mY2);
}

void WallItem::mousePressEvent(QGraphicsSceneMouseEvent * event)
{
	AbstractItem::mousePressEvent(event);
	mDragged = (flags() & ItemIsMovable) || mOverlappedWithRobot;
	mOldX1 = qAbs(mX1 - event->scenePos().x());
	mOldY1 = qAbs(mY1 - event->scenePos().y());
}

void WallItem::mouseMoveEvent(QGraphicsSceneMouseEvent * event)
{
	QPointF const oldPos = pos();
	if (SettingsManager::value("2dShowGrid").toBool() && mDragged && ((flags() & ItemIsMovable) || mOverlappedWithRobot)){
		QPointF const pos = event->scenePos();
		int indexGrid = SettingsManager::value("2dGridCellSize").toInt();
		qreal const deltaX = (mX1 - mX2);
		qreal const deltaY = (mY1 - mY2);
		mX1 = pos.x() - mOldX1;
		mY1 = pos.y() - mOldY1;
		this->reshapeBeginWithGrid(indexGrid);
		this->setDraggedEndWithGrid(deltaX, deltaY);
		mCellNumbX1 = mX1/indexGrid;
		mCellNumbY1 = mY1/indexGrid;
		mCellNumbX2 = mX2/indexGrid;
		mCellNumbY2 = mY2/indexGrid;
	} else {
		QGraphicsItem::mouseMoveEvent(event);
	}
	// Items under cursor cannot be dragged when adding new item,
	// but it mustn`t confuse the case when item is unmovable
	// because overapped with robot
	if (mDragged && ((flags() & ItemIsMovable) || mOverlappedWithRobot)) {
		emit wallDragged(this, realShape(), oldPos);
	}
	event->accept();
}

bool WallItem::isDragged()
{
	return mDragged;
}

void WallItem::mouseReleaseEvent(QGraphicsSceneMouseEvent * event)
{
	QGraphicsItem::mouseReleaseEvent(event);
	mDragged = false;
}

QDomElement WallItem::serialize(QDomDocument &document, QPoint const &topLeftPicture)
{
	QDomElement wallNode = document.createElement(mSerializeName);
	wallNode.setAttribute("begin", QString::number(mX1 + scenePos().x() - topLeftPicture.x())
			+ ":" + QString::number(mY1 + scenePos().y() - topLeftPicture.y()));
	wallNode.setAttribute("end", QString::number(mX2 + scenePos().x() - topLeftPicture.x())
			+ ":" + QString::number(mY2 + scenePos().y() - topLeftPicture.y()));
	return wallNode;
}

void WallItem::deserializePenBrush(QDomElement const &element)
{
	Q_UNUSED(element)
	setPrivateData();
}

void WallItem::onOverlappedWithRobot(bool overlapped)
{
	mOverlappedWithRobot = overlapped;
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: RecentlyUsedMasterPages.hxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: rt $ $Date: 2004-08-04 08:59:59 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef SD_TOOLPANEL_CONTROLS_RECENTLY_USED_MASTER_PAGES_HXX
#define SD_TOOLPANEL_CONTROLS_RECENTLY_USED_MASTER_PAGES_HXX

#include "tools/SdGlobalResourceContainer.hxx"
#include <osl/mutex.hxx>
#include <tools/link.hxx>
#include <vcl/image.hxx>
#include <vector>
#include <tools/string.hxx>

#include "DrawDocShell.hxx"
#include "MasterPageContainer.hxx"

#ifndef _COM_SUN_STAR_UNO_XINTERFACE_HPP_
#include <com/sun/star/uno/XInterface.hpp>
#endif

class SdPage;

namespace sd {
class MasterPageObserverEvent;
}

namespace sd { namespace toolpanel { namespace controls {

/** This singleton holds a list of the most recently used master pages.
*/
class RecentlyUsedMasterPages
    : public SdGlobalResource
{
public:
    /** Return the single instance of this class.
    */
    static RecentlyUsedMasterPages& Instance (void);

    void AddEventListener (const Link& rEventListener);
    void RemoveEventListener (const Link& rEventListener);

    int GetMasterPageCount (void) const;
    String GetURL (int nIndex) const;
    String GetMasterPageName (int nIndex) const;
    SdPage* GetSlide (int nIndex) const;
    SdPage* GetMasterPage (int nIndex) const;
    Image GetMasterPagePreview (int nIndex, int nWidth) const;

private:
    /** The single instance of this class.  It is created on demand when
        Instance() is called for the first time.
    */
    static RecentlyUsedMasterPages* mpInstance;

    ::std::vector<Link> maListeners;

    typedef ::std::vector<MasterPageContainer::Token> MasterPageList;
    class PageByNameFinder;
    MasterPageList maMasterPages;
    unsigned long int mnMaxListSize;

    RecentlyUsedMasterPages (void);
    virtual ~RecentlyUsedMasterPages (void);

    /** Call this method after a new object has been created.
    */
    void LateInit (void);

    /// The copy constructor is not implemented.  Do not use!
    RecentlyUsedMasterPages (const RecentlyUsedMasterPages&);

    /// The assignment operator is not implemented.  Do not use!
    RecentlyUsedMasterPages& operator= (const RecentlyUsedMasterPages&);

    void SendEvent (void);
    DECL_LINK(MasterPageChangeListener, MasterPageObserverEvent*);

    /** Add a descriptor for the specified master page to the end of the
        list of most recently used master pages.  When the page is already a
        member of that list the associated descriptor is moved to the end of
        the list to make it the most recently used entry.
        @param bMakePersistent
            When <TRUE/> is given then the new list of recently used master
            pages is written back into the configuration to make it
            persistent.  Giving <FALSE/> to ommit this is used while loading
            the persistent list from the configuration.
    */
    void AddMasterPage (
        const String& rsName,
        bool bMakePersistent = true);

    /** Load the list of recently used master pages from the registry where
        it was saved to make it persistent.
    */
    void LoadPersistentValues (void);

    /** Save the list of recently used master pages to the registry to make
        it presistent.
    */
    void SavePersistentValues (void);

    ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface>
        OpenConfiguration (
            const ::rtl::OUString& rsRootName,
            bool bReadOnly);
    ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface>
        GetConfigurationNode (
            const ::com::sun::star::uno::Reference<
            ::com::sun::star::uno::XInterface>& xRoot,
          const ::rtl::OUString& sPathToNode);
};

} } } // end of namespace ::sd::toolpanel::controls

#endif
<commit_msg>INTEGRATION: CWS impress19 (1.3.94); FILE MERGED 2004/11/16 10:29:36 af 1.3.94.1: #i37233# AddMasterPage() uses a token instead of a master page name.<commit_after>/*************************************************************************
 *
 *  $RCSfile: RecentlyUsedMasterPages.hxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: hr $ $Date: 2004-11-26 15:10:00 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef SD_TOOLPANEL_CONTROLS_RECENTLY_USED_MASTER_PAGES_HXX
#define SD_TOOLPANEL_CONTROLS_RECENTLY_USED_MASTER_PAGES_HXX

#include "tools/SdGlobalResourceContainer.hxx"
#include <osl/mutex.hxx>
#include <tools/link.hxx>
#include <vcl/image.hxx>
#include <vector>
#include <tools/string.hxx>

#include "DrawDocShell.hxx"
#include "MasterPageContainer.hxx"

#ifndef _COM_SUN_STAR_UNO_XINTERFACE_HPP_
#include <com/sun/star/uno/XInterface.hpp>
#endif

class SdPage;

namespace sd {
class MasterPageObserverEvent;
}

namespace sd { namespace toolpanel { namespace controls {

/** This singleton holds a list of the most recently used master pages.
*/
class RecentlyUsedMasterPages
    : public SdGlobalResource
{
public:
    /** Return the single instance of this class.
    */
    static RecentlyUsedMasterPages& Instance (void);

    void AddEventListener (const Link& rEventListener);
    void RemoveEventListener (const Link& rEventListener);

    int GetMasterPageCount (void) const;
    String GetURL (int nIndex) const;
    String GetMasterPageName (int nIndex) const;
    SdPage* GetSlide (int nIndex) const;
    SdPage* GetMasterPage (int nIndex) const;
    Image GetMasterPagePreview (int nIndex, int nWidth) const;

private:
    /** The single instance of this class.  It is created on demand when
        Instance() is called for the first time.
    */
    static RecentlyUsedMasterPages* mpInstance;

    ::std::vector<Link> maListeners;

    typedef ::std::vector<MasterPageContainer::Token> MasterPageList;
    class PageByNameFinder;
    MasterPageList maMasterPages;
    unsigned long int mnMaxListSize;

    RecentlyUsedMasterPages (void);
    virtual ~RecentlyUsedMasterPages (void);

    /** Call this method after a new object has been created.
    */
    void LateInit (void);

    /// The copy constructor is not implemented.  Do not use!
    RecentlyUsedMasterPages (const RecentlyUsedMasterPages&);

    /// The assignment operator is not implemented.  Do not use!
    RecentlyUsedMasterPages& operator= (const RecentlyUsedMasterPages&);

    void SendEvent (void);
    DECL_LINK(MasterPageChangeListener, MasterPageObserverEvent*);

    /** Add a descriptor for the specified master page to the end of the
        list of most recently used master pages.  When the page is already a
        member of that list the associated descriptor is moved to the end of
        the list to make it the most recently used entry.
        @param bMakePersistent
            When <TRUE/> is given then the new list of recently used master
            pages is written back into the configuration to make it
            persistent.  Giving <FALSE/> to ommit this is used while loading
            the persistent list from the configuration.
    */
    void AddMasterPage (
        MasterPageContainer::Token aToken,
        bool bMakePersistent = true);

    /** Load the list of recently used master pages from the registry where
        it was saved to make it persistent.
    */
    void LoadPersistentValues (void);

    /** Save the list of recently used master pages to the registry to make
        it presistent.
    */
    void SavePersistentValues (void);

    ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface>
        OpenConfiguration (
            const ::rtl::OUString& rsRootName,
            bool bReadOnly);
    ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface>
        GetConfigurationNode (
            const ::com::sun::star::uno::Reference<
            ::com::sun::star::uno::XInterface>& xRoot,
          const ::rtl::OUString& sPathToNode);
};

} } } // end of namespace ::sd::toolpanel::controls

#endif
<|endoftext|>


<commit_before>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include "isearchcontext.h"
#include "match_master.h"
#include "match_context.h"
#include "match_tools.h"
#include "match_params.h"
#include "matcher.h"
#include "sessionmanager.h"
#include <vespa/searchcore/grouping/groupingcontext.h>
#include <vespa/searchlib/engine/errorcodes.h>
#include <vespa/searchlib/engine/docsumrequest.h>
#include <vespa/searchlib/engine/searchrequest.h>
#include <vespa/searchlib/engine/searchreply.h>
#include <vespa/searchlib/features/setup.h>
#include <vespa/searchlib/fef/test/plugin/setup.h>

#include <vespa/log/log.h>
LOG_SETUP(".proton.matching.matcher");

using search::fef::Properties;
using namespace search::fef::indexproperties::matching;
using namespace search::engine;
using namespace search::grouping;
using search::DocumentMetaData;
using search::LidUsageStats;
using search::FeatureSet;
using search::attribute::IAttributeContext;
using search::fef::MatchDataLayout;
using search::fef::MatchData;
using search::queryeval::Blueprint;
using search::queryeval::SearchIterator;
using vespalib::Doom;

namespace proton::matching {

namespace {

// used to give out empty blacklist blueprints
struct StupidMetaStore : search::IDocumentMetaStore {
    bool getGid(DocId, GlobalId &) const override { return false; }
    bool getGidEvenIfMoved(DocId, GlobalId &) const override { return false; }
    bool getLid(const GlobalId &, DocId &) const override { return false; }
    DocumentMetaData getMetaData(const GlobalId &) const override { return DocumentMetaData(); }
    void getMetaData(const BucketId &, DocumentMetaData::Vector &) const override { }
    DocId getCommittedDocIdLimit() const override { return 1; }
    DocId getNumUsedLids() const override { return 0; }
    DocId getNumActiveLids() const override { return 0; }
    uint64_t getCurrentGeneration() const override { return 0; }
    LidUsageStats getLidUsageStats() const override { return LidUsageStats(); }
    Blueprint::UP createBlackListBlueprint() const override { return Blueprint::UP(); }
};

FeatureSet::SP findFeatureSet(const DocsumRequest &req,
                              MatchToolsFactory &mtf, bool summaryFeatures) {
    std::vector<uint32_t> docs;
    for (size_t i = 0; i < req.hits.size(); ++i) {
        if (req.hits[i].docid != search::endDocId) {
            docs.push_back(req.hits[i].docid);
        }
    }
    std::sort(docs.begin(), docs.end());
    return MatchMaster::getFeatureSet(mtf, docs, summaryFeatures);
}

size_t numThreads(size_t hits, size_t minHits) {
    return static_cast<size_t>(std::ceil(double(hits) / double(minHits)));
}

class LimitedThreadBundleWrapper final : public vespalib::ThreadBundle
{
public:
    LimitedThreadBundleWrapper(vespalib::ThreadBundle &threadBundle, uint32_t maxThreads) :
        _threadBundle(threadBundle),
        _maxThreads(std::min(maxThreads, static_cast<uint32_t>(threadBundle.size())))
    { }
private:
    size_t size() const override { return _maxThreads; }
    void run(const std::vector<vespalib::Runnable*> &targets) override {
        _threadBundle.run(targets);
    }
    vespalib::ThreadBundle &_threadBundle;
    const uint32_t          _maxThreads;
};

bool willNotNeedRanking(const SearchRequest & request, const GroupingContext & groupingContext) {
    return (!groupingContext.needRanking() && (request.maxhits == 0))
           || (!request.sortSpec.empty() && (request.sortSpec.find("[rank]") == vespalib::string::npos));
}

}  // namespace proton::matching::<unnamed>

FeatureSet::SP
Matcher::getFeatureSet(const DocsumRequest & req, ISearchContext & searchCtx, IAttributeContext & attrCtx,
                       SessionManager & sessionMgr, bool summaryFeatures)
{
    SessionId sessionId(&req.sessionId[0], req.sessionId.size());
    if (!sessionId.empty()) {
        const Properties &cache_props = req.propertiesMap.cacheProperties();
        bool searchSessionCached = cache_props.lookup("query").found();
        if (searchSessionCached) {
            SearchSession::SP session(sessionMgr.pickSearch(sessionId));
            if (session.get()) {
                MatchToolsFactory &mtf = session->getMatchToolsFactory();
                FeatureSet::SP result = findFeatureSet(req, mtf, summaryFeatures);
                session->releaseEnumGuards();
                return result;
            }
        }
    }

    StupidMetaStore metaStore;
    MatchToolsFactory::UP mtf = create_match_tools_factory(req, searchCtx, attrCtx, metaStore,
                                                           req.propertiesMap.featureOverrides());
    if (!mtf->valid()) {
        LOG(warning, "getFeatureSet(%s): query execution failed (invalid query). Returning empty feature set",
                     (summaryFeatures ? "summary features" : "rank features"));
        return FeatureSet::SP(new FeatureSet());
    }
    return findFeatureSet(req, *mtf, summaryFeatures);
}

Matcher::Matcher(const search::index::Schema &schema, const Properties &props, const vespalib::Clock &clock,
                 QueryLimiter &queryLimiter, const IConstantValueRepo &constantValueRepo, uint32_t distributionKey)
    : _indexEnv(schema, props, constantValueRepo),
      _blueprintFactory(),
      _rankSetup(),
      _viewResolver(ViewResolver::createFromSchema(schema)),
      _statsLock(),
      _stats(),
      _clock(clock),
      _queryLimiter(queryLimiter),
      _distributionKey(distributionKey)
{
    search::features::setup_search_features(_blueprintFactory);
    search::fef::test::setup_fef_test_plugin(_blueprintFactory);
    _rankSetup.reset(new search::fef::RankSetup(_blueprintFactory, _indexEnv));
    _rankSetup->configure(); // reads config values from the property map
    if (!_rankSetup->compile()) {
        throw vespalib::IllegalArgumentException("failed to compile rank setup", VESPA_STRLOC);
    }
}

MatchingStats
Matcher::getStats()
{
    std::lock_guard<std::mutex> guard(_statsLock);
    MatchingStats stats = std::move(_stats);
    _stats = std::move(MatchingStats());
    _stats.softDoomFactor(stats.softDoomFactor());
    return stats;
}

using search::fef::indexproperties::softtimeout::Enabled;
using search::fef::indexproperties::softtimeout::Factor;

std::unique_ptr<MatchToolsFactory>
Matcher::create_match_tools_factory(const search::engine::Request &request, ISearchContext &searchContext,
                                    IAttributeContext &attrContext, const search::IDocumentMetaStore &metaStore,
                                    const Properties &feature_overrides) const
{
    const Properties & rankProperties = request.propertiesMap.rankProperties();
    bool softTimeoutEnabled = Enabled::lookup(rankProperties, _rankSetup->getSoftTimeoutEnabled());
    double factor = softTimeoutEnabled
                    ? Factor::lookup(rankProperties, _stats.softDoomFactor())
                    : 0.95;
    int64_t safeLeft = request.getTimeLeft() * factor;
    fastos::TimeStamp safeDoom(fastos::ClockSystem::now() + safeLeft);
    if (softTimeoutEnabled) {
        LOG(debug, "Soft-timeout computed factor=%1.3f, used factor=%1.3f, softTimeout=%lu softDoom=%ld hardDoom=%ld",
                   _stats.softDoomFactor(), factor, safeLeft, safeDoom.ns(), request.getTimeOfDoom().ns());
    }
    return std::make_unique<MatchToolsFactory>(_queryLimiter, vespalib::Doom(_clock, safeDoom),
                                               vespalib::Doom(_clock, request.getTimeOfDoom()), searchContext,
                                               attrContext, request.getStackRef(), request.location, _viewResolver,
                                               metaStore, _indexEnv, *_rankSetup, rankProperties, feature_overrides);
}

SearchReply::UP
Matcher::handleGroupingSession(SessionManager &sessionMgr, GroupingContext & groupingContext,
                               GroupingSession::UP groupingSession)
{
    SearchReply::UP reply = std::make_unique<SearchReply>();
    groupingSession->continueExecution(groupingContext);
    groupingContext.getResult().swap(reply->groupResult);
    if (!groupingSession->finished()) {
        sessionMgr.insert(std::move(groupingSession));
    }
    return reply;
}

size_t
Matcher::computeNumThreadsPerSearch(Blueprint::HitEstimate hits, const Properties & rankProperties) const {
    size_t threads = NumThreadsPerSearch::lookup(rankProperties, _rankSetup->getNumThreadsPerSearch());
    uint32_t minHitsPerThread = MinHitsPerThread::lookup(rankProperties, _rankSetup->getMinHitsPerThread());
    if ((threads > 1) && (minHitsPerThread > 0)) {
        threads = (hits.empty) ? 1 : std::min(threads, numThreads(hits.estHits, minHitsPerThread));
    }
    return threads;
}

SearchReply::UP
Matcher::match(const SearchRequest &request, vespalib::ThreadBundle &threadBundle,
               ISearchContext &searchContext, IAttributeContext &attrContext,
               SessionManager &sessionMgr, const search::IDocumentMetaStore &metaStore,
               SearchSession::OwnershipBundle &&owned_objects)
{
    fastos::StopWatch total_matching_time;
    total_matching_time.start();
    MatchingStats my_stats;
    SearchReply::UP reply = std::make_unique<SearchReply>();
    { // we want to measure full set-up and tear-down time as part of
      // collateral time
        GroupingContext groupingContext(_clock, request.getTimeOfDoom(),
                                        &request.groupSpec[0], request.groupSpec.size());
        SessionId sessionId(&request.sessionId[0], request.sessionId.size());
        bool shouldCacheSearchSession = false;
        bool shouldCacheGroupingSession = false;
        if (!sessionId.empty()) {
            const Properties &cache_props = request.propertiesMap.cacheProperties();
            shouldCacheGroupingSession = cache_props.lookup("grouping").found();
            shouldCacheSearchSession = cache_props.lookup("query").found();
            if (shouldCacheGroupingSession) {
                GroupingSession::UP session(sessionMgr.pickGrouping(sessionId));
                if (session.get()) {
                    return handleGroupingSession(sessionMgr, groupingContext, std::move(session));
                }
            }
        }
        const Properties *feature_overrides = &request.propertiesMap.featureOverrides();
        if (shouldCacheSearchSession) {
            owned_objects.feature_overrides.reset(new Properties(*feature_overrides));
            feature_overrides = owned_objects.feature_overrides.get();
        }
        MatchToolsFactory::UP mtf = create_match_tools_factory(request, searchContext, attrContext,
                                                               metaStore, *feature_overrides);
        if (!mtf->valid()) {
            reply->errorCode = ECODE_QUERY_PARSE_ERROR;
            reply->errorMessage = "query execution failed (invalid query)";
            return reply;
        }

        MatchParams params(searchContext.getDocIdLimit(), _rankSetup->getHeapSize(), _rankSetup->getArraySize(),
                           _rankSetup->getRankScoreDropLimit(), request.offset, request.maxhits,
                           !_rankSetup->getSecondPhaseRank().empty(), !willNotNeedRanking(request, groupingContext));

        ResultProcessor rp(attrContext, metaStore, sessionMgr, groupingContext, sessionId,
                           request.sortSpec, params.offset, params.hits);

        const Properties & rankProperties = request.propertiesMap.rankProperties();
        size_t numThreadsPerSearch = computeNumThreadsPerSearch(mtf->estimate(), rankProperties);
        LimitedThreadBundleWrapper limitedThreadBundle(threadBundle, numThreadsPerSearch);
        MatchMaster master;
        uint32_t numSearchPartitions = NumSearchPartitions::lookup(rankProperties,
                                                                   _rankSetup->getNumSearchPartitions());
        ResultProcessor::Result::UP result = master.match(params, limitedThreadBundle, *mtf, rp,
                                                          _distributionKey, numSearchPartitions);
        my_stats = MatchMaster::getStats(std::move(master));

        bool wasLimited = mtf->match_limiter().was_limited();
        size_t spaceEstimate = mtf->match_limiter().getDocIdSpaceEstimate();
        uint32_t estHits = mtf->estimate().estHits;
        if (shouldCacheSearchSession && ((result->_numFs4Hits != 0) || shouldCacheGroupingSession)) {
            SearchSession::SP session = std::make_shared<SearchSession>(sessionId, request.getTimeOfDoom(),
                                                                        std::move(mtf), std::move(owned_objects));
            session->releaseEnumGuards();
            sessionMgr.insert(std::move(session));
        }
        reply = std::move(result->_reply);

        uint32_t numActiveLids = metaStore.getNumActiveLids();
        // note: this is actually totalSpace+1, since 0 is reserved
        uint32_t totalSpace = metaStore.getCommittedDocIdLimit();
        LOG(debug, "docid limit = %d", totalSpace);
        LOG(debug, "num active lids = %d", numActiveLids);
        LOG(debug, "space Estimate = %zd", spaceEstimate);
        if (spaceEstimate >= totalSpace) {
            // estimate is too high, clamp it
            spaceEstimate = totalSpace;
        } else {
            // account for docid 0 reserved
            spaceEstimate += 1;
        }
        size_t covered = (spaceEstimate *  numActiveLids) / totalSpace;
        LOG(debug, "covered = %zd", covered);

        SearchReply::Coverage & coverage = reply->coverage;
        coverage.setActive(numActiveLids);
        //TODO this should be calculated with ClusterState calculator.
        coverage.setSoonActive(numActiveLids);
        coverage.setCovered(covered);
        if (wasLimited) {
            coverage.degradeMatchPhase();
            LOG(debug, "was limited, degraded from match phase");
        }
        if (my_stats.softDoomed()) {
            coverage.degradeTimeout();
            coverage.setCovered(my_stats.docsCovered());
            LOG(debug, "soft doomed, degraded from timeout covered = %lu", coverage.getCovered()); }
        LOG(debug, "numThreadsPerSearch = %zu. Configured = %d, estimated hits=%d, totalHits=%ld",
            numThreadsPerSearch, _rankSetup->getNumThreadsPerSearch(), estHits, reply->totalHitCount);
    }
    total_matching_time.stop();
    my_stats.queryCollateralTime(total_matching_time.elapsed().sec() - my_stats.queryLatencyAvg());
    {
        fastos::TimeStamp softLimit = uint64_t((1.0 - _rankSetup->getSoftTimeoutTailCost()) * request.getTimeout());
        fastos::TimeStamp duration = request.getTimeUsed();
        std::lock_guard<std::mutex> guard(_statsLock);
        _stats.add(my_stats);
        if (my_stats.softDoomed()) {
            LOG(info, "Triggered softtimeout limit=%1.3f and duration=%1.3f", softLimit.sec(), duration.sec());
            _stats.updatesoftDoomFactor(request.getTimeout(), softLimit, duration);
        }
    }
    return reply;
}

FeatureSet::SP
Matcher::getSummaryFeatures(const DocsumRequest & req, ISearchContext & searchCtx,
                            IAttributeContext & attrCtx, SessionManager &sessionMgr)
{
    return getFeatureSet(req, searchCtx, attrCtx, sessionMgr, true);
}

FeatureSet::SP
Matcher::getRankFeatures(const DocsumRequest & req, ISearchContext & searchCtx,
                         IAttributeContext & attrCtx, SessionManager &sessionMgr)
{
    return getFeatureSet(req, searchCtx, attrCtx, sessionMgr, false);
}

}
<commit_msg>Ill placed brace.<commit_after>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include "isearchcontext.h"
#include "match_master.h"
#include "match_context.h"
#include "match_tools.h"
#include "match_params.h"
#include "matcher.h"
#include "sessionmanager.h"
#include <vespa/searchcore/grouping/groupingcontext.h>
#include <vespa/searchlib/engine/errorcodes.h>
#include <vespa/searchlib/engine/docsumrequest.h>
#include <vespa/searchlib/engine/searchrequest.h>
#include <vespa/searchlib/engine/searchreply.h>
#include <vespa/searchlib/features/setup.h>
#include <vespa/searchlib/fef/test/plugin/setup.h>

#include <vespa/log/log.h>
LOG_SETUP(".proton.matching.matcher");

using search::fef::Properties;
using namespace search::fef::indexproperties::matching;
using namespace search::engine;
using namespace search::grouping;
using search::DocumentMetaData;
using search::LidUsageStats;
using search::FeatureSet;
using search::attribute::IAttributeContext;
using search::fef::MatchDataLayout;
using search::fef::MatchData;
using search::queryeval::Blueprint;
using search::queryeval::SearchIterator;
using vespalib::Doom;

namespace proton::matching {

namespace {

// used to give out empty blacklist blueprints
struct StupidMetaStore : search::IDocumentMetaStore {
    bool getGid(DocId, GlobalId &) const override { return false; }
    bool getGidEvenIfMoved(DocId, GlobalId &) const override { return false; }
    bool getLid(const GlobalId &, DocId &) const override { return false; }
    DocumentMetaData getMetaData(const GlobalId &) const override { return DocumentMetaData(); }
    void getMetaData(const BucketId &, DocumentMetaData::Vector &) const override { }
    DocId getCommittedDocIdLimit() const override { return 1; }
    DocId getNumUsedLids() const override { return 0; }
    DocId getNumActiveLids() const override { return 0; }
    uint64_t getCurrentGeneration() const override { return 0; }
    LidUsageStats getLidUsageStats() const override { return LidUsageStats(); }
    Blueprint::UP createBlackListBlueprint() const override { return Blueprint::UP(); }
};

FeatureSet::SP findFeatureSet(const DocsumRequest &req,
                              MatchToolsFactory &mtf, bool summaryFeatures) {
    std::vector<uint32_t> docs;
    for (size_t i = 0; i < req.hits.size(); ++i) {
        if (req.hits[i].docid != search::endDocId) {
            docs.push_back(req.hits[i].docid);
        }
    }
    std::sort(docs.begin(), docs.end());
    return MatchMaster::getFeatureSet(mtf, docs, summaryFeatures);
}

size_t numThreads(size_t hits, size_t minHits) {
    return static_cast<size_t>(std::ceil(double(hits) / double(minHits)));
}

class LimitedThreadBundleWrapper final : public vespalib::ThreadBundle
{
public:
    LimitedThreadBundleWrapper(vespalib::ThreadBundle &threadBundle, uint32_t maxThreads) :
        _threadBundle(threadBundle),
        _maxThreads(std::min(maxThreads, static_cast<uint32_t>(threadBundle.size())))
    { }
private:
    size_t size() const override { return _maxThreads; }
    void run(const std::vector<vespalib::Runnable*> &targets) override {
        _threadBundle.run(targets);
    }
    vespalib::ThreadBundle &_threadBundle;
    const uint32_t          _maxThreads;
};

bool willNotNeedRanking(const SearchRequest & request, const GroupingContext & groupingContext) {
    return (!groupingContext.needRanking() && (request.maxhits == 0))
           || (!request.sortSpec.empty() && (request.sortSpec.find("[rank]") == vespalib::string::npos));
}

}  // namespace proton::matching::<unnamed>

FeatureSet::SP
Matcher::getFeatureSet(const DocsumRequest & req, ISearchContext & searchCtx, IAttributeContext & attrCtx,
                       SessionManager & sessionMgr, bool summaryFeatures)
{
    SessionId sessionId(&req.sessionId[0], req.sessionId.size());
    if (!sessionId.empty()) {
        const Properties &cache_props = req.propertiesMap.cacheProperties();
        bool searchSessionCached = cache_props.lookup("query").found();
        if (searchSessionCached) {
            SearchSession::SP session(sessionMgr.pickSearch(sessionId));
            if (session.get()) {
                MatchToolsFactory &mtf = session->getMatchToolsFactory();
                FeatureSet::SP result = findFeatureSet(req, mtf, summaryFeatures);
                session->releaseEnumGuards();
                return result;
            }
        }
    }

    StupidMetaStore metaStore;
    MatchToolsFactory::UP mtf = create_match_tools_factory(req, searchCtx, attrCtx, metaStore,
                                                           req.propertiesMap.featureOverrides());
    if (!mtf->valid()) {
        LOG(warning, "getFeatureSet(%s): query execution failed (invalid query). Returning empty feature set",
                     (summaryFeatures ? "summary features" : "rank features"));
        return FeatureSet::SP(new FeatureSet());
    }
    return findFeatureSet(req, *mtf, summaryFeatures);
}

Matcher::Matcher(const search::index::Schema &schema, const Properties &props, const vespalib::Clock &clock,
                 QueryLimiter &queryLimiter, const IConstantValueRepo &constantValueRepo, uint32_t distributionKey)
    : _indexEnv(schema, props, constantValueRepo),
      _blueprintFactory(),
      _rankSetup(),
      _viewResolver(ViewResolver::createFromSchema(schema)),
      _statsLock(),
      _stats(),
      _clock(clock),
      _queryLimiter(queryLimiter),
      _distributionKey(distributionKey)
{
    search::features::setup_search_features(_blueprintFactory);
    search::fef::test::setup_fef_test_plugin(_blueprintFactory);
    _rankSetup.reset(new search::fef::RankSetup(_blueprintFactory, _indexEnv));
    _rankSetup->configure(); // reads config values from the property map
    if (!_rankSetup->compile()) {
        throw vespalib::IllegalArgumentException("failed to compile rank setup", VESPA_STRLOC);
    }
}

MatchingStats
Matcher::getStats()
{
    std::lock_guard<std::mutex> guard(_statsLock);
    MatchingStats stats = std::move(_stats);
    _stats = std::move(MatchingStats());
    _stats.softDoomFactor(stats.softDoomFactor());
    return stats;
}

using search::fef::indexproperties::softtimeout::Enabled;
using search::fef::indexproperties::softtimeout::Factor;

std::unique_ptr<MatchToolsFactory>
Matcher::create_match_tools_factory(const search::engine::Request &request, ISearchContext &searchContext,
                                    IAttributeContext &attrContext, const search::IDocumentMetaStore &metaStore,
                                    const Properties &feature_overrides) const
{
    const Properties & rankProperties = request.propertiesMap.rankProperties();
    bool softTimeoutEnabled = Enabled::lookup(rankProperties, _rankSetup->getSoftTimeoutEnabled());
    double factor = softTimeoutEnabled
                    ? Factor::lookup(rankProperties, _stats.softDoomFactor())
                    : 0.95;
    int64_t safeLeft = request.getTimeLeft() * factor;
    fastos::TimeStamp safeDoom(fastos::ClockSystem::now() + safeLeft);
    if (softTimeoutEnabled) {
        LOG(debug, "Soft-timeout computed factor=%1.3f, used factor=%1.3f, softTimeout=%lu softDoom=%ld hardDoom=%ld",
                   _stats.softDoomFactor(), factor, safeLeft, safeDoom.ns(), request.getTimeOfDoom().ns());
    }
    return std::make_unique<MatchToolsFactory>(_queryLimiter, vespalib::Doom(_clock, safeDoom),
                                               vespalib::Doom(_clock, request.getTimeOfDoom()), searchContext,
                                               attrContext, request.getStackRef(), request.location, _viewResolver,
                                               metaStore, _indexEnv, *_rankSetup, rankProperties, feature_overrides);
}

SearchReply::UP
Matcher::handleGroupingSession(SessionManager &sessionMgr, GroupingContext & groupingContext,
                               GroupingSession::UP groupingSession)
{
    SearchReply::UP reply = std::make_unique<SearchReply>();
    groupingSession->continueExecution(groupingContext);
    groupingContext.getResult().swap(reply->groupResult);
    if (!groupingSession->finished()) {
        sessionMgr.insert(std::move(groupingSession));
    }
    return reply;
}

size_t
Matcher::computeNumThreadsPerSearch(Blueprint::HitEstimate hits, const Properties & rankProperties) const {
    size_t threads = NumThreadsPerSearch::lookup(rankProperties, _rankSetup->getNumThreadsPerSearch());
    uint32_t minHitsPerThread = MinHitsPerThread::lookup(rankProperties, _rankSetup->getMinHitsPerThread());
    if ((threads > 1) && (minHitsPerThread > 0)) {
        threads = (hits.empty) ? 1 : std::min(threads, numThreads(hits.estHits, minHitsPerThread));
    }
    return threads;
}

SearchReply::UP
Matcher::match(const SearchRequest &request, vespalib::ThreadBundle &threadBundle,
               ISearchContext &searchContext, IAttributeContext &attrContext,
               SessionManager &sessionMgr, const search::IDocumentMetaStore &metaStore,
               SearchSession::OwnershipBundle &&owned_objects)
{
    fastos::StopWatch total_matching_time;
    total_matching_time.start();
    MatchingStats my_stats;
    SearchReply::UP reply = std::make_unique<SearchReply>();
    { // we want to measure full set-up and tear-down time as part of
      // collateral time
        GroupingContext groupingContext(_clock, request.getTimeOfDoom(),
                                        &request.groupSpec[0], request.groupSpec.size());
        SessionId sessionId(&request.sessionId[0], request.sessionId.size());
        bool shouldCacheSearchSession = false;
        bool shouldCacheGroupingSession = false;
        if (!sessionId.empty()) {
            const Properties &cache_props = request.propertiesMap.cacheProperties();
            shouldCacheGroupingSession = cache_props.lookup("grouping").found();
            shouldCacheSearchSession = cache_props.lookup("query").found();
            if (shouldCacheGroupingSession) {
                GroupingSession::UP session(sessionMgr.pickGrouping(sessionId));
                if (session.get()) {
                    return handleGroupingSession(sessionMgr, groupingContext, std::move(session));
                }
            }
        }
        const Properties *feature_overrides = &request.propertiesMap.featureOverrides();
        if (shouldCacheSearchSession) {
            owned_objects.feature_overrides.reset(new Properties(*feature_overrides));
            feature_overrides = owned_objects.feature_overrides.get();
        }
        MatchToolsFactory::UP mtf = create_match_tools_factory(request, searchContext, attrContext,
                                                               metaStore, *feature_overrides);
        if (!mtf->valid()) {
            reply->errorCode = ECODE_QUERY_PARSE_ERROR;
            reply->errorMessage = "query execution failed (invalid query)";
            return reply;
        }

        MatchParams params(searchContext.getDocIdLimit(), _rankSetup->getHeapSize(), _rankSetup->getArraySize(),
                           _rankSetup->getRankScoreDropLimit(), request.offset, request.maxhits,
                           !_rankSetup->getSecondPhaseRank().empty(), !willNotNeedRanking(request, groupingContext));

        ResultProcessor rp(attrContext, metaStore, sessionMgr, groupingContext, sessionId,
                           request.sortSpec, params.offset, params.hits);

        const Properties & rankProperties = request.propertiesMap.rankProperties();
        size_t numThreadsPerSearch = computeNumThreadsPerSearch(mtf->estimate(), rankProperties);
        LimitedThreadBundleWrapper limitedThreadBundle(threadBundle, numThreadsPerSearch);
        MatchMaster master;
        uint32_t numSearchPartitions = NumSearchPartitions::lookup(rankProperties,
                                                                   _rankSetup->getNumSearchPartitions());
        ResultProcessor::Result::UP result = master.match(params, limitedThreadBundle, *mtf, rp,
                                                          _distributionKey, numSearchPartitions);
        my_stats = MatchMaster::getStats(std::move(master));

        bool wasLimited = mtf->match_limiter().was_limited();
        size_t spaceEstimate = mtf->match_limiter().getDocIdSpaceEstimate();
        uint32_t estHits = mtf->estimate().estHits;
        if (shouldCacheSearchSession && ((result->_numFs4Hits != 0) || shouldCacheGroupingSession)) {
            SearchSession::SP session = std::make_shared<SearchSession>(sessionId, request.getTimeOfDoom(),
                                                                        std::move(mtf), std::move(owned_objects));
            session->releaseEnumGuards();
            sessionMgr.insert(std::move(session));
        }
        reply = std::move(result->_reply);

        uint32_t numActiveLids = metaStore.getNumActiveLids();
        // note: this is actually totalSpace+1, since 0 is reserved
        uint32_t totalSpace = metaStore.getCommittedDocIdLimit();
        LOG(debug, "docid limit = %d", totalSpace);
        LOG(debug, "num active lids = %d", numActiveLids);
        LOG(debug, "space Estimate = %zd", spaceEstimate);
        if (spaceEstimate >= totalSpace) {
            // estimate is too high, clamp it
            spaceEstimate = totalSpace;
        } else {
            // account for docid 0 reserved
            spaceEstimate += 1;
        }
        size_t covered = (spaceEstimate *  numActiveLids) / totalSpace;
        LOG(debug, "covered = %zd", covered);

        SearchReply::Coverage & coverage = reply->coverage;
        coverage.setActive(numActiveLids);
        //TODO this should be calculated with ClusterState calculator.
        coverage.setSoonActive(numActiveLids);
        coverage.setCovered(covered);
        if (wasLimited) {
            coverage.degradeMatchPhase();
            LOG(debug, "was limited, degraded from match phase");
        }
        if (my_stats.softDoomed()) {
            coverage.degradeTimeout();
            coverage.setCovered(my_stats.docsCovered());
            LOG(debug, "soft doomed, degraded from timeout covered = %lu", coverage.getCovered());
        }
        LOG(debug, "numThreadsPerSearch = %zu. Configured = %d, estimated hits=%d, totalHits=%ld",
            numThreadsPerSearch, _rankSetup->getNumThreadsPerSearch(), estHits, reply->totalHitCount);
    }
    total_matching_time.stop();
    my_stats.queryCollateralTime(total_matching_time.elapsed().sec() - my_stats.queryLatencyAvg());
    {
        fastos::TimeStamp softLimit = uint64_t((1.0 - _rankSetup->getSoftTimeoutTailCost()) * request.getTimeout());
        fastos::TimeStamp duration = request.getTimeUsed();
        std::lock_guard<std::mutex> guard(_statsLock);
        _stats.add(my_stats);
        if (my_stats.softDoomed()) {
            LOG(info, "Triggered softtimeout limit=%1.3f and duration=%1.3f", softLimit.sec(), duration.sec());
            _stats.updatesoftDoomFactor(request.getTimeout(), softLimit, duration);
        }
    }
    return reply;
}

FeatureSet::SP
Matcher::getSummaryFeatures(const DocsumRequest & req, ISearchContext & searchCtx,
                            IAttributeContext & attrCtx, SessionManager &sessionMgr)
{
    return getFeatureSet(req, searchCtx, attrCtx, sessionMgr, true);
}

FeatureSet::SP
Matcher::getRankFeatures(const DocsumRequest & req, ISearchContext & searchCtx,
                         IAttributeContext & attrCtx, SessionManager &sessionMgr)
{
    return getFeatureSet(req, searchCtx, attrCtx, sessionMgr, false);
}

}
<|endoftext|>