adding async chunk generation

This commit is contained in:
2025-05-17 23:10:31 +02:00
parent e6982f99e5
commit be29d9a3be
19 changed files with 167 additions and 75 deletions

View File

@@ -2,8 +2,6 @@
#include <Block.h> #include <Block.h>
#include <World.h> #include <World.h>
#include <cmath>
#define CHUNK_POS_TO_INDEX(px, py, pz) (px * CHUNK_SIZE.x * CHUNK_SIZE.z + pz * CHUNK_SIZE.z + py) #define CHUNK_POS_TO_INDEX(px, py, pz) (px * CHUNK_SIZE.x * CHUNK_SIZE.z + pz * CHUNK_SIZE.z + py)
Chunk::Chunk(World& world, Scop::Vec2i offset) : m_data(CHUNK_VOLUME, 0), m_offset(offset), m_position(std::move(offset) * Scop::Vec2i{ CHUNK_SIZE.x, CHUNK_SIZE.z }), m_world(world) Chunk::Chunk(World& world, Scop::Vec2i offset) : m_data(CHUNK_VOLUME, 0), m_offset(offset), m_position(std::move(offset) * Scop::Vec2i{ CHUNK_SIZE.x, CHUNK_SIZE.z }), m_world(world)
@@ -21,7 +19,7 @@ void Chunk::GenerateChunk()
for(std::uint32_t z = 0; z < CHUNK_SIZE.z; z++) for(std::uint32_t z = 0; z < CHUNK_SIZE.z; z++)
{ {
// Implement noise here // Implement noise here
std::uint32_t height = 4 + std::sin(x) + std::cos(z); std::uint32_t height = std::min(static_cast<std::uint32_t>(4 + (std::sin(x) + std::cos(z)) * 2), CHUNK_SIZE.y);
for(std::uint32_t y = 0; y < height; y++) for(std::uint32_t y = 0; y < height; y++)
m_data[CHUNK_POS_TO_INDEX(x, y, z)] = 1; m_data[CHUNK_POS_TO_INDEX(x, y, z)] = 1;
} }
@@ -125,7 +123,7 @@ void Chunk::UploadMesh()
mesh->AddSubMesh({ std::move(m_mesh_data), std::move(m_mesh_index_data) }); mesh->AddSubMesh({ std::move(m_mesh_data), std::move(m_mesh_index_data) });
Scop::Actor& actor = m_world.GetScene().CreateActor(mesh); Scop::Actor& actor = m_world.GetScene().CreateActor(mesh);
//actor.GetModelRef().SetMaterial(m_world.GetBlockMaterial(), 0); actor.GetModelRef().SetMaterial(m_world.GetBlockMaterial(), 0);
actor.SetScale(Scop::Vec3f{ 2.0f }); actor.SetScale(Scop::Vec3f{ 2.0f });
actor.SetPosition(Scop::Vec3f(m_position.x, 0.0f, m_position.y)); actor.SetPosition(Scop::Vec3f(m_position.x, 0.0f, m_position.y));
p_actor = &actor; p_actor = &actor;

View File

@@ -2,12 +2,11 @@
#define CHUNK_H #define CHUNK_H
#include <vector> #include <vector>
#include <atomic>
#include <ScopGraphics.h> #include <ScopGraphics.h>
#include <ScopMaths.h> #include <ScopMaths.h>
constexpr Scop::Vec3ui CHUNK_SIZE = Scop::Vec3ui{ 32, 256, 32 }; constexpr Scop::Vec3ui CHUNK_SIZE = Scop::Vec3ui{ 32, 32, 32 };
constexpr std::uint32_t CHUNK_VOLUME = CHUNK_SIZE.x * CHUNK_SIZE.y * CHUNK_SIZE.z; constexpr std::uint32_t CHUNK_VOLUME = CHUNK_SIZE.x * CHUNK_SIZE.y * CHUNK_SIZE.z;
class Chunk class Chunk

View File

@@ -1,8 +1,11 @@
#ifndef UTILS_H #ifndef UTILS_H
#define UTILS_H #define UTILS_H
#include <mutex>
#include <queue>
#include <unistd.h> #include <unistd.h>
#include <filesystem> #include <filesystem>
#include <condition_variable>
#include <ScopMaths.h> #include <ScopMaths.h>
@@ -42,4 +45,36 @@ namespace std
}; };
} }
template <typename T>
class ThreadSafeQueue
{
public:
inline void Push(T item)
{
const std::unique_lock<std::mutex> lock(m_mutex);
m_queue.push(item);
m_cond.notify_one();
}
inline T Pop()
{
std::unique_lock<std::mutex> lock(m_mutex);
m_cond.wait(lock, [this]() { return !m_queue.empty(); });
T item = m_queue.front();
m_queue.pop();
return item;
}
bool IsEmpty() const
{
const std::unique_lock<std::mutex> lock(m_mutex);
return m_queue.empty();
}
private:
std::queue<T> m_queue;
mutable std::mutex m_mutex;
std::condition_variable m_cond;
};
#endif #endif

View File

