464 lines
16 KiB
C++
464 lines
16 KiB
C++
/*
|
|
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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
* \brief Brief info.
|
|
*
|
|
* Detailed info.
|
|
*
|
|
*/
|
|
#include "run.h"
|
|
|
|
#if AC_BUILD_RT_VISUALIZATION
|
|
#include <SDL.h> // Note: using local version in src/3rdparty dir
|
|
#include <math.h> // ceil
|
|
#include <string.h> // 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
|