Skip to content

Commit

Permalink
Merge pull request #889 from UniStuttgart-VISUS/frontend_view_render_…
Browse files Browse the repository at this point in the history
…input

Frontend: Tiling + Explicit View Render Input
  • Loading branch information
tobiasrau authored Nov 3, 2021
2 parents 6c24062 + 294f29f commit 492852a
Show file tree
Hide file tree
Showing 15 changed files with 362 additions and 108 deletions.
5 changes: 5 additions & 0 deletions core/include/mmcore/view/AbstractView.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ class MEGAMOLCORE_API AbstractView : public Module, public megamol::frontend_res
*/
virtual void SetCamera(Camera camera, bool isMutable = true);

/**
* Return the current camera
*/
virtual Camera GetCamera() const;

/**
* ...
*/
Expand Down
15 changes: 4 additions & 11 deletions core/include/mmcore/view/AbstractView_EventConsumption.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

#include "AbstractView.h"
#include "FrontendResource.h"

#include "RenderInput.h"
#include "ImageWrapper.h"

namespace megamol {
Expand All @@ -23,28 +25,19 @@ namespace view {
MEGAMOLCORE_API void view_consume_keyboard_events(AbstractView& view, megamol::frontend::FrontendResource const& resource);
MEGAMOLCORE_API void view_consume_mouse_events(AbstractView& view, megamol::frontend::FrontendResource const& resource);
MEGAMOLCORE_API void view_consume_window_events(AbstractView& view, megamol::frontend::FrontendResource const& resource);
MEGAMOLCORE_API void view_consume_framebuffer_events(AbstractView & view, megamol::frontend::FrontendResource const& resource);
MEGAMOLCORE_API void view_poke_rendering(AbstractView& view, megamol::frontend_resources::ImageWrapper& result_image);
MEGAMOLCORE_API void view_poke_rendering(AbstractView& view, megamol::frontend_resources::RenderInput const& render_input, megamol::frontend_resources::ImageWrapper& result_image);

// to do this right we should be able to as a view object which runtime resources it expects (keyboard inputs, gl context)
// and just pass those resources to the view when rendering a frame
// until we implement the 'optimal' approach, this is the best we can do
MEGAMOLCORE_API std::vector<std::string> get_gl_view_runtime_resources_requests();
MEGAMOLCORE_API std::vector<std::string> get_view_runtime_resources_requests();

MEGAMOLCORE_API bool view_rendering_execution(
void* module_ptr
, std::vector<megamol::frontend::FrontendResource> const& resources
, megamol::frontend_resources::ImageWrapper& result_image
);

// before rendering the first frame views need to know the current framebuffer size
// because they may have beed added to the graph after the initial framebuffer size event, we need this init callback to give them that info
MEGAMOLCORE_API bool view_init_rendering_state(
void* module_ptr
, std::vector<megamol::frontend::FrontendResource> const& resources
, megamol::frontend_resources::ImageWrapper& result_image
);

} /* end namespace view */
} /* end namespace core */
} /* end namespace megamol */
Expand Down
4 changes: 4 additions & 0 deletions core/src/view/AbstractView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ void megamol::core::view::AbstractView::SetCamera(Camera camera, bool isMutable)
_cameraIsMutable = isMutable;
}

megamol::core::view::Camera megamol::core::view::AbstractView::GetCamera() const {
return _camera;
}

void megamol::core::view::AbstractView::CalcCameraClippingPlanes(float border) {
if (_cameraIsMutable) {
auto cam_pose = _camera.get<Camera::Pose>();
Expand Down
116 changes: 48 additions & 68 deletions core/src/view/AbstractView_EventConsumption.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#include "Framebuffer_Events.h"
#include "KeyboardMouse_Events.h"
#include "Window_Events.h"
#include "IOpenGL_Context.h"

#include <chrono>

Expand Down Expand Up @@ -59,38 +58,62 @@ void view_consume_window_events(AbstractView& view, megamol::frontend::FrontendR
}
}

void view_consume_framebuffer_events(AbstractView& view, megamol::frontend::FrontendResource const& resource) {
GET_RESOURCE(FramebufferEvents)//{
for (auto& e: events.size_events)
view.Resize(static_cast<unsigned int>(e.width), static_cast<unsigned int>(e.height));
}
}

// this is a weird place to measure passed program time, but we do it here so we satisfy _mmcRenderViewContext and nobody else needs to know
static std::chrono::high_resolution_clock::time_point render_view_context_timer_start;

