diff --git a/core/include/mmcore/view/AbstractCallRender3D.h b/core/include/mmcore/view/AbstractCallRender3D.h index a403be6cf1..7e2de03c0c 100644 --- a/core/include/mmcore/view/AbstractCallRender3D.h +++ b/core/include/mmcore/view/AbstractCallRender3D.h @@ -84,7 +84,10 @@ namespace view { this->camParams = camParams; } - + inline void SetCameraView(const vislib::math::Point pos, const vislib::math::Point la, + const vislib::math::Vector up) { + this->camParams->SetView(pos, la, up); + } /** * Assignment operator diff --git a/plugins/OSPRay_plugin/src/OSPRayRenderer.cpp b/plugins/OSPRay_plugin/src/OSPRayRenderer.cpp index a6f411877e..8a089da053 100644 --- a/plugins/OSPRay_plugin/src/OSPRayRenderer.cpp +++ b/plugins/OSPRay_plugin/src/OSPRayRenderer.cpp @@ -25,6 +25,8 @@ #include #include +#define OSPRAY_MEASUREMENT 1 + using namespace megamol::ospray; /* @@ -257,7 +259,7 @@ bool OSPRayRenderer::Render(megamol::core::Call& call) { ospCommit(world); auto t2 = std::chrono::high_resolution_clock::now(); const auto duration = std::chrono::duration_cast(t2 - t1).count(); - vislib::sys::Log::DefaultLog.WriteMsg(242, "OSPRayRenderer: Commiting World took: %d microseconds", duration); + vislib::sys::Log::DefaultLog.WriteMsg(2, "OSPRayRenderer: Commiting World took: %d microseconds", duration); } if (material_has_changed && !data_has_changed) { this->changeMaterial(); @@ -300,9 +302,12 @@ bool OSPRayRenderer::Render(megamol::core::Call& call) { accum_time.amount += duration.count(); accum_time.count += 1; - if (accum_time.amount >= static_cast(1e6)) { +#ifndef OSPRAY_MEASUREMENT + if (accum_time.amount >= static_cast(1e6)) +#endif + { const unsigned long long int mean_rendertime = accum_time.amount / accum_time.count; - vislib::sys::Log::DefaultLog.WriteMsg(242, "OSPRayRenderer: Rendering took: %d microseconds", mean_rendertime); + vislib::sys::Log::DefaultLog.WriteMsg(2, "OSPRayRenderer: Rendering took: %d microseconds", mean_rendertime); accum_time.count = 0; accum_time.amount = 0; } diff --git a/plugins/pbs/src/FBOTransmitter2.cpp b/plugins/pbs/src/FBOTransmitter2.cpp index fbce2aa1cd..b3631bc03f 100644 --- a/plugins/pbs/src/FBOTransmitter2.cpp +++ b/plugins/pbs/src/FBOTransmitter2.cpp @@ -2,6 +2,7 @@ #include "FBOTransmitter2.h" #include +#include #include "glad/glad.h" @@ -31,6 +32,8 @@ # include #endif +#define PBS_MEASUREMENTS 1 + //#define _DEBUG 1 megamol::pbs::FBOTransmitter2::FBOTransmitter2() @@ -194,10 +197,18 @@ void megamol::pbs::FBOTransmitter2::AfterRender(megamol::core::view::AbstractVie vislib::sys::Log::DefaultLog.WriteInfo( "IceT gets image with xoff: %d, yoff: %d, tile_width: %d, tile_height: %d\n", xoff, yoff, tile_width, tile_height); +#endif +# ifdef PBS_MEASUREMENTS + auto const start = std::chrono::high_resolution_clock::now(); # endif auto const icet_comp_image = icetCompositeImage(col_buf.data(), depth_buf.data(), tilevp, nullptr, nullptr, static_cast(backgroundColor.data())); -# if _DEBUG +# ifdef PBS_MEASUREMENTS + auto const end = std::chrono::high_resolution_clock::now(); + vislib::sys::Log::DefaultLog.WriteMsg(2, "FBOTransmitter2: Compositing Time %d ms", + std::chrono::duration_cast(end - start).count()); +# endif +#if _DEBUG vislib::sys::Log::DefaultLog.WriteInfo("FBOTransmitter2: IceT - Composite Image Done\n"); # endif @@ -256,7 +267,6 @@ void megamol::pbs::FBOTransmitter2::AfterRender(megamol::core::view::AbstractVie this->color_buf_read_->resize(col_buf.size()); this->depth_buf_read_->resize(depth_buf.size()); - #ifdef WITH_MPI // std::copy(col_buf.begin(), col_buf.end(), this->color_buf_read_->begin()); memcpy(this->color_buf_read_->data(), icet_col_buf, width * height * col_buf_el_size_); diff --git a/plugins/pbs/src/PNGWriter.cpp b/plugins/pbs/src/PNGWriter.cpp new file mode 100644 index 0000000000..54609f26ed --- /dev/null +++ b/plugins/pbs/src/PNGWriter.cpp @@ -0,0 +1,148 @@ +#include "PNGWriter.h" +#include "vislib/sys/Path.h" + + +namespace megamol { +namespace pbs { + + /* + * CinematicView::render2file_setup + */ +bool PNGWriter::setup(std::string _fullpath) { + + // init png data struct + this->pngdata.buffer = nullptr; + this->pngdata.ptr = nullptr; + this->pngdata.infoptr = nullptr; + + this->pngdata.path = vislib::sys::Path::GetDirectoryName(_fullpath.c_str()); + this->pngdata.filename = _fullpath; + + vislib::sys::Path::MakeDirectory(this->pngdata.path.c_str()); + + return true; +} + + + + +/* + * PNGWriter::render2file_write_png + */ +bool PNGWriter::render2file() { + + // open final image file + if (!this->pngdata.file.Open(this->pngdata.filename.c_str(), vislib::sys::File::WRITE_ONLY, + vislib::sys::File::SHARE_EXCLUSIVE, vislib::sys::File::CREATE_OVERWRITE)) { + throw vislib::Exception("[PNGWriter] [startAnimRendering] Cannot open output file", __FILE__, __LINE__); + } + + // init png lib + this->pngdata.ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, &this->pngError, &this->pngWarn); + if (this->pngdata.ptr == nullptr) { + throw vislib::Exception("[PNGWriter] [startAnimRendering] Cannot create png structure", __FILE__, __LINE__); + } + this->pngdata.infoptr = png_create_info_struct(this->pngdata.ptr); + if (this->pngdata.infoptr == nullptr) { + throw vislib::Exception("[PNGWriter] [startAnimRendering] Cannot create png info", __FILE__, __LINE__); + } + png_set_write_fn(this->pngdata.ptr, static_cast(&this->pngdata.file), &this->pngWrite, &this->pngFlush); + png_set_IHDR(this->pngdata.ptr, this->pngdata.infoptr, this->pngdata.width, this->pngdata.height, 8, + PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + + if (this->pngdata.buffer == nullptr) { + throw vislib::Exception( + "[PNGWriter] [writeTextureToPng] Failed to create Screenshot: Cannot read image data", __FILE__, __LINE__); + } + + BYTE** rows = nullptr; + try { + rows = new BYTE*[this->pngdata.height]; + for (UINT i = 0; i < this->pngdata.height; i++) { + rows[this->pngdata.height - (1 + i)] = this->pngdata.buffer + this->pngdata.bpp * i * this->pngdata.width; + } + png_set_rows(this->pngdata.ptr, this->pngdata.infoptr, rows); + + png_write_png(this->pngdata.ptr, this->pngdata.infoptr, PNG_TRANSFORM_IDENTITY, nullptr); + + ARY_SAFE_DELETE(rows); + } catch (...) { + if (rows != nullptr) { + ARY_SAFE_DELETE(rows); + } + throw; + } + + if (this->pngdata.ptr != nullptr) { + if (this->pngdata.infoptr != nullptr) { + png_destroy_write_struct(&this->pngdata.ptr, &this->pngdata.infoptr); + } else { + png_destroy_write_struct(&this->pngdata.ptr, (png_infopp) nullptr); + } + } + + try { + this->pngdata.file.Flush(); + } catch (...) { + } + try { + this->pngdata.file.Close(); + } catch (...) { + } + + return true; +} + + +/* + * CinematicView::render2file_finish + */ +bool PNGWriter::finish() { + + if (this->pngdata.ptr != nullptr) { + if (this->pngdata.infoptr != nullptr) { + png_destroy_write_struct(&this->pngdata.ptr, &this->pngdata.infoptr); + } else { + png_destroy_write_struct(&this->pngdata.ptr, (png_infopp) nullptr); + } + } + + try { + this->pngdata.file.Flush(); + } catch (...) { + } + try { + this->pngdata.file.Close(); + } catch (...) { + } + + //ARY_SAFE_DELETE(this->pngdata.buffer); + + vislib::sys::Log::DefaultLog.WriteInfo("[PNGWriter] STOPPED rendering."); + return true; +} + +void PNGWriter::set_buffer(BYTE* _buffer, unsigned _width, unsigned _height, unsigned int _bytesPerPixel = 4) { + if (_buffer != nullptr) { + // Create new byte buffer + this->pngdata.bpp = _bytesPerPixel; + this->pngdata.width = _width; + this->pngdata.height = _height; + + //this->pngdata.buffer = new BYTE[this->pngdata.width * this->pngdata.height * this->pngdata.bpp]; + //if (this->pngdata.buffer == nullptr) { + // throw vislib::Exception( + // "[PNGWriter] [startAnimRendering] Cannot allocate image buffer.", __FILE__, __LINE__); + //} + this->pngdata.buffer = _buffer; + + } else { + this->pngdata.bpp = 0; + this->pngdata.width = 0; + this->pngdata.height = 0; + vislib::sys::Log::DefaultLog.WriteError("[PNGWriter] Input buffer is empty."); + } +} + +} // namespace pbs +} // namespace megamol \ No newline at end of file diff --git a/plugins/pbs/src/PNGWriter.h b/plugins/pbs/src/PNGWriter.h new file mode 100644 index 0000000000..bdde48c85e --- /dev/null +++ b/plugins/pbs/src/PNGWriter.h @@ -0,0 +1,87 @@ +#pragma once +#include "png.h" +#include "vislib/sys/File.h" +#include "vislib/sys/Log.h" +#include "vislib/sys/FastFile.h" +#include +#include + +namespace megamol { +namespace pbs { + +class PNGWriter { +public: + + /** Render to file functions */ + bool setup(std::string _fullpath); + + /** */ + bool render2file(); + + /** */ + bool finish(); + + /** */ + void set_buffer(BYTE* _buffer, unsigned int _width, unsigned int _height, unsigned int _bytesPerPixel); + + /** + * Error handling function for png export + * + * @param pngPtr The png structure pointer + * @param msg The error message + */ + static void PNGAPI pngError(png_structp pngPtr, png_const_charp msg) { + throw vislib::Exception(msg, __FILE__, __LINE__); + } + + /** + * Error handling function for png export + * + * @param pngPtr The png structure pointer + * @param msg The error message + */ + static void PNGAPI pngWarn(png_structp pngPtr, png_const_charp msg) { + vislib::sys::Log::DefaultLog.WriteMsg(vislib::sys::Log::LEVEL_WARN, "Png-Warning: %s\n", msg); + } + + /** + * Write function for png export + * + * @param pngPtr The png structure pointer + * @param buf The pointer to the buffer to be written + * @param size The number of bytes to be written + */ + static void PNGAPI pngWrite(png_structp pngPtr, png_bytep buf, png_size_t size) { + vislib::sys::File* f = static_cast(png_get_io_ptr(pngPtr)); + f->Write(buf, size); + } + + /** + * Flush function for png export + * + * @param pngPtr The png structure pointer + */ + static void PNGAPI pngFlush(png_structp pngPtr) { + vislib::sys::File* f = static_cast(png_get_io_ptr(pngPtr)); + f->Flush(); + } + + + + private: + + struct pngData { + BYTE* buffer = nullptr; + vislib::sys::FastFile file; + unsigned int width; + unsigned int height; + unsigned int bpp; + std::string path; + std::string filename; + png_structp ptr = nullptr; + png_infop infoptr = nullptr; + } pngdata; + +}; +} // namespace pbs +} // namespace megamol \ No newline at end of file diff --git a/plugins/pbs/src/RendernodeView.cpp b/plugins/pbs/src/RendernodeView.cpp index 9d81148811..9cf0996d74 100644 --- a/plugins/pbs/src/RendernodeView.cpp +++ b/plugins/pbs/src/RendernodeView.cpp @@ -9,6 +9,7 @@ #include "mmcore/param/IntParam.h" #include "mmcore/param/StringParam.h" + #include "mmcore/cluster/SyncDataSourcesCall.h" #include "mmcore/cluster/mpi/MpiCall.h" #include "vislib/RawStorageSerialiser.h" @@ -16,7 +17,20 @@ #include "vislib/sys/SystemInformation.h" //#define RV_DEBUG_OUTPUT = 1 - +#define CINEMA = 1 +#define MEASUREMENTS = 1 + +#ifdef CINEMA +# include +# include +# include "PNGWriter.h" +# include "mmcore/view/CallRender3D.h" +# include "vislib/graphics/CameraParamsStore.h" +# include "vislib/sys/Environment.h" +static std::vector _cinemaCams; +static unsigned int _index = 0; +static unsigned int _loopID = 0; +#endif megamol::pbs::RendernodeView::RendernodeView() : request_mpi_slot_("requestMPI", "Requests initialization of MPI and the communicator for the view.") @@ -31,7 +45,7 @@ megamol::pbs::RendernodeView::RendernodeView() , comm_(0x04000000) #endif , rank_(-1) - , bcast_rank_(-2) + , bcast_rank_(0) , comm_size_(0) { request_mpi_slot_.SetCompatibleCall(); this->MakeSlotAvailable(&request_mpi_slot_); @@ -126,6 +140,9 @@ bool megamol::pbs::RendernodeView::process_msgs(Message_t const& msgs) { void megamol::pbs::RendernodeView::Render(const mmcRenderViewContext& context) { +#ifdef MEASUREMENTS + auto const start = std::chrono::high_resolution_clock::now(); +#endif #ifdef WITH_MPI this->initMPI(); // 0 time, 1 instanceTime @@ -173,6 +190,7 @@ void megamol::pbs::RendernodeView::Render(const mmcRenderViewContext& context) { } // initialize rendering + int allFnameDirty = 0; auto ss = this->sync_data_slot_.CallAs(); if (ss != nullptr) { if (!(*ss)(0)) { // check for dirty filenamesslot @@ -180,7 +198,6 @@ void megamol::pbs::RendernodeView::Render(const mmcRenderViewContext& context) { return; } int fnameDirty = ss->getFilenameDirty(); - int allFnameDirty = 0; MPI_Allreduce(&fnameDirty, &allFnameDirty, 1, MPI_INT, MPI_LAND, this->comm_); # ifdef RV_DEBUG_OUTPUT vislib::sys::Log::DefaultLog.WriteInfo("RendernodeView: allFnameDirty: %d", allFnameDirty); @@ -217,12 +234,199 @@ void megamol::pbs::RendernodeView::Render(const mmcRenderViewContext& context) { this->getTileW(), this->getTileH()); } + +# ifdef CINEMA + unsigned int numLoops = 5; + + + // All ranks calculate the camera positions + if (allFnameDirty) { + // we need to call the render callback to get the correct bbox + if (!crv->operator()(core::view::CallRenderView::CALL_RENDER)) { + vislib::sys::Log::DefaultLog.WriteError("RendernodeView: Failed to call render on dependend view."); + } + _index = 0; + auto view = this->getConnectedView(); + core::CallerSlot* crSlot = dynamic_cast(view->FindSlot("rendering")); + if (crSlot == nullptr) return; + core::view::CallRender3D* cr = crSlot->CallAs(); + if (cr == nullptr) return; + + // auto box = cr->GetBoundingBoxes().ObjectSpaceBBox(); + auto box = cr->GetBoundingBoxes().WorldSpaceBBox(); + std::array dims = {box.Width(), box.Height(), box.Depth()}; + unsigned int max_dim = std::distance(dims.begin(), std::max_element(dims.begin(), dims.end())); + + unsigned int num_sections = 16; + unsigned int num_angles = 16; + float length_step_size = box.LongestEdge() / (num_sections - 3); // includes end of cylinder + float angle_step_size = 2 * 3.14159265358979f / num_angles; + vislib::math::Point la; + vislib::math::Point pos; + + std::function(float, float)> parametrization; + float radius; + std::array start; + std::array direction; + //= []() { print_num(42); } + if (max_dim == 0) { // x + radius = std::sqrt(std::pow(box.Height(), 2) + std::pow(box.Depth(), 2)) / 2; + start = {box.GetLeft(), box.GetBottom() + box.Height() / 2, box.GetFront() - box.Depth() / 2}; + direction = {1, 0, 0}; + + parametrization = [](float r, float angle) { + return std::array{0, r * cos(angle), r * sin(angle)}; + }; + + } else if (max_dim == 1) { // y + radius = std::sqrt(std::pow(box.Width(), 2) + std::pow(box.Depth(), 2)) / 2; + start = {box.GetLeft() + box.Width() / 2, box.GetBottom(), box.GetFront() - box.Depth() / 2}; + direction = {0, 1, 0}; + + parametrization = [](float r, float angle) { + return std::array{r * cos(angle), 0, r * sin(angle)}; + }; + } else { // z + radius = std::sqrt(std::pow(box.Height(), 2) + std::pow(box.Width(), 2)) / 2; + start = {box.GetLeft() + box.Width() / 2, box.GetBottom() + box.Height() / 2, box.GetFront()}; + direction = {0, 0, 1}; + + parametrization = [](float r, float angle) { + return std::array{r * cos(angle), r * sin(angle), 0}; + }; + } + + _cinemaCams.resize(num_sections * num_angles); + + float radius_offset_cylinder = 5.0f; + float radius_offset_spheres = 2.0f; + + // start sphere + for (unsigned int j = 0; j < num_angles; j++) { + for (unsigned int n = 0; n < 3; n++) { + + pos[n] = start[n] + parametrization((radius + radius_offset_spheres), angle_step_size * j)[n] + + -(radius + radius_offset_spheres) * direction[n]; + + la[n] = start[n]; + } + _cinemaCams[j].SetView(pos, la, {direction[0], direction[1], direction[2]}); + } + + + // middle part + for (unsigned int i = 0; i < num_sections - 2; i++) { + for (unsigned int j = 0; j < num_angles; j++) { + for (unsigned int n = 0; n < 3; n++) { + + pos[n] = start[n] + parametrization(radius + radius_offset_cylinder, angle_step_size * j)[n] + + length_step_size * i * direction[n]; + + la[n] = start[n] + length_step_size * i * direction[n]; + } + _cinemaCams[(i + 1) * num_angles + j].SetView(pos, la, {direction[0], direction[1], direction[2]}); + } + } + + // end sphere + for (unsigned int j = 0; j < num_angles; j++) { + for (unsigned int n = 0; n < 3; n++) { + + pos[n] = start[n] + parametrization((radius + radius_offset_spheres), angle_step_size * j)[n] + + (radius + radius_offset_spheres + box.LongestEdge()) * direction[n]; + + la[n] = start[n] + box.LongestEdge() * direction[n]; + } + _cinemaCams[(num_sections - 1) * (num_angles) + j].SetView( + pos, la, {direction[0], direction[1], direction[2]}); + } + + } + if (!_cinemaCams.empty()) { + auto view = this->getConnectedView(); + core::CallerSlot* crSlot = dynamic_cast(view->FindSlot("rendering")); + if (crSlot == nullptr) return; + core::view::CallRender3D* cr = crSlot->CallAs(); + if (cr == nullptr) return; + + // std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + if (_index < _cinemaCams.size()) { + cr->SetCameraView( + _cinemaCams[_index].Position(), _cinemaCams[_index].LookAt(), _cinemaCams[_index].Up()); + } + } +# endif + + crv->SetOutputBuffer(GL_BACK, this->getViewportWidth(), this->getViewportHeight()); if (!crv->operator()(core::view::CallRenderView::CALL_RENDER)) { vislib::sys::Log::DefaultLog.WriteError("RendernodeView: Failed to call render on dependend view."); } + +# ifdef CINEMA + if (!_cinemaCams.empty()) { + std::stringstream _path; + std::stringstream _filename; + if (_index >= 0 && this->rank_ == bcast_rank_) { + + + _filename << _loopID << "_" << std::setfill('0') << std::setw(3) << _index << ".png"; + +# ifndef _WIN32 + _path << "/dev/shm/"; + // get job number + std::string jobID = std::string(vislib::sys::Environment::GetVariable("SLURM_JOB_ID")); + if (jobID.empty()) jobID = "test"; + _path << jobID << "/"; +# endif + + + // read FBO + std::vector col_buf(crv->ViewportWidth() * crv->ViewportHeight() * 3); + glReadPixels( + 0, 0, crv->ViewportWidth(), crv->ViewportHeight(), GL_RGB, GL_UNSIGNED_BYTE, col_buf.data()); + + try { + PNGWriter png_writer; + png_writer.setup((_path.str() + _filename.str()).c_str()); + png_writer.set_buffer( + reinterpret_cast(col_buf.data()), crv->ViewportWidth(), crv->ViewportHeight(), 3); + png_writer.render2file(); + png_writer.finish(); + } catch (...) { + vislib::sys::Log::DefaultLog.WriteError("RendernodeView: Exception while writing PNG\n"); + } + } + + + if (_index >= _cinemaCams.size() - 1) { + _index = 0; + _loopID++; + } else { + _index++; + } + + if (_loopID + 1 >= numLoops) { + vislib::sys::Log::DefaultLog.WriteInfo("RendernodeView: All screenshots taken. Shutting down."); +# ifndef _WIN32 + if (this->rank_ == bcast_rank_) { + const std::string scratch = std::string(vislib::sys::Environment::GetVariable("SCRATCH")) + "/"; + std::stringstream move_command; + move_command << "mv " << _path.str() << " " << scratch; + ::system(move_command.str().c_str()); + } +# endif + this->GetCoreInstance()->Shutdown(); + return; + } + } + +# endif // CINEMA + + glFinish(); } else { # ifdef RV_DEBUG_OUTPUT @@ -233,6 +437,12 @@ void megamol::pbs::RendernodeView::Render(const mmcRenderViewContext& context) { // sync barrier MPI_Barrier(this->comm_); #endif + +#ifdef MEASUREMENTS + auto const end = std::chrono::high_resolution_clock::now(); + vislib::sys::Log::DefaultLog.WriteMsg(2, "RendernodeView: Rendering Time %d ms", + std::chrono::duration_cast(end - start).count()); +#endif }