Completely rework texture loading.
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "KeyboardMovementController.hpp"
|
||||
#include "assets/stores.hpp"
|
||||
#include "engine/Average.hpp"
|
||||
#include "engine/buffers/UniqueFrameSuballocation.hpp"
|
||||
#include "engine/debug/drawers.hpp"
|
||||
@@ -39,6 +40,19 @@ namespace fgl::engine
|
||||
|
||||
static Average< float, 60 * 15 > rolling_ms_average;
|
||||
|
||||
void preStage( vk::CommandBuffer& cmd_buffer )
|
||||
{
|
||||
ZoneScopedN( "Pre-Stage" );
|
||||
|
||||
getTextureStore().stage( cmd_buffer );
|
||||
}
|
||||
|
||||
void postStage()
|
||||
{
|
||||
ZoneScopedN( "Post-Stage" );
|
||||
getTextureStore().confirmStaged();
|
||||
}
|
||||
|
||||
void EngineContext::run()
|
||||
{
|
||||
TracyCZoneN( TRACY_PrepareEngine, "Inital Run", true );
|
||||
@@ -54,12 +68,9 @@ namespace fgl::engine
|
||||
PerFrameSuballocation< HostSingleT< PointLight > > point_lights { global_ubo_buffer,
|
||||
SwapChain::MAX_FRAMES_IN_FLIGHT };
|
||||
|
||||
Texture debug_tex { Texture::loadFromFile( "models/textures/DebugTexture.png" ) };
|
||||
Sampler sampler { vk::Filter::eLinear,
|
||||
vk::Filter::eLinear,
|
||||
vk::SamplerMipmapMode::eLinear,
|
||||
vk::SamplerAddressMode::eClampToEdge };
|
||||
debug_tex.getImageView().getSampler() = std::move( sampler );
|
||||
std::shared_ptr< Texture > debug_tex {
|
||||
getTextureStore().load( "models/textures/DebugTexture.png", vk::Format::eR8G8B8A8Unorm )
|
||||
};
|
||||
|
||||
constexpr std::uint32_t matrix_default_size { 64_MiB };
|
||||
constexpr std::uint32_t draw_parameter_default_size { 64_MiB };
|
||||
@@ -146,6 +157,8 @@ namespace fgl::engine
|
||||
|
||||
if ( auto command_buffer = m_renderer.beginFrame(); command_buffer )
|
||||
{
|
||||
preStage( command_buffer );
|
||||
|
||||
ZoneScopedN( "Render" );
|
||||
//Update
|
||||
const std::uint16_t frame_index { m_renderer.getFrameIndex() };
|
||||
@@ -194,6 +207,8 @@ namespace fgl::engine
|
||||
|
||||
FrameMark;
|
||||
}
|
||||
|
||||
postStage();
|
||||
}
|
||||
|
||||
Device::getInstance().device().waitIdle();
|
||||
@@ -259,6 +274,7 @@ namespace fgl::engine
|
||||
}
|
||||
}*/
|
||||
|
||||
/*
|
||||
{
|
||||
ZoneScopedN( "Load phyiscs test" );
|
||||
std::vector< std::shared_ptr< Model > > models { Model::createModelsFromScene(
|
||||
@@ -312,6 +328,7 @@ namespace fgl::engine
|
||||
|
||||
m_game_objects_root.addGameObject( std::move( floor ) );
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
{
|
||||
|
||||
18
src/engine/assets/AssetManager.cpp
Normal file
18
src/engine/assets/AssetManager.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// Created by kj16609 on 6/6/24.
|
||||
//
|
||||
|
||||
#include "AssetManager.hpp"
|
||||
|
||||
#include "stores.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
inline static TextureStore tex_store {};
|
||||
|
||||
TextureStore& getTextureStore()
|
||||
{
|
||||
return tex_store;
|
||||
}
|
||||
|
||||
} // namespace fgl::engine
|
||||
117
src/engine/assets/AssetManager.hpp
Normal file
117
src/engine/assets/AssetManager.hpp
Normal file
@@ -0,0 +1,117 @@
|
||||
//
|
||||
// Created by kj16609 on 6/6/24.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
template < typename T >
|
||||
class AssetStore;
|
||||
|
||||
template < typename T >
|
||||
struct AssetInterface
|
||||
{
|
||||
//! Stages the asset to the device (GPU)
|
||||
virtual void stage( vk::CommandBuffer& buffer ) = 0;
|
||||
|
||||
friend class AssetStore< T >;
|
||||
|
||||
bool m_been_staged { false };
|
||||
|
||||
public:
|
||||
|
||||
//! Is the Asset ready to be used. (Returns false if not staged)
|
||||
inline bool isReady() const { return m_been_staged; }
|
||||
|
||||
inline void setReady() { m_been_staged = true; };
|
||||
|
||||
virtual ~AssetInterface() = default;
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
class AssetStore
|
||||
{
|
||||
static_assert(
|
||||
std::is_base_of_v< AssetInterface< T >, T >, "AssetStore<T, TKey>: T must inherit from AssetInterface" );
|
||||
|
||||
//! Items that are actively in use.
|
||||
//std::unordered_map< TKey, std::weak_ptr< T > > active_map {};
|
||||
|
||||
//! Assets needing to be staged
|
||||
//TODO: ASYNC Ring buffer queue
|
||||
std::queue< std::shared_ptr< T > > to_stage;
|
||||
//TODO: Add tracy monitor to mutex
|
||||
std::mutex queue_mtx {};
|
||||
|
||||
//! Assets currently being staged.
|
||||
std::vector< std::shared_ptr< T > > processing {};
|
||||
|
||||
public:
|
||||
|
||||
//TODO: Come up with a better design the the loading function.
|
||||
/// We should have a way to prevent a asset from being loaded multiple times.
|
||||
template < typename... T_Args >
|
||||
//TODO: This genuinely seems like a GCC Bug. Perhaps try to find a workaround later or report as such.
|
||||
//requires std::constructible_from< T, T_Args... >
|
||||
std::shared_ptr< T > load( T_Args&&... args )
|
||||
{
|
||||
ZoneScoped;
|
||||
std::lock_guard guard { queue_mtx };
|
||||
|
||||
T* ptr { new T( std::forward< T_Args >( args )... ) };
|
||||
std::shared_ptr< T > s_ptr { ptr };
|
||||
|
||||
to_stage.push( s_ptr );
|
||||
|
||||
return s_ptr;
|
||||
}
|
||||
|
||||
//! Returns true if all items to be staged were submitted to the queue
|
||||
//! Returns false if more items remain
|
||||
bool stage( vk::CommandBuffer& buffer )
|
||||
{
|
||||
ZoneScoped;
|
||||
std::lock_guard guard { queue_mtx };
|
||||
//! Number of items to process during a stage step
|
||||
constexpr std::size_t max_count { 16 };
|
||||
|
||||
for ( std::size_t i = 0; i < max_count; ++i )
|
||||
{
|
||||
if ( to_stage.empty() ) break;
|
||||
|
||||
processing.emplace_back( to_stage.front() );
|
||||
to_stage.pop();
|
||||
}
|
||||
|
||||
if ( processing.size() == 0 ) return true;
|
||||
|
||||
for ( const auto& ptr : processing )
|
||||
{
|
||||
ptr->stage( buffer );
|
||||
}
|
||||
|
||||
return to_stage.empty();
|
||||
}
|
||||
|
||||
void confirmStaged()
|
||||
{
|
||||
for ( const auto& ptr : processing )
|
||||
{
|
||||
ptr->dropStaging();
|
||||
ptr->setReady();
|
||||
}
|
||||
|
||||
//TODO: Map this into a weak ptr in order to prevent duplication.
|
||||
processing.clear();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace fgl::engine
|
||||
17
src/engine/assets/stores.hpp
Normal file
17
src/engine/assets/stores.hpp
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// Created by kj16609 on 6/7/24.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "engine/texture/Texture.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
class Texture;
|
||||
|
||||
using TextureStore = AssetStore< Texture>;
|
||||
|
||||
TextureStore& getTextureStore();
|
||||
|
||||
} // namespace fgl::engine
|
||||
@@ -87,15 +87,21 @@ namespace fgl::engine
|
||||
descriptor_writes.push_back( write );
|
||||
}
|
||||
|
||||
void DescriptorSet::bindTexture( std::uint32_t binding_idx, Texture& tex )
|
||||
void DescriptorSet::bindTexture( std::uint32_t binding_idx, std::shared_ptr< Texture >& tex_ptr )
|
||||
{
|
||||
assert( binding_idx < m_infos.size() && "Binding index out of range" );
|
||||
assert(
|
||||
std::holds_alternative< std::monostate >( m_infos[ binding_idx ] )
|
||||
&& "Update must be called between each array bind" );
|
||||
|
||||
assert( tex_ptr );
|
||||
|
||||
//TODO: Bind temporary texture if tex_ptr is not ready.
|
||||
|
||||
Texture& tex { *tex_ptr };
|
||||
|
||||
m_infos[ binding_idx ] = tex.getImageView().descriptorInfo(
|
||||
tex.getImageView().getSampler()->getVkSampler(), vk::ImageLayout::eShaderReadOnlyOptimal );
|
||||
tex.getImageView().getSampler().getVkSampler(), vk::ImageLayout::eShaderReadOnlyOptimal );
|
||||
|
||||
vk::WriteDescriptorSet write {};
|
||||
write.dstSet = m_set;
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace fgl::engine
|
||||
void bindAttachment(
|
||||
std::uint32_t binding_idx, ImageView& view, vk::ImageLayout layout, vk::Sampler sampler = VK_NULL_HANDLE );
|
||||
|
||||
void bindTexture( std::uint32_t binding_idx, Texture& tex );
|
||||
void bindTexture( std::uint32_t binding_idx, std::shared_ptr< Texture >& tex_ptr );
|
||||
|
||||
void setName( const std::string str );
|
||||
};
|
||||
|
||||
@@ -4,49 +4,43 @@
|
||||
|
||||
#include "FileBrowser.hpp"
|
||||
|
||||
#include "engine/assets/stores.hpp"
|
||||
#include "engine/filesystem/scanner/FileScanner.hpp"
|
||||
#include "engine/gui/safe_include.hpp"
|
||||
#include "engine/image/ImageView.hpp"
|
||||
#include "engine/image/Sampler.hpp"
|
||||
#include "engine/texture/Texture.hpp"
|
||||
|
||||
namespace fgl::engine::filesystem
|
||||
{
|
||||
|
||||
inline static std::vector< std::unique_ptr< FileScanner > > scanners {};
|
||||
inline static FileScanner* current_scanner { nullptr };
|
||||
inline static std::optional< DirInfo > root {};
|
||||
inline static DirInfo* current { nullptr };
|
||||
inline static std::once_flag flag {};
|
||||
inline static std::optional< Texture > folder_texture { std::nullopt };
|
||||
inline static std::shared_ptr< Texture > folder_texture { nullptr };
|
||||
inline static std::shared_ptr< Texture > file_texture { nullptr };
|
||||
|
||||
const std::filesystem::path path { "/home/kj16609/Desktop/Projects/cxx/Mecha/models" };
|
||||
const std::filesystem::path test_path { "/home/kj16609/Desktop/Projects/cxx/Mecha/models" };
|
||||
|
||||
void prepareFileGUI()
|
||||
{
|
||||
ZoneScoped;
|
||||
scanners.emplace_back( std::make_unique< FileScanner >( path ) );
|
||||
|
||||
//Prepare textures needed.
|
||||
folder_texture = Texture::loadFromFile( "./models/folder.png" );
|
||||
Sampler sampler { vk::Filter::eLinear,
|
||||
vk::Filter::eLinear,
|
||||
vk::SamplerMipmapMode::eLinear,
|
||||
vk::SamplerAddressMode::eClampToEdge };
|
||||
|
||||
folder_texture->getImageView().getSampler() = std::move( sampler );
|
||||
folder_texture = getTextureStore().load( "./models/folder.png", vk::Format::eR8G8B8A8Unorm );
|
||||
file_texture = getTextureStore().load( "./models/file.png", vk::Format::eR8G8B8A8Unorm );
|
||||
|
||||
auto cmd_buffer { Device::getInstance().beginSingleTimeCommands() };
|
||||
|
||||
folder_texture->stage( cmd_buffer );
|
||||
|
||||
Device::getInstance().endSingleTimeCommands( cmd_buffer );
|
||||
|
||||
folder_texture->dropStaging();
|
||||
root = DirInfo( test_path );
|
||||
current = &root.value();
|
||||
}
|
||||
|
||||
void FileBrowser::drawGui( FrameInfo& info )
|
||||
{
|
||||
ZoneScoped;
|
||||
//std::call_once( flag, []() { scanners.emplace_back( std::make_unique< FileScanner >( path ) ); } );
|
||||
//std::call_once( flag, []() { scanners.emplace_back( std::make_unique< FileScanner >( test_path ) ); } );
|
||||
std::call_once( flag, []() { prepareFileGUI(); } );
|
||||
|
||||
/*
|
||||
@@ -67,23 +61,24 @@ namespace fgl::engine::filesystem
|
||||
}
|
||||
*/
|
||||
|
||||
ImGui::Text( "Scanners: %ld", scanners.size() );
|
||||
|
||||
const auto size { ImGui::GetWindowSize() };
|
||||
constexpr float desired_size { 128.0f };
|
||||
const float extra { std::fmod( size.x, desired_size ) };
|
||||
const auto cols { ( size.x - extra ) / desired_size };
|
||||
|
||||
if ( ImGui::BeginTable( "Files", cols ) )
|
||||
if ( current && ImGui::BeginTable( "Files", cols ) )
|
||||
{
|
||||
for ( auto& scanner : scanners )
|
||||
//List folders first
|
||||
for ( std::size_t i = 0; i < current->folderCount(); ++i )
|
||||
{
|
||||
//Print out all files found at the inital depth
|
||||
for ( const FileInfo& file : *scanner )
|
||||
{
|
||||
if ( file.depth > 1 ) continue;
|
||||
if ( ImGui::TableNextColumn() ) drawFile( file );
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
drawFolder( current->dir( i ) );
|
||||
}
|
||||
|
||||
for ( std::size_t i = 0; i < current->fileCount(); ++i )
|
||||
{
|
||||
ImGui::TableNextColumn();
|
||||
drawFile( current->file( i ) );
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
@@ -92,19 +87,62 @@ namespace fgl::engine::filesystem
|
||||
ImGui::Columns( 1 );
|
||||
}
|
||||
|
||||
void FileBrowser::drawFile( const FileInfo& data )
|
||||
enum FileType
|
||||
{
|
||||
TEXTURE,
|
||||
MODEL,
|
||||
BINARY,
|
||||
DEFAULT = BINARY,
|
||||
};
|
||||
|
||||
FileType getFileType( const std::filesystem::path path )
|
||||
{
|
||||
//TODO: NEVER TRUST FILE EXTENSIONS!
|
||||
const auto extension { path.extension() };
|
||||
|
||||
//Map
|
||||
static const std::map< FileType, std::vector< std::string_view > > map {
|
||||
{ TEXTURE, { ".jpg", ".png" } }, { MODEL, { ".glb", ".obj", ".gltf" } }
|
||||
};
|
||||
|
||||
for ( const auto& [ type, extensions ] : map )
|
||||
{
|
||||
//Check if the file extensions matches the list for this type
|
||||
if ( std::find( extensions.begin(), extensions.end(), extension ) != extensions.end() )
|
||||
{
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
//Default
|
||||
return DEFAULT;
|
||||
}
|
||||
|
||||
void drawTexture()
|
||||
{}
|
||||
|
||||
void drawModel()
|
||||
{}
|
||||
|
||||
void drawBinary()
|
||||
{}
|
||||
|
||||
void FileBrowser::drawFile( FileInfo& data )
|
||||
{
|
||||
ZoneScoped;
|
||||
ImGui::PushID( data.path.c_str() );
|
||||
ImGui::Text( data.filename.c_str() );
|
||||
ImGui::Text( data.ext.c_str() );
|
||||
|
||||
if ( data.is_folder ) // Folders have no extension
|
||||
// file_texture->drawImGui( { 128, 128 } );
|
||||
file_texture->drawImGuiButton( { 128, 128 } );
|
||||
if ( ImGui::BeginDragDropSource() )
|
||||
{
|
||||
if ( folder_texture->drawImGuiButton( { 128, 128 } ) )
|
||||
{
|
||||
std::cout << "Pressed thing" << std::endl;
|
||||
}
|
||||
ImGui::
|
||||
SetDragDropPayload( "_FILE_INFO", &data, sizeof( data ), ImGuiCond_Once /* Only copy the data once */ );
|
||||
ImGui::SetTooltip( data.filename.c_str() );
|
||||
|
||||
ImGui::EndDragDropSource();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
@@ -113,4 +151,27 @@ namespace fgl::engine::filesystem
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
void FileBrowser::drawFolder( DirInfo& data )
|
||||
{
|
||||
ZoneScoped;
|
||||
ImGui::PushID( data.path.c_str() );
|
||||
|
||||
ImGui::Text( data.path.filename().c_str() );
|
||||
ImGui::Text( "Folder" );
|
||||
|
||||
if ( folder_texture->drawImGuiButton( { 128, 128 } ) )
|
||||
{
|
||||
openFolder( data );
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
void FileBrowser::openFolder( DirInfo& dir )
|
||||
{
|
||||
if ( !current ) throw std::runtime_error( "No current folder?" );
|
||||
|
||||
current = &dir;
|
||||
}
|
||||
|
||||
} // namespace fgl::engine::filesystem
|
||||
|
||||
@@ -18,12 +18,15 @@ namespace fgl::engine::filesystem
|
||||
{
|
||||
static FileBrowser& getInstance();
|
||||
|
||||
static void goUp();
|
||||
|
||||
static void addFolderToRoot();
|
||||
|
||||
static void openFolderRoot( const std::string str );
|
||||
static void openFolder( DirInfo& dir );
|
||||
|
||||
static void drawGui( FrameInfo& info );
|
||||
static void drawFile( const FileInfo& data );
|
||||
static void drawFile( FileInfo& data );
|
||||
static void drawFolder( DirInfo& data );
|
||||
};
|
||||
|
||||
} // namespace fgl::engine::filesystem
|
||||
@@ -8,144 +8,59 @@
|
||||
|
||||
#include "FileScanner.hpp"
|
||||
|
||||
#include <queue>
|
||||
|
||||
#include "engine/logging/logging.hpp"
|
||||
|
||||
namespace fgl::engine::filesystem
|
||||
{
|
||||
|
||||
FileInfo& FileScannerGenerator::operator()()
|
||||
DirInfo::DirInfo( const std::filesystem::path& dir, DirInfo* dir_parent ) :
|
||||
path( dir ),
|
||||
total_size( 0 ),
|
||||
parent( dir_parent )
|
||||
{
|
||||
if ( m_h.done() ) throw std::runtime_error( "FileScannerGenerator is done but operator was still called" );
|
||||
m_h();
|
||||
|
||||
if ( m_h.promise().exception ) std::rethrow_exception( m_h.promise().exception );
|
||||
if ( m_h.promise().value.has_value() )
|
||||
for ( auto itter = std::filesystem::directory_iterator( dir ); itter != std::filesystem::directory_iterator();
|
||||
++itter )
|
||||
{
|
||||
return m_h.promise().value.value();
|
||||
if ( itter->is_regular_file() )
|
||||
{
|
||||
files.emplace_back( *itter );
|
||||
}
|
||||
else if ( itter->is_directory() )
|
||||
{
|
||||
nested_dirs_to_scan.push( *itter );
|
||||
}
|
||||
else
|
||||
throw std::runtime_error( "Unknown/Unspported file type" );
|
||||
}
|
||||
else
|
||||
throw std::runtime_error( "Failed to get value from FileScannerGenerator." );
|
||||
}
|
||||
|
||||
FileScannerGenerator scan_files( const std::filesystem::path path )
|
||||
std::size_t DirInfo::fileCount() const
|
||||
{
|
||||
if ( !std::filesystem::exists( path ) )
|
||||
return files.size();
|
||||
}
|
||||
|
||||
FileInfo& DirInfo::file( const std::size_t index )
|
||||
{
|
||||
return files[ index ];
|
||||
}
|
||||
|
||||
DirInfo& DirInfo::dir( const std::size_t index )
|
||||
{
|
||||
if ( index >= nested_dirs.size() + nested_dirs_to_scan.size() ) throw std::runtime_error( "Index OOB" );
|
||||
|
||||
if ( index >= nested_dirs.size() )
|
||||
{
|
||||
log::error( "Expected path does not exist: {}", path.string() );
|
||||
throw std::runtime_error( format_ns::format( "Path {} does not exist.", path ).c_str() );
|
||||
}
|
||||
|
||||
auto dir_empty = []( const std::filesystem::path& dir_path ) -> bool
|
||||
{ return std::filesystem::directory_iterator( dir_path ) == std::filesystem::directory_iterator(); };
|
||||
|
||||
if ( dir_empty( path ) ) co_return FileInfo { path, path, 0, 0 };
|
||||
|
||||
std::queue< std::pair< std::filesystem::path, std::uint8_t > > dirs {};
|
||||
|
||||
dirs.push( { path, 0 } );
|
||||
|
||||
while ( dirs.size() > 0 )
|
||||
{
|
||||
const auto [ dir, depth ] { std::move( dirs.front() ) };
|
||||
dirs.pop();
|
||||
std::vector< std::filesystem::path > nested_dirs {};
|
||||
|
||||
log::debug( "Searching {}", dir );
|
||||
|
||||
//Recurse through the directory.
|
||||
for ( auto itter = std::filesystem::directory_iterator( dir );
|
||||
itter != std::filesystem::directory_iterator(); )
|
||||
while ( nested_dirs.size() <= index )
|
||||
{
|
||||
log::debug( "Found: {}", dir );
|
||||
if ( itter->is_directory() )
|
||||
{
|
||||
//Add directory to scan list.
|
||||
nested_dirs.emplace_back( *itter );
|
||||
++itter;
|
||||
continue;
|
||||
}
|
||||
auto to_scan { std::move( nested_dirs_to_scan.front() ) };
|
||||
nested_dirs_to_scan.pop();
|
||||
log::debug( "Processed folder: {}", to_scan );
|
||||
|
||||
FileInfo info {
|
||||
*itter, path, itter->is_regular_file() ? itter->file_size() : 0, std::uint8_t( depth + 1 )
|
||||
};
|
||||
|
||||
++itter;
|
||||
|
||||
//If we are at the last file and there are no more directories to scan then return.
|
||||
if ( itter == std::filesystem::directory_iterator() && dirs.size() == 0 && nested_dirs.size() == 0 )
|
||||
{
|
||||
log::debug( "co_return: {}", info.path );
|
||||
co_return std::move( info );
|
||||
}
|
||||
else
|
||||
{
|
||||
log::debug( "co_yield: {}", info.path );
|
||||
co_yield std::move( info );
|
||||
}
|
||||
}
|
||||
|
||||
//Add the nested dirs to the scanlist and yield them
|
||||
for ( std::size_t i = 0; i < nested_dirs.size(); ++i )
|
||||
{
|
||||
FileInfo info { nested_dirs.at( i ), path, 0, std::uint8_t( depth + 1 ) };
|
||||
// Check if the directory is empty and if it's not then add it to the scan queue.
|
||||
|
||||
const bool is_empty { dir_empty( nested_dirs.at( i ) ) };
|
||||
|
||||
//If the dir is empty, if we are done searching our current nested list, and there are no more dirs to process, Return
|
||||
if ( is_empty && i == nested_dirs.size() - 1 && dirs.size() == 0 )
|
||||
{
|
||||
log::debug( "co_return: {}", info.path );
|
||||
co_return std::move( info );
|
||||
}
|
||||
|
||||
if ( !is_empty )
|
||||
{
|
||||
dirs.push( { nested_dirs.at( i ), depth + 1 } );
|
||||
}
|
||||
|
||||
co_yield std::move( info );
|
||||
nested_dirs.emplace_back( to_scan, this );
|
||||
}
|
||||
}
|
||||
|
||||
spdlog::critical( "Got to an illegal spot!" );
|
||||
std::unreachable();
|
||||
return nested_dirs[ index ];
|
||||
}
|
||||
|
||||
FileScanner::FileScanner( const std::filesystem::path& path ) : m_path( path ), file_scanner( scan_files( path ) )
|
||||
{
|
||||
files.emplace_back( std::move( file_scanner() ) );
|
||||
}
|
||||
|
||||
const FileInfo& FileScanner::at( std::size_t index )
|
||||
{
|
||||
if ( index >= files.size() && !file_scanner.m_h.done() )
|
||||
{
|
||||
// Index is higher then what we have.
|
||||
// Scanner is also NOT done.
|
||||
// We use the coroutine to fetch what the next file should be.
|
||||
|
||||
std::size_t diff { index - ( files.size() - 1 ) };
|
||||
|
||||
while ( diff > 0 && !file_scanner.m_h.done() )
|
||||
{
|
||||
files.emplace_back( std::move( file_scanner() ) );
|
||||
--diff;
|
||||
}
|
||||
}
|
||||
|
||||
if ( index >= files.size() )
|
||||
throw std::
|
||||
runtime_error( format_ns::format( "index = {}: size < index : {} < {}", index, files.size(), index )
|
||||
.c_str() );
|
||||
|
||||
return files.at( index );
|
||||
}
|
||||
|
||||
bool FileScanner::iterator::operator==( const std::unreachable_sentinel_t ) const
|
||||
{
|
||||
return m_scanner.file_scanner.m_h.done() && ( m_idx == m_scanner.files.size() );
|
||||
}
|
||||
} // namespace fgl::engine::filesystem
|
||||
@@ -3,19 +3,47 @@
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#ifndef ATLASGAMEMANAGER_FILESCANNER_HPP
|
||||
#define ATLASGAMEMANAGER_FILESCANNER_HPP
|
||||
|
||||
#include <coroutine>
|
||||
#include <exception>
|
||||
#include <filesystem>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "engine/logging/logging.hpp"
|
||||
|
||||
namespace fgl::engine::filesystem
|
||||
{
|
||||
struct FileInfo;
|
||||
|
||||
struct DirInfo
|
||||
{
|
||||
std::filesystem::path path;
|
||||
std::size_t total_size;
|
||||
std::vector< FileInfo > files {};
|
||||
std::vector< DirInfo > nested_dirs {};
|
||||
std::queue< std::filesystem::path > nested_dirs_to_scan {};
|
||||
DirInfo* parent { nullptr };
|
||||
|
||||
public:
|
||||
|
||||
inline DirInfo* up() const { return parent; }
|
||||
|
||||
std::size_t fileCount() const;
|
||||
FileInfo& file( const std::size_t index );
|
||||
|
||||
inline std::size_t folderCount() const { return nested_dirs.size() + nested_dirs_to_scan.size(); }
|
||||
|
||||
DirInfo& dir( const std::size_t index );
|
||||
|
||||
DirInfo() = delete;
|
||||
|
||||
DirInfo( const std::filesystem::path& path, DirInfo* parent = nullptr );
|
||||
|
||||
DirInfo( const DirInfo& other ) = delete;
|
||||
DirInfo& operator=( const DirInfo& other ) = delete;
|
||||
|
||||
DirInfo( DirInfo&& ) = default;
|
||||
DirInfo& operator=( DirInfo&& ) = default;
|
||||
};
|
||||
|
||||
struct FileInfo
|
||||
{
|
||||
@@ -23,23 +51,15 @@ namespace fgl::engine::filesystem
|
||||
std::string ext;
|
||||
std::filesystem::path path;
|
||||
std::size_t size;
|
||||
std::uint8_t depth;
|
||||
std::filesystem::path relative;
|
||||
bool is_folder;
|
||||
|
||||
FileInfo() = delete;
|
||||
|
||||
FileInfo(
|
||||
std::filesystem::path path_in,
|
||||
const std::filesystem::path& source,
|
||||
const std::size_t filesize,
|
||||
const std::uint8_t file_depth ) :
|
||||
FileInfo( std::filesystem::path path_in ) :
|
||||
filename( path_in.filename().string() ),
|
||||
ext( path_in.extension().string() ),
|
||||
path( path_in ),
|
||||
size( filesize ),
|
||||
depth( file_depth ),
|
||||
relative( std::filesystem::relative( std::move( path_in ), source ) ),
|
||||
size( std::filesystem::file_size( path_in ) ),
|
||||
is_folder( std::filesystem::is_directory( path ) )
|
||||
{}
|
||||
|
||||
@@ -50,117 +70,4 @@ namespace fgl::engine::filesystem
|
||||
FileInfo& operator=( FileInfo&& other ) = default;
|
||||
};
|
||||
|
||||
struct FileScannerGenerator
|
||||
{
|
||||
struct promise_type;
|
||||
using handle_type = std::coroutine_handle< promise_type >;
|
||||
|
||||
struct promise_type
|
||||
{
|
||||
std::optional< FileInfo > value { std::nullopt };
|
||||
std::exception_ptr exception { nullptr };
|
||||
|
||||
FileScannerGenerator get_return_object()
|
||||
{
|
||||
return FileScannerGenerator( handle_type::from_promise( *this ) );
|
||||
}
|
||||
|
||||
std::suspend_always initial_suspend() noexcept { return {}; }
|
||||
|
||||
std::suspend_always final_suspend() noexcept { return {}; }
|
||||
|
||||
void unhandled_exception()
|
||||
{
|
||||
exception = std::current_exception();
|
||||
log::critical( "Exception thrown in FileScanner" );
|
||||
}
|
||||
|
||||
void return_value( FileInfo&& from )
|
||||
{
|
||||
if ( from.filename == "" )
|
||||
throw std::runtime_error( "FileScannerGenerator: return value had no filename!" );
|
||||
|
||||
value = std::move( from );
|
||||
}
|
||||
|
||||
std::suspend_always yield_value( FileInfo&& from )
|
||||
{
|
||||
if ( from.filename == "" )
|
||||
throw std::runtime_error( "FromScannerGenerator:: yield value had no filename!" );
|
||||
|
||||
value = std::forward< FileInfo >( from );
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
handle_type m_h;
|
||||
|
||||
FileScannerGenerator() = delete;
|
||||
|
||||
FileScannerGenerator( handle_type h ) : m_h( h ) {}
|
||||
|
||||
~FileScannerGenerator()
|
||||
{
|
||||
log::debug( "Destroying Generator" );
|
||||
m_h.destroy();
|
||||
}
|
||||
|
||||
FileInfo& operator()();
|
||||
};
|
||||
|
||||
class FileScanner
|
||||
{
|
||||
private:
|
||||
|
||||
std::filesystem::path m_path;
|
||||
FileScannerGenerator file_scanner;
|
||||
std::vector< FileInfo > files {};
|
||||
const FileInfo& at( std::size_t index );
|
||||
|
||||
friend class iterator;
|
||||
|
||||
class iterator
|
||||
{
|
||||
std::size_t m_idx { 0 };
|
||||
FileScanner& m_scanner;
|
||||
|
||||
public:
|
||||
|
||||
iterator() = delete;
|
||||
|
||||
iterator( const std::size_t idx, FileScanner& scanner ) : m_idx( idx ), m_scanner( scanner ) {}
|
||||
|
||||
FileScanner::iterator& operator++()
|
||||
{
|
||||
++m_idx;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Operator != required to check for end I assume. Where if the this returns true then we are good to continue
|
||||
// So instead we can just return the state of the scanner. And if the scanner is complete then we'll return false here.
|
||||
//bool operator !=
|
||||
bool operator==( const std::unreachable_sentinel_t ) const;
|
||||
|
||||
// Required for the for loop
|
||||
const FileInfo& operator*() { return m_scanner.at( m_idx ); }
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
FileScanner() = delete;
|
||||
FileScanner( const std::filesystem::path& path );
|
||||
|
||||
FileScanner( const std::string_view path ) : FileScanner( std::filesystem::path( path ) ) {}
|
||||
|
||||
FileScanner( const FileScanner& other ) = delete; //Copy
|
||||
FileScanner& operator=( const FileScanner& other ) = delete; //Copy-Assign
|
||||
|
||||
iterator begin() { return iterator( 0, *this ); }
|
||||
|
||||
//This *probably* isn't required(?) but the for loop will want it anyways. So we can just return literaly anything here since it's not used anyways.
|
||||
std::unreachable_sentinel_t end() { return {}; }
|
||||
|
||||
std::filesystem::path path() const { return m_path; }
|
||||
};
|
||||
} // namespace fgl::engine::filesystem
|
||||
#endif //ATLASGAMEMANAGER_FILESCANNER_HPP
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
ImageView::ImageView( std::shared_ptr< ImageHandle >& img ) : m_resource( img )
|
||||
ImageView::ImageView( std::shared_ptr< ImageHandle >& img ) : m_resource( img ), m_sampler()
|
||||
{
|
||||
vk::ImageViewCreateInfo view_info {};
|
||||
view_info.image = img->getVkImage();
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace fgl::engine
|
||||
{
|
||||
std::shared_ptr< ImageHandle > m_resource;
|
||||
|
||||
std::optional< Sampler > m_sampler { std::nullopt };
|
||||
Sampler m_sampler;
|
||||
|
||||
vk::DescriptorImageInfo m_descriptor_info {};
|
||||
|
||||
@@ -38,7 +38,12 @@ namespace fgl::engine
|
||||
ImageView( ImageView&& other ) noexcept :
|
||||
m_resource( std::move( other.m_resource ) ),
|
||||
m_descriptor_info( std::move( other.m_descriptor_info ) ),
|
||||
m_image_view( std::move( other.m_image_view ) )
|
||||
m_image_view( std::move( other.m_image_view ) ),
|
||||
m_sampler(
|
||||
vk::Filter::eLinear,
|
||||
vk::Filter::eLinear,
|
||||
vk::SamplerMipmapMode::eLinear,
|
||||
vk::SamplerAddressMode::eClampToEdge )
|
||||
{
|
||||
other.m_image_view = VK_NULL_HANDLE;
|
||||
}
|
||||
@@ -59,7 +64,7 @@ namespace fgl::engine
|
||||
|
||||
vk::Image& getVkImage();
|
||||
|
||||
std::optional< Sampler >& getSampler() { return m_sampler; };
|
||||
Sampler& getSampler() { return m_sampler; };
|
||||
|
||||
vk::DescriptorImageInfo descriptorInfo( vk::Sampler sampler, vk::ImageLayout layout ) const;
|
||||
vk::DescriptorImageInfo descriptorInfo( vk::ImageLayout layout ) const;
|
||||
|
||||
@@ -16,7 +16,13 @@ namespace fgl::engine
|
||||
|
||||
public:
|
||||
|
||||
Sampler() = delete;
|
||||
Sampler() :
|
||||
Sampler(
|
||||
vk::Filter::eLinear,
|
||||
vk::Filter::eLinear,
|
||||
vk::SamplerMipmapMode::eLinear,
|
||||
vk::SamplerAddressMode::eClampToBorder )
|
||||
{}
|
||||
|
||||
Sampler(
|
||||
vk::Filter min_filter,
|
||||
|
||||
@@ -133,18 +133,13 @@ namespace fgl::engine
|
||||
return model_ptr;
|
||||
}
|
||||
|
||||
void Model::syncBuffers( vk::CommandBuffer& cmd_buffer )
|
||||
void Model::stage( vk::CommandBuffer& cmd_buffer )
|
||||
{
|
||||
assert( !m_primitives.empty() );
|
||||
for ( auto& primitive : m_primitives )
|
||||
{
|
||||
primitive.m_vertex_buffer.stage( cmd_buffer );
|
||||
primitive.m_index_buffer.stage( cmd_buffer );
|
||||
|
||||
if ( primitive.m_texture.has_value() )
|
||||
{
|
||||
primitive.m_texture->stage( cmd_buffer );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace fgl::engine
|
||||
static std::vector< std::shared_ptr< Model > > createModelsFromScene(
|
||||
Device& device, const std::filesystem::path& path, Buffer& vertex_buffer, Buffer& index_buffer );
|
||||
|
||||
void syncBuffers( vk::CommandBuffer& cmd_buffer );
|
||||
void stage( vk::CommandBuffer& cmd_buffer );
|
||||
|
||||
const std::string& getName() const { return m_name; }
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace fgl::engine
|
||||
OrientedBoundingBox< CoordinateSpace::Model > m_bounding_box;
|
||||
PrimitiveMode m_mode;
|
||||
|
||||
std::optional< Texture > m_texture { std::nullopt };
|
||||
std::shared_ptr< Texture > m_texture;
|
||||
|
||||
Primitive(
|
||||
VertexBufferSuballocation&& vertex_buffer,
|
||||
@@ -59,13 +59,13 @@ namespace fgl::engine
|
||||
VertexBufferSuballocation&& vertex_buffer,
|
||||
IndexBufferSuballocation&& index_buffer,
|
||||
const OrientedBoundingBox< CoordinateSpace::Model >& bounding_box,
|
||||
Texture&& texture,
|
||||
std::shared_ptr< Texture >&& texture,
|
||||
const PrimitiveMode mode ) :
|
||||
m_vertex_buffer( std::move( vertex_buffer ) ),
|
||||
m_index_buffer( std::move( index_buffer ) ),
|
||||
m_bounding_box( bounding_box ),
|
||||
m_mode( mode ),
|
||||
m_texture( std::move( texture ) )
|
||||
m_texture( std::forward< decltype( m_texture ) >( texture ) )
|
||||
{}
|
||||
|
||||
Primitive() = delete;
|
||||
|
||||
@@ -131,7 +131,7 @@ namespace fgl::engine
|
||||
return root.accessors.at( prim.attributes.at( attrib ) );
|
||||
}
|
||||
|
||||
Texture SceneBuilder::loadTexture( const tinygltf::Primitive& prim, const tinygltf::Model& root )
|
||||
std::shared_ptr< Texture > SceneBuilder::loadTexture( const tinygltf::Primitive& prim, const tinygltf::Model& root )
|
||||
{
|
||||
ZoneScoped;
|
||||
const auto mat_idx { prim.material };
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace fgl::engine
|
||||
const tinygltf::Accessor& getAccessorForAttribute(
|
||||
const tinygltf::Primitive& prim, const tinygltf::Model& root, const std::string attrib ) const;
|
||||
|
||||
Texture loadTexture( const tinygltf::Primitive& prim, const tinygltf::Model& root );
|
||||
std::shared_ptr< Texture > loadTexture( const tinygltf::Primitive& prim, const tinygltf::Model& root );
|
||||
|
||||
public:
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#include "ModelBuilder.hpp"
|
||||
#include "engine/assets/stores.hpp"
|
||||
#include "engine/descriptors/DescriptorSet.hpp"
|
||||
#include "engine/image/ImageView.hpp"
|
||||
#include "engine/image/Sampler.hpp"
|
||||
@@ -253,14 +254,18 @@ namespace fgl::engine
|
||||
sampler.wrapS == sampler.wrapT
|
||||
&& "Can't support different wrap modes for textures on each axis" );
|
||||
|
||||
Texture tex { Texture::loadFromFile( filepath.parent_path() / source.uri ) };
|
||||
//TOOD: Get format from texture info and convert to vkFOrmat
|
||||
|
||||
std::shared_ptr< Texture > tex {
|
||||
getTextureStore().load( filepath.parent_path() / source.uri, vk::Format::eR8G8B8A8Unorm )
|
||||
};
|
||||
Sampler smp { translateFilterToVK( sampler.minFilter ),
|
||||
translateFilterToVK( sampler.magFilter ),
|
||||
vk::SamplerMipmapMode::eLinear,
|
||||
translateWarppingToVk( sampler.wrapS ) };
|
||||
|
||||
tex.getImageView().getSampler() = std::move( smp );
|
||||
tex.createImGuiSet();
|
||||
tex->getImageView().getSampler() = std::move( smp );
|
||||
tex->createImGuiSet();
|
||||
|
||||
Texture::getTextureDescriptorSet().bindTexture( 0, tex );
|
||||
Texture::getTextureDescriptorSet().update();
|
||||
@@ -268,10 +273,6 @@ namespace fgl::engine
|
||||
//Stage texture
|
||||
auto cmd { Device::getInstance().beginSingleTimeCommands() };
|
||||
|
||||
tex.stage( cmd );
|
||||
Device::getInstance().endSingleTimeCommands( cmd );
|
||||
tex.dropStaging();
|
||||
|
||||
Primitive prim { std::move( vertex_buffer ),
|
||||
std::move( index_buffer ),
|
||||
bounding_box,
|
||||
|
||||
@@ -46,12 +46,12 @@ namespace fgl::engine
|
||||
// If the textureless flag is on and we have a texture then skip the primitive.c
|
||||
if ( options & TEXTURELESS )
|
||||
{
|
||||
if ( primitive.m_texture.has_value() ) continue;
|
||||
if ( primitive.m_texture ) continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Flag is not present
|
||||
if ( !primitive.m_texture.has_value() ) continue;
|
||||
if ( !primitive.m_texture ) continue;
|
||||
}
|
||||
|
||||
const auto key { std::make_pair( matrix_info.texture_idx, primitive.m_index_buffer.getOffset() ) };
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#pragma once
|
||||
#include "engine/GameObject.hpp"
|
||||
#include "engine/primitives/Frustum.hpp"
|
||||
#include "engine/texture/TextureHandle.hpp"
|
||||
#include "engine/texture/Texture.hpp"
|
||||
#include "engine/utils.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
|
||||
@@ -6,10 +6,8 @@
|
||||
|
||||
#include <initializer_list>
|
||||
|
||||
#include "TextureHandle.hpp"
|
||||
#include "engine/FrameInfo.hpp"
|
||||
#include "engine/buffers/BufferSuballocation.hpp"
|
||||
#include "engine/descriptors/DescriptorPool.hpp"
|
||||
#include "engine/descriptors/DescriptorSet.hpp"
|
||||
#include "engine/image/ImageView.hpp"
|
||||
#include "objectloaders/stb_image.h"
|
||||
@@ -27,9 +25,8 @@
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
inline static std::unordered_map< std::string, std::weak_ptr< TextureHandle > > texture_map;
|
||||
|
||||
std::tuple< std::vector< std::byte >, int, int, int > loadTexture( const std::filesystem::path& path )
|
||||
std::tuple< std::vector< std::byte >, int, int, vk::Format >
|
||||
loadTexture( const std::filesystem::path& path, const vk::Format format )
|
||||
{
|
||||
ZoneScoped;
|
||||
if ( !std::filesystem::exists( path ) ) throw std::runtime_error( "Failed to open file: " + path.string() );
|
||||
@@ -49,42 +46,14 @@ namespace fgl::engine
|
||||
|
||||
stbi_image_free( data_c );
|
||||
|
||||
return { std::move( data ), x, y, 4 };
|
||||
}
|
||||
//TODO: Write check to ensure the format matches the number of channels
|
||||
|
||||
Texture Texture::loadFromFile( const std::filesystem::path& path )
|
||||
{
|
||||
ZoneScoped;
|
||||
log::debug( "Loading texture: {}", path );
|
||||
//TODO: Make some way of cleaning the map when loading textures
|
||||
|
||||
if ( texture_map.contains( path.string() ) )
|
||||
{
|
||||
if ( auto itter = texture_map.find( path.string() ); !itter->second.expired() )
|
||||
{
|
||||
return Texture( itter->second.lock() );
|
||||
}
|
||||
else
|
||||
{
|
||||
//Texture is expired. So it'll need to be reloaded.
|
||||
texture_map.erase( itter );
|
||||
}
|
||||
}
|
||||
|
||||
const auto data { loadTexture( path ) };
|
||||
Texture tex { data };
|
||||
tex.m_handle->m_image_view->setName( path.string() );
|
||||
|
||||
texture_map.emplace( path.string(), tex.m_handle );
|
||||
|
||||
log::debug( "Loaded texture at {} with res {}x{}", path, tex.getExtent().width, tex.getExtent().height );
|
||||
|
||||
return std::move( tex );
|
||||
return { std::move( data ), x, y, format };
|
||||
}
|
||||
|
||||
void Texture::drawImGui( vk::Extent2D extent )
|
||||
{
|
||||
if ( this->m_handle->m_imgui_set == VK_NULL_HANDLE ) createImGuiSet();
|
||||
if ( this->m_imgui_set == VK_NULL_HANDLE ) createImGuiSet();
|
||||
|
||||
if ( extent == vk::Extent2D() )
|
||||
{
|
||||
@@ -98,7 +67,7 @@ namespace fgl::engine
|
||||
|
||||
bool Texture::drawImGuiButton( vk::Extent2D extent )
|
||||
{
|
||||
if ( this->m_handle->m_imgui_set == VK_NULL_HANDLE ) createImGuiSet();
|
||||
if ( this->m_imgui_set == VK_NULL_HANDLE ) createImGuiSet();
|
||||
|
||||
if ( extent == vk::Extent2D() )
|
||||
{
|
||||
@@ -107,44 +76,63 @@ namespace fgl::engine
|
||||
|
||||
const ImVec2 imgui_size { static_cast< float >( extent.width ), static_cast< float >( extent.height ) };
|
||||
|
||||
if ( !isReady() )
|
||||
{
|
||||
//TODO: Render placeholder
|
||||
log::warn( "Attempted to render texture {} but texture was not ready!", this->m_texture_id );
|
||||
return ImGui::Button( "No texture :(" );
|
||||
}
|
||||
|
||||
return ImGui::ImageButton( static_cast< ImTextureID >( getImGuiDescriptorSet() ), imgui_size );
|
||||
}
|
||||
|
||||
Texture Texture::generateFromPerlinNoise( int x_size, int y_size, std::size_t seed )
|
||||
{
|
||||
ZoneScoped;
|
||||
const std::vector< std::byte > data { generatePerlinImage( { x_size, y_size }, 15, seed ) };
|
||||
|
||||
Texture tex { data, x_size, y_size, 4 };
|
||||
|
||||
return std::move( tex );
|
||||
}
|
||||
|
||||
Texture::Texture( std::shared_ptr< TextureHandle > handle ) : m_handle( std::move( handle ) )
|
||||
{}
|
||||
|
||||
Texture::Texture( const std::tuple< std::vector< std::byte >, int, int, int >& tuple ) :
|
||||
Texture::Texture( const std::tuple< std::vector< std::byte >, int, int, vk::Format >& tuple ) :
|
||||
Texture( std::get< 0 >( tuple ), std::get< 1 >( tuple ), std::get< 2 >( tuple ), std::get< 3 >( tuple ) )
|
||||
{}
|
||||
|
||||
Texture::Texture( const std::vector< std::byte >& data, const int x, const int y, const int channels ) :
|
||||
Texture( data, vk::Extent2D( x, y ), channels )
|
||||
Texture::Texture( const std::vector< std::byte >& data, const int x, const int y, const vk::Format format ) :
|
||||
Texture( data, vk::Extent2D( x, y ), format )
|
||||
{}
|
||||
|
||||
Texture::Texture( const std::vector< std::byte >& data, const vk::Extent2D extent, const int channels ) :
|
||||
m_handle( std::make_shared< TextureHandle >( data, extent, channels ) )
|
||||
Texture::Texture( const std::vector< std::byte >& data, const vk::Extent2D extent, const vk::Format format ) :
|
||||
m_extent( extent )
|
||||
{
|
||||
ZoneScoped;
|
||||
static TextureID tex_counter { 0 };
|
||||
|
||||
auto image = std::make_shared< Image >(
|
||||
extent,
|
||||
format,
|
||||
vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled,
|
||||
vk::ImageLayout::eUndefined,
|
||||
vk::ImageLayout::eShaderReadOnlyOptimal );
|
||||
|
||||
m_image_view = image->getView();
|
||||
|
||||
m_texture_id = tex_counter++;
|
||||
|
||||
m_staging = std::make_unique< BufferSuballocation >( getGlobalStagingBuffer(), data.size() );
|
||||
//Copy data info buffer
|
||||
std::memcpy( reinterpret_cast< unsigned char* >( m_staging->ptr() ), data.data(), data.size() );
|
||||
}
|
||||
|
||||
Texture::Texture( const std::filesystem::path& path, const vk::Format format ) :
|
||||
Texture( loadTexture( path, format ) )
|
||||
{}
|
||||
|
||||
Texture::~Texture()
|
||||
{
|
||||
if ( m_imgui_set != VK_NULL_HANDLE ) ImGui_ImplVulkan_RemoveTexture( m_imgui_set );
|
||||
}
|
||||
|
||||
void Texture::stage( vk::CommandBuffer& cmd )
|
||||
{
|
||||
ZoneScoped;
|
||||
assert( m_handle && "Attempted to stage invalid texture (No handle)" );
|
||||
|
||||
//assert( m_handle->m_staging && "Can't stage. No staging buffer made" );
|
||||
//assert( m_staging && "Can't stage. No staging buffer made" );
|
||||
|
||||
//TODO: I need some way of telling if a Texture HAS been staged rather then simply checking if the staging buffer is present
|
||||
// Since just checking if the buffer is present could not mean it has been staged (IE staged but not dropped, Or never created in the first place)
|
||||
if ( !m_handle->m_staging ) return;
|
||||
// Texutres are made with a staging buffer in RAM, Thus if the buffer has been dropped then we have been sucesfully staged.
|
||||
if ( !m_staging ) return;
|
||||
|
||||
vk::ImageSubresourceRange range;
|
||||
range.aspectMask = vk::ImageAspectFlagBits::eColor;
|
||||
@@ -156,7 +144,7 @@ namespace fgl::engine
|
||||
vk::ImageMemoryBarrier barrier {};
|
||||
barrier.oldLayout = vk::ImageLayout::eUndefined;
|
||||
barrier.newLayout = vk::ImageLayout::eTransferDstOptimal;
|
||||
barrier.image = m_handle->m_image_view->getVkImage();
|
||||
barrier.image = m_image_view->getVkImage();
|
||||
barrier.subresourceRange = range;
|
||||
barrier.srcAccessMask = {};
|
||||
barrier.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
|
||||
@@ -172,7 +160,7 @@ namespace fgl::engine
|
||||
barriers_to );
|
||||
|
||||
vk::BufferImageCopy region {};
|
||||
region.bufferOffset = m_handle->m_staging->getOffset();
|
||||
region.bufferOffset = m_staging->getOffset();
|
||||
region.bufferRowLength = 0;
|
||||
region.bufferImageHeight = 0;
|
||||
|
||||
@@ -182,21 +170,17 @@ namespace fgl::engine
|
||||
region.imageSubresource.layerCount = 1;
|
||||
|
||||
region.imageOffset = vk::Offset3D( 0, 0, 0 );
|
||||
region.imageExtent = vk::Extent3D( m_handle->m_extent, 1 );
|
||||
region.imageExtent = vk::Extent3D( m_extent, 1 );
|
||||
|
||||
cmd.copyBufferToImage(
|
||||
m_handle->m_staging->getVkBuffer(),
|
||||
m_handle->m_image_view->getVkImage(),
|
||||
vk::ImageLayout::eTransferDstOptimal,
|
||||
1,
|
||||
®ion );
|
||||
m_staging->getVkBuffer(), m_image_view->getVkImage(), vk::ImageLayout::eTransferDstOptimal, 1, ®ion );
|
||||
|
||||
//Transfer back to eGeneral
|
||||
|
||||
vk::ImageMemoryBarrier barrier_from {};
|
||||
barrier_from.oldLayout = barrier.newLayout;
|
||||
barrier_from.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
|
||||
barrier_from.image = m_handle->m_image_view->getVkImage();
|
||||
barrier_from.image = m_image_view->getVkImage();
|
||||
barrier_from.subresourceRange = range;
|
||||
barrier_from.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
|
||||
barrier_from.dstAccessMask = vk::AccessFlagBits::eShaderRead;
|
||||
@@ -214,57 +198,55 @@ namespace fgl::engine
|
||||
|
||||
void Texture::dropStaging()
|
||||
{
|
||||
m_handle->m_staging.reset();
|
||||
assert( m_staging );
|
||||
m_staging.reset();
|
||||
}
|
||||
|
||||
vk::DescriptorImageInfo Texture::getDescriptor() const
|
||||
{
|
||||
return m_handle->m_image_view->descriptorInfo( vk::ImageLayout::eGeneral );
|
||||
return m_image_view->descriptorInfo( vk::ImageLayout::eGeneral );
|
||||
}
|
||||
|
||||
vk::Extent2D Texture::getExtent() const
|
||||
{
|
||||
return m_handle->m_image_view->getExtent();
|
||||
return m_image_view->getExtent();
|
||||
}
|
||||
|
||||
ImageView& Texture::getImageView()
|
||||
{
|
||||
assert( m_handle );
|
||||
assert( m_handle->m_image_view );
|
||||
return *m_handle->m_image_view;
|
||||
assert( m_image_view );
|
||||
return *m_image_view;
|
||||
}
|
||||
|
||||
void Texture::createImGuiSet()
|
||||
{
|
||||
#if ENABLE_IMGUI
|
||||
if ( m_handle->m_imgui_set != VK_NULL_HANDLE ) return;
|
||||
if ( m_imgui_set != VK_NULL_HANDLE ) return;
|
||||
|
||||
auto& view { m_handle->m_image_view };
|
||||
auto& view { m_image_view };
|
||||
|
||||
assert( view );
|
||||
|
||||
VkImageView vk_view { view->getVkView() };
|
||||
assert( vk_view );
|
||||
|
||||
assert( view->getSampler() );
|
||||
VkSampler vk_sampler { view->getSampler()->getVkSampler() };
|
||||
VkSampler vk_sampler { view->getSampler().getVkSampler() };
|
||||
|
||||
m_handle->m_imgui_set =
|
||||
ImGui_ImplVulkan_AddTexture( vk_sampler, vk_view, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL );
|
||||
m_imgui_set = ImGui_ImplVulkan_AddTexture( vk_sampler, vk_view, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL );
|
||||
#endif
|
||||
}
|
||||
|
||||
vk::DescriptorSet& Texture::getImGuiDescriptorSet()
|
||||
{
|
||||
assert( m_handle );
|
||||
assert( m_handle->m_imgui_set != VK_NULL_HANDLE );
|
||||
return m_handle->m_imgui_set;
|
||||
assert( !m_staging );
|
||||
assert( m_imgui_set != VK_NULL_HANDLE );
|
||||
return m_imgui_set;
|
||||
}
|
||||
|
||||
TextureID Texture::getID() const
|
||||
{
|
||||
assert( m_handle );
|
||||
return m_handle->m_texture_id;
|
||||
assert( !m_staging );
|
||||
return m_texture_id;
|
||||
}
|
||||
|
||||
DescriptorSet& Texture::getTextureDescriptorSet()
|
||||
|
||||
@@ -8,36 +8,62 @@
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#include "TextureHandle.hpp"
|
||||
#include "engine/assets/AssetManager.hpp"
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
class BufferSuballocation;
|
||||
class ImageView;
|
||||
class DescriptorSet;
|
||||
class TextureHandle;
|
||||
|
||||
//TODO: Implement texture handle map to avoid loading the same texture multiple times
|
||||
class Texture
|
||||
{
|
||||
std::shared_ptr< TextureHandle > m_handle;
|
||||
//! Has this texture been submitted to the GPU?
|
||||
bool submitte_to_gpu { false };
|
||||
using TextureID = std::uint32_t;
|
||||
|
||||
[[nodiscard]] Texture( const std::tuple< std::vector< std::byte >, int, int, int >& );
|
||||
[[nodiscard]] Texture( std::shared_ptr< TextureHandle > handle );
|
||||
class Texture;
|
||||
|
||||
using TextureStore = AssetStore< Texture >;
|
||||
|
||||
//TODO: Implement texture handle map to avoid loading the same texture multiple times
|
||||
class Texture final : public AssetInterface< Texture >, public std::enable_shared_from_this< Texture >
|
||||
{
|
||||
template < typename T >
|
||||
friend class AssetStore;
|
||||
|
||||
//TODO: Implement reusing texture ids
|
||||
TextureID m_texture_id { std::numeric_limits< TextureID >::infinity() };
|
||||
|
||||
std::shared_ptr< ImageView > m_image_view {};
|
||||
|
||||
std::unique_ptr< BufferSuballocation > m_staging { nullptr };
|
||||
|
||||
vk::Extent2D m_extent { 0, 0 };
|
||||
|
||||
vk::DescriptorSet m_imgui_set { VK_NULL_HANDLE };
|
||||
|
||||
[[nodiscard]] Texture( const std::tuple< std::vector< std::byte >, int, int, vk::Format >& );
|
||||
|
||||
[[nodiscard]]
|
||||
Texture( const std::vector< std::byte >& data, const int x, const int y, const vk::Format texture_format );
|
||||
|
||||
[[nodiscard]]
|
||||
Texture( const std::vector< std::byte >& data, const vk::Extent2D extent, const vk::Format texture_format );
|
||||
|
||||
[[nodiscard]] Texture( const std::filesystem::path& path, const vk::Format format );
|
||||
|
||||
void stage( vk::CommandBuffer& cmd ) override;
|
||||
void dropStaging();
|
||||
|
||||
public:
|
||||
|
||||
[[nodiscard]] Texture( const std::vector< std::byte >& data, const int x, const int y, const int channels );
|
||||
[[nodiscard]] Texture( const std::vector< std::byte >& data, const vk::Extent2D extent, const int channels );
|
||||
Texture() = delete;
|
||||
|
||||
Texture( const Texture& ) = default;
|
||||
~Texture();
|
||||
|
||||
Texture( const Texture& ) = delete;
|
||||
Texture& operator=( const Texture& ) = delete;
|
||||
|
||||
Texture( Texture&& other ) = default;
|
||||
Texture& operator=( Texture&& ) = default;
|
||||
|
||||
void stage( vk::CommandBuffer& cmd );
|
||||
void dropStaging();
|
||||
Texture( Texture&& other ) = delete;
|
||||
Texture& operator=( Texture&& ) = delete;
|
||||
|
||||
[[nodiscard]] TextureID getID() const;
|
||||
|
||||
@@ -50,11 +76,8 @@ namespace fgl::engine
|
||||
|
||||
void createImGuiSet();
|
||||
|
||||
[[nodiscard]] static Texture generateFromPerlinNoise( int x_size, int y_size, std::size_t seed = 0 );
|
||||
[[nodiscard]] static Texture loadFromFile( const std::filesystem::path& path );
|
||||
|
||||
void drawImGui( vk::Extent2D extent = {} );
|
||||
bool drawImGuiButton(vk::Extent2D extent = {});
|
||||
bool drawImGuiButton( vk::Extent2D extent = {} );
|
||||
|
||||
static DescriptorSet& getTextureDescriptorSet();
|
||||
};
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
//
|
||||
// Created by kj16609 on 1/22/24.
|
||||
//
|
||||
|
||||
#include "TextureHandle.hpp"
|
||||
|
||||
#include "engine/buffers/BufferSuballocation.hpp"
|
||||
#include "engine/image/Image.hpp"
|
||||
#include "engine/image/ImageView.hpp"
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Weffc++"
|
||||
#pragma GCC diagnostic ignored "-Wold-style-cast"
|
||||
#pragma GCC diagnostic ignored "-Wconversion"
|
||||
#include "imgui/backends/imgui_impl_vulkan.h"
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
|
||||
TextureHandle::TextureHandle(
|
||||
const std::vector< std::byte >& data, const vk::Extent2D extent, [[maybe_unused]] const int channels ) :
|
||||
m_extent( extent )
|
||||
{
|
||||
ZoneScoped;
|
||||
static TextureID tex_counter { 0 };
|
||||
|
||||
constexpr auto format { vk::Format::eR8G8B8A8Unorm };
|
||||
|
||||
auto image = std::make_shared< Image >(
|
||||
extent,
|
||||
format,
|
||||
vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled,
|
||||
vk::ImageLayout::eUndefined,
|
||||
vk::ImageLayout::eShaderReadOnlyOptimal );
|
||||
|
||||
m_image_view = image->getView();
|
||||
|
||||
m_texture_id = tex_counter++;
|
||||
|
||||
m_staging = std::make_unique< BufferSuballocation >( getGlobalStagingBuffer(), data.size() );
|
||||
//Copy data info buffer
|
||||
std::memcpy( reinterpret_cast< unsigned char* >( m_staging->ptr() ), data.data(), data.size() );
|
||||
}
|
||||
|
||||
TextureHandle::~TextureHandle()
|
||||
{
|
||||
if ( m_imgui_set != VK_NULL_HANDLE ) ImGui_ImplVulkan_RemoveTexture( m_imgui_set );
|
||||
}
|
||||
|
||||
} // namespace fgl::engine
|
||||
@@ -1,40 +0,0 @@
|
||||
//
|
||||
// Created by kj16609 on 1/22/24.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
namespace fgl::engine
|
||||
{
|
||||
class ImageView;
|
||||
class BufferSuballocation;
|
||||
|
||||
using TextureID = std::uint32_t;
|
||||
|
||||
class TextureHandle
|
||||
{
|
||||
//TODO: Implement reusing texture ids
|
||||
TextureID m_texture_id { std::numeric_limits< TextureID >::infinity() };
|
||||
|
||||
std::shared_ptr< ImageView > m_image_view {};
|
||||
|
||||
std::unique_ptr< BufferSuballocation > m_staging { nullptr };
|
||||
|
||||
vk::Extent2D m_extent { 0, 0 };
|
||||
|
||||
vk::DescriptorSet m_imgui_set { VK_NULL_HANDLE };
|
||||
|
||||
friend class Texture;
|
||||
|
||||
public:
|
||||
|
||||
TextureHandle( const std::vector< std::byte >& data, const vk::Extent2D extent, const int channels );
|
||||
~TextureHandle();
|
||||
};
|
||||
|
||||
} // namespace fgl::engine
|
||||
Reference in New Issue
Block a user