17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-27 12:57:29 +01:00
Files
maszyna/rendering/opengl46/opengl46gpuparticles.cpp

242 lines
9.2 KiB
C++

/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#include "stdafx.h"
#include "rendering/opengl46/opengl46gpuparticles.h"
#include "utilities/Logs.h"
#include "utilities/utilities.h"
#include "gl/vao.h"
namespace
{
std::unique_ptr<gl::program> make_program(std::vector<std::string> const &Names)
{
std::vector<std::unique_ptr<gl::shader>> shaders;
std::vector<std::reference_wrapper<const gl::shader>> references;
for (auto const &name : Names)
{
shaders.emplace_back(std::make_unique<gl::shader>(name));
references.emplace_back(*shaders.back());
}
return std::make_unique<gl::program>(references);
}
std::uint32_t groups(std::uint32_t const Count)
{
return (Count + gl::PARTICLE_GROUP_SIZE - 1) / gl::PARTICLE_GROUP_SIZE;
}
} // namespace
opengl46_gpu_particles::~opengl46_gpu_particles()
{
// the vertex array goes with the context; deleted here only if it's still there
if ((m_vao != 0) && (::glfwGetCurrentContext() != nullptr))
::glDeleteVertexArrays(1, &m_vao);
}
bool opengl46_gpu_particles::gpu_init()
{
try
{
m_smokesimulate = make_program({"particles_smoke_simulate.comp"});
m_smokeprepare = make_program({"particles_smoke_prepare.comp"});
m_smokedraw = make_program({"particles_smoke.vert", "particles_smoke.frag"});
m_precipitationprepare = make_program({"particles_precipitation.comp"});
m_precipitationdraw = make_program({"particles_precipitation.vert", "particles_precipitation.frag"});
}
catch (gl::shader_exception const &e)
{
ErrorLog("particles: can't use the programs of the particles of the gpu, the smoke stays on the cpu: " + std::string(e.what()));
return false;
}
// the attributes of the instances: three vec4 of gl::particle_draw
::glCreateVertexArrays(1, &m_vao);
for (GLuint attribute = 0; attribute < 3; ++attribute)
{
::glEnableVertexArrayAttrib(m_vao, attribute);
::glVertexArrayAttribFormat(m_vao, attribute, 4, GL_FLOAT, GL_FALSE, attribute * sizeof(glm::vec4));
::glVertexArrayAttribBinding(m_vao, attribute, 0);
}
::glVertexArrayBindingDivisor(m_vao, 0, 1);
m_smokecommand = std::make_unique<gl::ssbo>(4 * sizeof(GLuint), gl::PARTICLE_COMMAND_SSBO_BINDING);
m_precipitationcommand = std::make_unique<gl::ssbo>(4 * sizeof(GLuint), gl::PARTICLE_COMMAND_SSBO_BINDING);
m_smokereadback = std::make_unique<gl::readback>(4 * sizeof(GLuint));
m_precipitationreadback = std::make_unique<gl::readback>(4 * sizeof(GLuint));
return true;
}
void opengl46_gpu_particles::gpu_clear()
{
m_particles.reset();
m_sources.reset();
m_owners.reset();
m_smokeoutput.reset();
m_precipitationoutput.reset();
}
void opengl46_gpu_particles::gpu_layout(std::uint32_t const Places, std::uint32_t const Kept, std::vector<std::uint32_t> const &Owners, std::size_t const Sources)
{
auto const places{static_cast<GLsizeiptr>(Places)};
// the particles of the places kept go over to the new storage, the new places are dead
auto particles{std::make_unique<gl::ssbo>(places * static_cast<GLsizeiptr>(sizeof(gl::particle_smoke)), gl::PARTICLE_SSBO_BINDING)};
::glClearNamedBufferData(*particles, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, nullptr);
if (m_particles && (Kept > 0))
::glCopyNamedBufferSubData(*m_particles, *particles, 0, 0, static_cast<GLsizeiptr>(Kept) * static_cast<GLsizeiptr>(sizeof(gl::particle_smoke)));
m_particles = std::move(particles);
auto const ensure = [](std::unique_ptr<gl::ssbo> &Buffer, GLsizeiptr const Size, GLuint const Binding) {
if (!Buffer)
Buffer = std::make_unique<gl::ssbo>(Size, Binding);
else
Buffer->resize(Size);
};
ensure(m_owners, places * static_cast<GLsizeiptr>(sizeof(std::uint32_t)), gl::PARTICLE_OWNER_SSBO_BINDING);
ensure(m_smokeoutput, places * static_cast<GLsizeiptr>(sizeof(gl::particle_draw)), gl::PARTICLE_OUTPUT_SSBO_BINDING);
ensure(m_sources, static_cast<GLsizeiptr>(std::max<std::size_t>(Sources, 1) * sizeof(gl::particle_smoke_source)), gl::PARTICLE_SOURCE_SSBO_BINDING);
m_owners->update(Owners.data(), 0, static_cast<GLsizeiptr>(Owners.size() * sizeof(std::uint32_t)));
}
void opengl46_gpu_particles::gpu_simulate(smoke_simulation const &Simulation, std::vector<gl::particle_smoke_source> const &Records)
{
if (false == Records.empty())
m_sources->update(Records.data(), 0, static_cast<GLsizeiptr>(Records.size() * sizeof(gl::particle_smoke_source)));
m_simulatetimer.begin();
m_particles->bind_base();
m_sources->bind_base();
m_owners->bind_base();
auto &program{*m_smokesimulate};
::glProgramUniform1ui(program, 0, Simulation.places);
::glProgramUniform4fv(program, 1, 1, glm::value_ptr(Simulation.step));
::glProgramUniform4fv(program, 2, 1, glm::value_ptr(Simulation.wind));
::glProgramUniform1ui(program, 3, static_cast<GLuint>(Simulation.seed));
program.dispatch(groups(Simulation.places));
::glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
m_simulatetimer.end();
}
void opengl46_gpu_particles::collect(gl::readback &Readback, std::size_t &Drawn)
{
GLuint command[4];
if (Readback.pop(command))
Drawn = command[1];
}
void opengl46_gpu_particles::draw(GLuint const Output, GLuint const Command, gl::program &Program)
{
auto const culling{::glIsEnabled(GL_CULL_FACE) == GL_TRUE};
if (culling)
::glDisable(GL_CULL_FACE);
Program.bind();
// the vertex array is bound behind the back of gl::vao, which is told nothing is bound afterwards
gl::vao::unbind();
::glVertexArrayVertexBuffer(m_vao, 0, Output, 0, sizeof(gl::particle_draw));
::glBindVertexArray(m_vao);
::glBindBuffer(GL_DRAW_INDIRECT_BUFFER, Command);
::glDrawArraysIndirect(GL_TRIANGLE_STRIP, nullptr);
::glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);
gl::vao::unbind();
gl::program::unbind();
if (culling)
::glEnable(GL_CULL_FACE);
}
void opengl46_gpu_particles::gpu_render_smoke(smoke_draw const &Draw, std::size_t &Drawn)
{
collect(*m_smokereadback, Drawn);
m_smoketimer.begin();
GLuint const command[4]{4, 0, 0, 0};
m_smokecommand->update(command, 0, sizeof(command));
m_particles->bind_base();
m_sources->bind_base();
m_owners->bind_base();
m_smokeoutput->bind_base();
m_smokecommand->bind_base();
auto &program{*m_smokeprepare};
::glProgramUniform1ui(program, 0, Draw.places);
::glProgramUniform3fv(program, 1, 1, glm::value_ptr(Draw.origin));
::glProgramUniformMatrix4fv(program, 2, 1, GL_FALSE, glm::value_ptr(Draw.view));
::glProgramUniform4fv(program, 3, 1, glm::value_ptr(Draw.options));
::glProgramUniform4fv(program, 4, 5, glm::value_ptr(Draw.planes[0]));
program.dispatch(groups(Draw.places));
::glMemoryBarrier(GL_COMMAND_BARRIER_BIT | GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT | GL_BUFFER_UPDATE_BARRIER_BIT);
::glProgramUniform4fv(*m_smokedraw, 0, 1, glm::value_ptr(Draw.soft));
draw(*m_smokeoutput, *m_smokecommand, *m_smokedraw);
m_smoketimer.end();
m_smokereadback->push(*m_smokecommand);
}
void opengl46_gpu_particles::gpu_collect_precipitation(std::size_t &Drawn)
{
collect(*m_precipitationreadback, Drawn);
}
void opengl46_gpu_particles::gpu_precipitation_storage(std::uint32_t const Capacity)
{
auto const size{static_cast<GLsizeiptr>(Capacity) * static_cast<GLsizeiptr>(sizeof(gl::particle_draw))};
if (!m_precipitationoutput)
m_precipitationoutput = std::make_unique<gl::ssbo>(size, gl::PARTICLE_OUTPUT_SSBO_BINDING);
else
m_precipitationoutput->resize(size);
}
void opengl46_gpu_particles::gpu_render_precipitation(precipitation_draw const &Draw)
{
m_precipitationtimer.begin();
GLuint const command[4]{4, 0, 0, 0};
m_precipitationcommand->update(command, 0, sizeof(command));
m_precipitationoutput->bind_base();
m_precipitationcommand->bind_base();
auto &program{*m_precipitationprepare};
::glProgramUniform4uiv(program, 0, 1, glm::value_ptr(Draw.counts));
::glProgramUniform4fv(program, 1, 3, glm::value_ptr(Draw.offsets[0]));
::glProgramUniform4fv(program, 4, 1, glm::value_ptr(Draw.sizes));
::glProgramUniform4fv(program, 5, 1, glm::value_ptr(Draw.velocity));
::glProgramUniform4fv(program, 6, 1, glm::value_ptr(Draw.timing));
::glProgramUniformMatrix4fv(program, 7, 1, GL_FALSE, glm::value_ptr(Draw.view));
::glProgramUniform4fv(program, 11, 1, glm::value_ptr(Draw.options));
::glProgramUniform4fv(program, 12, 5, glm::value_ptr(Draw.planes[0]));
program.dispatch(groups(Draw.count));
::glMemoryBarrier(GL_COMMAND_BARRIER_BIT | GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT | GL_BUFFER_UPDATE_BARRIER_BIT);
::glProgramUniform4fv(*m_precipitationdraw, 0, 1, glm::value_ptr(Draw.soft));
::glProgramUniform4fv(*m_precipitationdraw, 1, 1, glm::value_ptr(Draw.pixel));
draw(*m_precipitationoutput, *m_precipitationcommand, *m_precipitationdraw);
m_precipitationtimer.end();
m_precipitationreadback->push(*m_precipitationcommand);
}
std::string opengl46_gpu_particles::info_times()
{
std::string text;
auto const add = [&text](gl::gpu_timer &Timer, char const *Name) {
if (auto const time = Timer.result())
text += std::string(text.empty() ? " particles:" : ",") + " " + Name + " " + to_string(static_cast<double>(*time) / 1000000.0, 3) + " ms";
};
if (places() > 0)
{
add(m_simulatetimer, "smoke simulation");
add(m_smoketimer, "smoke");
}
if (precipitation_count() > 0)
add(m_precipitationtimer, precipitation_snow() ? "snow" : "rain");
if (false == text.empty())
text += "\n";
return text;
}