fixed oversight in uf::Mesh::convert, fixed texture streaming not working in OpenGL (the graphic.material.textures weren't updated) fixed missing texture being used in VXGI for vulkan (think it was from reinitializing the texture object itself if it was aliased rather than just resetting the handles)

This commit is contained in:
ecker 2025-08-14 23:32:08 -05:00
parent 9ec35a26e6
commit ab9aa0ae13
6 changed files with 136 additions and 100 deletions

1
.gitignore vendored
View File

@ -34,6 +34,7 @@
*.app *.app
*.spv *.spv
*.elf *.elf
*.elf.unstripped
# Logs # Logs
*.log *.log

View File

@ -78,7 +78,7 @@
"stream": { "stream": {
"tag": "worldspawn", "tag": "worldspawn",
"player": "info_player_spawn", "player": "info_player_spawn",
"enabled": "auto", "enabled": true, // "auto",
"radius": 32, "radius": 32,
"every": 4 "every": 4
} }

View File

@ -1 +1 @@
vulkan opengl

View File

@ -158,7 +158,10 @@ namespace uf {
} bounds; } bounds;
*/ */
uf::stl::vector<buffer_t> buffers; uf::stl::vector<buffer_t> buffers;
uf::stl::vector<uf::stl::string> buffer_paths; // crunge, but it's better this way
// crunge, but it's better this way
uf::stl::vector<uf::stl::string> buffer_paths;
uf::stl::vector<ext::RENDERER::AttributeDescriptor> buffer_descriptors;
protected: protected:
void _destroy( uf::Mesh::Input& input ); void _destroy( uf::Mesh::Input& input );
void _bind( bool interleaved = uf::Mesh::defaultInterleaved ); void _bind( bool interleaved = uf::Mesh::defaultInterleaved );
@ -300,32 +303,42 @@ namespace uf {
template<typename From, typename To> template<typename From, typename To>
void convert() { void convert() {
// if mesh data is interleaved, skip conversion
if ( this->isInterleaved() ) { if ( this->isInterleaved() ) {
UF_MSG_DEBUG("Downcasting/upcasting requested yet mesh is interleaved, ignoring..."); UF_MSG_DEBUG("Downcasting/upcasting requested yet mesh is interleaved, ignoring...");
return; return;
} }
auto fromEnum = uf::renderer::typeToEnum<From>(); auto fromEnum = uf::renderer::typeToEnum<From>();
auto toEnum = uf::renderer::typeToEnum<To>(); auto toEnum = uf::renderer::typeToEnum<To>();
if ( toEnum == fromEnum ) return; if ( toEnum == fromEnum ) return;
for ( auto& attribute : this->vertex.attributes ) { for ( auto& attribute : this->vertex.attributes ) {
if ( attribute.descriptor.type == toEnum ) continue;
if ( attribute.descriptor.type != fromEnum ) continue; if ( attribute.descriptor.type != fromEnum ) continue;
float scale = (float) sizeof(To) / (float) sizeof(From); size_t elementCount = this->vertex.count * attribute.descriptor.components;
size_t elements = this->vertex.count * attribute.descriptor.components;
size_t bytes = elements * sizeof(To);
auto& srcBuffer = this->buffers[attribute.buffer]; auto& srcBuffer = this->buffers[attribute.buffer];
if ( srcBuffer.empty() ) continue; if ( srcBuffer.empty() ) continue;
uf::stl::vector<uint8_t> dstBuffer( srcBuffer.size() * scale ); uf::stl::vector<uint8_t> dstBuffer( elementCount * sizeof(To) );
attribute.pointer = (uint8_t*) dstBuffer.data(); const From* srcPtr = (const From*) (srcBuffer.data());
attribute.length *= scale; To* dstPtr = (To*) (dstBuffer.data());
if ( toEnum == uf::renderer::enums::Type::USHORT )
for ( size_t i = 0; i < elementCount; ++i ) dstPtr[i] = uf::quant::quantize_f32u16(srcPtr[i]);
else if ( fromEnum == uf::renderer::enums::Type::USHORT )
for ( size_t i = 0; i < elementCount; ++i) dstPtr[i] = uf::quant::dequantize_u16f32(srcPtr[i]);
else
for ( size_t i = 0; i < elementCount; ++i ) dstPtr[i] = (To) srcPtr[i];
srcBuffer.swap( dstBuffer );
attribute.pointer = (uint8_t*) ( srcBuffer.data() );
attribute.descriptor.type = toEnum; attribute.descriptor.type = toEnum;
attribute.descriptor.size *= scale; attribute.descriptor.size = sizeof(To) * attribute.descriptor.components;
attribute.length = sizeof(To) * elementCount;
if ( toEnum == uf::renderer::enums::Type::FLOAT ) { if ( toEnum == uf::renderer::enums::Type::FLOAT ) {
switch ( attribute.descriptor.components ) { switch ( attribute.descriptor.components ) {
@ -334,14 +347,16 @@ namespace uf {
case 3: attribute.descriptor.format = uf::renderer::enums::Format::R32G32B32_SFLOAT; break; case 3: attribute.descriptor.format = uf::renderer::enums::Format::R32G32B32_SFLOAT; break;
case 4: attribute.descriptor.format = uf::renderer::enums::Format::R32G32B32A32_SFLOAT; break; case 4: attribute.descriptor.format = uf::renderer::enums::Format::R32G32B32A32_SFLOAT; break;
} }
} else if ( toEnum == uf::renderer::enums::Type::FLOAT16 ) { }
else if ( toEnum == uf::renderer::enums::Type::FLOAT16 ) {
switch ( attribute.descriptor.components ) { switch ( attribute.descriptor.components ) {
case 1: attribute.descriptor.format = uf::renderer::enums::Format::R16_SFLOAT; break; case 1: attribute.descriptor.format = uf::renderer::enums::Format::R16_SFLOAT; break;
case 2: attribute.descriptor.format = uf::renderer::enums::Format::R16G16_SFLOAT; break; case 2: attribute.descriptor.format = uf::renderer::enums::Format::R16G16_SFLOAT; break;
case 3: attribute.descriptor.format = uf::renderer::enums::Format::R16G16B16_SFLOAT; break; case 3: attribute.descriptor.format = uf::renderer::enums::Format::R16G16B16_SFLOAT; break;
case 4: attribute.descriptor.format = uf::renderer::enums::Format::R16G16B16A16_SFLOAT; break; case 4: attribute.descriptor.format = uf::renderer::enums::Format::R16G16B16A16_SFLOAT; break;
} }
} else if ( toEnum == uf::renderer::enums::Type::USHORT ) { }
else if ( toEnum == uf::renderer::enums::Type::USHORT ) {
switch ( attribute.descriptor.components ) { switch ( attribute.descriptor.components ) {
case 1: attribute.descriptor.format = uf::renderer::enums::Format::R16_UINT; break; case 1: attribute.descriptor.format = uf::renderer::enums::Format::R16_UINT; break;
case 2: attribute.descriptor.format = uf::renderer::enums::Format::R16G16_UINT; break; case 2: attribute.descriptor.format = uf::renderer::enums::Format::R16G16_UINT; break;
@ -349,20 +364,6 @@ namespace uf {
case 4: attribute.descriptor.format = uf::renderer::enums::Format::R16G16B16A16_UINT; break; case 4: attribute.descriptor.format = uf::renderer::enums::Format::R16G16B16A16_UINT; break;
} }
} }
From* srcPtr = (From*) (srcBuffer.data());
To* dstPtr = (To*) (dstBuffer.data());
if ( toEnum == uf::renderer::enums::Type::USHORT ) {
for ( size_t i = 0; i < elements; ++i ) dstPtr[i] = uf::quant::quantize_f32u16(srcPtr[i]);
} else if ( fromEnum == uf::renderer::enums::Type::USHORT ) {
for ( size_t i = 0; i < elements; ++i ) dstPtr[i] = uf::quant::dequantize_u16f32(srcPtr[i]);
} else {
for ( size_t i = 0; i < elements; ++i ) dstPtr[i] = srcPtr[i];
}
srcBuffer.swap( dstBuffer );
} }
} }
}; };
@ -421,10 +422,10 @@ namespace ext {
} }
namespace std { namespace std {
template <> template <>
struct hash<ext::RENDERER::GraphicDescriptor> { struct hash<ext::RENDERER::GraphicDescriptor> {
size_t operator()(const ext::RENDERER::GraphicDescriptor& descriptor) const { return descriptor.hash(); } size_t operator()(const ext::RENDERER::GraphicDescriptor& descriptor) const { return descriptor.hash(); }
}; };
} }
#undef UF_RENDERER #undef UF_RENDERER

