Skip to content

Commit

Permalink
Add a MaterialManager.
Browse files Browse the repository at this point in the history
More SoA stuff. Ensures we get one global bindless array of images.
  • Loading branch information
Themaister committed Dec 12, 2023
1 parent cc51654 commit f6c37cd
Show file tree
Hide file tree
Showing 9 changed files with 282 additions and 45 deletions.
4 changes: 4 additions & 0 deletions application/application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "application.hpp"
#include "asset_manager.hpp"
#include "thread_group.hpp"
#include "material_manager.hpp"
#ifdef HAVE_GRANITE_RENDERER
#include "common_renderer_data.hpp"
#endif
Expand Down Expand Up @@ -336,5 +337,8 @@ void Application::post_frame()
if (auto *manager = GRANITE_ASSET_MANAGER())
manager->iterate(GRANITE_THREAD_GROUP());
}

if (auto *manager = Global::material_manager())
manager->iterate(Global::asset_manager());
}
}
15 changes: 15 additions & 0 deletions application/global/global_managers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ struct GlobalManagers

FilesystemInterface *filesystem;
AssetManagerInterface *asset_manager;
MaterialManagerInterface *material_manager;
EventManagerInterface *event_manager;
ThreadGroupInterface *thread_group;
UI::UIManagerInterface *ui_manager;
Expand Down Expand Up @@ -93,6 +94,11 @@ AssetManagerInterface *asset_manager()
return global_managers.asset_manager;
}

MaterialManagerInterface *material_manager()
{
return global_managers.material_manager;
}