@@ -1,3 +1,5 @@
#include <future>
#include <ScopCore.h> #include <ScopCore.h>
#include <World.h> #include <World.h>
@@ -7,12 +9,31 @@ World::World(Scop::Scene& scene) : m_scene(scene), m_narrator(scene.CreateNarrat
{ {
Scop::Vec2ui32 map_size; Scop::Vec2ui32 map_size;
Scop::MaterialTextures material_params; Scop::MaterialTextures material_params;
material_params.albedo = std::make_shared<Scop::Texture>(Scop::LoadBMPFile(GetResourcesPath() / "prototype.bmp", map_size), map_size.x, map_size.y); material_params.albedo = std::make_shared<Scop::Texture>(Scop::LoadBMPFile(GetResourcesPath() / "dirt.bmp", map_size), map_size.x, map_size.y);
p_block_material = std::make_shared<Scop::Material>(material_params); p_block_material = std::make_shared<Scop::Material>(material_params);
auto narrator_update = [this](Scop::NonOwningPtr<Scop::Scene> scene, Scop::Inputs& input, float delta) auto narrator_update = [this](Scop::NonOwningPtr<Scop::Scene> scene, Scop::Inputs& input, float delta)
{ {
GenerateWorld(); Scop::FirstPerson3D* camera = reinterpret_cast<Scop::FirstPerson3D*>(m_scene.GetCamera().get());
std::int32_t x_chunk = static_cast<std::int32_t>(camera->GetPosition().x) / static_cast<std::int32_t>(CHUNK_SIZE.x);
std::int32_t z_chunk = static_cast<std::int32_t>(camera->GetPosition().z) / static_cast<std::int32_t>(CHUNK_SIZE.z);
Scop::Vec2i current_chunk_position{ x_chunk, z_chunk };
if(m_generation_status != GenerationState::Ready || current_chunk_position != m_previous_chunk_position)
{
if(m_generation_status == GenerationState::Ready)
{
UnloadChunks(current_chunk_position);
auto _ = std::async(std::launch::async, &World::GenerateWorld, this, current_chunk_position);
m_generation_status = GenerationState::Working;
}
else if(m_generation_status == GenerationState::Finished)
{
m_generation_status = GenerationState::Ready;
m_previous_chunk_position = current_chunk_position;
}
}
if(m_generation_status != GenerationState::Working)
Upload(); Upload();
}; };
@@ -27,23 +48,14 @@ World::World(Scop::Scene& scene) : m_scene(scene), m_narrator(scene.CreateNarrat
return &it->second; return &it->second;
} }
void World::GenerateWorld() void World::UnloadChunks(Scop::Vec2i current_chunk_position)
{ {
Scop::FirstPerson3D* camera = reinterpret_cast<Scop::FirstPerson3D*>(m_scene.GetCamera().get());
std::int32_t x_chunk = static_cast<std::int32_t>(camera->GetPosition().x) / static_cast<std::int32_t>(CHUNK_SIZE.x);
std::int32_t z_chunk = static_cast<std::int32_t>(camera->GetPosition().z) / static_cast<std::int32_t>(CHUNK_SIZE.z);
Scop::Vec2i current_chunk_position{ x_chunk, z_chunk };
if(current_chunk_position == m_previous_chunk_position)
return;
for(auto it = m_chunks.begin(); it != m_chunks.end();) for(auto it = m_chunks.begin(); it != m_chunks.end();)
{ {
Scop::Vec3i pos = it->first; Scop::Vec3i pos = it->first;
float x_dist = std::abs(pos.x - current_chunk_position.x); float x_dist = std::abs(pos.x - current_chunk_position.x);
float z_dist = std::abs(pos.z - current_chunk_position.y); float z_dist = std::abs(pos.z - current_chunk_position.y);
if(RENDER_DISTANCE < x_dist || RENDER_DISTANCE < z_dist) if(RENDER_DISTANCE_HALF < x_dist || RENDER_DISTANCE_HALF < z_dist)
{ {
if(it->second.GetActor()) if(it->second.GetActor())
m_scene.RemoveActor(*it->second.GetActor()); m_scene.RemoveActor(*it->second.GetActor());
@@ -52,25 +64,41 @@ void World::GenerateWorld()
else else
++it; ++it;
} }
}
for(std::int32_t x = x_chunk - RENDER_DISTANCE; x <= x_chunk + RENDER_DISTANCE; x++) void World::GenerateWorld(Scop::Vec2i current_chunk_position)
{ {
for(std::int32_t z = z_chunk - RENDER_DISTANCE; z <= z_chunk + RENDER_DISTANCE; z++) m_generation_status = GenerationState::Working;
std::vector<std::future<void>> futures;
for(std::int32_t x = current_chunk_position.x - RENDER_DISTANCE_HALF; x <= current_chunk_position.x + RENDER_DISTANCE_HALF; x++)
{
for(std::int32_t z = current_chunk_position.y - RENDER_DISTANCE_HALF; z <= current_chunk_position.y + RENDER_DISTANCE_HALF; z++)
{ {
auto res = m_chunks.try_emplace(Scop::Vec2i{ x, z }, *this, Scop::Vec2i{ x, z }); auto res = m_chunks.try_emplace(Scop::Vec2i{ x, z }, *this, Scop::Vec2i{ x, z });
if(res.second) if(res.second)
res.first->second.GenerateChunk(); {
futures.push_back(std::async(std::launch::async, &Chunk::GenerateChunk, &res.first->second));
if(!res.first->second.GetActor())
m_chunks_to_upload.Push(res.first->second);
} }
} }
}
for(auto& chunk : m_chunks) for(auto& future: futures)
chunk.second.GenerateMesh(); future.wait();
m_generation_status = GenerationState::Finished;
m_previous_chunk_position = current_chunk_position;
} }
void World::Upload() void World::Upload()
{ {
for(auto& chunk : m_chunks) if(m_chunks_to_upload.IsEmpty())
chunk.second.UploadMesh(); return;
Scop::RenderCore::Get().ShouldStackSubmits(true);
for(std::size_t i = 0; i < CHUNKS_UPLOAD_PER_FRAME && !m_chunks_to_upload.IsEmpty(); i++)
{
auto chunk = m_chunks_to_upload.Pop().get();
chunk.GenerateMesh();
chunk.UploadMesh();
}
Scop::RenderCore::Get().WaitQueueIdle(KVF_GRAPHICS_QUEUE);
Scop::RenderCore::Get().ShouldStackSubmits(false);
} }

View File

@@ -1,6 +1,7 @@
#ifndef WORLD_H #ifndef WORLD_H
#define WORLD_H #define WORLD_H
#include <atomic>
#include <unordered_map> #include <unordered_map>
#include <ScopGraphics.h> #include <ScopGraphics.h>
@@ -8,7 +9,16 @@
#include <Chunk.h> #include <Chunk.h>
#include <Utils.h> #include <Utils.h>
constexpr std::uint8_t RENDER_DISTANCE = 1; constexpr std::uint8_t RENDER_DISTANCE = 10;
constexpr std::uint8_t RENDER_DISTANCE_HALF = RENDER_DISTANCE / 2;
constexpr std::uint8_t CHUNKS_UPLOAD_PER_FRAME = 1;
enum class GenerationState: std::uint8_t
{
Ready,
Working,
Finished,
};
class World class World
{ {
@@ -22,15 +32,18 @@ class World
~World() = default; ~World() = default;
private: private:
void GenerateWorld(); void UnloadChunks(Scop::Vec2i current_chunk_position);
void GenerateWorld(Scop::Vec2i current_chunk_position);
void Upload(); void Upload();
private: private:
std::unordered_map<Scop::Vec2i, Chunk> m_chunks; std::unordered_map<Scop::Vec2i, Chunk> m_chunks;
ThreadSafeQueue<std::reference_wrapper<Chunk>> m_chunks_to_upload;
std::shared_ptr<Scop::Material> p_block_material; std::shared_ptr<Scop::Material> p_block_material;
Scop::Narrator& m_narrator; Scop::Narrator& m_narrator;
Scop::Scene& m_scene; Scop::Scene& m_scene;
Scop::Vec2i m_previous_chunk_position; Scop::Vec2i m_previous_chunk_position;
std::atomic<GenerationState> m_generation_status = GenerationState::Ready;
}; };
#endif #endif

BIN
Resources/dirt.bmp git.filemode.normal_file

Binary file not shown.

After

Width:  |  Height:  |  Size: 822 B

View File

@@ -45,7 +45,7 @@ _GRAY := $(shell $(TPUT) setaf 8)
_PURPLE := $(shell $(TPUT) setaf 5) _PURPLE := $(shell $(TPUT) setaf 5)
ifeq ($(DEBUG), true) ifeq ($(DEBUG), true)
CXXFLAGS += -g -D DEBUG -D IMGUI_IMPL_VULKAN_NO_PROTOTYPES -I ThirdParty/imgui CXXFLAGS += -g3 -D DEBUG -D IMGUI_IMPL_VULKAN_NO_PROTOTYPES -I ThirdParty/imgui
SRCS += $(wildcard $(addsuffix /*.cpp, ./Runtime/Sources/Debug)) SRCS += $(wildcard $(addsuffix /*.cpp, ./Runtime/Sources/Debug))
SRCS += $(wildcard $(addsuffix /*.cpp, ./ThirdParty/imgui)) SRCS += $(wildcard $(addsuffix /*.cpp, ./ThirdParty/imgui))
SRCS += $(wildcard $(addsuffix /*.cpp, ./ThirdParty/imgui/backends)) SRCS += $(wildcard $(addsuffix /*.cpp, ./ThirdParty/imgui/backends))

View File

@@ -27,7 +27,7 @@ namespace Scop
std::memcpy(data.GetData() + vertices.size() * sizeof(Vertex), indices.data(), indices.size() * sizeof(std::uint32_t)); std::memcpy(data.GetData() + vertices.size() * sizeof(Vertex), indices.data(), indices.size() * sizeof(std::uint32_t));
buffer.Init(vertices.size() * sizeof(Vertex), indices.size() * sizeof(std::uint32_t), 0, std::move(data)); buffer.Init(vertices.size() * sizeof(Vertex), indices.size() * sizeof(std::uint32_t), 0, std::move(data));
this->index_size = index_size == 0 ? indices.size() : index_size; this->index_size = index_size == 0 ? indices.size() : index_size;
triangle_count = index_size / 3; triangle_count = this->index_size / 3;
} }
inline void SetData(const std::vector<Vertex>& vertices, const std::vector<std::uint32_t>& indices, std::size_t index_size = 0) inline void SetData(const std::vector<Vertex>& vertices, const std::vector<std::uint32_t>& indices, std::size_t index_size = 0)
@@ -41,7 +41,7 @@ namespace Scop
buffer.SetVertexData(std::move(index_data)); buffer.SetVertexData(std::move(index_data));
this->index_size = index_size == 0 ? indices.size() : index_size; this->index_size = index_size == 0 ? indices.size() : index_size;
triangle_count = index_size / 3; triangle_count = this->index_size / 3;
} }
}; };

View File

@@ -34,6 +34,8 @@ namespace Scop
~Model() = default; ~Model() = default;
private: private:
inline static std::shared_ptr<Material> s_default_material = nullptr;
Vec3f m_center = { 0.0f, 0.0f, 0.0f }; Vec3f m_center = { 0.0f, 0.0f, 0.0f };
std::vector<std::shared_ptr<Material>> m_materials; std::vector<std::shared_ptr<Material>> m_materials;
std::shared_ptr<Mesh> p_mesh; std::shared_ptr<Mesh> p_mesh;

View File

@@ -15,7 +15,7 @@ namespace Scop
public: public:
GPUBuffer() = default; GPUBuffer() = default;
void Init(BufferType type, VkDeviceSize size, VkBufferUsageFlags usage, CPUBuffer data, std::string_view name = {}); void Init(BufferType type, VkDeviceSize size, VkBufferUsageFlags usage, CPUBuffer data, std::string_view name = {}, bool dedicated_alloc = false);
void Destroy() noexcept; void Destroy() noexcept;
bool CopyFrom(const GPUBuffer& buffer, std::size_t src_offset = 0, std::size_t dst_offset = 0) noexcept; bool CopyFrom(const GPUBuffer& buffer, std::size_t src_offset = 0, std::size_t dst_offset = 0) noexcept;
@@ -43,7 +43,7 @@ namespace Scop
MemoryBlock m_memory = NULL_MEMORY_BLOCK; MemoryBlock m_memory = NULL_MEMORY_BLOCK;
private: private:
void CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, std::string_view name); void CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, std::string_view name, bool dedicated_alloc);
private: private:
inline static std::size_t s_buffer_count = 0; inline static std::size_t s_buffer_count = 0;
@@ -52,6 +52,7 @@ namespace Scop
VkBufferUsageFlags m_usage = 0; VkBufferUsageFlags m_usage = 0;
VkMemoryPropertyFlags m_flags = 0; VkMemoryPropertyFlags m_flags = 0;
bool m_is_dedicated_alloc = false;
}; };
class VertexBuffer : public GPUBuffer class VertexBuffer : public GPUBuffer
@@ -77,7 +78,7 @@ namespace Scop
{ {
m_vertex_offset = 0; m_vertex_offset = 0;
m_index_offset = vertex_size; m_index_offset = vertex_size;
GPUBuffer::Init(BufferType::LowDynamic, vertex_size + index_size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | additional_flags, std::move(data), std::move(name)); GPUBuffer::Init(BufferType::LowDynamic, vertex_size + index_size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | additional_flags, std::move(data), std::move(name), true);
} }
void SetVertexData(CPUBuffer data); void SetVertexData(CPUBuffer data);
void SetIndexData(CPUBuffer data); void SetIndexData(CPUBuffer data);

View File

@@ -12,12 +12,13 @@ namespace Scop
class MemoryChunk class MemoryChunk
{ {
public: public:
MemoryChunk(VkDevice device, VkPhysicalDevice physical, VkDeviceSize size, std::int32_t memory_type_index); MemoryChunk(VkDevice device, VkPhysicalDevice physical, VkDeviceSize size, std::int32_t memory_type_index, bool is_dedicated);
[[nodiscard]] std::optional<MemoryBlock> Allocate(VkDeviceSize size, VkDeviceSize alignment); [[nodiscard]] std::optional<MemoryBlock> Allocate(VkDeviceSize size, VkDeviceSize alignment);
void Deallocate(const MemoryBlock& block); void Deallocate(const MemoryBlock& block);
[[nodiscard]] inline bool Has(const MemoryBlock& block) const noexcept { return block.memory == m_memory; } [[nodiscard]] inline bool Has(const MemoryBlock& block) const noexcept { return block.memory == m_memory; }
[[nodiscard]] inline std::int32_t GetMemoryTypeIndex() const noexcept { return m_memory_type_index; } [[nodiscard]] inline std::int32_t GetMemoryTypeIndex() const noexcept { return m_memory_type_index; }
[[nodiscard]] inline bool IsDedicated() const noexcept { return m_is_dedicated; }
~MemoryChunk(); ~MemoryChunk();
@@ -29,6 +30,7 @@ namespace Scop
void* p_map = nullptr; void* p_map = nullptr;
VkDeviceSize m_size = 0; VkDeviceSize m_size = 0;
std::int32_t m_memory_type_index; std::int32_t m_memory_type_index;
bool m_is_dedicated;
}; };
} }

View File

@@ -1,6 +1,7 @@
#ifndef __SCOP_VULKAN_MEMORY_DEVICE_ALLOCATOR__ #ifndef __SCOP_VULKAN_MEMORY_DEVICE_ALLOCATOR__
#define __SCOP_VULKAN_MEMORY_DEVICE_ALLOCATOR__ #define __SCOP_VULKAN_MEMORY_DEVICE_ALLOCATOR__
#include <mutex>
#include <vector> #include <vector>
#include <memory> #include <memory>
#include <cstdint> #include <cstdint>
@@ -29,6 +30,8 @@ namespace Scop
VkDevice m_device = VK_NULL_HANDLE; VkDevice m_device = VK_NULL_HANDLE;
VkPhysicalDevice m_physical = VK_NULL_HANDLE; VkPhysicalDevice m_physical = VK_NULL_HANDLE;
std::size_t m_allocations_count = 0; std::size_t m_allocations_count = 0;
std::mutex m_alloc_mutex;
std::mutex m_dealloc_mutex;
}; };
} }

View File

@@ -6,9 +6,6 @@
#include <cstdint> #include <cstdint>
#include <optional> #include <optional>
#ifdef DEBUG
#define KVF_ENABLE_VALIDATION_LAYERS
#endif
#include <kvf.h> #include <kvf.h>
#include <Renderer/Memory/DeviceAllocator.h> #include <Renderer/Memory/DeviceAllocator.h>
@@ -37,16 +34,20 @@ namespace Scop
[[nodiscard]] inline VkDevice GetDevice() const noexcept { return m_device; } [[nodiscard]] inline VkDevice GetDevice() const noexcept { return m_device; }
[[nodiscard]] inline VkPhysicalDevice GetPhysicalDevice() const noexcept { return m_physical_device; } [[nodiscard]] inline VkPhysicalDevice GetPhysicalDevice() const noexcept { return m_physical_device; }
[[nodiscard]] inline DeviceAllocator& GetAllocator() noexcept { return m_allocator; } [[nodiscard]] inline DeviceAllocator& GetAllocator() noexcept { return m_allocator; }
[[nodiscard]] inline bool StackSubmits() const noexcept { return m_stack_submits; }
[[nodiscard]] inline std::shared_ptr<class Shader> GetDefaultVertexShader() const { return m_internal_shaders[DEFAULT_VERTEX_SHADER_ID]; } [[nodiscard]] inline std::shared_ptr<class Shader> GetDefaultVertexShader() const { return m_internal_shaders[DEFAULT_VERTEX_SHADER_ID]; }
[[nodiscard]] inline std::shared_ptr<class Shader> GetBasicFragmentShader() const { return m_internal_shaders[BASIC_FRAGMENT_SHADER_ID]; } [[nodiscard]] inline std::shared_ptr<class Shader> GetBasicFragmentShader() const { return m_internal_shaders[BASIC_FRAGMENT_SHADER_ID]; }
[[nodiscard]] inline std::shared_ptr<class Shader> GetDefaultFragmentShader() const { return m_internal_shaders[DEFAULT_FRAGMENT_SHADER_ID]; } [[nodiscard]] inline std::shared_ptr<class Shader> GetDefaultFragmentShader() const { return m_internal_shaders[DEFAULT_FRAGMENT_SHADER_ID]; }
inline void WaitDeviceIdle() const noexcept { vkDeviceWaitIdle(m_device); } inline void WaitDeviceIdle() const noexcept { vkDeviceWaitIdle(m_device); }
inline void WaitQueueIdle(KvfQueueType queue) const noexcept { vkQueueWaitIdle(kvfGetDeviceQueue(m_device, queue)); }
inline static bool IsInit() noexcept { return s_instance != nullptr; } inline static bool IsInit() noexcept { return s_instance != nullptr; }
inline static RenderCore& Get() noexcept { return *s_instance; } inline static RenderCore& Get() noexcept { return *s_instance; }
inline void ShouldStackSubmits(bool should) noexcept { m_stack_submits = should; }
#define SCOP_VULKAN_GLOBAL_FUNCTION(fn) PFN_##fn fn = nullptr; #define SCOP_VULKAN_GLOBAL_FUNCTION(fn) PFN_##fn fn = nullptr;
#define SCOP_VULKAN_INSTANCE_FUNCTION(fn) PFN_##fn fn = nullptr; #define SCOP_VULKAN_INSTANCE_FUNCTION(fn) PFN_##fn fn = nullptr;
#define SCOP_VULKAN_DEVICE_FUNCTION(fn) PFN_##fn fn = nullptr; #define SCOP_VULKAN_DEVICE_FUNCTION(fn) PFN_##fn fn = nullptr;
@@ -70,6 +71,7 @@ namespace Scop
VkInstance m_instance = VK_NULL_HANDLE; VkInstance m_instance = VK_NULL_HANDLE;
VkDevice m_device = VK_NULL_HANDLE; VkDevice m_device = VK_NULL_HANDLE;
VkPhysicalDevice m_physical_device = VK_NULL_HANDLE; VkPhysicalDevice m_physical_device = VK_NULL_HANDLE;
bool m_stack_submits = false;
}; };
} }

View File

@@ -141,7 +141,7 @@ namespace Scop
ImGui::Text("Swapchain images count %ld", p_renderer->GetSwapchain().GetSwapchainImages().size()); ImGui::Text("Swapchain images count %ld", p_renderer->GetSwapchain().GetSwapchainImages().size());
ImGui::Text("Drawcalls %ld", p_renderer->GetDrawCallsCounterRef()); ImGui::Text("Drawcalls %ld", p_renderer->GetDrawCallsCounterRef());
ImGui::Text("Polygon drawn %ld", p_renderer->GetPolygonDrawnCounterRef()); ImGui::Text("Polygon drawn %ld", p_renderer->GetPolygonDrawnCounterRef());
ImGui::Text("Allocations count %ld", RenderCore::Get().GetAllocator().GetAllocationsCount()); ImGui::Text("Allocations count %ld / %u", RenderCore::Get().GetAllocator().GetAllocationsCount(), props.limits.maxMemoryAllocationCount);
ImGui::Text("Buffer count %ld", GPUBuffer::GetBufferCount()); ImGui::Text("Buffer count %ld", GPUBuffer::GetBufferCount());
ImGui::Text("Image count %ld", Image::GetImageCount()); ImGui::Text("Image count %ld", Image::GetImageCount());
ImGui::Text("Window dimensions: %ux%u", p_renderer->GetWindow()->GetWidth(), p_renderer->GetWindow()->GetHeight()); ImGui::Text("Window dimensions: %ux%u", p_renderer->GetWindow()->GetWidth(), p_renderer->GetWindow()->GetHeight());

View File

@@ -12,13 +12,17 @@ namespace Scop
if(p_mesh) if(p_mesh)
m_materials.resize(p_mesh->GetSubMeshCount() + 1); m_materials.resize(p_mesh->GetSubMeshCount() + 1);
if(!s_default_material)
{
CPUBuffer default_pixels{ kvfFormatSize(VK_FORMAT_R8G8B8A8_SRGB) }; CPUBuffer default_pixels{ kvfFormatSize(VK_FORMAT_R8G8B8A8_SRGB) };
default_pixels.GetDataAs<std::uint32_t>()[0] = 0xFFFFFFFF; default_pixels.GetDataAs<std::uint32_t>()[0] = 0xFFFFFFFF;
std::shared_ptr<Texture> default_texture = std::make_shared<Texture>(std::move(default_pixels), 1, 1, VK_FORMAT_R8G8B8A8_SRGB); std::shared_ptr<Texture> default_texture = std::make_shared<Texture>(std::move(default_pixels), 1, 1, VK_FORMAT_R8G8B8A8_SRGB);
MaterialTextures textures; MaterialTextures textures;
textures.albedo = default_texture; textures.albedo = default_texture;
m_materials.back() = std::make_shared<Material>(textures); s_default_material = std::make_shared<Material>(textures);
}
m_materials.back() = s_default_material;
} }
void Model::Draw(VkCommandBuffer cmd, const DescriptorSet& matrices_set, const GraphicPipeline& pipeline, DescriptorSet& set, std::size_t& drawcalls, std::size_t& polygondrawn, std::size_t frame_index) const void Model::Draw(VkCommandBuffer cmd, const DescriptorSet& matrices_set, const GraphicPipeline& pipeline, DescriptorSet& set, std::size_t& drawcalls, std::size_t& polygondrawn, std::size_t frame_index) const

View File

@@ -4,7 +4,7 @@
namespace Scop namespace Scop
{ {
void GPUBuffer::Init(BufferType type, VkDeviceSize size, VkBufferUsageFlags usage, CPUBuffer data, std::string_view name) void GPUBuffer::Init(BufferType type, VkDeviceSize size, VkBufferUsageFlags usage, CPUBuffer data, std::string_view name, bool dedicated_alloc)
{ {
if(type == BufferType::Constant) if(type == BufferType::Constant)
{ {
@@ -30,7 +30,7 @@ namespace Scop
if(type == BufferType::Staging && data.Empty()) if(type == BufferType::Staging && data.Empty())
Warning("Vulkan: trying to create staging buffer without data (wtf?)"); Warning("Vulkan: trying to create staging buffer without data (wtf?)");
CreateBuffer(size, m_usage, m_flags, std::move(name)); CreateBuffer(size, m_usage, m_flags, std::move(name), dedicated_alloc);
if(!data.Empty()) if(!data.Empty())
{ {
@@ -41,7 +41,7 @@ namespace Scop
PushToGPU(); PushToGPU();
} }
void GPUBuffer::CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, std::string_view name) void GPUBuffer::CreateBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, std::string_view name, bool dedicated_alloc)
{ {
auto device = RenderCore::Get().GetDevice(); auto device = RenderCore::Get().GetDevice();
m_buffer = kvfCreateBuffer(device, usage, size); m_buffer = kvfCreateBuffer(device, usage, size);
@@ -49,7 +49,7 @@ namespace Scop
VkMemoryRequirements mem_requirements; VkMemoryRequirements mem_requirements;
RenderCore::Get().vkGetBufferMemoryRequirements(device, m_buffer, &mem_requirements); RenderCore::Get().vkGetBufferMemoryRequirements(device, m_buffer, &mem_requirements);
m_memory = RenderCore::Get().GetAllocator().Allocate(size, mem_requirements.alignment, *FindMemoryType(mem_requirements.memoryTypeBits, properties)); m_memory = RenderCore::Get().GetAllocator().Allocate(size, mem_requirements.alignment, *FindMemoryType(mem_requirements.memoryTypeBits, properties), dedicated_alloc);
//m_memory = RenderCore::Get().GetAllocator().Allocate(mem_requirements.size, mem_requirements.alignment, *FindMemoryType(mem_requirements.memoryTypeBits, properties)); //m_memory = RenderCore::Get().GetAllocator().Allocate(mem_requirements.size, mem_requirements.alignment, *FindMemoryType(mem_requirements.memoryTypeBits, properties));
RenderCore::Get().vkBindBufferMemory(device, m_buffer, m_memory.memory, m_memory.offset); RenderCore::Get().vkBindBufferMemory(device, m_buffer, m_memory.memory, m_memory.offset);
@@ -73,12 +73,10 @@ namespace Scop
name_info.objectHandle = reinterpret_cast<std::uint64_t>(m_buffer); name_info.objectHandle = reinterpret_cast<std::uint64_t>(m_buffer);
name_info.pObjectName = m_name.c_str(); name_info.pObjectName = m_name.c_str();
RenderCore::Get().vkSetDebugUtilsObjectNameEXT(RenderCore::Get().GetDevice(), &name_info); RenderCore::Get().vkSetDebugUtilsObjectNameEXT(RenderCore::Get().GetDevice(), &name_info);
Message("Vulkan: % buffer created", m_name);
#else
Message("Vulkan: buffer created");
#endif #endif
m_is_dedicated_alloc = dedicated_alloc;
s_buffer_count++; s_buffer_count++;
} }
@@ -99,10 +97,16 @@ namespace Scop
kvfBeginCommandBuffer(cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); kvfBeginCommandBuffer(cmd, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
kvfCopyBufferToBuffer(cmd, m_buffer, buffer.Get(), buffer.GetSize(), src_offset, dst_offset); kvfCopyBufferToBuffer(cmd, m_buffer, buffer.Get(), buffer.GetSize(), src_offset, dst_offset);
kvfEndCommandBuffer(cmd); kvfEndCommandBuffer(cmd);
if(!RenderCore::Get().StackSubmits())
{
VkFence fence = kvfCreateFence(RenderCore::Get().GetDevice()); VkFence fence = kvfCreateFence(RenderCore::Get().GetDevice());
kvfSubmitSingleTimeCommandBuffer(RenderCore::Get().GetDevice(), cmd, KVF_GRAPHICS_QUEUE, fence); kvfSubmitSingleTimeCommandBuffer(RenderCore::Get().GetDevice(), cmd, KVF_GRAPHICS_QUEUE, fence);
kvfWaitForFence(RenderCore::Get().GetDevice(), fence); kvfWaitForFence(RenderCore::Get().GetDevice(), fence);
kvfDestroyFence(RenderCore::Get().GetDevice(), fence); kvfDestroyFence(RenderCore::Get().GetDevice(), fence);
kvfDestroyCommandBuffer(RenderCore::Get().GetDevice(), cmd);
}
else
kvfSubmitSingleTimeCommandBuffer(RenderCore::Get().GetDevice(), cmd, KVF_GRAPHICS_QUEUE, VK_NULL_HANDLE);
return true; return true;
} }
@@ -111,12 +115,11 @@ namespace Scop
GPUBuffer new_buffer; GPUBuffer new_buffer;
new_buffer.m_usage = (this->m_usage & 0xFFFFFFFC) | VK_BUFFER_USAGE_TRANSFER_DST_BIT; new_buffer.m_usage = (this->m_usage & 0xFFFFFFFC) | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
new_buffer.m_flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; new_buffer.m_flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
new_buffer.CreateBuffer(m_memory.size, new_buffer.m_usage, new_buffer.m_flags, m_name); new_buffer.CreateBuffer(m_memory.size, new_buffer.m_usage, new_buffer.m_flags, m_name, m_is_dedicated_alloc);
if(new_buffer.CopyFrom(*this)) if(new_buffer.CopyFrom(*this))
Swap(new_buffer); Swap(new_buffer);
new_buffer.Destroy(); new_buffer.Destroy();
Message("Vulkan: pushed buffer to GPU memory");
} }
void GPUBuffer::Destroy() noexcept void GPUBuffer::Destroy() noexcept
@@ -128,7 +131,6 @@ namespace Scop
RenderCore::Get().GetAllocator().Deallocate(m_memory); RenderCore::Get().GetAllocator().Deallocate(m_memory);
m_buffer = VK_NULL_HANDLE; m_buffer = VK_NULL_HANDLE;
m_memory = NULL_MEMORY_BLOCK; m_memory = NULL_MEMORY_BLOCK;
Message("Vulkan: destroyed buffer");
s_buffer_count--; s_buffer_count--;
} }

View File

@@ -57,9 +57,6 @@ namespace Scop
name_info.objectHandle = reinterpret_cast<std::uint64_t>(m_image); name_info.objectHandle = reinterpret_cast<std::uint64_t>(m_image);
name_info.pObjectName = name.data(); name_info.pObjectName = name.data();
RenderCore::Get().vkSetDebugUtilsObjectNameEXT(RenderCore::Get().GetDevice(), &name_info); RenderCore::Get().vkSetDebugUtilsObjectNameEXT(RenderCore::Get().GetDevice(), &name_info);
Message("Vulkan: % image created", name);
#else
Message("Vulkan: image created");
#endif #endif
s_image_count++; s_image_count++;
@@ -151,7 +148,6 @@ namespace Scop
m_memory = NULL_MEMORY_BLOCK; m_memory = NULL_MEMORY_BLOCK;
kvfDestroyImage(RenderCore::Get().GetDevice(), m_image); kvfDestroyImage(RenderCore::Get().GetDevice(), m_image);
} }
Message("Vulkan: image destroyed");
m_image = VK_NULL_HANDLE; m_image = VK_NULL_HANDLE;
m_memory = NULL_MEMORY_BLOCK; m_memory = NULL_MEMORY_BLOCK;
m_image = VK_NULL_HANDLE; m_image = VK_NULL_HANDLE;

View File

@@ -6,8 +6,8 @@
namespace Scop namespace Scop
{ {
MemoryChunk::MemoryChunk(VkDevice device, VkPhysicalDevice physical, VkDeviceSize size, std::int32_t memory_type_index) MemoryChunk::MemoryChunk(VkDevice device, VkPhysicalDevice physical, VkDeviceSize size, std::int32_t memory_type_index, bool is_dedicated)
: m_device(device), m_physical(physical), m_size(size), m_memory_type_index(memory_type_index) : m_device(device), m_physical(physical), m_size(size), m_memory_type_index(memory_type_index), m_is_dedicated(is_dedicated)
{ {
Verify(device != VK_NULL_HANDLE, "Memory Chunk : invalid device"); Verify(device != VK_NULL_HANDLE, "Memory Chunk : invalid device");
VkMemoryAllocateInfo alloc_info{}; VkMemoryAllocateInfo alloc_info{};

View File

@@ -12,6 +12,7 @@ namespace Scop
{ {
Verify(m_device != VK_NULL_HANDLE, "invalid device"); Verify(m_device != VK_NULL_HANDLE, "invalid device");
Verify(m_physical != VK_NULL_HANDLE, "invalid physical device"); Verify(m_physical != VK_NULL_HANDLE, "invalid physical device");
const std::lock_guard<std::mutex> guard(m_alloc_mutex);
if(!dedicated_chunk) if(!dedicated_chunk)
{ {
for(auto& chunk : m_chunks) for(auto& chunk : m_chunks)
@@ -24,7 +25,7 @@ namespace Scop
} }
} }
} }
m_chunks.emplace_back(std::make_unique<MemoryChunk>(m_device, m_physical, (CHUNK_SIZE < size + alignment ? size + alignment : CHUNK_SIZE), memory_type_index)); m_chunks.emplace_back(std::make_unique<MemoryChunk>(m_device, m_physical, (CHUNK_SIZE < size + alignment ? size + alignment : CHUNK_SIZE), memory_type_index, dedicated_chunk));
std::optional<MemoryBlock> block = m_chunks.back()->Allocate(size, alignment); std::optional<MemoryBlock> block = m_chunks.back()->Allocate(size, alignment);
m_allocations_count++; m_allocations_count++;
if(block.has_value()) if(block.has_value())
@@ -37,11 +38,17 @@ namespace Scop
{ {
Verify(m_device != VK_NULL_HANDLE, "invalid device"); Verify(m_device != VK_NULL_HANDLE, "invalid device");
Verify(m_physical != VK_NULL_HANDLE, "invalid physical device"); Verify(m_physical != VK_NULL_HANDLE, "invalid physical device");
for(auto& chunk : m_chunks) const std::lock_guard<std::mutex> guard(m_dealloc_mutex);
for(auto it = m_chunks.begin(); it != m_chunks.end(); ++it)
{ {
if(chunk->Has(block)) if((*it)->Has(block))
{ {
chunk->Deallocate(block); (*it)->Deallocate(block);
if((*it)->IsDedicated())
{
m_chunks.erase(it);
m_allocations_count--;
}
return; return;
} }
} }