View File

@ -231,6 +231,7 @@ namespace {
attribute.offset = value["offset"].as(attribute.offset);\ attribute.offset = value["offset"].as(attribute.offset);\
attribute.stride = value["stride"].as(attribute.stride);\ attribute.stride = value["stride"].as(attribute.stride);\
attribute.length = value["length"].as(attribute.length);\ attribute.length = value["length"].as(attribute.length);\
mesh.buffer_descriptors.emplace_back(attribute.descriptor);\
});\ });\
} }
@ -240,6 +241,8 @@ namespace {
DESERIALIZE_MESH(indirect); DESERIALIZE_MESH(indirect);
mesh.buffers.reserve( json["buffers"].size() ); mesh.buffers.reserve( json["buffers"].size() );
mesh.buffer_paths.reserve( json["buffers"].size() );
mesh.buffer_descriptors.reserve( json["buffers"].size() );
ext::json::forEach( json["buffers"], [&]( ext::json::Value& value ){ ext::json::forEach( json["buffers"], [&]( ext::json::Value& value ){
const uf::stl::string filename = value.as<uf::stl::string>(); const uf::stl::string filename = value.as<uf::stl::string>();
const uf::stl::string directory = uf::io::directory( graph.name ); const uf::stl::string directory = uf::io::directory( graph.name );
@ -298,7 +301,6 @@ namespace {
{ {
#if UF_ENV_DREAMCAST && GL_QUANTIZED_SHORT #if UF_ENV_DREAMCAST && GL_QUANTIZED_SHORT
mesh.convert<float, uint16_t>(); mesh.convert<float, uint16_t>();
UF_MSG_DEBUG("Quantizing mesh to GL_QUANTIZED_SHORT");
#else #else
auto conversion = graph.metadata["decode"]["conversion"].as<uf::stl::string>(); auto conversion = graph.metadata["decode"]["conversion"].as<uf::stl::string>();
if ( conversion != "" ) { if ( conversion != "" ) {
@ -314,8 +316,9 @@ namespace {
else if ( conversion == "float" ) mesh.convert<uint16_t, float>(); else if ( conversion == "float" ) mesh.convert<uint16_t, float>();
} }
#endif #endif
mesh.updateDescriptor();
} }
mesh.updateDescriptor();
return mesh; return mesh;
} }

View File

@ -25,7 +25,7 @@
#if UF_USE_OPENGL #if UF_USE_OPENGL
#define UF_GRAPH_SPARSE_READ_MESH 1 #define UF_GRAPH_SPARSE_READ_MESH 1
#else #else
#define UF_GRAPH_SPARSE_READ_MESH 0 #define UF_GRAPH_SPARSE_READ_MESH 1
#endif #endif
#endif #endif
@ -44,6 +44,40 @@ namespace {
return hash; return hash;
} }
void bindTextures( uf::renderer::Graphic& graphic ) {
graphic.material.textures.clear();
auto& scene = uf::scene::getCurrentScene();
auto& storage = uf::graph::globalStorage ? uf::graph::storage : scene.getComponent<pod::Graph::Storage>();
// for ( auto& s : graph.samplers ) graphic.material.samplers.emplace_back( storage.samplers.map[s] );
// for ( auto pair : storage.samplers.map ) graphic.material.samplers.emplace_back( pair.second );
// for ( auto& key : storage.samplers.keys ) graphic.material.samplers.emplace_back( storage.samplers.map[key] );
// for ( auto& i : graph.images ) graphic.material.textures.emplace_back().aliasTexture( storage.texture2Ds.map[i] );
// for ( auto pair : storage.texture2Ds.map ) graphic.material.textures.emplace_back().aliasTexture( pair.second );
for ( auto& key : storage.texture2Ds.keys ) graphic.material.textures.emplace_back().aliasTexture( storage.texture2Ds.map[key] );
// bind scene's voxel texture
#if UF_USE_VULKAN
if ( uf::renderer::settings::pipelines::vxgi ) {
auto& scene = uf::scene::getCurrentScene();
auto& sceneTextures = scene.getComponent<pod::SceneTextures>();
for ( auto& t : sceneTextures.voxels.drawId ) graphic.material.textures.emplace_back().aliasTexture(t);
for ( auto& t : sceneTextures.voxels.instanceId ) graphic.material.textures.emplace_back().aliasTexture(t);
for ( auto& t : sceneTextures.voxels.normalX ) graphic.material.textures.emplace_back().aliasTexture(t);
for ( auto& t : sceneTextures.voxels.normalY ) graphic.material.textures.emplace_back().aliasTexture(t);
for ( auto& t : sceneTextures.voxels.radianceR ) graphic.material.textures.emplace_back().aliasTexture(t);
for ( auto& t : sceneTextures.voxels.radianceG ) graphic.material.textures.emplace_back().aliasTexture(t);
for ( auto& t : sceneTextures.voxels.radianceB ) graphic.material.textures.emplace_back().aliasTexture(t);
for ( auto& t : sceneTextures.voxels.radianceA ) graphic.material.textures.emplace_back().aliasTexture(t);
for ( auto& t : sceneTextures.voxels.count ) graphic.material.textures.emplace_back().aliasTexture(t);
for ( auto& t : sceneTextures.voxels.output ) graphic.material.textures.emplace_back().aliasTexture(t);
}
#endif
}
// lazy load animations if requested // lazy load animations if requested
void loadAnimation( const uf::stl::string& name ) { void loadAnimation( const uf::stl::string& name ) {
auto& scene = uf::scene::getCurrentScene(); auto& scene = uf::scene::getCurrentScene();
@ -344,34 +378,7 @@ void uf::graph::initializeGraphics( pod::Graph& graph, uf::Object& entity, uf::M
#endif #endif
*/ */
{ ::bindTextures( graphic );
// for ( auto& s : graph.samplers ) graphic.material.samplers.emplace_back( storage.samplers.map[s] );
// for ( auto pair : storage.samplers.map ) graphic.material.samplers.emplace_back( pair.second );
// for ( auto& key : storage.samplers.keys ) graphic.material.samplers.emplace_back( storage.samplers.map[key] );
// for ( auto& i : graph.images ) graphic.material.textures.emplace_back().aliasTexture( storage.texture2Ds.map[i] );
// for ( auto pair : storage.texture2Ds.map ) graphic.material.textures.emplace_back().aliasTexture( pair.second );
for ( auto& key : storage.texture2Ds.keys ) graphic.material.textures.emplace_back().aliasTexture( storage.texture2Ds.map[key] );
// bind scene's voxel texture
#if UF_USE_VULKAN
if ( uf::renderer::settings::pipelines::vxgi ) {
auto& scene = uf::scene::getCurrentScene();
auto& sceneTextures = scene.getComponent<pod::SceneTextures>();
for ( auto& t : sceneTextures.voxels.drawId ) graphic.material.textures.emplace_back().aliasTexture(t);
for ( auto& t : sceneTextures.voxels.instanceId ) graphic.material.textures.emplace_back().aliasTexture(t);
for ( auto& t : sceneTextures.voxels.normalX ) graphic.material.textures.emplace_back().aliasTexture(t);
for ( auto& t : sceneTextures.voxels.normalY ) graphic.material.textures.emplace_back().aliasTexture(t);
for ( auto& t : sceneTextures.voxels.radianceR ) graphic.material.textures.emplace_back().aliasTexture(t);
for ( auto& t : sceneTextures.voxels.radianceG ) graphic.material.textures.emplace_back().aliasTexture(t);
for ( auto& t : sceneTextures.voxels.radianceB ) graphic.material.textures.emplace_back().aliasTexture(t);
for ( auto& t : sceneTextures.voxels.radianceA ) graphic.material.textures.emplace_back().aliasTexture(t);
for ( auto& t : sceneTextures.voxels.count ) graphic.material.textures.emplace_back().aliasTexture(t);
for ( auto& t : sceneTextures.voxels.output ) graphic.material.textures.emplace_back().aliasTexture(t);
}
#endif
}
uf::stl::string root = uf::io::directory( graph.name ); uf::stl::string root = uf::io::directory( graph.name );
size_t texture2Ds = 0; size_t texture2Ds = 0;
@ -2166,21 +2173,18 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) {
mesh.index.count += drawCommand.indices; mesh.index.count += drawCommand.indices;
} }
// load mesh data #define STREAM_MESH_DATA( N ) \
for ( auto& attribute : mesh.index.attributes ) { for ( auto& attribute : mesh.N.attributes ) {\
if ( ranges.count(attribute.buffer) <= 0 ) { if ( ranges.count(attribute.buffer) <= 0 ) { \
mesh.buffers[attribute.buffer].clear(); mesh.buffers[attribute.buffer].clear();\
} else { } else {\
mesh.buffers[attribute.buffer] = uf::io::readAsBuffer( mesh.buffer_paths[attribute.buffer], ranges[attribute.buffer] ); attribute.descriptor = mesh.buffer_descriptors[attribute.buffer];\
mesh.buffers[attribute.buffer] = uf::io::readAsBuffer( mesh.buffer_paths[attribute.buffer], ranges[attribute.buffer] );\
}\
} }
}
for ( auto& attribute : mesh.vertex.attributes ) { STREAM_MESH_DATA( index );
if ( ranges.count(attribute.buffer) <= 0 ) { STREAM_MESH_DATA( vertex );
mesh.buffers[attribute.buffer].clear();
} else {
mesh.buffers[attribute.buffer] = uf::io::readAsBuffer( mesh.buffer_paths[attribute.buffer], ranges[attribute.buffer] );
}
}
// keep the vertex data intact // keep the vertex data intact
#else #else
// disable remaining draw commands // disable remaining draw commands
@ -2197,13 +2201,15 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) {
} }
} }
// load mesh data #define STREAM_MESH_DATA( N ) \
for ( auto& attribute : mesh.index.attributes ) { for ( auto& attribute : mesh.N.attributes ) {\
mesh.buffers[attribute.buffer] = uf::io::readAsBuffer( mesh.buffer_paths[attribute.buffer] ); if ( !mesh.buffers[attribute.buffer].empty() || mesh.buffer_paths.empty() ) continue;\
} attribute.descriptor = mesh.buffer_descriptors[attribute.buffer];\
for ( auto& attribute : mesh.vertex.attributes ) { mesh.buffers[attribute.buffer] = uf::io::readAsBuffer( mesh.buffer_paths[attribute.buffer] );\
mesh.buffers[attribute.buffer] = uf::io::readAsBuffer( mesh.buffer_paths[attribute.buffer] ); }
}
STREAM_MESH_DATA( index );
STREAM_MESH_DATA( vertex );
#endif #endif
if ( graph.settings.stream.textures ) { if ( graph.settings.stream.textures ) {
@ -2239,13 +2245,11 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) {
} }
// iterate through our ref counts // iterate through our ref counts
// to-do: figure out why this doesn't work for OpenGL (texture ID handles might be wrong, might be better to store the old texture ID handle to use it and then update to that handle)
for ( auto& [ key, count ] : textureReferences ) { for ( auto& [ key, count ] : textureReferences ) {
auto& texture = storage.texture2Ds[key]; auto& texture = storage.texture2Ds[key];
auto& image = storage.images[key]; auto& image = storage.images[key];
bool visible = count > 0; bool visible = count > 0;
// load texture
if ( visible && (!texture.generated() || texture.aliased) ) { if ( visible && (!texture.generated() || texture.aliased) ) {
// load image // load image
if ( image.getPixels().empty() ) image.open(image.getFilename(), false); if ( image.getPixels().empty() ) image.open(image.getFilename(), false);
@ -2264,7 +2268,13 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) {
// avoids manipulating the aliased texture // avoids manipulating the aliased texture
if ( texture.aliased ) { if ( texture.aliased ) {
texture = {}; texture.aliased = false;
#if UF_USE_OPENGL
texture.image = 0;
#else
texture.image = {};
texture.view = {};
#endif
} }
texture.sampler.descriptor.filter.min = filter; texture.sampler.descriptor.filter.min = filter;
@ -2275,7 +2285,7 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) {
#if UF_ENV_DREAMCAST #if UF_ENV_DREAMCAST
image.clear(); image.clear();
#endif #endif
} else if ( !visible && texture.generated() ) { } else if ( !visible && (texture.generated() && !texture.aliased) ) {
// unload image // unload image
image.clear(); image.clear();
// defer destruction of texture // defer destruction of texture
@ -2286,20 +2296,39 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) {
} }
} }
} else { // this shouldn't be reached } else { // this shouldn't be reached
// load mesh data #define LOAD_MESH_DATA( N ) \
for ( auto& attribute : mesh.index.attributes ) { for ( auto& attribute : mesh.N.attributes ) {\
if ( !mesh.buffers[attribute.buffer].empty() || mesh.buffer_paths.empty() ) continue; if ( !mesh.buffers[attribute.buffer].empty() || mesh.buffer_paths.empty() ) continue;\
mesh.buffers[attribute.buffer] = uf::io::readAsBuffer( mesh.buffer_paths[attribute.buffer] ); mesh.buffers[attribute.buffer] = uf::io::readAsBuffer( mesh.buffer_paths[attribute.buffer] );\
} }
for ( auto& attribute : mesh.vertex.attributes ) {
if ( !mesh.buffers[attribute.buffer].empty() || mesh.buffer_paths.empty() ) continue; LOAD_MESH_DATA( index );
mesh.buffers[attribute.buffer] = uf::io::readAsBuffer( mesh.buffer_paths[attribute.buffer] ); LOAD_MESH_DATA( vertex );
}
{
#if UF_ENV_DREAMCAST && GL_QUANTIZED_SHORT
mesh.convert<float, uint16_t>();
#else
auto conversion = graph.metadata["decode"]["conversion"].as<uf::stl::string>();
if ( conversion != "" ) {
#if UF_USE_FLOAT16
if ( conversion == "float16" ) mesh.convert<float, float16>();
else if ( conversion == "float" ) mesh.convert<float16, float>();
#endif
#if UF_USE_BFLOAT16
if ( conversion == "bfloat16" ) mesh.convert<float, bfloat16>();
else if ( conversion == "float" ) mesh.convert<bfloat16, float>();
#endif
if ( conversion == "uint16_t" ) mesh.convert<float, uint16_t>();
else if ( conversion == "float" ) mesh.convert<uint16_t, float>();
} }
#endif
} }
mesh.updateDescriptor(); mesh.updateDescriptor();
// may or may not be necessary (OpenGL might need a re-record, Vulkan seems fine for the main deferred pass but VXGI doesn't ever get to update since null textures get used sometimes)
uf::renderer::states::rebuild = true; // uf::renderer::states::rebuild = true;
// update graphic // update graphic
if ( /*(graph.metadata["renderer"]["separate"].as<bool>()) &&*/ graph.metadata["renderer"]["render"].as<bool>() ) { if ( /*(graph.metadata["renderer"]["separate"].as<bool>()) &&*/ graph.metadata["renderer"]["render"].as<bool>() ) {
@ -2310,11 +2339,13 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) {
if ( rebuild ) { if ( rebuild ) {
// uf::renderer::states::rebuild = true; // uf::renderer::states::rebuild = true;
} }
::bindTextures( graphic );
} else { } else {
uf::graph::initializeGraphics( graph, entity, mesh ); uf::graph::initializeGraphics( graph, entity, mesh );
} }
} }
// bind mesh to physics state // bind mesh to physics state
// to-do: figure out why the mesh just suddenly breaks when re-streamed in dreamcast (could just be the version of reactphysics)
{ {
auto phyziks = tag["physics"]; auto phyziks = tag["physics"];
if ( !ext::json::isObject( phyziks ) ) phyziks = metadataJson["physics"]; if ( !ext::json::isObject( phyziks ) ) phyziks = metadataJson["physics"];