more agony (oversight fixes and dreamcast build works again (although it doesn't resolve my outstanding issues of too large of a scene mesh......))

This commit is contained in:
ecker 2025-08-13 22:12:38 -05:00
parent bc406d5b62
commit eadf732ee5
12 changed files with 147 additions and 36 deletions

View File

@ -313,7 +313,8 @@ $(EXT_EX_DLL): $(OBJS_EXT_DLL)
$(TARGET): $(OBJS) #./bin/dreamcast/romdisk.o
$(CXX) $(FLAGS) $(INCS) -D_arch_dreamcast -D_arch_sub_pristine -Wall -fno-builtin -ml -Wl,-Ttext=0x8c010000 -T/opt/dreamcast/kos/utils/ldscripts/shlelf.xc -nodefaultlibs $(KOS_LIB_PATHS) $(LIBS) -o $(TARGET) $(OBJS) -Wl,--start-group $(DEPS) -Wl,--end-group
$(KOS_STRIP) --strip-unneeded $(TARGET)
cp $(TARGET) $(TARGET).unstripped
$(KOS_STRIP) --strip-unneeded $(TARGET)
./bin/dreamcast/$(TARGET_NAME).cdi: $(TARGET)
cd ./bin/dreamcast/; ./elf2cdi.sh $(TARGET_NAME)

View File

@ -79,7 +79,7 @@
"tag": "worldspawn",
"player": "info_player_spawn",
"enabled": "auto",
"radius": 32,
"radius": 16,
"every": 4
}
}

View File

@ -11,12 +11,12 @@
// exact matches
"worldspawn": {
"physics": { "type": "mesh", "static": true },
"grid": { "size": [8,1,8], "epsilon": 0.001, "cleanup": true, "print": true },
"grid": { "size": [16,1,16], "epsilon": 0.001, "cleanup": true, "print": true, "clip": true },
"optimize meshlets": { "simplify": 0.125, "print": false },
"unwrap mesh": true
},
"worldspawn_skybox": {
"grid": { "size": [8,1,8], "epsilon": 0.001, "cleanup": true, "print": true },
"grid": { "size": [16,1,16], "epsilon": 0.001, "cleanup": true, "print": true, "clip": true },
"optimize meshlets": { "simplify": 0.125, "print": false },
"unwrap mesh": true
},

View File

@ -1 +1 @@
opengl
vulkan

View File

