Skip to content

Commit 487e056

Browse files
committed
added support for #45
1 parent a5c98e7 commit 487e056

10 files changed

Lines changed: 142 additions & 39 deletions

File tree

src/device/DeviceInterface.cpp

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,16 @@ inline IDeviceInterface::DeviceImageProxy<T> mapToProxyDevice(const DeviceImageB
8787
};
8888
}
8989

90+
template <typename T>
91+
inline IDeviceInterface::DeviceImageProxy<T> mapToProxyHost(const DeviceImageBase<T>& buffer)
92+
{
93+
return IDeviceInterface::DeviceImageProxy<T>{
94+
.DataPtr = const_cast<T*>(buffer.Data.HostPtr),
95+
.Width = buffer.Width,
96+
.Height = buffer.Height
97+
};
98+
}
99+
90100
template <typename T>
91101
inline IDeviceInterface::DeviceStreamProxy<T> mapToProxyDevice(const DeviceStreamBase<T>& buffer)
92102
{
@@ -928,7 +938,7 @@ IDeviceInterface::DeviceImageProxy<float> DeviceInterface::loadAOVImageForDevice
928938
const std::string& actual_name = handleAOVName(aov_name);
929939

930940
if (const auto it = mDeviceData.aovs.find(actual_name); it != mDeviceData.aovs.end()) {
931-
IG_ASSERT(it->second.Width == mFramebufferWidth && it->second.Height == mFramebufferHeight, "Size of framebuffer changed inbetween iterations");
941+
IG_ASSERT(it->second.Width == mFramebufferWidth && it->second.Height == mFramebufferHeight, "Size of framebuffer changed between iterations");
932942
it->second.Data.syncForDevice();
933943
if (willBeModified)
934944
it->second.Data.markDirtyOnDevice();
@@ -957,19 +967,25 @@ IDeviceInterface::DeviceImageProxy<float> DeviceInterface::loadAOVImageForHost(c
957967
const std::string& actual_name = handleAOVName(aov_name);
958968

959969
if (const auto it = mDeviceData.aovs.find(actual_name); it != mDeviceData.aovs.end()) {
960-
IG_ASSERT(it->second.Width == mFramebufferWidth && it->second.Height == mFramebufferHeight, "Size of framebuffer changed inbetween iterations");
970+
IG_ASSERT(it->second.Width == mFramebufferWidth && it->second.Height == mFramebufferHeight, "Size of framebuffer changed between iterations");
961971
it->second.Data.syncForHost();
962972
if (willBeModified)
963973
it->second.Data.markDirtyOnHost();
964-
return DeviceImageProxy<float>{
965-
.DataPtr = it->second.Data.HostPtr,
966-
.Width = it->second.Width,
967-
.Height = it->second.Height
968-
};
974+
return mapToProxyHost(it->second);
969975
} else {
970-
// Note: Currently only the device side is allowed to create new AOVs. This is only by design to catch some common mistakes, but if a use-case arise we might change this in the future.
971-
IG_LOG(L_ERROR) << "Unknown aov '" << actual_name << "' access for host" << std::endl;
972-
return DeviceImageProxy<float>::Invalid();
976+
const size_t expectedSize = framebufferArea() * 3;
977+
978+
auto aov = DeviceImage{
979+
.Data = createAOVArray(mDeviceID, expectedSize),
980+
.Width = mFramebufferWidth,
981+
.Height = mFramebufferHeight
982+
};
983+
984+
if (willBeModified)
985+
aov.Data.markDirtyOnHost();
986+
else
987+
aov.Data.fillHostWithZero();
988+
return mapToProxyHost(mDeviceData.aovs.try_emplace(actual_name, std::move(aov)).first->second);
973989
}
974990
}
975991

src/frontend/cli/main.cpp

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ int main(int argc, char** argv)
9797
}
9898

9999
runtime->mergeParametersFrom(cmd.UserEntries);
100+
101+
if (!cmd.ContinueImage.empty())
102+
runtime->loadPreviousFramebuffer(cmd.ContinueImage);
103+
100104
timer_loading.stop();
101105

102106
auto orientation = runtime->initialCameraOrientation();
@@ -111,6 +115,9 @@ int main(int argc, char** argv)
111115
if (cmd.SPP.has_value() && (cmd.SPP.value() % SPI) != 0)
112116
IG_LOG(L_WARNING) << "Given spp " << cmd.SPP.value() << " is not a multiple of the spi " << SPI << ". Using spp " << desired_iter * SPI << " instead" << std::endl;
113117

118+
if (runtime->currentSampleCount() >= (size_t)cmd.SPP.value_or(std::numeric_limits<int>::max()))
119+
IG_LOG(L_WARNING) << "Requested spp already satisfied. No need to continue." << std::endl;
120+
114121
StatusObserver observer(!cmd.NoColor, 2, desired_iter * SPI /* Approx */, cmd.RenderTime.value_or(0));
115122
observer.begin();
116123

@@ -119,7 +126,7 @@ int main(int argc, char** argv)
119126
std::vector<double> samples_sec;
120127

121128
SectionTimer timer_render;
122-
while (true) {
129+
while (runtime->currentSampleCount() < (size_t)cmd.SPP.value_or(std::numeric_limits<int>::max())) {
123130
if (!cmd.NoProgress)
124131
observer.update(runtime->currentSampleCount());
125132

@@ -132,15 +139,18 @@ int main(int argc, char** argv)
132139
auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - ticks).count();
133140

134141
samples_sec.emplace_back(1000.0 * double(SPI * runtime->framebufferWidth() * runtime->framebufferHeight()) / double(elapsed_ms));
135-
if (desired_iter > 0 && samples_sec.size() == desired_iter)
136-
break;
137-
else if (cmd.RenderTime.has_value() && timer_render.duration_ms / 1000 > cmd.RenderTime.value())
142+
if (cmd.RenderTime.has_value() && timer_render.duration_ms / 1000 > cmd.RenderTime.value())
138143
break;
139144
}
140145

141146
if (!cmd.NoProgress)
142147
observer.end();
143148

149+
if (samples_sec.empty()) {
150+
IG_LOG(L_WARNING) << "Nothing rendered." << std::endl;
151+
return EXIT_SUCCESS;
152+
}
153+
144154
SectionTimer timer_saving;
145155
timer_saving.start();
146156
if (!runtime->saveFramebuffer(cmd.Output))

src/frontend/common/ProgramOptions.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ ProgramOptions::ProgramOptions(int argc, char** argv, ApplicationType type, cons
5353
app.set_help_flag("-h,--help", "Shows help message and exit");
5454

5555
app.add_option("scene", InputScene, "Scene file to load. Can be a Ignis scene file or a glTF file.")->required()->check(CLI::ExistingFile);
56+
57+
if (type != ApplicationType::Trace)
58+
app.add_option("--continue", ContinueImage, "Image to continue rendering from. Must be an output of Ignis with embedded meta-data and the same input scene and settings.")->check(CLI::ExistingFile);
59+
5660
app.add_flag("-q,--quiet", Quiet, "Do not print messages into console");
5761
app.add_flag_callback(
5862
"-v,--verbose", [&]() { VerbosityLevel = L_DEBUG; }, "Set the verbosity level to 'debug'. Shortcut for --log-level debug");

src/frontend/common/ProgramOptions.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ class ProgramOptions {
8585
Path Output;
8686
Path InputScene;
8787
Path InputRay;
88+
Path ContinueImage;
8889

8990
Path ScriptDir;
9091

src/frontend/view/main.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ int main(int argc, char** argv)
9393
}
9494

9595
runtime->mergeParametersFrom(cmd.UserEntries);
96+
97+
if (!cmd.ContinueImage.empty())
98+
runtime->loadPreviousFramebuffer(cmd.ContinueImage);
99+
96100
timer_loading.stop();
97101

98102
const auto def = runtime->initialCameraOrientation();

src/runtime/Image.cpp

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -594,37 +594,37 @@ Image Image::load(const Path& path, ImageMetaData* metaData, const std::string*
594594
int channels = 0;
595595
std::unordered_set<std::string> channel_names; // Only populated if metaData != nullptr
596596
for (int c = 0; c < exr_header.num_channels; ++c) {
597-
const std::string name = to_lowercase(std::string(exr_header.channels[c].name));
597+
const std::string name = std::string(exr_header.channels[c].name);
598598

599599
if (optionalLayerName) {
600-
if (name == (*optionalLayerName + ".a"))
600+
if (string_equal_icase(name, (*optionalLayerName + ".a")))
601601
idxA = c;
602-
else if (name == (*optionalLayerName + ".r"))
602+
else if (string_equal_icase(name, (*optionalLayerName + ".r")))
603603
idxR = c;
604-
else if (name == (*optionalLayerName + ".g"))
604+
else if (string_equal_icase(name, (*optionalLayerName + ".g")))
605605
idxG = c;
606-
else if (name == (*optionalLayerName + ".b"))
606+
else if (string_equal_icase(name, (*optionalLayerName + ".b")))
607607
idxB = c;
608-
else if (name == (*optionalLayerName + ".y"))
608+
else if (string_equal_icase(name, (*optionalLayerName + ".y")))
609609
idxY = c;
610-
else if (name == *optionalLayerName)
610+
else if (string_equal_icase(name, *optionalLayerName))
611611
idxY = c;
612612
} else {
613-
if (name == "a" || name == "default.a")
613+
if (string_equal_icase(name, "a") || string_equal_icase(name, "default.a"))
614614
idxA = c;
615-
else if (name == "r" || name == "default.r")
615+
else if (string_equal_icase(name, "r") || string_equal_icase(name, "default.r"))
616616
idxR = c;
617-
else if (name == "g" || name == "default.g")
617+
else if (string_equal_icase(name, "g") || string_equal_icase(name, "default.g"))
618618
idxG = c;
619-
else if (name == "b" || name == "default.b")
619+
else if (string_equal_icase(name, "b") || string_equal_icase(name, "default.b"))
620620
idxB = c;
621-
else if (name == "y" || name == "default.y")
621+
else if (string_equal_icase(name, "y") || string_equal_icase(name, "default.y"))
622622
idxY = c;
623623
}
624624

625625
if (metaData) {
626626
const std::string extractedName = extractChannelName(name);
627-
if (extractedName != "a" && extractedName != "r" && extractedName != "g" && extractedName != "b" && extractedName != "y")
627+
if (!string_equal_icase(extractedName, "a") && !string_equal_icase(extractedName, "r") && !string_equal_icase(extractedName, "g") && !string_equal_icase(extractedName, "b") && !string_equal_icase(extractedName, "y"))
628628
channel_names.emplace(extractedName);
629629
}
630630
++channels;

src/runtime/Runtime.cpp

Lines changed: 70 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,67 @@ void Runtime::clearFramebuffer(const std::string& name)
469469
mDevice->clearFramebuffer(name);
470470
}
471471

472+
bool Runtime::loadPreviousFramebuffer(const Path& path)
473+
{
474+
if (mOptions.IsTracer) {
475+
IG_LOG(L_ERROR) << "Trying to load a previous framebuffer for a trace runtime!" << std::endl;
476+
return false;
477+
}
478+
479+
ImageMetaData metaData;
480+
Image image = Image::load(path, &metaData);
481+
if (!image.isValid()) {
482+
IG_LOG(L_ERROR) << "Could not load previous framebuffer " << path << std::endl;
483+
return false;
484+
}
485+
image.flipY();
486+
487+
if (!metaData.Frame.has_value()
488+
|| !metaData.Iteration.has_value()
489+
|| !metaData.SamplePerIteration.has_value()
490+
|| !metaData.SamplePerPixel.has_value()) {
491+
IG_LOG(L_ERROR) << "Trying to load a previous framebuffer using an image without proper meta-data. Are you sure this image was generated with Ignis?" << std::endl;
492+
return false;
493+
}
494+
495+
if (metaData.TechniqueType.has_value() && *metaData.TechniqueType != technique())
496+
IG_LOG(L_WARNING) << "Previous framebuffer was rendered with a different technique. Previous '" << *metaData.TechniqueType << "' might be incompatible with the current '" << technique() << "'." << std::endl;
497+
if (metaData.CameraType.has_value() && *metaData.CameraType != camera())
498+
IG_LOG(L_WARNING) << "Previous framebuffer was rendered with a different camera. Previous '" << *metaData.CameraType << "' might be incompatible with the current '" << camera() << "'." << std::endl;
499+
500+
if (image.channels != 3)
501+
image = image.castTo(3);
502+
503+
IG_ASSERT(mDevice, "Expected device to be available");
504+
mDevice->resize(mFilmWidth, mFilmHeight); // Ensure the device is properly sized
505+
506+
const auto framebuffer = mDevice->getFramebufferForHost({}, true);
507+
std::memcpy(framebuffer.Data, image.pixels.get(), image.width * image.height * image.channels * sizeof(float));
508+
509+
for (const auto& layerName : metaData.AdditionalLayerNames) {
510+
if (layerName.empty() || layerName == "Color")
511+
continue;
512+
513+
Image layer = Image::load(path, nullptr, &layerName);
514+
IG_ASSERT(layer.isValid(), "Loading a promised layer from an existing image failed.");
515+
layer.flipY();
516+
517+
if (layer.channels != 3)
518+
layer = layer.castTo(3);
519+
520+
const auto aov = mDevice->getFramebufferForHost(layerName, true);
521+
std::memcpy(aov.Data, layer.pixels.get(), layer.width * layer.height * layer.channels * sizeof(float));
522+
}
523+
524+
mCurrentIteration = *metaData.Iteration;
525+
mCurrentSampleCount = *metaData.SamplePerPixel;
526+
mCurrentFrame = *metaData.Frame;
527+
528+
IG_LOG(L_INFO) << "Continuing rendering from " << mCurrentSampleCount << " samples per pixel." << std::endl;
529+
530+
return true;
531+
}
532+
472533
size_t Runtime::getBufferSizeInBytes(const std::string& name) const
473534
{
474535
return mDevice->getBufferSizeInBytes(name);
@@ -806,20 +867,17 @@ bool Runtime::saveFramebuffer(const Path& path) const
806867
float* dst_g = &images[width * height * (3 * aov + 1)];
807868
float* dst_b = &images[width * height * (3 * aov + 2)];
808869

809-
const auto pixelF = [&](size_t ind) {
810-
float r = src[ind * 3 + 0];
811-
float g = src[ind * 3 + 1];
812-
float b = src[ind * 3 + 2];
813-
814-
dst_r[ind] = r * scale;
815-
dst_g[ind] = g * scale;
816-
dst_b[ind] = b * scale;
817-
};
818-
819870
tbb::parallel_for(tbb::blocked_range<size_t>(0, width * height),
820871
[&](tbb::blocked_range<size_t> r) {
821-
for (size_t i = r.begin(); i < r.end(); ++i)
822-
pixelF(i);
872+
for (size_t i = r.begin(); i < r.end(); ++i) {
873+
const float r = src[i * 3 + 0];
874+
const float g = src[i * 3 + 1];
875+
const float b = src[i * 3 + 2];
876+
877+
dst_r[i] = r * scale;
878+
dst_g[i] = g * scale;
879+
dst_b[i] = b * scale;
880+
}
823881
});
824882
}
825883

src/runtime/Runtime.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ class IG_LIB Runtime {
7777
/// Will clear specific framebuffer
7878
void clearFramebuffer(const std::string& name);
7979

80+
/// Will try to recover the framebuffer and spp settings from a previous image
81+
bool loadPreviousFramebuffer(const Path& path);
82+
8083
[[nodiscard]] size_t getBufferSizeInBytes(const std::string& name) const;
8184
[[nodiscard]] BufferAccessor getBufferForDevice(const std::string& name) const;
8285
BufferAccessor requestBufferForDevice(const std::string& name, size_t sizeInBytes) const;

src/runtime/StringUtils.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ bool string_ends_with(const std::string& str, const std::string& suffix)
6363
return string_ends_with((std::string_view)str, (std::string_view)suffix);
6464
}
6565

66+
bool string_equal_icase(std::string_view a, std::string_view b)
67+
{
68+
return std::equal(a.begin(), a.end(), b.begin(), b.end(), [](auto ca, auto cb) { return ::tolower(ca) == ::tolower(cb); });
69+
}
70+
6671
void string_left_trim(std::string& s)
6772
{
6873
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) {

src/runtime/StringUtils.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ namespace IG {
2222
[[nodiscard]] IG_LIB bool string_ends_with(std::string_view str, std::string_view suffix);
2323
[[nodiscard]] IG_LIB bool string_ends_with(const std::string& str, const std::string& suffix);
2424

25+
[[nodiscard]] IG_LIB bool string_equal_icase(std::string_view a, std::string_view b);
26+
2527
/// Trim string from the left side in place
2628
IG_LIB void string_left_trim(std::string& s);
2729

0 commit comments

Comments
 (0)