void view_poke_rendering(AbstractView& view, megamol::frontend_resources::ImageWrapper& result_image) {
void view_poke_rendering(AbstractView& view, megamol::frontend_resources::RenderInput const& render_input, megamol::frontend_resources::ImageWrapper& result_image) {
static bool started_timer = false;
if (!started_timer) {
render_view_context_timer_start = std::chrono::high_resolution_clock::now();
started_timer = true;
}

const auto render = [&]() {
const double instanceTime = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - render_view_context_timer_start)
.count() / static_cast<double>(1000);
const double instanceTime_sec = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now() - render_view_context_timer_start)
.count() / static_cast<double>(1000);

const double time_sec = view.DefaultTime(instanceTime_sec);

// copy render inputs from frontend so we can update time
auto renderinput = render_input;

renderinput.instanceTime_sec = instanceTime_sec;
renderinput.time_sec = time_sec;

view.Resize(renderinput.local_view_framebuffer_resolution.x, renderinput.local_view_framebuffer_resolution.y);

auto camera = view.GetCamera();

Camera::AspectRatio aspect = {renderinput.global_framebuffer_resolution.x / static_cast<float>(renderinput.global_framebuffer_resolution.y)};
Camera::ImagePlaneTile tile = {renderinput.local_tile_relative_begin, renderinput.local_tile_relative_end};

switch (camera.getProjectionType()) {
case Camera::ProjectionType::ORTHOGRAPHIC:{
auto intrinsics = camera.get<Camera::OrthographicParameters>();
intrinsics.aspect = aspect;
intrinsics.image_plane_tile = tile;
camera.setOrthographicProjection(intrinsics);
break;
}
case Camera::ProjectionType::PERSPECTIVE: {
auto intrinsics = camera.get<Camera::PerspectiveParameters>();
intrinsics.aspect = aspect;
intrinsics.image_plane_tile = tile;
camera.setPerspectiveProjection(intrinsics);
break;
}
case Camera::ProjectionType::UNKNOWN:
default:
break;
}

const double time = view.DefaultTime(instanceTime);
view.SetCamera(camera);

result_image = view.Render(time, instanceTime);
};

render();
result_image = view.Render(renderinput.time_sec, renderinput.instanceTime_sec);
}

std::vector<std::string> get_gl_view_runtime_resources_requests() {
return {"KeyboardEvents", "MouseEvents", "WindowEvents", "FramebufferEvents"};
std::vector<std::string> get_view_runtime_resources_requests() {
return {"ViewRenderInput", "KeyboardEvents", "MouseEvents", "WindowEvents"};
}

bool view_rendering_execution(
Expand All @@ -108,58 +131,15 @@ bool view_rendering_execution(

megamol::core::view::AbstractView& view = *view_ptr;

// resources are in order of initial requests from get_gl_view_runtime_resources_requests()
megamol::core::view::view_consume_keyboard_events(view, resources[0]);
megamol::core::view::view_consume_mouse_events(view, resources[1]);
megamol::core::view::view_consume_window_events(view, resources[2]);
megamol::core::view::view_consume_framebuffer_events(view, resources[3]);
megamol::core::view::view_poke_rendering(view, result_image);
// resources are in order of initial requests from get_view_runtime_resources_requests()
megamol::core::view::view_consume_keyboard_events(view, resources[1]);
megamol::core::view::view_consume_mouse_events(view, resources[2]);
megamol::core::view::view_consume_window_events(view, resources[3]);
megamol::core::view::view_poke_rendering(view, resources[0].getResource<megamol::frontend_resources::RenderInput>(), result_image);

return true;
}

bool view_init_rendering_state(
void* module_ptr
, std::vector<megamol::frontend::FrontendResource> const& resources
, megamol::frontend_resources::ImageWrapper& result_image
) {
megamol::core::view::AbstractView* view_ptr =
dynamic_cast<megamol::core::view::AbstractView*>(static_cast<megamol::core::Module*>(module_ptr));

if (!view_ptr) {
std::cout << "error. module is not a view module. could not use as rendering entry point." << std::endl;
return false;
}

megamol::core::view::AbstractView& view = *view_ptr;

// fake resize events for view to consume
auto& framebuffer_events = const_cast<megamol::frontend_resources::FramebufferEvents&>(resources[3].getResource<megamol::frontend_resources::FramebufferEvents>());
auto& framebuffer_size = framebuffer_events.previous_state;
framebuffer_events.size_events.push_back(framebuffer_size);

auto& window_events = const_cast<megamol::frontend_resources::WindowEvents&>(resources[2].getResource<megamol::frontend_resources::WindowEvents>());
auto& window_width = window_events.previous_state.width;
auto& window_height = window_events.previous_state.height;
if(window_width <= 1 || window_height <= 1) {
window_width = framebuffer_size.width;
window_height = framebuffer_size.height;
}
window_events.size_events.push_back({window_width, window_height});

auto& mouse_events = const_cast<megamol::frontend_resources::MouseEvents&>(resources[1].getResource<megamol::frontend_resources::MouseEvents>());
mouse_events.position_events.push_back({
mouse_events.previous_state.x_cursor_position,
mouse_events.previous_state.y_cursor_position});

// resources are in order of initial requests from get_gl_view_runtime_resources_requests()
megamol::core::view::view_consume_mouse_events(view, resources[1]);
megamol::core::view::view_consume_window_events(view, resources[2]);
megamol::core::view::view_consume_framebuffer_events(view, resources[3]);

return true;
}

} /* end namespace view */
} /* end namespace core */
} /* end namespace megamol */
Expand Down
45 changes: 45 additions & 0 deletions frontend/main/src/CLIConfigParsing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ static std::string remote_rendernode_zmq_source_option = "rendernode-zmq-source"
static std::string remote_headnode_broadcast_quit_option = "headnode-broadcast-quit";
static std::string remote_headnode_broadcast_project_option = "headnode-broadcast-project";
static std::string remote_headnode_connect_at_start_option = "headnode-connect-at-start";
static std::string framebuffer_option = "framebuffer";
static std::string viewport_tile_option = "tile";
static std::string help_option = "h,help";