@ -58,12 +58,8 @@
#define GL_SILENCE_DEPRECATION
#include <OpenGL/gl.h>
#else
#if UF_ENV_DREAMCAST
#include <GLdc/gl.h>
#else
#include <GL/gl.h>
#endif
#endif
struct ImGui_ImplOpenGL2_Data
{

View File

@ -317,6 +317,9 @@ namespace uf {
size_t bytes = elements * sizeof(To);
auto& srcBuffer = this->buffers[attribute.buffer];
if ( srcBuffer.empty() ) continue;
uf::stl::vector<uint8_t> dstBuffer( srcBuffer.size() * scale );
attribute.pointer = (uint8_t*) dstBuffer.data();

View File

@ -16,6 +16,8 @@
#define UF_GRAPH_LOAD_MULTITHREAD 0
#endif
#define UF_GRAPH_EXTENDED 1
#if 0 && UF_ENV_DREAMCAST
#define UF_DEBUG_TIMER_MULTITRACE_START(...) UF_TIMER_MULTITRACE_START(__VA_ARGS__)
#define UF_DEBUG_TIMER_MULTITRACE(...) UF_TIMER_MULTITRACE(__VA_ARGS__)
@ -238,6 +240,9 @@ namespace {
const uf::stl::string filename = value.as<uf::stl::string>();
const uf::stl::string directory = uf::io::directory( graph.name );
// uf::io::readAsBuffer( directory + "/" + filename )
#if !UF_GRAPH_EXTENDED
mesh.buffers.emplace_back(uf::io::readAsBuffer( directory + "/" + filename ));
#else
if ( graph.metadata["stream"]["enabled"].as<bool>() ) {
mesh.buffers.emplace_back();
mesh.buffer_paths.emplace_back(directory + "/" + filename );
@ -245,6 +250,7 @@ namespace {
// to-do: make it work for interleaved meshes
mesh.buffers.emplace_back(uf::io::readAsBuffer( directory + "/" + filename ));
}
#endif
});
// load non vertex/index buffers
@ -261,7 +267,8 @@ namespace {
#if UF_ENV_DREAMCAST
// remove extraneous buffers
if ( graph.metadata["renderer"]["separate"].as<bool>() ) {
// if ( graph.metadata["renderer"]["separate"].as<bool>() )
{
uf::stl::vector<uf::stl::string> attributesKept = ext::json::vector<uf::stl::string>(graph.metadata["decode"]["attributes"]);
if ( !mesh.isInterleaved() ) {
uf::stl::vector<size_t> remove; remove.reserve(mesh.vertex.attributes.size());
@ -283,9 +290,8 @@ namespace {
}
#endif
mesh.updateDescriptor();
#if 0
if ( graph.metadata["renderer"]["separate"].as<bool>() ) {
// if ( graph.metadata["renderer"]["separate"].as<bool>() )
{
#if UF_ENV_DREAMCAST && GL_QUANTIZED_SHORT
mesh.convert<float, uint16_t>();
UF_MSG_DEBUG("Quantizing mesh to GL_QUANTIZED_SHORT");
@ -306,7 +312,6 @@ namespace {
#endif
mesh.updateDescriptor();
}
#endif
return mesh;
}

View File

@ -17,13 +17,19 @@
#define UF_DEBUG_TIMER_MULTITRACE_START(...) UF_TIMER_MULTITRACE_START(__VA_ARGS__)
#define UF_DEBUG_TIMER_MULTITRACE(...) UF_TIMER_MULTITRACE(__VA_ARGS__)
#define UF_DEBUG_TIMER_MULTITRACE_END(...) UF_TIMER_MULTITRACE_END(__VA_ARGS__)
#define UF_GRAPH_SPARSE_READ_MESH 1
#else
#define UF_DEBUG_TIMER_MULTITRACE_START(...)
#define UF_DEBUG_TIMER_MULTITRACE(...)
#define UF_DEBUG_TIMER_MULTITRACE_END(...)
#if UF_USE_OPENGL
#define UF_GRAPH_SPARSE_READ_MESH 1
#else
#define UF_GRAPH_SPARSE_READ_MESH 0
#endif
#endif
#define UF_GRAPH_SPARSE_READ_MESH 1
#define UF_GRAPH_EXTENDED 1
namespace {
bool newGraphAdded = true;
@ -1230,7 +1236,9 @@ void uf::graph::process( pod::Graph& graph ) {
}
UF_DEBUG_TIMER_MULTITRACE("Updating master graph");
#if UF_GRAPH_EXTENDED
uf::graph::reload( graph );
#endif
uf::graph::reload();
storage.instanceAddresses.keys = storage.instances.keys;
@ -1412,6 +1420,74 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent )
storage.entities[objectKeyName] = &entity;
//
#if !UF_GRAPH_EXTENDED
if ( 0 <= node.mesh && node.mesh < graph.meshes.size() ) {
auto model = uf::transform::model( transform );
auto& mesh = storage.meshes.map[graph.meshes[node.mesh]];
auto& primitives = storage.primitives.map[graph.primitives[node.mesh]];
pod::Instance::Bounds bounds;
// setup instances
for ( auto i = 0; i < primitives.size(); ++i ) {
auto& primitive = primitives[i];
size_t instanceID = storage.instances.keys.size();
auto instanceKeyName = graph.instances.emplace_back(std::to_string(instanceID));
auto& instance = storage.instances[instanceKeyName];
instance = primitive.instance;
instance.model = model;
instance.previous = model;
instance.objectID = objectID;
instance.jointID = graph.metadata["renderer"]["skinned"].as<bool>() ? 0 : -1;
bounds.min = uf::vector::min( bounds.min, instance.bounds.min );
bounds.max = uf::vector::max( bounds.max, instance.bounds.max );
if ( mesh.indirect.count && mesh.indirect.count <= primitives.size() ) {
auto& attribute = mesh.indirect.attributes.front();
auto& buffer = mesh.buffers[mesh.isInterleaved(mesh.indirect.interleaved) ? mesh.indirect.interleaved : attribute.buffer];
pod::DrawCommand* drawCommands = (pod::DrawCommand*) buffer.data();
auto& drawCommand = drawCommands[i];
drawCommand.instanceID = instanceID;
}
}
if ( /*(graph.metadata["renderer"]["separate"].as<bool>()) &&*/ graph.metadata["renderer"]["render"].as<bool>() ) {
uf::graph::initializeGraphics( graph, entity, mesh );
}
{
auto phyziks = tag["physics"];
if ( !ext::json::isObject( phyziks ) ) phyziks = metadataJson["physics"];
else metadataJson["physics"] = phyziks;
if ( ext::json::isObject( phyziks ) ) {
uf::stl::string type = phyziks["type"].as<uf::stl::string>();
if ( type == "mesh" ) {
auto& collider = entity.getComponent<pod::PhysicsState>();
collider.stats.mass = phyziks["mass"].as(collider.stats.mass);
collider.stats.friction = phyziks["friction"].as(collider.stats.friction);
collider.stats.restitution = phyziks["restitution"].as(collider.stats.restitution);
collider.stats.inertia = uf::vector::decode( phyziks["inertia"], collider.stats.inertia );
collider.stats.gravity = uf::vector::decode( phyziks["gravity"], collider.stats.gravity );
uf::physics::impl::create( entity.as<uf::Object>(), mesh, !phyziks["static"].as<bool>(true) );
} else {
auto min = uf::matrix::multiply<float>( model, bounds.min, 1.0f );
auto max = uf::matrix::multiply<float>( model, bounds.max, 1.0f );
pod::Vector3f center = (max + min) * 0.5f;
pod::Vector3f corner = uf::vector::abs(max - min) * 0.5f;
metadataJson["physics"]["center"] = uf::vector::encode( center );
metadataJson["physics"]["corner"] = uf::vector::encode( corner );
}
}
}
}
#else
if ( 0 <= node.mesh && node.mesh < graph.meshes.size() ) {
auto model = uf::transform::model( transform );
auto& mesh = storage.meshes.map[graph.meshes[node.mesh]];
@ -1467,6 +1543,7 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent )
}
}
}
#endif
for ( auto index : node.children ) uf::graph::process( graph, index, entity );
}
@ -1555,10 +1632,12 @@ void uf::graph::update( pod::Graph& graph, float delta ) {
auto& storage = uf::graph::globalStorage ? uf::graph::storage : scene.getComponent<pod::Graph::Storage>();
// get last update time
#if UF_GRAPH_EXTENDED
if ( graph.settings.stream.enabled && graph.settings.stream.hash != 0 && uf::physics::time::current - graph.settings.stream.lastUpdate > graph.settings.stream.every ) {
graph.settings.stream.lastUpdate = uf::physics::time::current;
uf::graph::reload( graph );
}
#endif
// update our instances
#if !UF_ENV_DREAMCAST
@ -1702,14 +1781,14 @@ void uf::graph::tick( pod::Graph::Storage& storage ) {
*/
bool rebuild = false;
uf::stl::vector<pod::Instance> instances = storage.instances.flatten();
rebuild = rebuild || storage.buffers.instance.update( (const void*) instances.data(), instances.size() * sizeof(pod::Instance) );
rebuild = storage.buffers.instance.update( (const void*) instances.data(), instances.size() * sizeof(pod::Instance) ) || rebuild;
/*
uf::stl::vector<pod::Instance::Addresses> instanceAddresses; instanceAddresses.reserve(storage.instanceAddresses.map.size());
for ( auto& key : storage.instances.keys ) instanceAddresses.emplace_back( storage.instanceAddresses.map[key] );
if ( !instanceAddresses.empty() ) rebuild = rebuild || storage.buffers.instanceAddresses.update( (const void*) instanceAddresses.data(), instanceAddresses.size() * sizeof(pod::Instance::Addresses) );
if ( !instanceAddresses.empty() ) rebuild = storage.buffers.instanceAddresses.update( (const void*) instanceAddresses.data(), instanceAddresses.size() * sizeof(pod::Instance::Addresses) ) || rebuild;
*/
uf::stl::vector<pod::Instance::Addresses> instanceAddresses = storage.instanceAddresses.flatten();
rebuild = rebuild || storage.buffers.instanceAddresses.update( (const void*) instanceAddresses.data(), instanceAddresses.size() * sizeof(pod::Instance::Addresses) );
rebuild = storage.buffers.instanceAddresses.update( (const void*) instanceAddresses.data(), instanceAddresses.size() * sizeof(pod::Instance::Addresses) ) || rebuild;
uf::stl::vector<pod::Matrix4f> joints; joints.reserve(storage.joints.map.size());
for ( auto& key : storage.joints.keys ) {
@ -1717,7 +1796,7 @@ void uf::graph::tick( pod::Graph::Storage& storage ) {
joints.reserve( joints.size() + matrices.size() );
for ( auto& mat : matrices ) joints.emplace_back( mat );
}
/*if ( !joints.empty() )*/ rebuild = rebuild || storage.buffers.joint.update( (const void*) joints.data(), joints.size() * sizeof(pod::Matrix4f) );
/*if ( !joints.empty() )*/ rebuild = storage.buffers.joint.update( (const void*) joints.data(), joints.size() * sizeof(pod::Matrix4f) ) || rebuild;
if ( ::newGraphAdded ) {
#if 1
@ -1735,9 +1814,9 @@ void uf::graph::tick( pod::Graph::Storage& storage ) {
for ( auto& key : storage.materials.keys ) materials.emplace_back( storage.materials.map[key] );
for ( auto& key : storage.textures.keys ) textures.emplace_back( storage.textures.map[key] );
#endif
rebuild = rebuild || storage.buffers.drawCommands.update( (const void*) drawCommands.data(), drawCommands.size() * sizeof(pod::DrawCommand) );
rebuild = rebuild || storage.buffers.material.update( (const void*) materials.data(), materials.size() * sizeof(pod::Material) );
rebuild = rebuild || storage.buffers.texture.update( (const void*) textures.data(), textures.size() * sizeof(pod::Texture) );
rebuild = storage.buffers.drawCommands.update( (const void*) drawCommands.data(), drawCommands.size() * sizeof(pod::DrawCommand) ) || rebuild;
rebuild = storage.buffers.material.update( (const void*) materials.data(), materials.size() * sizeof(pod::Material) ) || rebuild;
rebuild = storage.buffers.texture.update( (const void*) textures.data(), textures.size() * sizeof(pod::Texture) ) || rebuild;
::newGraphAdded = false;
@ -1918,6 +1997,10 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) {
radius = 0;
}
if ( mesh.buffer_paths.empty() ) {
radius = 0;
}
if ( radius > 0 && mesh.indirect.count && mesh.indirect.count <= primitives.size() ) {
// deduce draw command (indirect) buffer to write to
auto& attribute = mesh.indirect.attributes.front();
@ -2051,24 +2134,28 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) {
} else {
// load mesh data
for ( auto& attribute : mesh.index.attributes ) {
if ( mesh.buffers[attribute.buffer].empty() ) mesh.buffers[attribute.buffer] = uf::io::readAsBuffer( mesh.buffer_paths[attribute.buffer] );
if ( !mesh.buffers[attribute.buffer].empty() || mesh.buffer_paths.empty() ) continue;
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.buffers[attribute.buffer] = uf::io::readAsBuffer( mesh.buffer_paths[attribute.buffer] );
if ( !mesh.buffers[attribute.buffer].empty() || mesh.buffer_paths.empty() ) continue;
mesh.buffers[attribute.buffer] = uf::io::readAsBuffer( mesh.buffer_paths[attribute.buffer] );
}
}
mesh.updateDescriptor();
// process textures
// update graphic
#if 0
if ( (graph.metadata["renderer"]["separate"].as<bool>()) && graph.metadata["renderer"]["render"].as<bool>() ) {
#endif
if ( graph.metadata["renderer"]["render"].as<bool>() ) {
if ( /*(graph.metadata["renderer"]["separate"].as<bool>()) &&*/ graph.metadata["renderer"]["render"].as<bool>() ) {
bool exists = entity.hasComponent<uf::renderer::Graphic>();
if ( exists ) {
auto& graphic = entity.getComponent<uf::renderer::Graphic>();
graphic.updateMesh( mesh );
bool rebuild = graphic.updateMesh( mesh );
if ( rebuild ) {
// uf::renderer::states::rebuild = true;
}
} else {
uf::graph::initializeGraphics( graph, entity, mesh );
}

View File

@ -566,6 +566,14 @@ pod::Graph ext::gltf::load( const uf::stl::string& filename, const uf::Serialize
// disable lightmap loading, 99.999% of the time a previously baked lightmap will not work due to changing STs
graph.metadata["lights"]["lightmapped"] = false;
}
// disable streaming
{
graph.settings.stream.enabled = false;
graph.settings.stream.radius = 0;
graph.settings.stream.every = 0;
}
return graph;
}
#endif

View File

@ -378,7 +378,6 @@ if ( meshopt.should ) {
mesh.bindIndirect<pod::DrawCommand>();
mesh.bind<UF_GRAPH_MESH_FORMAT>(false); // default to de-interleaved regardless of requirement (makes things easier)
UF_MSG_DEBUG("{}: {}", keyName, meshlets.size() );
for ( auto& meshlet : meshlets ) {
auto& drawCommand = drawCommands.emplace_back(pod::DrawCommand{
.indices = meshlet.indices.size(),

View File

@ -186,12 +186,24 @@ bool ext::vulkan::Buffer::update( const void* data, VkDeviceSize length, bool st
// if ( !data || !length ) return false;
if ( !length ) return false;
if ( !buffer ) return false;
if ( length > allocationInfo.size ) {
UF_MSG_WARNING("Buffer update of {} exceeds buffer size of {}", length, allocationInfo.size );
Buffer& b = *const_cast<Buffer*>(this);
b.destroy(true);
b.initialize( data, length, usage, memoryProperties, stage );
// to-do: fix this because it's a thorn in my side when a mesh needs to update
if ( length > allocationInfo.size ) {
UF_MSG_WARNING("Buffer update of {} exceeds buffer size of {}", length, allocationInfo.size);
auto savedUsage = usage;
auto savedMemProps = memoryProperties;
auto savedAlignment = alignment;
Buffer& oldBuffer = *const_cast<Buffer*>(this);
Buffer newBuffer = {};
oldBuffer.swap(newBuffer);
newBuffer.destroy(true);
oldBuffer.initialize(*device, savedAlignment);
oldBuffer.initialize(data, length, savedUsage, savedMemProps, stage);
return true;
}
if ( !data ) return false;

View File

@ -4,7 +4,7 @@ CC = gcc
CXX = $(KOS_CCPLUS)
RENDERER = opengl
TARGET_EXTENSION = elf
OPTIMIZATIONS = -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -fstrict-aliasing -ffast-math -fno-unroll-all-loops -fno-optimize-sibling-calls -fschedule-insns2 -fomit-frame-pointer -DUF_NO_EXCEPTIONS -fno-exceptions -flto # -g
OPTIMIZATIONS = -Os -ffunction-sections -fdata-sections -Wl,--gc-sections -fstrict-aliasing -ffast-math -fno-unroll-all-loops -fno-optimize-sibling-calls -fschedule-insns2 -fomit-frame-pointer -DUF_NO_EXCEPTIONS -fno-exceptions -flto -g
WARNINGS = -Wno-attributes -Wno-conversion-null
FLAGS += $(KOS_CPPFLAGS) -m4-single -std=c++17 $(OPTIMIZATIONS) $(WARNINGS) -fdiagnostics-color=always
INCS += $(KOS_INC_PATHS) -I/opt/dreamcast/sh-elf/sh-elf/include