/* Copyright (C) 2014-2020, Johannes Pekkila, Miikka Vaisala. This file is part of Astaroth. Astaroth 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 3 of the License, or (at your option) any later version. Astaroth 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 General Public License for more details. You should have received a copy of the GNU General Public License along with Astaroth. If not, see . */ /** * @file * \brief Brief info. * * Detailed info. * */ #include "run.h" #if AC_BUILD_RT_VISUALIZATION #include // Note: using local version in src/3rdparty dir #include // ceil #include // memcpy #include "config_loader.h" #include "errchk.h" #include "math_utils.h" #include "model/host_forcing.h" #include "model/host_memory.h" #include "model/host_timestep.h" #include "model/model_reduce.h" #include "model/model_rk3.h" #include "timer_hires.h" // NEED TO BE DEFINED HERE. IS NOT NOTICED BY compile_acc call. #define LFORCING (1) #define LSINK (0) // Window SDL_Renderer* renderer = NULL; static SDL_Window* window = NULL; static int window_width = 800; static int window_height = 600; static const int window_bpp = 32; // Bits per pixel // Surfaces SDL_Surface* surfaces[NUM_VTXBUF_HANDLES]; static int datasurface_width = -1; static int datasurface_height = -1; static int k_slice = 0; static int k_slice_max = 0; // Colors static SDL_Color color_bg = (SDL_Color){30, 30, 35, 255}; static const int num_tiles = NUM_VTXBUF_HANDLES + 1; static const int tiles_per_row = 3; /* * ============================================================================= * Camera * ============================================================================= */ /* typedef struct { float x, y; } float2; */ typedef struct { float x, y, w, h; } vec4; typedef struct { float2 pos; float scale; } Camera; static Camera camera = (Camera){(float2){.0f, .0f}, 1.f}; static inline vec4 project_ortho(const float2& pos, const float2& bbox, const float2& wdims) { const vec4 rect = (vec4){camera.scale * (pos.x - camera.pos.x) + 0.5f * wdims.x, camera.scale * (pos.y - camera.pos.y) + 0.5f * wdims.y, camera.scale * bbox.x, camera.scale * bbox.y}; return rect; } /* * ============================================================================= * Renderer * ============================================================================= */ static int renderer_init(const int& mx, const int& my) { // Init video SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_EVENTS); // Setup window window = SDL_CreateWindow("Astaroth", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, window_width, window_height, SDL_WINDOW_SHOWN); ERRCHK_ALWAYS(window); // Setup SDL renderer renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); // SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP); SDL_GetWindowSize(window, &window_width, &window_height); SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); // Linear filtering datasurface_width = mx; datasurface_height = my; // vec drawing uses the surface of the first component, no memory issues here for (int i = 0; i < NUM_VTXBUF_HANDLES; ++i) surfaces[i] = SDL_CreateRGBSurfaceWithFormat(0, datasurface_width, datasurface_height, window_bpp, SDL_PIXELFORMAT_RGBA8888); camera.pos = (float2){.5f * tiles_per_row * datasurface_width - .5f * datasurface_width, -.5f * (num_tiles / tiles_per_row) * datasurface_height + .5f * datasurface_height}; camera.scale = min(window_width / float(datasurface_width * tiles_per_row), window_height / float(datasurface_height * (num_tiles / tiles_per_row))); SDL_RendererInfo renderer_info; SDL_GetRendererInfo(renderer, &renderer_info); printf("SDL renderer max texture dims: (%d, %d)\n", renderer_info.max_texture_width, renderer_info.max_texture_height); return 0; } static int set_pixel(const int& i, const int& j, const uint32_t& color, SDL_Surface* surface) { uint32_t* pixels = (uint32_t*)surface->pixels; pixels[i + j * surface->w] = color; return 0; } static int draw_vertex_buffer(const AcMesh& mesh, const VertexBufferHandle& vertex_buffer, const int& tile) { const float xoffset = (tile % tiles_per_row) * datasurface_width; const float yoffset = -(tile / tiles_per_row) * datasurface_height; /* const float max = float(model_reduce_scal(mesh, RTYPE_MAX, vertex_buffer)); const float min = float(model_reduce_scal(mesh, RTYPE_MIN, vertex_buffer)); */ const float max = float(acReduceScal(RTYPE_MAX, vertex_buffer)); const float min = float(acReduceScal(RTYPE_MIN, vertex_buffer)); const float range = fabsf(max - min); const float mid = max - .5f * range; const int k = k_slice; // mesh.info.int_params[AC_mz] / 2; for (int j = 0; j < mesh.info.int_params[AC_my]; ++j) { for (int i = 0; i < mesh.info.int_params[AC_mx]; ++i) { ERRCHK(i < datasurface_width && j < datasurface_height); const int idx = acVertexBufferIdx(i, j, k, mesh.info); const uint8_t shade = (uint8_t)( 255.f * (fabsf(float(mesh.vertex_buffer[vertex_buffer][idx]) - mid)) / range); uint8_t color[4] = {0, 0, 0, 255}; color[tile % 3] = shade; const uint32_t mapped_color = SDL_MapRGBA(surfaces[vertex_buffer]->format, color[0], color[1], color[2], color[3]); set_pixel(i, j, mapped_color, surfaces[vertex_buffer]); } } const float2 pos = (float2){xoffset, yoffset}; const float2 bbox = (float2){.5f * datasurface_width, .5f * datasurface_height}; const float2 wsize = (float2){float(window_width), float(window_height)}; const vec4 rectf = project_ortho(pos, bbox, wsize); SDL_Rect rect = (SDL_Rect){int(rectf.x - rectf.w), int(wsize.y - rectf.y - rectf.h), int(ceil(2.f * rectf.w)), int(ceil(2.f * rectf.h))}; SDL_Texture* tex = SDL_CreateTextureFromSurface(renderer, surfaces[vertex_buffer]); SDL_RenderCopy(renderer, tex, NULL, &rect); SDL_DestroyTexture(tex); return 0; } static int draw_vertex_buffer_vec(const AcMesh& mesh, const VertexBufferHandle& vertex_buffer_a, const VertexBufferHandle& vertex_buffer_b, const VertexBufferHandle& vertex_buffer_c, const int& tile) { const float xoffset = (tile % tiles_per_row) * datasurface_width; const float yoffset = -(tile / tiles_per_row) * datasurface_height; /* const float maxx = float( max(model_reduce_scal(mesh, RTYPE_MAX, vertex_buffer_a), max(model_reduce_scal(mesh, RTYPE_MAX, vertex_buffer_b), model_reduce_scal(mesh, RTYPE_MAX, vertex_buffer_c)))); const float minn = float( min(model_reduce_scal(mesh, RTYPE_MIN, vertex_buffer_a), min(model_reduce_scal(mesh, RTYPE_MIN, vertex_buffer_b), model_reduce_scal(mesh, RTYPE_MIN, vertex_buffer_c)))); */ const float maxx = float(max( acReduceScal(RTYPE_MAX, vertex_buffer_a), max(acReduceScal(RTYPE_MAX, vertex_buffer_b), acReduceScal(RTYPE_MAX, vertex_buffer_c)))); const float minn = float(min( acReduceScal(RTYPE_MIN, vertex_buffer_a), min(acReduceScal(RTYPE_MIN, vertex_buffer_b), acReduceScal(RTYPE_MIN, vertex_buffer_c)))); const float range = fabsf(maxx - minn); const float mid = maxx - .5f * range; const int k = k_slice; // mesh.info.int_params[AC_mz] / 2; for (int j = 0; j < mesh.info.int_params[AC_my]; ++j) { for (int i = 0; i < mesh.info.int_params[AC_mx]; ++i) { ERRCHK(i < datasurface_width && j < datasurface_height); const int idx = acVertexBufferIdx(i, j, k, mesh.info); const uint8_t r = (uint8_t)( 255.f * (fabsf(float(mesh.vertex_buffer[vertex_buffer_a][idx]) - mid)) / range); const uint8_t g = (uint8_t)( 255.f * (fabsf(float(mesh.vertex_buffer[vertex_buffer_b][idx]) - mid)) / range); const uint8_t b = (uint8_t)( 255.f * (fabsf(float(mesh.vertex_buffer[vertex_buffer_c][idx]) - mid)) / range); const uint32_t mapped_color = SDL_MapRGBA(surfaces[vertex_buffer_a]->format, r, g, b, 255); set_pixel(i, j, mapped_color, surfaces[vertex_buffer_a]); } } const float2 pos = (float2){xoffset, yoffset}; const float2 bbox = (float2){.5f * datasurface_width, .5f * datasurface_height}; const float2 wsize = (float2){float(window_width), float(window_height)}; const vec4 rectf = project_ortho(pos, bbox, wsize); SDL_Rect rect = (SDL_Rect){int(rectf.x - rectf.w), int(wsize.y - rectf.y - rectf.h), int(ceil(2.f * rectf.w)), int(ceil(2.f * rectf.h))}; SDL_Texture* tex = SDL_CreateTextureFromSurface(renderer, surfaces[vertex_buffer_a]); SDL_RenderCopy(renderer, tex, NULL, &rect); SDL_DestroyTexture(tex); return 0; } static int renderer_draw(const AcMesh& mesh) { for (int i = 0; i < NUM_VTXBUF_HANDLES; ++i) draw_vertex_buffer(mesh, VertexBufferHandle(i), i); draw_vertex_buffer_vec(mesh, VTXBUF_UUX, VTXBUF_UUY, VTXBUF_UUZ, NUM_VTXBUF_HANDLES); // Drawing done, present SDL_RenderPresent(renderer); SDL_SetRenderDrawColor(renderer, color_bg.r, color_bg.g, color_bg.b, color_bg.a); SDL_RenderClear(renderer); for (int i = 0; i < NUM_VTXBUF_HANDLES; ++i) { const VertexBufferHandle vertex_buffer = VertexBufferHandle(i); /* printf("\t%s umax %e, min %e\n", vtxbuf_names[vertex_buffer], (double)model_reduce_scal(mesh, RTYPE_MAX, vertex_buffer), (double)model_reduce_scal(mesh, RTYPE_MIN, vertex_buffer)); */ printf("\t%s umax %e, min %e\n", vtxbuf_names[vertex_buffer], (double)acReduceScal(RTYPE_MAX, vertex_buffer), (double)acReduceScal(RTYPE_MIN, vertex_buffer)); } printf("\n"); return 0; } static int renderer_quit(void) { for (int i = 0; i < NUM_VTXBUF_HANDLES; ++i) SDL_FreeSurface(surfaces[i]); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); renderer = NULL; window = NULL; SDL_Quit(); return 0; } static int init_type = INIT_TYPE_GAUSSIAN_RADIAL_EXPL; // static int init_type = INIT_TYPE_SIMPLE_CORE; static bool running(AcMesh* mesh) { SDL_Event e; while (SDL_PollEvent(&e)) { if (e.type == SDL_QUIT) { return false; } else if (e.type == SDL_KEYDOWN) { if (e.key.keysym.sym == SDLK_ESCAPE) return false; if (e.key.keysym.sym == SDLK_SPACE) { init_type = (init_type + 1) % NUM_INIT_TYPES; acmesh_init_to(InitType(init_type), mesh); acLoad(*mesh); } if (e.key.keysym.sym == SDLK_i) { k_slice = (k_slice + 1) % k_slice_max; printf("k_slice %d\n", k_slice); } if (e.key.keysym.sym == SDLK_k) { k_slice = (k_slice - 1 + k_slice_max) % k_slice_max; printf("k_slice %d\n", k_slice); } } } return true; } static void check_input(const float& dt) { /* Camera movement */ const float camera_translate_rate = 1000.f / camera.scale; const float camera_scale_rate = 1.0001f; const uint8_t* keystates = (uint8_t*)SDL_GetKeyboardState(NULL); if (keystates[SDL_SCANCODE_UP]) camera.pos.y += camera_translate_rate * dt; if (keystates[SDL_SCANCODE_DOWN]) camera.pos.y -= camera_translate_rate * dt; if (keystates[SDL_SCANCODE_LEFT]) camera.pos.x -= camera_translate_rate * dt; if (keystates[SDL_SCANCODE_RIGHT]) camera.pos.x += camera_translate_rate * dt; if (keystates[SDL_SCANCODE_PAGEUP]) camera.scale += camera.scale * camera_scale_rate * dt; if (keystates[SDL_SCANCODE_PAGEDOWN]) camera.scale -= camera.scale * camera_scale_rate * dt; if (keystates[SDL_SCANCODE_COMMA]) set_timescale(AcReal(.1)); if (keystates[SDL_SCANCODE_PERIOD]) set_timescale(AcReal(1.)); } int run_renderer(const char* config_path) { /* Parse configs */ AcMeshInfo mesh_info; load_config(config_path, &mesh_info); renderer_init(mesh_info.int_params[AC_mx], mesh_info.int_params[AC_my]); AcMesh* mesh = acmesh_create(mesh_info); acmesh_init_to(InitType(init_type), mesh); #if LSINK vertex_buffer_set(VTXBUF_ACCRETION, 0.0, mesh); #endif acInit(mesh_info); acLoad(*mesh); Timer frame_timer; timer_reset(&frame_timer); Timer wallclock; timer_reset(&wallclock); Timer io_timer; timer_reset(&io_timer); const float desired_frame_time = 1.f / 60.f; int steps = 0; k_slice = mesh->info.int_params[AC_mz] / 2; k_slice_max = mesh->info.int_params[AC_mz]; #if LSINK AcReal accreted_mass = 0.0; #endif while (running(mesh)) { /* Input */ check_input(timer_diff_nsec(io_timer) / 1e9f); timer_reset(&io_timer); /* Step the simulation */ #if LSINK const AcReal sum_mass = acReduceScal(RTYPE_SUM, VTXBUF_ACCRETION); accreted_mass = accreted_mass + sum_mass; AcReal sink_mass = mesh_info.real_params[AC_M_sink_init] + accreted_mass; printf("sink mass is: %e \n", sink_mass); printf("accreted mass is: %e \n", accreted_mass); acLoadDeviceConstant(AC_M_sink, sink_mass); vertex_buffer_set(VTXBUF_ACCRETION, 0.0, mesh); #endif #if LFORCING const ForcingParams forcing_params = generateForcingParams(mesh->info); loadForcingParamsToDevice(forcing_params); #endif #if 1 const AcReal umax = acReduceVec(RTYPE_MAX, VTXBUF_UUX, VTXBUF_UUY, VTXBUF_UUZ); const AcReal dt = host_timestep(umax, mesh_info); acIntegrate(dt); #else ModelMesh* model_mesh = modelmesh_create(mesh->info); const AcReal umax = AcReal( model_reduce_vec(*model_mesh, RTYPE_MAX, VTXBUF_UUX, VTXBUF_UUY, VTXBUF_UUZ)); const AcReal dt = host_timestep(umax, mesh_info); acmesh_to_modelmesh(*mesh, model_mesh); model_rk3(dt, model_mesh); modelmesh_to_acmesh(*model_mesh, mesh); modelmesh_destroy(model_mesh); acLoad(*mesh); // Just a quick hack s.t. we do not have to add an // additional if to the render part #endif ++steps; /* Render */ const float timer_diff_sec = timer_diff_nsec(frame_timer) / 1e9f; if (timer_diff_sec >= desired_frame_time) { const int num_vertices = mesh->info.int_params[AC_mxy]; const int3 dst = (int3){0, 0, k_slice}; acBoundcondStep(); // acStore(mesh); acStoreWithOffset(dst, num_vertices, mesh); acSynchronizeStream(STREAM_ALL); renderer_draw(*mesh); // Bottleneck is here printf("Step #%d, dt: %f\n", steps, double(dt)); timer_reset(&frame_timer); } } printf("Wallclock time %f s\n", double(timer_diff_nsec(wallclock) / 1e9f)); acQuit(); acmesh_destroy(mesh); renderer_quit(); return 0; } #else // BUILD_RT_VISUALIZATION == 0 #include "errchk.h" int run_renderer(const char* /*config_path*/) { WARNING("Real-time visualization module not built. Set BUILD_RT_VISUALIZATION=ON with cmake."); return 1; } #endif // BUILD_RT_VISUALIZATION