static void files_exist(std::vector<std::string> vec, std::string const& type) {
Expand Down Expand Up @@ -412,6 +414,44 @@ static void window_handler(std::string const& option_name, cxxopts::ParseResult
}
};

static void framebuffer_handler(std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config)
{
auto string = parsed_options[option_name].as<std::string>();
// WIDTHxHEIGHT
std::regex geometry("(\\d+)x(\\d+)");
std::smatch match;
if (std::regex_match(string, match, geometry)) {
unsigned int width = std::stoul(match[1].str(), nullptr, 10);
unsigned int height = std::stoul(match[2].str(), nullptr, 10);
config.local_framebuffer_resolution = {{width, height}};
} else {
exit("framebuffer option needs to be in the following format: WIDTHxHEIGHT, e.g. 200x100");
}
};

static void viewport_tile_handler(std::string const& option_name, cxxopts::ParseResult const& parsed_options, RuntimeConfig& config)
{
auto string = parsed_options[option_name].as<std::string>();
// x,y;LWIDTHxLHEIGHT;GWIDTHxGHEIGHT
std::regex geometry("(\\d+),(\\d+):(\\d+)x(\\d+):(\\d+)x(\\d+)");
std::smatch match;
if (std::regex_match(string, match, geometry)) {
using UintPair = std::pair<unsigned int, unsigned int>;

auto read_pair = [&](int index) {
return UintPair{std::stoul(match[index].str(), nullptr, 10), std::stoul(match[index+1].str(), nullptr, 10)};
};

UintPair start_pixel = read_pair(1);
UintPair local_resolution = read_pair(3);
UintPair global_resolution = read_pair(5);

config.local_viewport_tile = {global_resolution, start_pixel, local_resolution};
} else {
exit("viewport tile option needs to be in the following format: x,y:LWIDTHxLHEIGHT:GWIDTHxGHEIGHT, e.g. 0,0:200x100:400x200");
}
};

using OptionsListEntry = std::tuple<std::string, std::string, std::shared_ptr<cxxopts::Value>, std::function<void(std::string const&, cxxopts::ParseResult const&, megamol::frontend::RuntimeConfig&)>>;

std::vector<OptionsListEntry> cli_options_list =
Expand Down Expand Up @@ -453,6 +493,11 @@ std::vector<OptionsListEntry> cli_options_list =
, {remote_headnode_broadcast_quit_option, "Headnode broadcasts mmQuit to rendernodes on shutdown", cxxopts::value<bool>(), remote_head_broadcast_quit_handler}
, {remote_headnode_broadcast_project_option,"Headnode broadcasts initial graph state after project loading at startup", cxxopts::value<bool>(), remote_head_broadcast_project_handler}
, {remote_headnode_connect_at_start_option, "Headnode starts sender thread at startup", cxxopts::value<bool>(), remote_head_connect_at_start_handler}
, {framebuffer_option, "Size of framebuffer, syntax: --framebuffer WIDTHxHEIGHT", cxxopts::value<std::string>(), framebuffer_handler}
, {viewport_tile_option, "Geometry of local viewport tile, syntax: --tile x,y:LWIDTHxLHEIGHT:GWIDTHxGHEIGHT"
"where x,y is the lower left start pixel of the local tile, "
"LWIDTHxLHEIGHT is the local framebuffer resolution, "
"GWIDTHxGHEIGHT is the global framebuffer resolution", cxxopts::value<std::string>(), viewport_tile_handler}
, {help_option, "Print help message", cxxopts::value<bool>(), empty_handler}
};

