Commit c15ec121 authored by Florian Oetke's avatar Florian Oetke
Browse files

(probably) working particle update

parent 164c2e25
......@@ -3,7 +3,7 @@
},
"Directional_light": {
"source_radius": 0.8,
"intensity": 140000.0,
"intensity": 130000.0,
"temperature": 4500,
"shadow_size": 24,
"near_plane": 1.0,
......
......@@ -2,11 +2,11 @@
"emitters": [{
"spawn": [
{"particles_per_second": 100, "time": 1},
{"particles_per_second": 0, "time": 2}
{"particles_per_second": 0, "time": 200}
],
"spawn_loop": true,
"ttl": {"mean": 10, "stddev": 1},
"ttl": {"mean": 2, "stddev": 0.5},
"velocity": {"mean": 2, "stddev": 0.5},
......
......@@ -92,7 +92,10 @@ namespace mirrage::ecs {
auto operator*() noexcept -> value_type& { return get(); }
auto operator-> () noexcept -> value_type* { return &get(); }
auto operator*() const noexcept -> const value_type& { return get(); }
auto operator-> () const noexcept -> const value_type* { return &get(); }
auto get() noexcept -> value_type&;
auto get() const noexcept -> const value_type&;
auto operator++() -> Entity_set_iterator&;
auto operator++(int) -> Entity_set_iterator
{
......@@ -118,9 +121,9 @@ namespace mirrage::ecs {
SortedPools& _sorted_pools;
UnsortedPools& _unsorted_pools;
util::maybe<value_type> _last_value;
Entity_id _entity;
Values _values;
mutable util::maybe<value_type> _last_value;
Entity_id _entity;
Values _values;
void _find_next_valid();
};
......
......@@ -32,6 +32,14 @@ namespace mirrage::ecs {
get_value<Cs>(_entities, _entity, _values)...);
return _last_value.get_or_throw();
}
template <class SortedPools, class UnsortedPools, class C1, class... Cs>
auto Entity_set_iterator<SortedPools, UnsortedPools, C1, Cs...>::get() const noexcept
-> const value_type&
{
_last_value.emplace(get_value<C1>(_entities, _entity, _values),
get_value<Cs>(_entities, _entity, _values)...);
return _last_value.get_or_throw();
}
template <class... SortedPools, class... UnsortedPools>
auto build_pool_mask(std::tuple<SortedPools*...>& sorted_pools,
......
......@@ -148,7 +148,7 @@ namespace mirrage::graphic {
return _memory_allocator.is_unified_memory_architecture();
}
auto max_frames_in_flight() const noexcept { return _delete_queue.capacity() + 2; }
auto max_frames_in_flight() const noexcept { return _delete_queue.capacity() + 1; }
auto vk_device() const noexcept { return &*_device; }
auto pipeline_cache() const noexcept { return **_pipeline_cache; }
......
......@@ -218,8 +218,10 @@ namespace mirrage::graphic {
// TODO
}
auto stacktrace = level == plog::error ? "\nAt:\n" + util::print_stacktrace() : "";
LOG(level) << "[VK" << type << "|" << data->pMessageIdName << "] " << data->pMessage
<< details.str();
<< details.str() << stacktrace;
return VK_FALSE;
}
......
......@@ -5,6 +5,7 @@
#include <mirrage/asset/asset_manager.hpp>
#include <mirrage/ecs/entity_manager.hpp>
#include <mirrage/graphic/texture.hpp>
#include <mirrage/utils/random.hpp>
#include <mirrage/utils/sf2_glm.hpp>
#include <mirrage/utils/small_vector.hpp>
#include <mirrage/utils/units.hpp>
......@@ -12,33 +13,24 @@
#include <glm/vec3.hpp>
#include <sf2/sf2.hpp>
#include <random>
#include <memory>
#include <tuple>
namespace mirrage::renderer {
// TODO: only for reference during IMPL, delete before merge
// emitter: {Particle_emitter_config, offset, relative-rotation, follow_entity, time-acc, toSpawn, particle-data, particle-cout, userdata?}
// affector: {position, rotation, relative/absolute, follow_entity, force:float, dir, decay:float}
// gravity: {..., force=10, dir={0,-1,0}, decay=0}
// point: {position=? dir={0,0,0}, decay=2}
// flow: {position=? dir={1,0,0}, decay=2}
// particle_system: {shared_ptr<emitter>[], offset, relative-rotation, follow_entity, affector}
class Particle_script {
public:
explicit Particle_script(vk::UniquePipeline pipeline) : _pipeline(std::move(pipeline)) {}
void bind(vk::CommandBuffer);
void bind(vk::CommandBuffer) const;
private:
vk::UniquePipeline _pipeline;
};
enum class Particle_blend_mode { solid, volumn, transparent };
sf2_enumDef(Particle_blend_mode, solid, volumn, transparent);
enum class Particle_blend_mode { solid, volume, transparent };
sf2_enumDef(Particle_blend_mode, solid, volume, transparent);
enum class Particle_geometry { billboard, ribbon, mesh };
sf2_enumDef(Particle_geometry, billboard, ribbon, mesh);
......@@ -67,15 +59,16 @@ namespace mirrage::renderer {
/// point: {position=? dir={0,0,0}, decay=2}
/// flow: {position=? dir={1,0,0}, decay=2}
struct Particle_effector_config {
glm::vec3 position{0, 0, 0};
glm::quat rotation{1, 0, 0, 0};
glm::vec3 position{0, 0, 0};
float force = 0.f;
glm::vec3 force_dir{0, 0, 0};
float distance_decay = 2.f;
bool fixed_dir = false;
bool fixed_dir = false; //< ignore position of effector when calculating the force
bool scale_with_mass = true;
bool absolute = false;
};
sf2_structDef(Particle_effector_config,
position,
......@@ -94,12 +87,13 @@ namespace mirrage::renderer {
Random_value<glm::vec4> size = {{1.f, 1.f, 1.f, 0.f}};
Random_value<glm::vec4> size_change = {{0.f, 0.f, 0.f, 0.f}};
float base_mass = 1.f;
float density = 0.f;
Random_value<float> sprite_rotation = {0.0f};
Random_value<float> sprite_rotation_change = {0.0f};
float base_mass = 1.f;
float density = 0.f;
float drag = 0.f;
Particle_blend_mode blend = Particle_blend_mode::transparent;
Particle_geometry geometry = Particle_geometry::billboard;
......@@ -113,8 +107,6 @@ namespace mirrage::renderer {
std::string model_id;
asset::Ptr<renderer::Model> model;
float drag = 0.f;
std::string update_script_id;
asset::Ptr<Particle_script> update_script;
};
......@@ -140,16 +132,13 @@ namespace mirrage::renderer {
struct Particle_emitter_spawn {
float particles_per_second = 10.f;
float variance = 0.f;
float stddev = 0.f;
float time = -1.f;
};
sf2_structDef(Particle_emitter_spawn, particles_per_second, variance, time);
sf2_structDef(Particle_emitter_spawn, particles_per_second, stddev, time);
// describes how new particles are created
struct Particle_emitter_config {
util::small_vector<Particle_emitter_spawn, 4> spawn;
bool spawn_loop = true;
Random_value<float> ttl = {1.f};
Random_value<float> velocity = {1.f};
......@@ -159,6 +148,9 @@ namespace mirrage::renderer {
glm::vec3 offset{0, 0, 0};
glm::quat rotation{1, 0, 0, 0};
util::small_vector<Particle_emitter_spawn, 4> spawn;
bool spawn_loop = true;
std::string emit_script_id;
asset::Ptr<Particle_script> emit_script;
......@@ -183,6 +175,26 @@ namespace mirrage::renderer {
sf2_structDef(Particle_system_config, emitters, effectors);
class Particle_emitter_gpu_data {
public:
auto valid() const noexcept { return _live_rev && *_live_rev == _rev; }
void set(const std::uint64_t* rev,
vk::Buffer,
std::int32_t offset,
std::int32_t count,
std::uint32_t feedback_idx);
private:
vk::Buffer _buffer;
const std::uint64_t* _live_rev = nullptr;
std::uint64_t _rev = 0;
std::int32_t _offset = 0;
std::int32_t _count = 0;
std::uint32_t _feedback_idx = 0;
friend class Particle_emitter;
};
class Particle_emitter {
public:
explicit Particle_emitter(const Particle_emitter_config& cfg) : _cfg(&cfg) {}
......@@ -200,6 +212,20 @@ namespace mirrage::renderer {
auto absolute() const noexcept { return _absolute; }
void incr_time(float dt) { _time_accumulator += dt; }
auto spawn(util::default_rand&) -> std::int32_t;
auto drawable() const noexcept { return _gpu_data && _gpu_data->valid(); }
auto particle_offset() const noexcept { return drawable() ? _gpu_data->_offset : 0; }
auto particle_count() const noexcept { return drawable() ? _gpu_data->_count : 0; }
auto particle_feedback_idx() const noexcept
{
return drawable() ? util::just(_gpu_data->_feedback_idx) : util::nothing;
}
auto particle_buffer() const noexcept { return drawable() ? _gpu_data->_buffer : vk::Buffer{}; }
auto particles_to_spawn() const noexcept { return _particles_to_spawn; }
auto last_timestep() const noexcept { return _last_timestep; }
auto gpu_data() -> std::shared_ptr<Particle_emitter_gpu_data>;
auto cfg() const noexcept -> auto& { return *_cfg; }
......@@ -212,7 +238,15 @@ namespace mirrage::renderer {
glm::quat _rotation{1, 0, 0, 0};
bool _absolute = false;
float _time_accumulator = 0.f;
float _time_accumulator = 0.f;
std::size_t _spawn_idx = 0;
float _spawn_entry_timer = 0;
std::int32_t _particles_to_spawn = 0;
float _last_timestep = 0;
// shared_ptr because its update after the async compute tasks finished
std::shared_ptr<Particle_emitter_gpu_data> _gpu_data;
};
class Particle_system : private std::enable_shared_from_this<Particle_system> {
......@@ -253,16 +287,19 @@ namespace mirrage::renderer {
auto emitter_rotation(const Particle_emitter& e) const noexcept
{
return e.absolute() ? e.rotation() : _rotation * e.rotation();
return e.absolute() ? e.rotation() : glm::normalize(_rotation * e.rotation());
}
private:
friend class Particle_pass;
asset::Ptr<Particle_system_config> _cfg;
bool _loaded = false;
Emitter_list _emitters;
Effector_list _effectors;
glm::vec3 _position{0, 0, 0};
glm::vec3 _last_position{0, 0, 0};
glm::quat _rotation{1, 0, 0, 0};
void _check_reload();
......@@ -281,7 +318,7 @@ namespace mirrage::renderer {
Particle_system particle_system;
};
class Particle_effector_comp : public ecs::Component<Particle_system_comp> {
class Particle_effector_comp : public ecs::Component<Particle_effector_comp> {
public:
static constexpr const char* name() { return "Particle_effector"; }
friend void load_component(ecs::Deserializer& state, Particle_effector_comp&);
......@@ -293,6 +330,15 @@ namespace mirrage::renderer {
Particle_effector_config effector;
};
extern auto create_particle_shared_desc_set_layout(graphic::Device&) -> vk::UniqueDescriptorSetLayout;
extern auto create_particle_script_pipeline_layout(graphic::Device& device,
vk::DescriptorSetLayout shared_desc_set,
vk::DescriptorSetLayout storage_buffer,
vk::DescriptorSetLayout uniform_buffer)
-> vk::UniquePipelineLayout;
} // namespace mirrage::renderer
namespace mirrage::asset {
......@@ -301,7 +347,6 @@ namespace mirrage::asset {
struct Loader<renderer::Particle_script> {
public:
Loader(graphic::Device& device,
vk::DescriptorSetLayout global_uniforms,
vk::DescriptorSetLayout storage_buffer,
vk::DescriptorSetLayout uniform_buffer);
......@@ -312,8 +357,9 @@ namespace mirrage::asset {
}
private:
graphic::Device& _device;
vk::UniquePipelineLayout _layout;
graphic::Device& _device;
vk::UniqueDescriptorSetLayout _shared_desc_set;
vk::UniquePipelineLayout _layout;
};
template <>
......
......@@ -3,12 +3,11 @@
#include <mirrage/renderer/deferred_renderer.hpp>
#include <mirrage/graphic/render_pass.hpp>
#include <mirrage/utils/random.hpp>
namespace mirrage::renderer {
// TODO: everything
class Particle_pass_factory;
/**
......@@ -28,17 +27,65 @@ namespace mirrage::renderer {
auto name() const noexcept -> const char* override { return "Particle"; }
private:
Deferred_renderer& _renderer;
ecs::Entity_manager& _ecs;
bool _update_submitted = false;
graphic::Fence _update_fence;
util::maybe<graphic::Static_buffer> _feedback_buffer;
util::maybe<graphic::Static_buffer> _new_particle_buffer;
util::maybe<graphic::Static_buffer> _old_particle_buffer;
// TODO: feedback-buffer (dynamic storage-buffer)
// TODO: active_computation {fence, {weak_ptr<particle-emitter>, particel-buffer, feedback-offset}[] }
struct Update_uniform_buffer {
graphic::Backed_buffer buffer;
graphic::DescriptorSet desc_set;
std::int32_t capacity = -1;
void reserve(Deferred_renderer& renderer, std::int32_t new_capacity);
};
struct Per_frame_data {
vk::UniqueCommandBuffer commands;
graphic::Backed_buffer particles;
graphic::Backed_buffer shared_uniforms;
graphic::DescriptorSet descriptor_set;
std::int32_t capacity = -1;
std::int32_t effector_capacity = -1;
std::vector<Update_uniform_buffer> particle_type_data;
std::int32_t next_free_particle_type_data = 0;
void reserve(Deferred_renderer& renderer,
std::int32_t particle_count,
std::int32_t particle_type_count,
std::int32_t global_effector_count);
auto next_particle_type_data() -> Update_uniform_buffer&;
};
struct Emitter_range {
std::int32_t offset;
std::int32_t count;
};
using Emitter_gpu_data = std::vector<std::weak_ptr<Particle_emitter_gpu_data>>;
Deferred_renderer& _renderer;
ecs::Entity_manager& _ecs;
util::default_rand _rand;
vk::DeviceSize _storage_buffer_offset_alignment;
vk::UniqueDescriptorSetLayout _descriptor_set_layout;
vk::UniquePipelineLayout _pipeline_layout;
std::uint64_t _rev = 0; //< used to invalidate data of old particle emitters
float _dt = 0.f;
bool _update_submitted = false;
graphic::Fence _update_fence;
graphic::Backed_buffer _feedback_buffer;
graphic::Backed_buffer _feedback_buffer_host;
std::size_t _feedback_buffer_size = 0;
Emitter_gpu_data _emitter_gpu_data;
std::vector<Per_frame_data> _per_frame_data;
std::int32_t _current_frame = 0;
bool _first_frame = true;
void _submit_update(Frame_data&);
void _sort_particles(Frame_data&);
auto _alloc_feedback_buffer(Frame_data&)
-> std::tuple<gsl::span<Emitter_range>, gsl::span<std::uint32_t>>;
void _update_descriptor_set(Per_frame_data&, util::maybe<Per_frame_data&>);
void _dispatch_updates(Frame_data&, vk::CommandBuffer, Per_frame_data&);
};
class Particle_pass_factory : public Render_pass_factory {
......
......@@ -115,14 +115,16 @@ namespace mirrage::renderer {
struct Particle_draw {
Particle_emitter* emitter;
Particle_system* system;
gsl::span<Particle_effector_config> effectors;
std::uint32_t culling_mask;
Particle_draw() = default;
Particle_draw(Particle_emitter& emitter,
Particle_system& system,
gsl::span<Particle_effector_config> effectors,
std::uint32_t culling_mask)
: emitter(&emitter), effectors(effectors), culling_mask(culling_mask)
: emitter(&emitter), system(&system), effectors(effectors), culling_mask(culling_mask)
{
}
};
......
#ifndef GLOBAL_UNIFORMS_INCLUDED
#define GLOBAL_UNIFORMS_INCLUDED
layout(set=0, binding = 0) uniform Global_uniforms {
layout(std140, set=0, binding = 0) uniform Global_uniforms {
mat4 view_proj_mat;
mat4 view_mat;
mat4 proj_mat;
......
#include "../random.glsl"
struct Random_vec4 {
vec4 mean_hsva;
vec4 stddev_hsva;
};
struct Random_float {
float mean;
float stddev;
// padded to vec4 if used as std140
};
vec2 normal_rand(uint seed, uint i, uint j) {
vec2 uv = vec2(floatConstruct(hash(uvec2(seed, i))), floatConstruct(hash(uvec2(seed, j))));
float x = 6.28318530718 * uv[1];
return sqrt(-2.0*log(uv[0])) * vec2(cos(x),sin(x));
}
float rand_float(Random_float range, float rand) {
return range.mean + range.stddev*rand;
}
struct Effector {
vec4 force_dir; // w=padding
vec4 position; // w=padding
float force;
float distance_decay;
float mass_scale; // used as a=mix(F/m, F, mass_scale)
float fixed_dir;
};
struct Particle {
vec4 position; // xyz + uintBitsToFloat(last_feedback_buffer_index)
vec4 velocity; // xyz + seed
vec4 ttl; // ttl_left, ttl_initial, <empty>, <empty>
};
layout(std140, set=0, binding = 0) uniform Shared_uniforms {
// TODO ?
int effector_count;
int padding1;
int padding2;
int padding3;
Effector effectors[];
} shared_uniforms;
//layout(set=1, binding = 0) uniform sampler2D color_sampler;
layout(std430, set=0, binding = 1) readonly buffer Particles_old {
Particle particles[];
} pin;
layout(std430, set=0, binding = 2) writeonly buffer Particles_new {
Particle particles[];
} pout;
struct Emitter_particle_range {
int offset;
int count;
};
layout(std430, set=0, binding = 3) buffer Feedback_buffer {
Emitter_particle_range ranges[];
} feedback;
layout(std430, set=0, binding = 4) buffer Feedback_mapping {
uint new_feedback_index[];
} feedback_mapping;
vec3 quaternion_rotate(vec3 dir, vec4 q) {
return dir + 2.0 * cross(q.xyz, cross(q.xyz, dir) + q.w * dir);
}
#version 450
#extension GL_ARB_separate_shader_objects : enable
#include "../random.glsl"
#include "base_particle_script.glsl"
layout (local_size_x = 32, local_size_y = 1, local_size_z = 1 ) in;
struct Particle {
vec3 position;
float ttl;
vec3 velocity;
float seed;
};
layout(std430, set=1, binding = 0) buffer ParticleDataIn {
int count;
Particle particles[];
} pin;
layout(std430, set=1, binding = 1) buffer ParticleDataOut {
int count;
Particle particles[];
} pout;
struct Effector {
vec3 force_dir;
float force;
vec3 position;
float distance_decay;
};
layout(std430, set=1, binding = 3) buffer EffectorData {
int count;
Effector effectors[];
};
struct Random_vec4 {
vec4 median_hsva;
vec4 stddev_hsva;
};
struct Random_float {
float median;
float stddev;
};
vec2 normal_rand(float seed, uint i, uint j) {
vec2 uv = vec2(floatConstruct(hash(uvec2(seed, i))), floatConstruct(hash(uvec2(seed, j))));
float x = 6.28318530718 * uv[1];
return sqrt(-2.0*log(uv[0])) * vec2(cos(x),sin(x));
}
float rand_float(Random_float range, float rand) {
return range.median + range.stddev*rand;
}
layout(std140, set=1, binding = 2) uniform Config {
Random_vec4 color; // hsva