working on fixing threading
parent
fddef65704
commit
22f8fedce2
|
@ -58,4 +58,13 @@ if (USE_EXTRAS)
|
|||
set_target_properties(FinalProject PROPERTIES COMPILE_FLAGS "-pthread")
|
||||
else()
|
||||
target_link_libraries(FinalProject PRIVATE glfw)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
#target_compile_options(BLT_TESTS PRIVATE /W4)
|
||||
if(${CMAKE_BUILD_TYPE} MATCHES Debug)
|
||||
target_link_options(FinalProject PRIVATE /DEBUG)
|
||||
endif()
|
||||
else()
|
||||
target_compile_options(FinalProject PRIVATE -Wall -Wextra -Wpedantic)
|
||||
endif()
|
|
@ -36,7 +36,7 @@ namespace fp {
|
|||
vbo_type type = ARRAY_BUFFER;
|
||||
vbo_mem_type mem_type = STATIC;
|
||||
|
||||
VBO(vbo_type type, void* data, int size, vbo_mem_type mem_type = STATIC): type(type), size(size), mem_type(mem_type) {
|
||||
VBO(vbo_type type, void* data, int size, vbo_mem_type mem_type = STATIC): size(size), type(type), mem_type(mem_type) {
|
||||
glGenBuffers(1, &vboID);
|
||||
bind();
|
||||
glBufferData(type, size, data, mem_type);
|
||||
|
|
|
@ -112,8 +112,8 @@ namespace fp::texture {
|
|||
int width, int height, GLint bind_type = GL_TEXTURE_2D,
|
||||
GLint color_mode = GL_RGBA
|
||||
):
|
||||
m_width(width), m_height(height), textureBindType(bind_type),
|
||||
textureColorMode(color_mode) {
|
||||
textureBindType(bind_type), textureColorMode(color_mode), m_width(width),
|
||||
m_height(height) {
|
||||
glGenTextures(1, &textureID);
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ namespace blt {
|
|||
private:
|
||||
volatile bool halted;
|
||||
int MAX_THREADS;
|
||||
std::queue<runnable*> runQueue {};
|
||||
std::queue<std::function<void()>*> runQueue {};
|
||||
std::vector<std::thread*> threads {};
|
||||
std::mutex queueMutex {};
|
||||
public:
|
||||
|
@ -40,7 +40,7 @@ namespace blt {
|
|||
*/
|
||||
void start();
|
||||
|
||||
void run(std::function<void()>& func);
|
||||
void run(std::function<void()>* func);
|
||||
|
||||
void run(runnable* func);
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <render/gl.h>
|
||||
#include <phmap.h>
|
||||
#include "blt/profiling/profiler.h"
|
||||
#include "util/threadpool.h"
|
||||
#include <render/frustum.h>
|
||||
|
||||
namespace fp {
|
||||
|
@ -151,7 +152,14 @@ namespace fp {
|
|||
|
||||
class world {
|
||||
private:
|
||||
phmap::flat_hash_map<chunk_pos, chunk*, _static::chunk_pos_hash, _static::chunk_pos_equality> chunk_storage;
|
||||
// not using the parallel functions of the map, we will do manual concurrency control
|
||||
// "The parallel hash maps are preferred when you have a few hash maps that will store a very large number of values.
|
||||
// The non-parallel hash maps are preferred if you have a large number of hash maps, each storing a relatively small number of values."
|
||||
phmap::parallel_flat_hash_map<chunk_pos, chunk*, _static::chunk_pos_hash, _static::chunk_pos_equality> chunk_storage;
|
||||
std::mutex chunkInsertMutex;
|
||||
std::thread* chunkGenerationThread;
|
||||
const unsigned int THREAD_COUNT = 16;
|
||||
volatile bool running = true;
|
||||
protected:
|
||||
void generateChunkMesh(chunk* chunk);
|
||||
|
||||
|
@ -166,9 +174,16 @@ namespace fp {
|
|||
neighbours[Z_NEG] = getChunk(chunk_pos{pos.x, pos.y, pos.z - 1});
|
||||
}
|
||||
|
||||
inline void spawnChunk(chunk* chunk) {
|
||||
inline bool spawnChunk(chunk* chunk) {
|
||||
// make sure not to insert null chunks
|
||||
if (chunk == nullptr)
|
||||
return;
|
||||
return false;
|
||||
// we must lock for the entire insertion including neighbour updates.
|
||||
// otherwise chunk updates might not be properly handled
|
||||
std::scoped_lock<std::mutex> lock(chunkInsertMutex);
|
||||
// or overwrite existing chunks (*memory leak*)
|
||||
if (chunk_storage.find(chunk->getPos()) != chunk_storage.end())
|
||||
return false;
|
||||
chunk_storage.insert({chunk->getPos(), chunk});
|
||||
|
||||
chunk_neighbours chunkNeighbours{};
|
||||
|
@ -178,6 +193,18 @@ namespace fp {
|
|||
if (p)
|
||||
p->setStatus(NEIGHBOUR_CREATE);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline static chunk_pos offsetCameraByChunk(int i, int j, int k) {
|
||||
const auto& pos = fp::camera::getPosition();
|
||||
int x = (int) pos.x();
|
||||
int y = (int) pos.y();
|
||||
int z = (int) pos.z();
|
||||
auto camera_chunk_pos = fp::_static::world_to_chunk({x, y, z});
|
||||
return chunk_pos{camera_chunk_pos.x + i, // chunk x
|
||||
camera_chunk_pos.y + j, // chunk y
|
||||
camera_chunk_pos.z + k}; // chunk z
|
||||
}
|
||||
|
||||
inline chunk* getChunk(const chunk_pos& pos) {
|
||||
|
@ -192,7 +219,7 @@ namespace fp {
|
|||
}
|
||||
|
||||
public:
|
||||
world() = default;
|
||||
world();
|
||||
|
||||
void update();
|
||||
|
||||
|
|
|
@ -52,14 +52,20 @@ int main() {
|
|||
|
||||
fp::window::init();
|
||||
|
||||
BLT_TRACE("Creating renderer");
|
||||
renderer = new fp::renderer();
|
||||
BLT_TRACE("Init graphics");
|
||||
fp::graphics::init(*renderer);
|
||||
BLT_TRACE("Creating textures");
|
||||
// textures must come first as blocks will require the IDs
|
||||
fp::registry::registerDefaultTextures();
|
||||
fp::registry::registerDefaultBlocks();
|
||||
BLT_TRACE("Registered textures!");
|
||||
|
||||
BLT_TRACE("Creating chunk shader!");
|
||||
chunk_shader = renderer->createShader(fp::shader(shader_chunk_vert, shader_chunk_frag));
|
||||
world = new fp::world();
|
||||
BLT_TRACE("World created!");
|
||||
|
||||
glEnable(GL_CULL_FACE);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
|
|
@ -8,8 +8,11 @@
|
|||
namespace fp {
|
||||
|
||||
shader* renderer::createShader(shader&& shader) {
|
||||
BLT_TRACE("Creating shader");
|
||||
auto s = new class shader(std::move(shader));
|
||||
BLT_TRACE("Shader created");
|
||||
shaders.push_back(s);
|
||||
BLT_TRACE("returning");
|
||||
return s;
|
||||
}
|
||||
}
|
|
@ -122,10 +122,12 @@ namespace fp::graphics {
|
|||
}
|
||||
|
||||
void init(renderer& renderer) {
|
||||
BLT_TRACE("Init font");
|
||||
if (FT_Init_FreeType(&ft)) {
|
||||
BLT_FATAL("Unable to init freetype library!");
|
||||
std::abort();
|
||||
}
|
||||
BLT_TRACE("Init face");
|
||||
if (FT_New_Face(ft, "assets/fonts/JetBrains Mono.ttf", 0, &monospaced_face)) {
|
||||
BLT_ERROR("Unable to load default monospaced (JetBrains Mono) font!");
|
||||
std::abort();
|
||||
|
@ -134,6 +136,7 @@ namespace fp::graphics {
|
|||
// disable alignment restrictions. This might cause issues with WebGL! FIXME: if it does
|
||||
// gl requires an alignment of 4. Since we are going to only use a single character of any width/height the alignment must be changed.
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
BLT_TRACE("Alignment set, generating characters");
|
||||
|
||||
generateCharacters(FONT_11);
|
||||
generateCharacters(FONT_12);
|
||||
|
@ -145,15 +148,20 @@ namespace fp::graphics {
|
|||
generateCharacters(FONT_72);
|
||||
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||
BLT_TRACE("Character generation complete!");
|
||||
|
||||
FT_Done_Face(monospaced_face);
|
||||
FT_Done_FreeType(ft);
|
||||
|
||||
BLT_TRACE("Creating font shaders and GL objects");
|
||||
// create the GL objects required to render texts
|
||||
text_shader = renderer.createShader(shader(shader_text_vert, shader_text_frag));
|
||||
BLT_TRACE("text shader created!");
|
||||
plane_shader = renderer.createShader(shader(shader_plane_vert, shader_plane_frag));
|
||||
BLT_TRACE("plane shader created!");
|
||||
quad_vao = new VAO();
|
||||
plane_vao = new VAO();
|
||||
BLT_TRACE("VAOs created!");
|
||||
|
||||
float vertices[6 * 4] = {
|
||||
// vertices uvs
|
||||
|
@ -167,11 +175,12 @@ namespace fp::graphics {
|
|||
0, 1.0, 0.0f, 1.0f,
|
||||
};
|
||||
|
||||
|
||||
BLT_TRACE("Binding VBOs");
|
||||
quad_vao->bindVBO(new VBO(ARRAY_BUFFER, vertices, sizeof(float) * 6 * 4), 0, 4);
|
||||
// since we will be updating the plane VBO regularly, we should tell the driver of this fact
|
||||
plane_vao->bindVBO(new VBO(ARRAY_BUFFER, nullptr, 0, DYNAMIC), 0, 3, GL_FLOAT, sizeof(float) * 3);
|
||||
plane_vao->bindElementVBO(new VBO(ELEMENT_BUFFER, nullptr, 0, DYNAMIC));
|
||||
BLT_TRACE("Init complete!");
|
||||
}
|
||||
|
||||
void cleanup() {
|
||||
|
|
|
@ -148,7 +148,7 @@ void fp::window::init(int width, int height) {
|
|||
//glfwSwapInterval(1);
|
||||
|
||||
updateWindowViewport(width, height);
|
||||
|
||||
BLT_TRACE("Updated window view port!");
|
||||
}
|
||||
|
||||
void fp::window::update() {
|
||||
|
|
|
@ -5,17 +5,6 @@
|
|||
*/
|
||||
#include <util/threadpool.h>
|
||||
|
||||
class runnable_function : public blt::runnable {
|
||||
private:
|
||||
std::function<void()>& func;
|
||||
public:
|
||||
explicit runnable_function(std::function<void()>& func): func(func) {}
|
||||
|
||||
void run() final {
|
||||
func();
|
||||
}
|
||||
};
|
||||
|
||||
blt::thread_pool::thread_pool(int maxThreads): MAX_THREADS(maxThreads) {
|
||||
halted = false;
|
||||
}
|
||||
|
@ -31,13 +20,16 @@ void blt::thread_pool::start() {
|
|||
[this]() -> void {
|
||||
while (!halted){
|
||||
// acquire a resource from the runnable queue
|
||||
while (runQueue.empty())
|
||||
std::this_thread::yield();
|
||||
std::unique_lock<std::mutex> lock(queueMutex);
|
||||
runnable* run = runQueue.front();
|
||||
auto* run = runQueue.front();
|
||||
runQueue.pop();
|
||||
lock.unlock();
|
||||
|
||||
// attempt to run the function
|
||||
run->run();
|
||||
if (run)
|
||||
(*run)();
|
||||
|
||||
delete(run);
|
||||
}
|
||||
|
@ -65,14 +57,15 @@ void blt::thread_pool::stop() {
|
|||
}
|
||||
}
|
||||
|
||||
void blt::thread_pool::run(std::function<void()>& func) {
|
||||
void blt::thread_pool::run(std::function<void()>* func) {
|
||||
std::scoped_lock<std::mutex> lock(queueMutex);
|
||||
runQueue.push(new runnable_function(func));
|
||||
runQueue.push(func);
|
||||
}
|
||||
|
||||
void blt::thread_pool::run(blt::runnable* func) {
|
||||
std::scoped_lock<std::mutex> lock(queueMutex);
|
||||
runQueue.push(func)
|
||||
runQueue.push(new std::function<void()>([&]() -> void {
|
||||
func->run();
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -120,20 +120,18 @@ void fp::world::generateChunkMesh(chunk* chunk) {
|
|||
|
||||
}
|
||||
|
||||
std::queue<fp::chunk_pos> chunks_to_generate{};
|
||||
|
||||
void fp::world::update() {
|
||||
auto target_delta = 1000000000 / std::stoi(fp::settings::get("FPS"));
|
||||
|
||||
while (fp::window::getCurrentDelta() < target_delta/2) {
|
||||
if (chunks_to_generate.empty())
|
||||
break;
|
||||
const auto& front = chunks_to_generate.front();
|
||||
|
||||
spawnChunk(generateChunk(front));
|
||||
|
||||
chunks_to_generate.pop();
|
||||
}
|
||||
// auto target_delta = 1000000000 / std::stoi(fp::settings::get("FPS"));
|
||||
//
|
||||
// while (fp::window::getCurrentDelta() < target_delta) {
|
||||
// if (chunks_to_generate.empty())
|
||||
// break;
|
||||
// const auto& front = chunks_to_generate.front();
|
||||
//
|
||||
// spawnChunk(generateChunk(front));
|
||||
//
|
||||
// chunks_to_generate.pop();
|
||||
// }
|
||||
}
|
||||
|
||||
void fp::world::render(fp::shader& shader) {
|
||||
|
@ -151,18 +149,11 @@ void fp::world::render(fp::shader& shader) {
|
|||
for (int j = -view_distance; j <= view_distance; j++) {
|
||||
for (int k = -view_distance; k <= view_distance; k++) {
|
||||
// get the chunks around the player's camera
|
||||
const auto& pos = fp::camera::getPosition();
|
||||
int x = (int) pos.x();
|
||||
int y = (int) pos.y();
|
||||
int z = (int) pos.z();
|
||||
auto camera_chunk_pos = fp::_static::world_to_chunk({x, y, z});
|
||||
chunk_pos adjusted_chunk_pos {camera_chunk_pos.x + i, // chunk x
|
||||
camera_chunk_pos.y + j, // chunk y
|
||||
camera_chunk_pos.z + k}; // chunk z
|
||||
// queue a chunk for generation if it doesn't exist. A separate thread should handle the generation.
|
||||
chunk_pos adjusted_chunk_pos = offsetCameraByChunk(i, j, k);
|
||||
// don't try to render null chunks! A separate thread should handle the generation.
|
||||
auto* chunk = this->getChunk(adjusted_chunk_pos);
|
||||
if (!chunk) {
|
||||
chunks_to_generate.push(adjusted_chunk_pos);
|
||||
// chunks_to_generate.push(adjusted_chunk_pos);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -175,12 +166,13 @@ void fp::world::render(fp::shader& shader) {
|
|||
chunk->updateChunkMesh();
|
||||
}
|
||||
|
||||
const auto p_min = blt::vec3{(float)i * CHUNK_SIZE, (float)j * CHUNK_SIZE, (float)k * CHUNK_SIZE};
|
||||
const auto p_min = blt::vec3{(float) i * CHUNK_SIZE, (float) j * CHUNK_SIZE,
|
||||
(float) k * CHUNK_SIZE};
|
||||
const auto p_max = p_min + blt::vec3{CHUNK_SIZE, CHUNK_SIZE, CHUNK_SIZE};
|
||||
|
||||
const auto& m = camera::getPVM();
|
||||
|
||||
if (frustum::isInsideFrustum(m, p_min))
|
||||
//if (frustum::isInsideFrustum(m, p_min))
|
||||
chunk->render(shader);
|
||||
}
|
||||
}
|
||||
|
@ -209,7 +201,9 @@ fp::chunk* fp::world::generateChunk(const fp::chunk_pos& pos) {
|
|||
float noise_total = 1;
|
||||
|
||||
for (int j = 1; j <= 8; j++)
|
||||
noise_total += stb_perlin_noise3(block_x / 256.0f, block_z / 256.0f, (float)j * 5.213953f, 0, 0, 0) * (float)(j);
|
||||
noise_total += stb_perlin_noise3(
|
||||
block_x / 256.0f, block_z / 256.0f, (float) j * 5.213953f, 0, 0, 0
|
||||
) * (float) (j);
|
||||
|
||||
noise_total /= 8;
|
||||
|
||||
|
@ -218,7 +212,9 @@ fp::chunk* fp::world::generateChunk(const fp::chunk_pos& pos) {
|
|||
for (int j = 0; j < CHUNK_SIZE; j++) {
|
||||
auto block_y = float(pos.y * CHUNK_SIZE + j);
|
||||
|
||||
float noise2 = stb_perlin_fbm_noise3(block_x / 32.0f, block_y / 32.0f, block_z / 32.0f, 2.0, 0.5, 5) + 0.75f;
|
||||
float noise2 = stb_perlin_fbm_noise3(
|
||||
block_x / 32.0f, block_y / 32.0f, block_z / 32.0f, 2.0, 0.5, 5
|
||||
) + 0.75f;
|
||||
|
||||
if (block_y < world_height && noise2 > 0)
|
||||
storage->set({i, j, k}, noise2 > 1 ? fp::registry::GRASS : fp::registry::STONE);
|
||||
|
@ -239,6 +235,54 @@ fp::world::~world() {
|
|||
BLT_WRITE_PROFILE(profile, "Chunk Mesh");
|
||||
for (auto& chunk : chunk_storage)
|
||||
delete (chunk.second);
|
||||
running = false;
|
||||
// for (int i = 0; i < THREAD_COUNT; i++)
|
||||
// chunkGenerationThread[i].join();
|
||||
chunkGenerationThread->join();
|
||||
delete chunkGenerationThread;
|
||||
}
|
||||
|
||||
fp::world::world() {
|
||||
chunkGenerationThread = new std::thread(
|
||||
[this]() -> void {
|
||||
while (running) {
|
||||
auto view_distance = std::stoi(fp::settings::get("VIEW_DISTANCE")) / 2;
|
||||
// get the chunks around the player's camera
|
||||
for (int i = -view_distance; i <= view_distance; i++) {
|
||||
for (int j = -view_distance; j <= view_distance; j++) {
|
||||
for (int k = -view_distance; k <= view_distance; k++) {
|
||||
chunk_pos adjusted_chunk_pos = offsetCameraByChunk(i, j, k);
|
||||
auto* chunk = this->getChunk(adjusted_chunk_pos);
|
||||
// only generate non-existent chunks
|
||||
if (chunk)
|
||||
continue;
|
||||
auto* generated_chunk = generateChunk(adjusted_chunk_pos);
|
||||
spawnChunk(generated_chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
// for (int i = 0; i < THREAD_COUNT; i++){
|
||||
// chunkGenerationThread[i] = std::thread{[this]() -> void {
|
||||
// while (running) {
|
||||
// std::unique_lock<std::mutex> clock(chunkQueueMutex);
|
||||
// if (chunks_to_generate.empty()) {
|
||||
// clock.unlock();
|
||||
// continue;
|
||||
// }
|
||||
// auto chunk_pos = chunks_to_generate.front();
|
||||
// chunks_to_generate.pop();
|
||||
// clock.unlock();
|
||||
//
|
||||
// auto* generated_chunk = generateChunk(chunk_pos);
|
||||
// spawnChunk(generated_chunk);
|
||||
// BLT_TRACE("Generated Chunk! %d, %d, %d", chunk_pos.x, chunk_pos.y,
|
||||
// chunk_pos.z);
|
||||
// }
|
||||
// }};
|
||||
// }
|
||||
}
|
||||
|
||||
void fp::chunk::render(fp::shader& shader) {
|
||||
|
|
Loading…
Reference in New Issue