* Lumiera source code always was copyrighted by individual contributors * there is no entity "Lumiera.org" which holds any copyrights * Lumiera source code is provided under the GPL Version 2+ == Explanations == Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above. For this to become legally effective, the ''File COPYING in the root directory is sufficient.'' The licensing header in each file is not strictly necessary, yet considered good practice; attaching a licence notice increases the likeliness that this information is retained in case someone extracts individual code files. However, it is not by the presence of some text, that legally binding licensing terms become effective; rather the fact matters that a given piece of code was provably copyrighted and published under a license. Even reformatting the code, renaming some variables or deleting parts of the code will not alter this legal situation, but rather creates a derivative work, which is likewise covered by the GPL! The most relevant information in the file header is the notice regarding the time of the first individual copyright claim. By virtue of this initial copyright, the first author is entitled to choose the terms of licensing. All further modifications are permitted and covered by the License. The specific wording or format of the copyright header is not legally relevant, as long as the intention to publish under the GPL remains clear. The extended wording was based on a recommendation by the FSF. It can be shortened, because the full terms of the license are provided alongside the distribution, in the file COPYING.
183 lines
7.5 KiB
C++
183 lines
7.5 KiB
C++
/*
|
||
DispatcherInterface(Test) - document and verify dispatcher for frame job creation
|
||
|
||
Copyright (C)
|
||
2012, Hermann Vosseler <Ichthyostega@web.de>
|
||
|
||
**Lumiera** is free software; you can redistribute it and/or modify it
|
||
under the terms of the GNU General Public License as published by the
|
||
Free Software Foundation; either version 2 of the License, or (at your
|
||
option) any later version. See the file COPYING for further details.
|
||
|
||
* *****************************************************************/
|
||
|
||
/** @file dispatcher-interface-test.cpp
|
||
** unit test \ref DispatcherInterface_test
|
||
*/
|
||
|
||
|
||
#include "lib/test/run.hpp"
|
||
#include "steam/engine/mock-dispatcher.hpp"
|
||
#include "steam/play/timings.hpp"
|
||
#include "lib/time/timevalue.hpp"
|
||
#include "lib/util.hpp"
|
||
|
||
#include <utility>
|
||
|
||
using test::Test;
|
||
|
||
|
||
namespace steam {
|
||
namespace engine{
|
||
namespace test {
|
||
|
||
using lib::time::FrameRate;
|
||
using lib::time::Offset;
|
||
using lib::time::Time;
|
||
using play::Timings;
|
||
|
||
using asset::Pipe;
|
||
using PID = asset::ID<Pipe>;
|
||
using mobject::ModelPort;
|
||
using lumiera::error::LUMIERA_ERROR_LOGIC;
|
||
|
||
|
||
|
||
|
||
|
||
|
||
/***************************************************************//**
|
||
* @test document and verify the engine::Dispatcher interface, used
|
||
* to translate a CalcStream into individual node jobs.
|
||
* This test covers the definition of the interface itself,
|
||
* together with the supporting types and the default
|
||
* implementation of the basic operations.
|
||
* It uses a mock Dispatcher implementation.
|
||
* @see JobPlanningPipeline_test
|
||
*/
|
||
class DispatcherInterface_test : public Test
|
||
{
|
||
|
||
virtual void
|
||
run (Arg)
|
||
{
|
||
seedRand();
|
||
resolveModelPort();
|
||
accessJobTicket();
|
||
pipelineBuilder();
|
||
}
|
||
|
||
|
||
/** @test the dispatcher can resolve a known ModelPort
|
||
* into the internal index number used on the Segmentation
|
||
* for the corresponding timeline (which exposes this ModelPort)
|
||
*/
|
||
void
|
||
resolveModelPort()
|
||
{
|
||
MockDispatcher dispatcher;
|
||
auto [port,sink] = dispatcher.getDummyConnection(1);
|
||
CHECK (1 == dispatcher.resolveModelPort (port));
|
||
|
||
// but when using some arbitrary unrelated ModelPort...
|
||
PID dazedPipe = Pipe::query ("id(dazed)");
|
||
ModelPort evil = reinterpret_cast<ModelPort&> (dazedPipe);
|
||
VERIFY_ERROR (LOGIC, dispatcher.resolveModelPort(evil));
|
||
}
|
||
|
||
|
||
|
||
/** @test the dispatcher knows how to pick the right JobTicket
|
||
* for each point on the timeline, and thus how to access
|
||
* the proper part of the render nodes responsible for
|
||
* rendering this part of the timeline
|
||
*/
|
||
void
|
||
accessJobTicket()
|
||
{
|
||
MockDispatcher dispatcher{MakeRec() // a first active segment
|
||
.attrib("start", Time{0,10}) // covering the time [10s ... 20s[
|
||
.attrib("after", Time{0,20})
|
||
.attrib("mark", 23) // pipeline-Hash used as marker to verify proper access
|
||
.genNode()
|
||
,MakeRec() // add a second Segment
|
||
.attrib("start", Time{0,20}) // covering the rest of the timeline from 20s on
|
||
.attrib("mark", 45)
|
||
.genNode()};
|
||
size_t portIDX = 1;
|
||
// Dispatcher-Interface: access JobTicket
|
||
JobTicket& ticket0 = dispatcher.getJobTicketFor (portIDX, -Time{0,5});
|
||
JobTicket& ticket1 = dispatcher.getJobTicketFor (portIDX, Time{0,15});
|
||
JobTicket& ticket2 = dispatcher.getJobTicketFor (portIDX, Time{0,25});
|
||
|
||
CHECK ( ticket0.empty()); // this ticket was drawn from an undefined part of the timeline
|
||
CHECK (not ticket1.empty()); // while this ticket belongs to the first segment
|
||
CHECK (not ticket2.empty()); // and this to the second segment
|
||
|
||
Job job0 = ticket0.createJobFor(-Time{0,5});
|
||
Job job1 = ticket1.createJobFor(Time{0,15});
|
||
Job job2 = ticket2.createJobFor(Time{0,25});
|
||
|
||
CHECK (MockJob::isNopJob(job0));
|
||
|
||
CHECK (Time(0,15) == job1.parameter.nominalTime);
|
||
CHECK (23 == job1.parameter.invoKey.part.a); // proof that this job is connected to segment #1
|
||
|
||
CHECK (Time(0,25) == job2.parameter.nominalTime);
|
||
CHECK (45 == job2.parameter.invoKey.part.a); // and this one to segment #2
|
||
}
|
||
|
||
|
||
|
||
/** @test for the actual use case, the dispatcher acts as entrance point
|
||
* to a job-planning pipeline builder, which in the end is an iterator
|
||
* to pull render jobs from
|
||
* @see JobPlanningPipeline_test for in-depth coverage of this complex topic
|
||
*/
|
||
void
|
||
pipelineBuilder()
|
||
{
|
||
MockDispatcher dispatcher{MakeRec() // a single segment covering the complete time-axis
|
||
.attrib("mark", 555) // marker to demonstrate proper connectivity
|
||
.genNode()};
|
||
|
||
play::Timings timings (FrameRate::PAL);
|
||
auto [port,sink] = dispatcher.getDummyConnection(1);
|
||
|
||
// Dispatcher-Interface: pipeline builder...
|
||
auto pipeline = dispatcher.forCalcStream (timings)
|
||
.timeRange(Time{200,0}, Time{300,0})
|
||
.pullFrom (port)
|
||
.feedTo (sink);
|
||
|
||
CHECK (not isnil (pipeline));
|
||
CHECK (5 == pipeline.currFrameNr()); // 5 * 1/25sec = 200ms
|
||
|
||
Job job = pipeline.buildJob(); // invoke the JobPlanning to build a Job for the first frame
|
||
CHECK (Time(200,0) == job.parameter.nominalTime);
|
||
CHECK (555 == job.parameter.invoKey.part.a); // the marker shows that this job is connected properly
|
||
|
||
++pipeline; // iterate to advance to the next frame
|
||
CHECK (not isnil (pipeline));
|
||
CHECK (6 == pipeline.currFrameNr());
|
||
job = pipeline.buildJob(); // build job for the next frame
|
||
CHECK (Time(240,0) == job.parameter.nominalTime);
|
||
CHECK (555 == job.parameter.invoKey.part.a);
|
||
|
||
++pipeline;
|
||
CHECK (7 == pipeline.currFrameNr());
|
||
job = pipeline.buildJob();
|
||
CHECK (Time(280,0) == job.parameter.nominalTime);
|
||
|
||
++pipeline; // iterate beyond end point
|
||
CHECK (isnil (pipeline)); // pipeline exhausted
|
||
}
|
||
};
|
||
|
||
|
||
/** Register this test class... */
|
||
LAUNCHER (DispatcherInterface_test, "unit engine");
|
||
|
||
|
||
|
||
}}} // namespace steam::engine::test
|