Expand Down
17 changes: 11 additions & 6 deletions frontend/main/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@
#include "Remote_Service.hpp"
#include "Profiling_Service.hpp"

#include <glad/glad.h> /// XXX see temporary fix below


static void log(std::string const& text) {
const std::string msg = "Main: " + text;
Expand Down Expand Up @@ -130,11 +128,20 @@ int main(const int argc, const char** argv) {

megamol::frontend::ImagePresentation_Service imagepresentation_service;
megamol::frontend::ImagePresentation_Service::Config imagepresentationConfig;
imagepresentationConfig.local_framebuffer_resolution = config.local_framebuffer_resolution;
imagepresentationConfig.local_viewport_tile = config.local_viewport_tile.has_value()
? std::make_optional(megamol::frontend::ImagePresentation_Service::Config::Tile{
config.local_viewport_tile.value().global_framebuffer_resolution,
config.local_viewport_tile.value().tile_start_pixel,
config.local_viewport_tile.value().tile_resolution
})
: std::nullopt;
imagepresentation_service.setPriority(3);

megamol::frontend::Command_Service command_service;
#ifdef PROFILING
megamol::frontend::Profiling_Service profiling_service;
#endif
imagepresentation_service.setPriority(3); // before render: do things after GL; post render: do things before GL
megamol::frontend::Command_Service command_service;
#ifdef MM_CUDA_ENABLED
megamol::frontend::CUDA_Service cuda_service;
cuda_service.setPriority(24);
Expand Down Expand Up @@ -232,8 +239,6 @@ int main(const int argc, const char** argv) {

imagepresentation_service.RenderNextFrame(); // executes graph views, those digest input events like keyboard/mouse, then render

/// XXX temporary fix to make sure that everything that happens post-draw ends up in default window fbo...
glBindFramebuffer(GL_FRAMEBUFFER, 0);
services.postGraphRender(); // render GUI, glfw swap buffers, stop frame timer
}

Expand Down
4 changes: 3 additions & 1 deletion frontend/resources/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
# # afterwards list the dependencies.
# set(DEP_LIST "${DEP_LIST};BUILD_MMSTD_DATATOOLS_PLUGIN BUILD_CORE" CACHE INTERNAL "")

require_external(glm)

require_external(json)
if (ENABLE_CUESDK)
require_external(CUESDK)
Expand All @@ -35,7 +37,7 @@ target_include_directories(${PROJECT_NAME} PUBLIC
#$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/>
#$<INSTALL_INTERFACE:include/>
)
target_link_libraries(${PROJECT_NAME} PUBLIC json)
target_link_libraries(${PROJECT_NAME} PUBLIC json glm)
if (ENABLE_CUESDK)
target_link_libraries(${PROJECT_NAME} PUBLIC CUESDK)
endif()
Expand Down
33 changes: 33 additions & 0 deletions frontend/resources/include/RenderInput.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* RenderInput.h
*
* Copyright (C) 2021 by VISUS (Universitaet Stuttgart).
* Alle Rechte vorbehalten.
*/

#pragma once

#include <glm/glm.hpp>
#include <optional>

namespace megamol {
namespace frontend_resources {

struct RenderInput {
glm::uvec2 global_framebuffer_resolution{0, 0}; //< e.g. overall powerwall resolution when tiling, not intended to be used by view
glm::uvec2 local_view_framebuffer_resolution{0, 0}; //< resolution with which the View should render into own framebuffer

glm::dvec2 local_tile_relative_begin{0.0, 0.0}, local_tile_relative_end{1.0, 1.0}; //< relative start and end of image tile in global framebuffer this view is rendering with local resolution. local resolution and effective tile resolution can differ. the view framebuffer should have local resolution.

struct CameraMatrices {
glm::mat4 view;
glm::mat4 projection;
};
std::optional<CameraMatrices> camera_matrices_override = std::nullopt; //< if camera matrices are overridden, this view still needs to render in local resolution

double instanceTime_sec = 0.0; //< monotone high resolution time in seconds since first frame rendering of some (any) view
double time_sec = 0.0; //< time computed by view::TimeControl(instanceTime)
};

} /* end namespace frontend_resources */
} /* end namespace megamol */
Loading

0 comments on commit 492852a

Please sign in to comment.