EventManagerInterface *event_manager()
{
return global_managers.event_manager;
Expand Down Expand Up @@ -152,6 +158,12 @@ void init(Factory &factory, ManagerFeatureFlags flags, unsigned max_threads, flo
global_managers.asset_manager = factory.create_asset_manager();
}

if (flags & MANAGER_FEATURE_MATERIAL_MANAGER_BIT)
{
if (!global_managers.material_manager)
global_managers.material_manager = factory.create_material_manager();
}

bool kick_threads = false;
if (flags & MANAGER_FEATURE_THREAD_GROUP_BIT)
{
Expand Down Expand Up @@ -233,6 +245,7 @@ void deinit()
delete global_managers.common_renderer_data;
delete global_managers.ui_manager;
delete global_managers.thread_group;
delete global_managers.material_manager;
delete global_managers.asset_manager;
delete global_managers.filesystem;
delete global_managers.event_manager;
Expand All @@ -243,6 +256,7 @@ void deinit()
global_managers.physics = nullptr;
global_managers.common_renderer_data = nullptr;
global_managers.filesystem = nullptr;
global_managers.material_manager = nullptr;
global_managers.asset_manager = nullptr;
global_managers.event_manager = nullptr;
global_managers.thread_group = nullptr;
Expand Down Expand Up @@ -281,6 +295,7 @@ void stop_audio_system()

FilesystemInterface *Factory::create_filesystem() { return nullptr; }
AssetManagerInterface *Factory::create_asset_manager() { return nullptr; }
MaterialManagerInterface *Factory::create_material_manager() { return nullptr; }
EventManagerInterface *Factory::create_event_manager() { return nullptr; }
ThreadGroupInterface *Factory::create_thread_group() { return nullptr; }
CommonRendererDataInterface *Factory::create_common_renderer_data() { return nullptr; }
Expand Down
5 changes: 5 additions & 0 deletions application/global/global_managers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ enum ManagerFeatureFlagBits
MANAGER_FEATURE_PHYSICS_BIT = 1 << 7,
MANAGER_FEATURE_LOGGING_BIT = 1 << 8,
MANAGER_FEATURE_ASSET_MANAGER_BIT = 1 << 9,
MANAGER_FEATURE_MATERIAL_MANAGER_BIT = 1 << 10,
MANAGER_FEATURE_DEFAULT_BITS = (MANAGER_FEATURE_FILESYSTEM_BIT |
MANAGER_FEATURE_ASSET_MANAGER_BIT |
MANAGER_FEATURE_MATERIAL_MANAGER_BIT |
MANAGER_FEATURE_EVENT_BIT |
MANAGER_FEATURE_THREAD_GROUP_BIT |
MANAGER_FEATURE_COMMON_RENDERER_DATA_BIT |
Expand All @@ -64,6 +66,7 @@ class Factory

virtual FilesystemInterface *create_filesystem();
virtual AssetManagerInterface *create_asset_manager();
virtual MaterialManagerInterface *create_material_manager();
virtual EventManagerInterface *create_event_manager();
virtual ThreadGroupInterface *create_thread_group();
virtual CommonRendererDataInterface *create_common_renderer_data();
Expand Down Expand Up @@ -100,6 +103,7 @@ void install_audio_system(Audio::BackendInterface *backend, Audio::MixerInterfac
Util::MessageQueueInterface *message_queue();
FilesystemInterface *filesystem();
AssetManagerInterface *asset_manager();
MaterialManagerInterface *material_manager();
EventManagerInterface *event_manager();
ThreadGroupInterface *thread_group();
UI::UIManagerInterface *ui_manager();
Expand All @@ -113,6 +117,7 @@ PhysicsSystemInterface *physics();
#define GRANITE_MESSAGE_QUEUE() static_cast<::Util::MessageQueue *>(::Granite::Global::message_queue())
#define GRANITE_FILESYSTEM() static_cast<::Granite::Filesystem *>(::Granite::Global::filesystem())
#define GRANITE_ASSET_MANAGER() static_cast<::Granite::AssetManager *>(::Granite::Global::asset_manager())
#define GRANITE_MATERIAL_MANAGER() static_cast<::Granite::MaterialManager *>(::Granite::Global::material_manager())
#define GRANITE_EVENT_MANAGER() static_cast<::Granite::EventManager *>(::Granite::Global::event_manager())
#define GRANITE_THREAD_GROUP() static_cast<::Granite::ThreadGroup *>(::Granite::Global::thread_group())
#define GRANITE_UI_MANAGER() static_cast<::Granite::UI::UIManager *>(::Granite::Global::ui_manager())
Expand Down
10 changes: 10 additions & 0 deletions application/global/global_managers_init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "thread_group.hpp"
#include "filesystem.hpp"
#include "asset_manager.hpp"
#include "material_manager.hpp"
#ifdef HAVE_GRANITE_RENDERER
#include "common_renderer_data.hpp"
#include "ui_manager.hpp"
Expand Down Expand Up @@ -82,6 +83,15 @@ struct FactoryImplementation : Factory
#endif
}

MaterialManager *create_material_manager() override
{
#ifdef HAVE_GRANITE_RENDERER
return new MaterialManager;
#else
return nullptr;
#endif
}

Audio::MixerInterface *create_audio_mixer() override
{
#ifdef HAVE_GRANITE_AUDIO
Expand Down
7 changes: 7 additions & 0 deletions application/global/global_managers_interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ class AssetManagerInterface
virtual ~AssetManagerInterface() = default;
};

class MaterialManagerInterface
{
public:
virtual ~MaterialManagerInterface() = default;
virtual void iterate(AssetManagerInterface *iface) = 0;
};

class ThreadGroupInterface
{
public:
Expand Down
1 change: 1 addition & 0 deletions renderer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ add_granite_internal_lib(granite-renderer
render_components.hpp render_components.cpp
mesh_util.hpp mesh_util.cpp
material_util.hpp material_util.cpp
material_manager.hpp material_manager.cpp
renderer.hpp renderer.cpp
flat_renderer.hpp flat_renderer.cpp
renderer_enums.hpp
Expand Down
126 changes: 126 additions & 0 deletions renderer/material_manager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#include "material_manager.hpp"
#include "device.hpp"
#include "limits.hpp"
#include "hashmap.hpp"

namespace Granite
{
MaterialManager::MaterialManager()
{
EVENT_MANAGER_REGISTER_LATCH(MaterialManager, on_device_created, on_device_destroyed, Vulkan::DeviceCreatedEvent);
material_payload.reserve(Vulkan::VULKAN_MAX_UBO_SIZE / MaterialPayloadSize);
bindless_texture_assets.reserve(Vulkan::VULKAN_NUM_BINDINGS_BINDLESS_VARYING);
allocator.reserve_max_resources_per_pool(256, 8 * Vulkan::VULKAN_NUM_BINDINGS_BINDLESS_VARYING);
allocator.set_bindless_resource_type(Vulkan::BindlessResourceType::Image);
}

MaterialOffsets MaterialManager::register_material(
const AssetID *assets, unsigned count, const void *payload_data,
size_t payload_size, bool force_unique)
{
Util::Hasher hasher;
if (!force_unique)
{
for (unsigned i = 0; i < count; i++)
hasher.u32(assets[i].id);
hasher.data(static_cast<const uint8_t *>(payload_data), payload_size);
}
auto *group = force_unique ? nullptr : material.find(hasher.get());

if (group)
return group->offsets;

if (bindless_texture_assets.size() + count > Vulkan::VULKAN_NUM_BINDINGS_BINDLESS_VARYING)
LOGE("Exceeding number of bindless slots.\n");
if (payload_size && material_payload.size() >= Vulkan::VULKAN_MAX_UBO_SIZE / MaterialPayloadSize)
LOGE("Exceeding number of material payload slots.\n");

MaterialOffsets offsets = {};
offsets.texture_offset = uint32_t(bindless_texture_assets.size());
offsets.uniform_offset = payload_size ? uint32_t(material_payload.size()) : UINT16_MAX;

if (!force_unique)
{
group = material.allocate();
group->offsets = offsets;
material.insert_replace(hasher.get(), group);
}

bindless_texture_assets.insert(bindless_texture_assets.end(), assets, assets + count);

if (payload_size)
{
material_payload.emplace_back();
memcpy(material_payload.back().raw, payload_data, payload_size);
}

return offsets;
}

void MaterialManager::set_material_payloads(Vulkan::CommandBuffer &cmd, unsigned set_index, unsigned binding)
{
if (material_payload.empty())
{
void *data = cmd.allocate_constant_data(set_index, binding, sizeof(MaterialRawPayload));
memset(data, 0, sizeof(MaterialRawPayload));
}
else
{
void *data = cmd.allocate_constant_data(set_index, binding, material_payload.size() * sizeof(MaterialRawPayload));
memcpy(data, material_payload.data(), material_payload.size() * sizeof(MaterialRawPayload));
}
}

void MaterialManager::set_bindless(Vulkan::CommandBuffer &cmd, unsigned set_index)
{
if (vk_set == VK_NULL_HANDLE)
{
allocator.begin();
vk_set = allocator.commit(*device);
}

cmd.set_bindless(set_index, vk_set);
}

void MaterialManager::iterate(AssetManagerInterface *)
{
auto &res = device->get_resource_manager();
allocator.begin();
for (auto &id : bindless_texture_assets)
allocator.push(*res.get_image_view(id));
vk_set = allocator.commit(*device);
}

void MaterialManager::on_device_created(const Vulkan::DeviceCreatedEvent &e)
{
device = &e.get_device();
}

void MaterialManager::on_device_destroyed(const Vulkan::DeviceCreatedEvent &)
{
allocator.reset();
device = nullptr;
}
}
98 changes: 98 additions & 0 deletions renderer/material_manager.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#pragma once
#include "global_managers.hpp"
#include "event.hpp"
#include "descriptor_set.hpp"
#include "asset_manager.hpp"
#include "application_wsi_events.hpp"
#include "hashmap.hpp"
#include <assert.h>
#include <stddef.h>

namespace Vulkan
{
class CommandBuffer;
};

namespace Granite
{
static constexpr uint32_t MaterialPayloadSize = 32;

struct MaterialOffsets
{
uint16_t texture_offset;
uint16_t uniform_offset;
};

class MaterialManager final : public MaterialManagerInterface, public EventHandler
{
public:
MaterialManager();
void iterate(AssetManagerInterface *iface) override;

MaterialOffsets register_material(const AssetID *assets, unsigned count,
const void *payload_data, size_t payload_size,
bool force_unique = false /* can be used for "animating" material properties */);

template <typename T>
inline T &get_material_payload(const MaterialOffsets &offsets)
{
static_assert(sizeof(T) <= MaterialPayloadSize, "sizeof(T) is too large.");
static_assert(alignof(T) <= alignof(max_align_t), "alignof(T) is too large.");
assert(offsets.uniform_offset < material_payload.size());
return reinterpret_cast<T &>(material_payload[offsets.uniform_offset]);
}

const AssetID *get_asset_ids(const MaterialOffsets &offsets)
{
assert(offsets.texture_offset < bindless_texture_assets.size());
return bindless_texture_assets.data() + offsets.texture_offset;
}

void set_bindless(Vulkan::CommandBuffer &cmd, unsigned set_index);
void set_material_payloads(Vulkan::CommandBuffer &cmd, unsigned set_index, unsigned binding);

private:
Vulkan::BindlessAllocator allocator;
Vulkan::Device *device = nullptr;

void on_device_created(const Vulkan::DeviceCreatedEvent &e);
void on_device_destroyed(const Vulkan::DeviceCreatedEvent &e);

struct MaterialRawPayload
{
MaterialRawPayload() {}
uint32_t raw[MaterialPayloadSize / sizeof(uint32_t)];
};

std::vector<MaterialRawPayload> material_payload;
std::vector<AssetID> bindless_texture_assets;
struct MaterialGroup : Util::IntrusiveHashMapEnabled<MaterialGroup>
{
MaterialOffsets offsets;
};
Util::IntrusiveHashMap<MaterialGroup> material;
VkDescriptorSet vk_set = VK_NULL_HANDLE;
};
}
Loading

0 comments on commit f6c37cd

Please sign in to comment.