From 0a29063869be73bbe262da156b968b75bf02dceb Mon Sep 17 00:00:00 2001 From: ecker Date: Tue, 19 Aug 2025 19:18:12 -0500 Subject: [PATCH] realized past me realized that aliasing buffers isn't foolproof and added a system to fetch buffer descriptors by name from a source, yet never used it (re-bind buffer descriptors on mesh updates, need to fix the crash for graph storage when it resizes...) --- bin/data/entities/model.json | 4 +- bin/dreamcast/data/config.json | 2 +- engine/inc/uf/engine/graph/graph.h | 2 +- engine/inc/uf/ext/opengl/buffer.h | 1 + engine/inc/uf/ext/opengl/device.h | 4 + engine/inc/uf/ext/opengl/shader.h | 5 + engine/inc/uf/ext/vulkan/shader.h | 1 + engine/src/engine/graph/graph.cpp | 975 ++++++++++++++++------------- engine/src/ext/opengl/buffer.cpp | 44 +- engine/src/ext/opengl/opengl.cpp | 6 + engine/src/ext/opengl/shader.cpp | 13 + engine/src/ext/vulkan/buffer.cpp | 11 +- engine/src/ext/vulkan/graphic.cpp | 1 + engine/src/ext/vulkan/shader.cpp | 5 + 14 files changed, 603 insertions(+), 471 deletions(-) diff --git a/bin/data/entities/model.json b/bin/data/entities/model.json index a7fa9332..1fa9da21 100644 --- a/bin/data/entities/model.json +++ b/bin/data/entities/model.json @@ -78,8 +78,8 @@ "stream": { "tag": "worldspawn", "player": "info_player_spawn", - "enabled": "auto", - "radius": 32, + "enabled": true, // "auto", + "radius": 16, "every": 1 } } diff --git a/bin/dreamcast/data/config.json b/bin/dreamcast/data/config.json index 1ca0895a..9d7964c9 100644 --- a/bin/dreamcast/data/config.json +++ b/bin/dreamcast/data/config.json @@ -17,7 +17,7 @@ } }, "graph": { - "initial buffer elements": 256 + "initial buffer elements": 128 }, "ext": { "opengl": { diff --git a/engine/inc/uf/engine/graph/graph.h b/engine/inc/uf/engine/graph/graph.h index 850c4744..c8b1188a 100644 --- a/engine/inc/uf/engine/graph/graph.h +++ b/engine/inc/uf/engine/graph/graph.h @@ -161,7 +161,7 @@ namespace uf { void UF_API initialize( uf::Object&, size_t = uf::graph::initialBufferElements ); void UF_API initialize( pod::Graph::Storage&, size_t = uf::graph::initialBufferElements ); void UF_API tick( uf::Object& ); - void UF_API tick( pod::Graph::Storage& ); + bool UF_API tick( pod::Graph::Storage& ); void UF_API render( uf::Object& ); void UF_API render( pod::Graph::Storage& ); void UF_API destroy( uf::Object&, bool soft = false ); diff --git a/engine/inc/uf/ext/opengl/buffer.h b/engine/inc/uf/ext/opengl/buffer.h index d1312fbb..c6dc531d 100644 --- a/engine/inc/uf/ext/opengl/buffer.h +++ b/engine/inc/uf/ext/opengl/buffer.h @@ -13,6 +13,7 @@ namespace ext { struct Device; struct UF_API Buffer { + bool aliased = false; Device* device; GLuint buffer = 0; diff --git a/engine/inc/uf/ext/opengl/device.h b/engine/inc/uf/ext/opengl/device.h index bcedfbb0..1d885851 100644 --- a/engine/inc/uf/ext/opengl/device.h +++ b/engine/inc/uf/ext/opengl/device.h @@ -38,6 +38,10 @@ namespace ext { } textures, buffers; + struct { + uf::stl::vector buffers; + } transient; + void initialize(); void destroy(); diff --git a/engine/inc/uf/ext/opengl/shader.h b/engine/inc/uf/ext/opengl/shader.h index 2e447e31..1a9e8db1 100644 --- a/engine/inc/uf/ext/opengl/shader.h +++ b/engine/inc/uf/ext/opengl/shader.h @@ -110,6 +110,11 @@ namespace ext { void destroy(); bool validate(); + bool hasBuffer( const uf::stl::string& name ); + void aliasBuffer( const Metadata::BufferDescriptor& descriptor ); + void aliasBuffer( const ext::opengl::Buffer& buffer ); + void aliasBuffer( const uf::stl::string& name, const ext::opengl::Buffer& = {}, const ext::opengl::RenderMode* renderMode = NULL, GLenum = 0 ); + bool hasUniform( const uf::stl::string& name ) const; Buffer& getUniformBuffer( const uf::stl::string& name ); diff --git a/engine/inc/uf/ext/vulkan/shader.h b/engine/inc/uf/ext/vulkan/shader.h index 62b1f09a..040a1466 100644 --- a/engine/inc/uf/ext/vulkan/shader.h +++ b/engine/inc/uf/ext/vulkan/shader.h @@ -144,6 +144,7 @@ namespace ext { bool hasBuffer( const uf::stl::string& name ); void aliasBuffer( const Metadata::BufferDescriptor& descriptor ); + void aliasBuffer( const ext::vulkan::Buffer& buffer ); void aliasBuffer( const uf::stl::string& name, const ext::vulkan::Buffer& = {}, const ext::vulkan::RenderMode* renderMode = NULL, VkBufferUsageFlags = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT ); bool hasUniform( const uf::stl::string& name ) const; diff --git a/engine/src/engine/graph/graph.cpp b/engine/src/engine/graph/graph.cpp index bfdf0f09..f6ef8ca5 100644 --- a/engine/src/engine/graph/graph.cpp +++ b/engine/src/engine/graph/graph.cpp @@ -44,6 +44,11 @@ namespace { return hash; } + // removes non-uniform aliased buffers + void resetBuffers( uf::renderer::Shader& shader ) { + shader.metadata.aliases.buffers.clear(); + } + void bindTextures( uf::renderer::Graphic& graphic ) { graphic.material.textures.clear(); @@ -78,6 +83,488 @@ namespace { #endif } + void bindShaders( pod::Graph& graph, uf::Object& entity, uf::Mesh& mesh ) { + auto& scene = uf::scene::getCurrentScene(); + auto& sceneTextures = scene.getComponent(); + auto& sceneMetadataJson = scene.getComponent(); + auto& storage = uf::graph::globalStorage ? uf::graph::storage : scene.getComponent(); + + auto& graphic = entity.getComponent(); + + uf::stl::string root = uf::io::directory( graph.name ); + size_t texture2Ds = 0; + size_t texture3Ds = 0; + for ( auto& texture : graphic.material.textures ) { + if ( texture.width > 1 && texture.height > 1 && texture.depth == 1 && texture.layers == 1 ) ++texture2Ds; + else if ( texture.width > 1 && texture.height > 1 && texture.depth > 1 && texture.layers == 1 ) ++texture3Ds; + } + + // standard pipeline + { + uf::stl::string vertexShaderFilename = graph.metadata["shaders"]["vertex"].as("/graph/base/vert.spv"); { + std::pair settings[] = { + { graph.metadata["renderer"]["skinned"].as(), "skinned.vert" }, + { !graph.metadata["renderer"]["separate"].as(), "instanced.vert" }, + }; + FOR_ARRAY(settings) if ( settings[i].first ) vertexShaderFilename = uf::string::replace( vertexShaderFilename, "vert", settings[i].second ); + vertexShaderFilename = entity.resolveURI( vertexShaderFilename, root ); + } + uf::stl::string geometryShaderFilename = graph.metadata["shaders"]["geometry"].as(""); if ( geometryShaderFilename != "" ) { + geometryShaderFilename = entity.resolveURI( geometryShaderFilename, root ); + } + uf::stl::string fragmentShaderFilename = graph.metadata["shaders"]["fragment"].as("/graph/base/frag.spv"); { + fragmentShaderFilename = entity.resolveURI( fragmentShaderFilename, root ); + } + + graphic.material.metadata.autoInitializeUniformBuffers = false; + graphic.material.attachShader(vertexShaderFilename, uf::renderer::enums::Shader::VERTEX); + graphic.material.attachShader(fragmentShaderFilename, uf::renderer::enums::Shader::FRAGMENT); + if ( geometryShaderFilename != "" && uf::renderer::device.enabledFeatures.geometryShader ) { // to-do: should cram in the attachShader itself + graphic.material.attachShader(geometryShaderFilename, uf::renderer::enums::Shader::GEOMETRY); + } + graphic.material.metadata.autoInitializeUniformBuffers = true; + // vertex shader + { + auto& shader = graphic.material.getShader("vertex"); + #if UF_USE_VULKAN + uint32_t maxPasses = 6; + shader.setSpecializationConstants({ + { "PASSES", maxPasses } + }); + #endif + } + // fragment shader + { + auto& shader = graphic.material.getShader("fragment"); + #if UF_USE_VULKAN + uint32_t maxTextures = graph.textures.size(); + shader.setSpecializationConstants({ + { "TEXTURES", maxTextures }, + }); + shader.setDescriptorCounts({ + { "samplerTextures", maxTextures }, + }); + #endif + } + } + + #if UF_USE_VULKAN + // depth only pipeline + { + uf::stl::string fragmentShaderFilename = graph.metadata["shaders"]["depth"]["fragmment"].as("/graph/depth/frag.spv"); + fragmentShaderFilename = entity.resolveURI( fragmentShaderFilename, root ); + + graphic.material.metadata.autoInitializeUniformBuffers = false; + graphic.material.attachShader(fragmentShaderFilename, uf::renderer::enums::Shader::FRAGMENT, "depth"); + graphic.material.metadata.autoInitializeUniformBuffers = true; + + // fragment shader + auto& shader = graphic.material.getShader("fragment", "depth"); + + uint32_t maxTextures = graph.textures.size(); + shader.setSpecializationConstants({ + { "TEXTURES", maxTextures }, + }); + shader.setDescriptorCounts({ + { "samplerTextures", maxTextures }, + }); + } + // culling pipeline + if ( uf::renderer::settings::pipelines::culling && graphic.descriptor.inputs.indirect.count ) { + uf::stl::string compShaderFilename = graph.metadata["shaders"][uf::renderer::settings::pipelines::names::culling]["compute"].as("/graph/cull/comp.spv"); + compShaderFilename = entity.resolveURI( compShaderFilename, root ); + + graphic.material.metadata.autoInitializeUniformBuffers = false; + graphic.material.attachShader(compShaderFilename, uf::renderer::enums::Shader::COMPUTE, uf::renderer::settings::pipelines::names::culling); + graphic.material.metadata.autoInitializeUniformBuffers = true; + + graphic.descriptor.bind.width = graphic.descriptor.inputs.indirect.count; + graphic.descriptor.bind.height = 1; + graphic.descriptor.bind.depth = 1; + + // compute shader + auto& shader = graphic.material.getShader("compute", uf::renderer::settings::pipelines::names::culling); + shader.aliasAttachment("depthPyramid"); + } + // vxgi pipeline + if ( uf::renderer::settings::pipelines::vxgi ) { + uf::stl::string geometryShaderFilename = graph.metadata["shaders"][uf::renderer::settings::pipelines::names::vxgi]["geometry"].as("/graph/voxelize/geom.spv"); { + geometryShaderFilename = entity.resolveURI( geometryShaderFilename, root ); + } + uf::stl::string fragmentShaderFilename = graph.metadata["shaders"][uf::renderer::settings::pipelines::names::vxgi]["fragment"].as("/graph/voxelize/frag.spv"); if ( geometryShaderFilename != "" ) { + fragmentShaderFilename = entity.resolveURI( fragmentShaderFilename, root ); + } + + graphic.material.metadata.autoInitializeUniformBuffers = false; + graphic.material.attachShader(fragmentShaderFilename, uf::renderer::enums::Shader::FRAGMENT, uf::renderer::settings::pipelines::names::vxgi); + graphic.material.metadata.autoInitializeUniformBuffers = true; + if ( geometryShaderFilename != "" && uf::renderer::device.enabledFeatures.geometryShader ) { // to-do: should cram in the attachShader itself + graphic.material.attachShader(geometryShaderFilename, uf::renderer::enums::Shader::GEOMETRY, uf::renderer::settings::pipelines::names::vxgi); + } + + uint32_t voxelTypes = 0; + if ( !sceneTextures.voxels.drawId.empty() ) ++voxelTypes; + if ( !sceneTextures.voxels.instanceId.empty() ) ++voxelTypes; + if ( !sceneTextures.voxels.normalX.empty() ) ++voxelTypes; + if ( !sceneTextures.voxels.normalY.empty() ) ++voxelTypes; + if ( !sceneTextures.voxels.radianceR.empty() ) ++voxelTypes; + if ( !sceneTextures.voxels.radianceG.empty() ) ++voxelTypes; + if ( !sceneTextures.voxels.radianceB.empty() ) ++voxelTypes; + if ( !sceneTextures.voxels.radianceA.empty() ) ++voxelTypes; + if ( !sceneTextures.voxels.count.empty() ) ++voxelTypes; + if ( !sceneTextures.voxels.output.empty() ) ++voxelTypes; + + uint32_t maxTextures = texture2Ds; + uint32_t maxCascades = texture3Ds / voxelTypes; + + // fragment shader + { + auto& shader = graphic.material.getShader("fragment", uf::renderer::settings::pipelines::names::vxgi); + shader.setSpecializationConstants({ + { "TEXTURES", maxTextures }, + { "CASCADES", maxCascades }, + }); + shader.setDescriptorCounts({ + { "samplerTextures", maxTextures }, + { "voxelDrawId", maxCascades }, + { "voxelInstanceId", maxCascades }, + { "voxelNormalX", maxCascades }, + { "voxelNormalY", maxCascades }, + { "voxelRadianceR", maxCascades }, + { "voxelRadianceG", maxCascades }, + { "voxelRadianceB", maxCascades }, + { "voxelRadianceA", maxCascades }, + { "voxelCount", maxCascades }, + { "voxelOutput", maxCascades }, + }); + } + } + // baking pipeline + if ( graph.metadata["baking"]["enabled"].as() ) { + uf::stl::string vertexShaderFilename = uf::io::resolveURI("/graph/baking/vert.spv"); + uf::stl::string fragmentShaderFilename = uf::io::resolveURI("/graph/baking/frag.spv"); + std::pair settings[] = { + { uf::renderer::settings::pipelines::rt, "rt.frag" }, + }; + FOR_ARRAY(settings) if ( settings[i].first ) fragmentShaderFilename = uf::string::replace( fragmentShaderFilename, "frag", settings[i].second ); + + graphic.material.metadata.autoInitializeUniformBuffers = false; + graphic.material.attachShader(vertexShaderFilename, uf::renderer::enums::Shader::VERTEX, "baking"); + graphic.material.attachShader(fragmentShaderFilename, uf::renderer::enums::Shader::FRAGMENT, "baking"); + graphic.material.metadata.autoInitializeUniformBuffers = true; + + // vertex shader + { + uint32_t maxPasses = 6; + + auto& shader = graphic.material.getShader("vertex", "baking"); + shader.setSpecializationConstants({ + { "PASSES", maxPasses }, + }); + } + + // fragment shader + { + size_t maxTextures = uf::config["engine"]["scenes"]["textures"]["max"]["2D"].as(512); + size_t maxCubemaps = uf::config["engine"]["scenes"]["textures"]["max"]["cube"].as(128); + size_t maxTextures3D = uf::config["engine"]["scenes"]["textures"]["max"]["3D"].as(128); + + auto& shader = graphic.material.getShader("fragment", "baking"); + shader.setSpecializationConstants({ + { "TEXTURES", maxTextures }, + { "CUBEMAPS", maxCubemaps }, + }); + shader.setDescriptorCounts({ + { "samplerTextures", maxTextures }, + { "samplerCubemaps", maxCubemaps }, + }); + } + } + + // rt pipeline + // to-do: segregate out buffer updating code + if ( uf::renderer::settings::pipelines::rt && mesh.vertex.count && graph.metadata["renderer"]["skinned"].as() ) { + struct PushConstant { + uint32_t jointID; + }; + + if ( mesh.isInterleaved( mesh.vertex ) ) { + uf::stl::string compShaderFilename = graph.metadata["shaders"]["skinning"]["compute"].as("/graph/skinning/skinning.interleaved.comp.spv"); { + compShaderFilename = entity.resolveURI( compShaderFilename, root ); + } + graphic.material.metadata.autoInitializeUniformBuffers = false; + graphic.material.attachShader(compShaderFilename, uf::renderer::enums::Shader::COMPUTE, "skinning"); + graphic.material.metadata.autoInitializeUniformBuffers = true; + + graphic.descriptor.bind.width = mesh.vertex.count; + graphic.descriptor.bind.height = 1; + graphic.descriptor.bind.depth = 1; + + // compute shader + auto& shader = graphic.material.getShader("compute", "skinning"); + + // bind buffers + struct { + uint32_t jointID; + } uniforms = { + .jointID = 0 + }; + + auto& vertexSourceData = mesh.buffers[mesh.vertex.interleaved]; + size_t vertexSourceDataIndex = graphic.initializeBuffer( (const void*) vertexSourceData.data(), vertexSourceData.size(), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR ); + + auto& vertexIn = graphic.buffers.at(graphic.descriptor.inputs.vertex.interleaved); + auto& vertexOut = graphic.buffers.at(vertexSourceDataIndex); + graphic.metadata.buffers["vertexSkinned"] = vertexSourceDataIndex; + + shader.updateBuffer( (const void*) &uniforms, sizeof(uniforms), shader.getUniformBuffer("UBO") ); + + ::resetBuffers( shader ); + shader.aliasBuffer( storage.buffers.joint ); + shader.aliasBuffer( vertexIn ); + shader.aliasBuffer( vertexOut ); + } else { + uf::stl::string compShaderFilename = graph.metadata["shaders"]["skinning"]["compute"].as("/graph/skinning/skinning.deinterleaved.comp.spv"); { + compShaderFilename = entity.resolveURI( compShaderFilename, root ); + } + + // graphic.material.metadata.autoInitializeUniformBuffers = false; + graphic.material.attachShader(compShaderFilename, uf::renderer::enums::Shader::COMPUTE, "skinning"); + // graphic.material.metadata.autoInitializeUniformBuffers = true; + + graphic.descriptor.bind.width = mesh.vertex.count; + graphic.descriptor.bind.height = 1; + graphic.descriptor.bind.depth = 1; + + uf::Mesh::Attribute vertexPos; + uf::Mesh::Attribute vertexJoints; + uf::Mesh::Attribute vertexWeights; + + size_t vertexPosIndex = 0; + size_t vertexJointsIndex = 0; + size_t vertexWeightsIndex = 0; + + for ( size_t i = 0; i < graphic.descriptor.inputs.vertex.attributes.size(); ++i ) { + auto& attribute = graphic.descriptor.inputs.vertex.attributes[i]; + + if ( attribute.buffer < 0 ) continue; + if ( attribute.descriptor.name == "position" ) { + vertexPos = attribute; + vertexPosIndex = graphic.metadata.buffers["vertex[position]"]; + } + else if ( attribute.descriptor.name == "joints" ) { + vertexJoints = attribute; + vertexJointsIndex = graphic.metadata.buffers["vertex[joints]"]; + } + else if ( attribute.descriptor.name == "weights" ) { + vertexWeights = attribute; + vertexWeightsIndex = graphic.metadata.buffers["vertex[weights]"]; + } + } + + auto& vertexSourceData = mesh.buffers[vertexPos.buffer]; + size_t vertexSourceDataIndex = graphic.initializeBuffer( (const void*) vertexSourceData.data(), vertexSourceData.size(), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR ); + + auto& vertexPositionBuffer = graphic.buffers.at(vertexPosIndex); + auto& vertexJointsBuffer = graphic.buffers.at(vertexJointsIndex); + auto& vertexWeightsBuffer = graphic.buffers.at(vertexWeightsIndex); + + auto& vertexOutPosition = graphic.buffers.at(vertexSourceDataIndex); + graphic.metadata.buffers["vertexSkinned"] = vertexSourceDataIndex; + + auto& shader = graphic.material.getShader("compute", "skinning"); + + struct { + uint32_t jointID; + } uniforms = { + .jointID = 0 + }; + + shader.updateBuffer( (const void*) &uniforms, sizeof(uniforms), shader.getUniformBuffer("UBO") ); + + // bind buffers + ::resetBuffers( shader ); + shader.aliasBuffer( storage.buffers.joint ); + shader.aliasBuffer( vertexPositionBuffer ); + shader.aliasBuffer( vertexJointsBuffer ); + shader.aliasBuffer( vertexWeightsBuffer ); + shader.aliasBuffer( vertexOutPosition ); + } + graphic.generateBottomAccelerationStructures(); + } + #endif + } + + void bindBuffers( uf::renderer::Graphic& graphic, uf::Mesh& mesh ) { + auto& scene = uf::scene::getCurrentScene(); + auto& sceneTextures = scene.getComponent(); + auto& sceneMetadataJson = scene.getComponent(); + auto& storage = uf::graph::globalStorage ? uf::graph::storage : scene.getComponent(); + + // draw command buffer for binding + uf::renderer::Buffer* indirect = NULL; + for ( auto& buffer : graphic.buffers ) if ( !indirect && buffer.usage & uf::renderer::enums::Buffer::INDIRECT ) indirect = &buffer; + + // standard pipeline + { + // vertex shader + { + auto& shader = graphic.material.getShader("vertex"); + + // bind buffers + ::resetBuffers( shader ); + #if UF_USE_VULKAN + shader.aliasBuffer( "camera", storage.buffers.camera ); + #else + shader.aliasBuffer( storage.buffers.camera ); + #endif + + #if UF_USE_VULKAN + shader.aliasBuffer( *indirect ); + #endif + shader.aliasBuffer( storage.buffers.instance ); + shader.aliasBuffer( storage.buffers.joint ); + } + // fragment shader + { + auto& shader = graphic.material.getShader("fragment"); + + // bind buffers + ::resetBuffers( shader ); + shader.aliasBuffer( storage.buffers.drawCommands ); + shader.aliasBuffer( storage.buffers.instance ); + shader.aliasBuffer( storage.buffers.instanceAddresses ); + shader.aliasBuffer( storage.buffers.material ); + shader.aliasBuffer( storage.buffers.texture ); + shader.aliasBuffer( storage.buffers.light ); + } + } + + + #if UF_USE_VULKAN + // depth only pipeline + { + // fragment shader + auto& shader = graphic.material.getShader("fragment", "depth"); + + // bind buffers + ::resetBuffers( shader ); + shader.aliasBuffer( storage.buffers.drawCommands ); + shader.aliasBuffer( storage.buffers.instance ); + shader.aliasBuffer( storage.buffers.instanceAddresses ); + shader.aliasBuffer( storage.buffers.material ); + shader.aliasBuffer( storage.buffers.texture ); + shader.aliasBuffer( storage.buffers.light ); + } + // culling pipeline + if ( uf::renderer::settings::pipelines::culling && indirect ) { + // compute shader + auto& shader = graphic.material.getShader("compute", uf::renderer::settings::pipelines::names::culling); + + // bind buffers + ::resetBuffers( shader ); + shader.aliasBuffer( "camera", storage.buffers.camera ); + shader.aliasBuffer( *indirect ); + shader.aliasBuffer( storage.buffers.instance ); + } + + // vxgi pipeline + if ( uf::renderer::settings::pipelines::vxgi ) { + // fragment shader + { + auto& shader = graphic.material.getShader("fragment", uf::renderer::settings::pipelines::names::vxgi); + + // bind buffers + ::resetBuffers( shader ); + shader.aliasBuffer( storage.buffers.drawCommands ); + shader.aliasBuffer( storage.buffers.instance ); + shader.aliasBuffer( storage.buffers.instanceAddresses ); + shader.aliasBuffer( storage.buffers.material ); + shader.aliasBuffer( storage.buffers.texture ); + shader.aliasBuffer( storage.buffers.light ); + } + } + // baking pipeline + if ( graphic.material.hasShader("vertex", "baking") /*graph.metadata["baking"]["enabled"].as()*/ ) { + // vertex shader + { + auto& shader = graphic.material.getShader("vertex", "baking"); + + // bind buffers + ::resetBuffers( shader ); + #if UF_USE_VULKAN + shader.aliasBuffer( *indirect ); + #endif + shader.aliasBuffer( storage.buffers.instance ); + shader.aliasBuffer( storage.buffers.joint ); + } + + // fragment shader + { + auto& shader = graphic.material.getShader("fragment", "baking"); + + // bind buffers + ::resetBuffers( shader ); + shader.aliasBuffer( "camera", storage.buffers.camera ); + shader.aliasBuffer( storage.buffers.drawCommands ); + shader.aliasBuffer( storage.buffers.instance ); + shader.aliasBuffer( storage.buffers.instanceAddresses ); + shader.aliasBuffer( storage.buffers.material ); + shader.aliasBuffer( storage.buffers.texture ); + shader.aliasBuffer( storage.buffers.light ); + } + } + + // rt pipeline + // to-do: segregate out buffer updating code + + // grab addresses + if ( uf::renderer::settings::invariant::deviceAddressing ) { + pod::DrawCommand* drawCommands = (pod::DrawCommand*) mesh.getBuffer( mesh.indirect ).data(); + for ( size_t drawID = 0; drawID < mesh.indirect.count; ++drawID ) { + auto& drawCommand = drawCommands[drawID]; + auto instanceID = drawCommand.instanceID; + auto instanceKeyName = std::to_string(instanceID); + + if ( storage.instanceAddresses.map.count(instanceKeyName) > 0 ) { + // UF_MSG_ERROR("DUPLICATE INSTANCE ID: {}", instanceKeyName); + } + + auto& instanceAddresses = storage.instanceAddresses.map[instanceKeyName]; + if ( mesh.vertex.count ) { + if ( mesh.isInterleaved( mesh.vertex ) ) { + instanceAddresses.vertex = graphic.buffers.at(graphic.descriptor.inputs.vertex.interleaved).getAddress()/* + (drawCommand.vertexID * mesh.vertex.size)*/; + } else { + for ( auto& attribute : graphic.descriptor.inputs.vertex.attributes ) { + if ( attribute.buffer < 0 ) continue; + if ( attribute.descriptor.name == "position" ) instanceAddresses.position = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; + else if ( attribute.descriptor.name == "uv" ) instanceAddresses.uv = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; + else if ( attribute.descriptor.name == "color" ) instanceAddresses.color = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; + else if ( attribute.descriptor.name == "st" ) instanceAddresses.st = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; + else if ( attribute.descriptor.name == "normal" ) instanceAddresses.normal = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; + else if ( attribute.descriptor.name == "tangent" ) instanceAddresses.tangent = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; + else if ( attribute.descriptor.name == "joints" ) instanceAddresses.joints = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; + else if ( attribute.descriptor.name == "weights" ) instanceAddresses.weights = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; + else if ( attribute.descriptor.name == "id" ) instanceAddresses.id = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; + } + } + } + if ( mesh.index.count ) { + if ( mesh.isInterleaved( mesh.index ) ) instanceAddresses.index = graphic.buffers.at(graphic.descriptor.inputs.index.interleaved).getAddress(); + else instanceAddresses.index = graphic.buffers.at(graphic.descriptor.inputs.index.attributes.front().buffer).getAddress(); + } + + if ( mesh.indirect.count ) { + if ( mesh.isInterleaved( mesh.indirect ) ) instanceAddresses.indirect = graphic.buffers.at(graphic.descriptor.inputs.indirect.interleaved).getAddress(); + else instanceAddresses.indirect = graphic.buffers.at(graphic.descriptor.inputs.indirect.attributes.front().buffer).getAddress(); + + instanceAddresses.drawID = drawID; + } + } + } + #endif + } + // lazy load animations if requested void loadAnimation( const uf::stl::string& name ) { auto& scene = uf::scene::getCurrentScene(); @@ -328,7 +815,6 @@ void uf::graph::initializeGraphics( pod::Graph& graph, uf::Object& entity, uf::M auto& sceneMetadataJson = scene.getComponent(); auto& storage = uf::graph::globalStorage ? uf::graph::storage : scene.getComponent(); - auto& graphic = entity.getComponent(); graphic.initialize(); graphic.initializeMesh( mesh ); @@ -376,436 +862,8 @@ void uf::graph::initializeGraphics( pod::Graph& graph, uf::Object& entity, uf::M */ ::bindTextures( graphic ); - - uf::stl::string root = uf::io::directory( graph.name ); - size_t texture2Ds = 0; - size_t texture3Ds = 0; - for ( auto& texture : graphic.material.textures ) { - if ( texture.width > 1 && texture.height > 1 && texture.depth == 1 && texture.layers == 1 ) ++texture2Ds; - else if ( texture.width > 1 && texture.height > 1 && texture.depth > 1 && texture.layers == 1 ) ++texture3Ds; - } - - // standard pipeline - uf::stl::string vertexShaderFilename = graph.metadata["shaders"]["vertex"].as("/graph/base/vert.spv"); { - std::pair settings[] = { - { graph.metadata["renderer"]["skinned"].as(), "skinned.vert" }, - { !graph.metadata["renderer"]["separate"].as(), "instanced.vert" }, - }; - FOR_ARRAY(settings) if ( settings[i].first ) vertexShaderFilename = uf::string::replace( vertexShaderFilename, "vert", settings[i].second ); - vertexShaderFilename = entity.resolveURI( vertexShaderFilename, root ); - } - uf::stl::string geometryShaderFilename = graph.metadata["shaders"]["geometry"].as(""); - uf::stl::string fragmentShaderFilename = graph.metadata["shaders"]["fragment"].as("/graph/base/frag.spv"); { - #if 0 - std::pair settings[] = { - { uf::renderer::settings::invariant::deferredSampling, "deferredSampling.frag" }, - }; - FOR_ARRAY(settings) if ( settings[i].first ) fragmentShaderFilename = uf::string::replace( fragmentShaderFilename, "frag", settings[i].second ); - #endif - fragmentShaderFilename = entity.resolveURI( fragmentShaderFilename, root ); - } - { - graphic.material.metadata.autoInitializeUniformBuffers = false; - graphic.material.attachShader(vertexShaderFilename, uf::renderer::enums::Shader::VERTEX); - graphic.material.attachShader(fragmentShaderFilename, uf::renderer::enums::Shader::FRAGMENT); - graphic.material.metadata.autoInitializeUniformBuffers = true; - } - - uf::renderer::Buffer* indirect = NULL; - for ( auto& buffer : graphic.buffers ) if ( !indirect && buffer.usage & uf::renderer::enums::Buffer::INDIRECT ) indirect = &buffer; - -/* - { - auto& renderMode = uf::renderer::getRenderMode("", true); - if ( !renderMode.hasBuffer("camera") ) { - renderMode.metadata.buffers["camera"] = renderMode.initializeBuffer( (const void*) nullptr, sizeof(pod::Camera::Viewports), uf::renderer::enums::Buffer::UNIFORM ); - } - } -*/ - { - auto& shader = graphic.material.getShader("vertex"); - #if UF_USE_VULKAN - shader.aliasBuffer( "camera", storage.buffers.camera ); - #else - shader.buffers.emplace_back( storage.buffers.camera.alias() ); - #endif -// // shader.buffers.emplace_back( storage.buffers.drawCommands.alias() ); - #if UF_USE_VULKAN - shader.buffers.emplace_back( indirect->alias() ); - #endif - shader.buffers.emplace_back( storage.buffers.instance.alias() ); - shader.buffers.emplace_back( storage.buffers.joint.alias() ); - #if UF_USE_VULKAN - uint32_t maxPasses = 6; - shader.setSpecializationConstants({ - { "PASSES", maxPasses } - }); - #endif - } - { - auto& shader = graphic.material.getShader("fragment"); - - shader.buffers.emplace_back( storage.buffers.drawCommands.alias() ); - shader.buffers.emplace_back( storage.buffers.instance.alias() ); - shader.buffers.emplace_back( storage.buffers.instanceAddresses.alias() ); - shader.buffers.emplace_back( storage.buffers.material.alias() ); - shader.buffers.emplace_back( storage.buffers.texture.alias() ); - shader.buffers.emplace_back( storage.buffers.light.alias() ); - - #if UF_USE_VULKAN - uint32_t maxTextures = graph.textures.size(); // texture2Ds; - shader.setSpecializationConstants({ - { "TEXTURES", maxTextures }, - }); - shader.setDescriptorCounts({ - { "samplerTextures", maxTextures }, - }); - #endif - } -#if UF_USE_VULKAN - // culling pipeline - if ( uf::renderer::settings::pipelines::culling ) { - uf::renderer::Buffer* indirect = NULL; - for ( auto& buffer : graphic.buffers ) if ( !indirect && buffer.usage & uf::renderer::enums::Buffer::INDIRECT ) indirect = &buffer; - UF_ASSERT( indirect ); - if ( indirect ) { - uf::stl::string compShaderFilename = graph.metadata["shaders"][uf::renderer::settings::pipelines::names::culling]["compute"].as("/graph/cull/comp.spv"); - { - graphic.material.metadata.autoInitializeUniformBuffers = false; - compShaderFilename = entity.resolveURI( compShaderFilename, root ); - graphic.material.attachShader(compShaderFilename, uf::renderer::enums::Shader::COMPUTE, uf::renderer::settings::pipelines::names::culling); - graphic.material.metadata.autoInitializeUniformBuffers = true; - } - graphic.descriptor.bind.width = graphic.descriptor.inputs.indirect.count; - graphic.descriptor.bind.height = 1; - graphic.descriptor.bind.depth = 1; - - auto& shader = graphic.material.getShader("compute", uf::renderer::settings::pipelines::names::culling); - - // shader.buffers.emplace_back( storage.buffers.camera.alias() ); - shader.aliasBuffer( "camera", storage.buffers.camera ); - shader.buffers.emplace_back( indirect->alias() ); - shader.buffers.emplace_back( storage.buffers.instance.alias() ); - - shader.aliasAttachment("depthPyramid"); - } - } - if ( geometryShaderFilename != "" && uf::renderer::device.enabledFeatures.geometryShader ) { - geometryShaderFilename = entity.resolveURI( geometryShaderFilename, root ); - graphic.material.attachShader(geometryShaderFilename, uf::renderer::enums::Shader::GEOMETRY); - } - // depth only pipeline - { - graphic.material.metadata.autoInitializeUniformBuffers = false; - uf::stl::string fragmentShaderFilename = graph.metadata["shaders"]["depth"]["fragmment"].as("/graph/depth/frag.spv"); - fragmentShaderFilename = entity.resolveURI( fragmentShaderFilename, root ); - graphic.material.attachShader(fragmentShaderFilename, uf::renderer::enums::Shader::FRAGMENT, "depth"); - graphic.material.metadata.autoInitializeUniformBuffers = true; - { - auto& shader = graphic.material.getShader("fragment", "depth"); - - uint32_t maxTextures = graph.textures.size(); // texture2Ds; - shader.setSpecializationConstants({ - { "TEXTURES", maxTextures }, - }); - shader.setDescriptorCounts({ - { "samplerTextures", maxTextures }, - }); - - shader.buffers.emplace_back( storage.buffers.drawCommands.alias() ); - shader.buffers.emplace_back( storage.buffers.instance.alias() ); - shader.buffers.emplace_back( storage.buffers.instanceAddresses.alias() ); - shader.buffers.emplace_back( storage.buffers.material.alias() ); - shader.buffers.emplace_back( storage.buffers.texture.alias() ); - shader.buffers.emplace_back( storage.buffers.light.alias() ); - } - } - // vxgi pipeline - if ( uf::renderer::settings::pipelines::vxgi ) { - uf::stl::string vertexShaderFilename = graph.metadata["shaders"][uf::renderer::settings::pipelines::names::vxgi]["vertex"].as("/graph/base/vert.spv"); - uf::stl::string geometryShaderFilename = graph.metadata["shaders"][uf::renderer::settings::pipelines::names::vxgi]["geometry"].as("/graph/voxelize/geom.spv"); - uf::stl::string fragmentShaderFilename = graph.metadata["shaders"][uf::renderer::settings::pipelines::names::vxgi]["fragment"].as("/graph/voxelize/frag.spv"); - - { - fragmentShaderFilename = entity.resolveURI( fragmentShaderFilename, root ); - graphic.material.metadata.autoInitializeUniformBuffers = false; - graphic.material.attachShader(fragmentShaderFilename, uf::renderer::enums::Shader::FRAGMENT, uf::renderer::settings::pipelines::names::vxgi); - graphic.material.metadata.autoInitializeUniformBuffers = true; - } - if ( geometryShaderFilename != "" && uf::renderer::device.enabledFeatures.geometryShader ) { - geometryShaderFilename = entity.resolveURI( geometryShaderFilename, root ); - graphic.material.attachShader(geometryShaderFilename, uf::renderer::enums::Shader::GEOMETRY, uf::renderer::settings::pipelines::names::vxgi); - } - { - uint32_t voxelTypes = 0; - if ( !sceneTextures.voxels.drawId.empty() ) ++voxelTypes; - if ( !sceneTextures.voxels.instanceId.empty() ) ++voxelTypes; - if ( !sceneTextures.voxels.normalX.empty() ) ++voxelTypes; - if ( !sceneTextures.voxels.normalY.empty() ) ++voxelTypes; - if ( !sceneTextures.voxels.radianceR.empty() ) ++voxelTypes; - if ( !sceneTextures.voxels.radianceG.empty() ) ++voxelTypes; - if ( !sceneTextures.voxels.radianceB.empty() ) ++voxelTypes; - if ( !sceneTextures.voxels.radianceA.empty() ) ++voxelTypes; - if ( !sceneTextures.voxels.count.empty() ) ++voxelTypes; - if ( !sceneTextures.voxels.output.empty() ) ++voxelTypes; - - uint32_t maxTextures = texture2Ds; - uint32_t maxCascades = texture3Ds / voxelTypes; - - auto& shader = graphic.material.getShader("fragment", uf::renderer::settings::pipelines::names::vxgi); - shader.setSpecializationConstants({ - { "TEXTURES", maxTextures }, - { "CASCADES", maxCascades }, - }); - shader.setDescriptorCounts({ - { "samplerTextures", maxTextures }, - { "voxelDrawId", maxCascades }, - { "voxelInstanceId", maxCascades }, - { "voxelNormalX", maxCascades }, - { "voxelNormalY", maxCascades }, - { "voxelRadianceR", maxCascades }, - { "voxelRadianceG", maxCascades }, - { "voxelRadianceB", maxCascades }, - { "voxelRadianceA", maxCascades }, - { "voxelCount", maxCascades }, - { "voxelOutput", maxCascades }, - }); - - shader.buffers.emplace_back( storage.buffers.drawCommands.alias() ); - shader.buffers.emplace_back( storage.buffers.instance.alias() ); - shader.buffers.emplace_back( storage.buffers.instanceAddresses.alias() ); - shader.buffers.emplace_back( storage.buffers.material.alias() ); - shader.buffers.emplace_back( storage.buffers.texture.alias() ); - shader.buffers.emplace_back( storage.buffers.light.alias() ); - } - } - // baking pipeline - if ( graph.metadata["baking"]["enabled"].as() ) { - { - graphic.material.metadata.autoInitializeUniformBuffers = false; - uf::stl::string vertexShaderFilename = uf::io::resolveURI("/graph/baking/vert.spv"); - uf::stl::string geometryShaderFilename = uf::io::resolveURI("/graph/baking/geom.spv"); - uf::stl::string fragmentShaderFilename = uf::io::resolveURI("/graph/baking/frag.spv"); - std::pair settings[] = { - { uf::renderer::settings::pipelines::rt, "rt.frag" }, - }; - FOR_ARRAY(settings) if ( settings[i].first ) fragmentShaderFilename = uf::string::replace( fragmentShaderFilename, "frag", settings[i].second ); - - graphic.material.attachShader(vertexShaderFilename, uf::renderer::enums::Shader::VERTEX, "baking"); - // graphic.material.attachShader(geometryShaderFilename, uf::renderer::enums::Shader::GEOMETRY, "baking"); - graphic.material.attachShader(fragmentShaderFilename, uf::renderer::enums::Shader::FRAGMENT, "baking"); - graphic.material.metadata.autoInitializeUniformBuffers = true; - } - { - uint32_t maxPasses = 6; - - auto& shader = graphic.material.getShader("vertex", "baking"); - shader.setSpecializationConstants({ - { "PASSES", maxPasses }, - }); - - #if UF_USE_VULKAN - shader.buffers.emplace_back( indirect->alias() ); - #endif - shader.buffers.emplace_back( storage.buffers.instance.alias() ); - shader.buffers.emplace_back( storage.buffers.joint.alias() ); - } - - { - size_t maxTextures = uf::config["engine"]["scenes"]["textures"]["max"]["2D"].as(512); - size_t maxCubemaps = uf::config["engine"]["scenes"]["textures"]["max"]["cube"].as(128); - size_t maxTextures3D = uf::config["engine"]["scenes"]["textures"]["max"]["3D"].as(128); - - auto& shader = graphic.material.getShader("fragment", "baking"); - shader.setSpecializationConstants({ - { "TEXTURES", maxTextures }, - { "CUBEMAPS", maxCubemaps }, - }); - shader.setDescriptorCounts({ - { "samplerTextures", maxTextures }, - { "samplerCubemaps", maxCubemaps }, - }); - - // shader.buffers.emplace_back( storage.buffers.camera.alias() ); - shader.aliasBuffer( "camera", storage.buffers.camera ); - shader.buffers.emplace_back( storage.buffers.drawCommands.alias() ); - shader.buffers.emplace_back( storage.buffers.instance.alias() ); - shader.buffers.emplace_back( storage.buffers.instanceAddresses.alias() ); - shader.buffers.emplace_back( storage.buffers.material.alias() ); - shader.buffers.emplace_back( storage.buffers.texture.alias() ); - shader.buffers.emplace_back( storage.buffers.light.alias() ); - } - } - if ( uf::renderer::settings::pipelines::rt ) { - // rt pipeline - if ( mesh.vertex.count && graph.metadata["renderer"]["skinned"].as() ) { - - struct PushConstant { - uint32_t jointID; - }; - - if ( mesh.isInterleaved( mesh.vertex ) ) { - auto& vertexSourceData = mesh.buffers[mesh.vertex.interleaved]; - size_t vertexSourceDataIndex = graphic.initializeBuffer( (const void*) vertexSourceData.data(), vertexSourceData.size(), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR ); - - /* - auto& vertexIn = graphic.buffers.at(vertexSourceDataIndex); - auto& vertexOut = graphic.buffers.at(graphic.descriptor.inputs.vertex.interleaved); - */ - auto& vertexIn = graphic.buffers.at(graphic.descriptor.inputs.vertex.interleaved); - auto& vertexOut = graphic.buffers.at(vertexSourceDataIndex); - graphic.metadata.buffers["vertexSkinned"] = vertexSourceDataIndex; - - uf::stl::string compShaderFilename = graph.metadata["shaders"]["skinning"]["compute"].as("/graph/skinning/skinning.interleaved.comp.spv"); - { - graphic.material.metadata.autoInitializeUniformBuffers = false; - compShaderFilename = entity.resolveURI( compShaderFilename, root ); - graphic.material.attachShader(compShaderFilename, uf::renderer::enums::Shader::COMPUTE, "skinning"); - graphic.material.metadata.autoInitializeUniformBuffers = true; - } - graphic.descriptor.bind.width = mesh.vertex.count; - graphic.descriptor.bind.height = 1; - graphic.descriptor.bind.depth = 1; - - auto& shader = graphic.material.getShader("compute", "skinning"); - - struct { - uint32_t jointID; - } uniforms = { - .jointID = 0 - }; - - shader.updateBuffer( (const void*) &uniforms, sizeof(uniforms), shader.getUniformBuffer("UBO") ); - - shader.buffers.emplace_back( storage.buffers.joint.alias() ); - shader.buffers.emplace_back( vertexIn.alias() ); - shader.buffers.emplace_back( vertexOut.alias() ); - } else { - uf::Mesh::Attribute vertexPos; - uf::Mesh::Attribute vertexJoints; - uf::Mesh::Attribute vertexWeights; - - size_t vertexPosIndex = 0; - size_t vertexJointsIndex = 0; - size_t vertexWeightsIndex = 0; - - for ( size_t i = 0; i < graphic.descriptor.inputs.vertex.attributes.size(); ++i ) { - auto& attribute = graphic.descriptor.inputs.vertex.attributes[i]; - - if ( attribute.buffer < 0 ) continue; - if ( attribute.descriptor.name == "position" ) { - vertexPos = attribute; - vertexPosIndex = graphic.metadata.buffers["vertex[position]"]; - } - else if ( attribute.descriptor.name == "joints" ) { - vertexJoints = attribute; - vertexJointsIndex = graphic.metadata.buffers["vertex[joints]"]; - } - else if ( attribute.descriptor.name == "weights" ) { - vertexWeights = attribute; - vertexWeightsIndex = graphic.metadata.buffers["vertex[weights]"]; - } - } - - auto& vertexSourceData = mesh.buffers[vertexPos.buffer]; - size_t vertexSourceDataIndex = graphic.initializeBuffer( (const void*) vertexSourceData.data(), vertexSourceData.size(), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR ); - - /* - auto& vertexPositionBuffer = graphic.buffers.at(vertexSourceDataIndex); - auto& vertexJointsBuffer = graphic.buffers.at(vertexJointsIndex); - auto& vertexWeightsBuffer = graphic.buffers.at(vertexWeightsIndex); - - auto& vertexOutPosition = graphic.buffers.at(vertexPosIndex); - */ - auto& vertexPositionBuffer = graphic.buffers.at(vertexPosIndex); - auto& vertexJointsBuffer = graphic.buffers.at(vertexJointsIndex); - auto& vertexWeightsBuffer = graphic.buffers.at(vertexWeightsIndex); - - auto& vertexOutPosition = graphic.buffers.at(vertexSourceDataIndex); - graphic.metadata.buffers["vertexSkinned"] = vertexSourceDataIndex; - - uf::stl::string compShaderFilename = graph.metadata["shaders"]["skinning"]["compute"].as("/graph/skinning/skinning.deinterleaved.comp.spv"); - { - // graphic.material.metadata.autoInitializeUniformBuffers = false; - compShaderFilename = entity.resolveURI( compShaderFilename, root ); - graphic.material.attachShader(compShaderFilename, uf::renderer::enums::Shader::COMPUTE, "skinning"); - // graphic.material.metadata.autoInitializeUniformBuffers = true; - } - graphic.descriptor.bind.width = mesh.vertex.count; - graphic.descriptor.bind.height = 1; - graphic.descriptor.bind.depth = 1; - - auto& shader = graphic.material.getShader("compute", "skinning"); - - struct { - uint32_t jointID; - } uniforms = { - .jointID = 0 - }; - - shader.updateBuffer( (const void*) &uniforms, sizeof(uniforms), shader.getUniformBuffer("UBO") ); - - shader.buffers.emplace_back( storage.buffers.joint.alias() ); - shader.buffers.emplace_back( vertexPositionBuffer.alias() ); - shader.buffers.emplace_back( vertexJointsBuffer.alias() ); - shader.buffers.emplace_back( vertexWeightsBuffer.alias() ); - shader.buffers.emplace_back( vertexOutPosition.alias() ); - } - } - graphic.generateBottomAccelerationStructures(); - } - - // grab addresses - if ( uf::renderer::settings::invariant::deviceAddressing ) { - pod::DrawCommand* drawCommands = (pod::DrawCommand*) mesh.getBuffer( mesh.indirect ).data(); - for ( size_t drawID = 0; drawID < mesh.indirect.count; ++drawID ) { - auto& drawCommand = drawCommands[drawID]; - auto instanceID = drawCommand.instanceID; - auto instanceKeyName = std::to_string(instanceID); - - /* - if ( storage.instanceAddresses.map.count(instanceKeyName) > 0 ) { - auto objectKeyName = std::to_string(objectID); - instanceKeyName = objectKeyName + "+" + instanceKeyName; - } - */ - if ( storage.instanceAddresses.map.count(instanceKeyName) > 0 ) { - // UF_MSG_ERROR("DUPLICATE INSTANCE ID: {}", instanceKeyName); - } - - auto& instanceAddresses = storage.instanceAddresses.map[instanceKeyName]; - if ( mesh.vertex.count ) { - if ( mesh.isInterleaved( mesh.vertex ) ) { - instanceAddresses.vertex = graphic.buffers.at(graphic.descriptor.inputs.vertex.interleaved).getAddress()/* + (drawCommand.vertexID * mesh.vertex.size)*/; - } else { - for ( auto& attribute : graphic.descriptor.inputs.vertex.attributes ) { - if ( attribute.buffer < 0 ) continue; - if ( attribute.descriptor.name == "position" ) instanceAddresses.position = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; - else if ( attribute.descriptor.name == "uv" ) instanceAddresses.uv = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; - else if ( attribute.descriptor.name == "color" ) instanceAddresses.color = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; - else if ( attribute.descriptor.name == "st" ) instanceAddresses.st = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; - else if ( attribute.descriptor.name == "normal" ) instanceAddresses.normal = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; - else if ( attribute.descriptor.name == "tangent" ) instanceAddresses.tangent = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; - else if ( attribute.descriptor.name == "joints" ) instanceAddresses.joints = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; - else if ( attribute.descriptor.name == "weights" ) instanceAddresses.weights = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; - else if ( attribute.descriptor.name == "id" ) instanceAddresses.id = graphic.buffers.at(attribute.buffer).getAddress()/* + (drawCommand.vertexID * attribute.stride)*/; - } - } - } - if ( mesh.index.count ) { - if ( mesh.isInterleaved( mesh.index ) ) instanceAddresses.index = graphic.buffers.at(graphic.descriptor.inputs.index.interleaved).getAddress(); - else instanceAddresses.index = graphic.buffers.at(graphic.descriptor.inputs.index.attributes.front().buffer).getAddress(); - } - - if ( mesh.indirect.count ) { - if ( mesh.isInterleaved( mesh.indirect ) ) instanceAddresses.indirect = graphic.buffers.at(graphic.descriptor.inputs.indirect.interleaved).getAddress(); - else instanceAddresses.indirect = graphic.buffers.at(graphic.descriptor.inputs.indirect.attributes.front().buffer).getAddress(); - - instanceAddresses.drawID = drawID; - } - } - } -#endif + ::bindShaders( graph, entity, mesh ); + ::bindBuffers( graphic, mesh ); graphic.process = true; } @@ -1846,13 +1904,33 @@ void uf::graph::initialize( pod::Graph::Storage& storage, size_t initialElements storage.buffers.light.initialize( (const void*) nullptr, sizeof(pod::Light) * initialElements, uf::renderer::enums::Buffer::STORAGE ); } void uf::graph::tick() { - if ( uf::graph::globalStorage ) return uf::graph::tick( uf::graph::storage ); - return uf::graph::tick( uf::scene::getCurrentScene() ); + if ( uf::graph::globalStorage ) uf::graph::tick( uf::graph::storage ); + else uf::graph::tick( uf::scene::getCurrentScene() ); } void uf::graph::tick( uf::Object& object ) { - return uf::graph::tick( object.getComponent() ); + auto& storage = object.getComponent(); + + bool rebuild = uf::graph::tick( storage ); + + // rebind buffers + if ( rebuild ) { + auto& graph = object.getComponent(); + for ( auto& node : graph.nodes ) { + if ( !(0 <= node.mesh && node.mesh < graph.meshes.size()) ) continue; + if ( !node.entity ) continue; + + auto& entity = node.entity->as(); + + if ( !entity.hasComponent() ) continue; + + auto& graphic = entity.getComponent(); + auto& mesh = storage.meshes.map[graph.meshes[node.mesh]]; + + ::bindBuffers( graphic, mesh ); + } + } } -void uf::graph::tick( pod::Graph::Storage& storage ) { +bool uf::graph::tick( pod::Graph::Storage& storage ) { /* uf::stl::vector instances; instances.reserve(storage.instances.map.size()); for ( auto& key : storage.instances.keys ) instances.emplace_back( storage.instances.map[key] ); @@ -1898,8 +1976,8 @@ void uf::graph::tick( pod::Graph::Storage& storage ) { rebuild = storage.buffers.texture.update( (const void*) textures.data(), textures.size() * sizeof(pod::Texture) ) || rebuild; ::newGraphAdded = false; - } + if ( rebuild ) { UF_MSG_DEBUG("Graph buffers requesting renderer update"); uf::renderer::states::rebuild = true; @@ -1915,19 +1993,21 @@ void uf::graph::tick( pod::Graph::Storage& storage ) { auto moved = std::move( shader.buffers ); for ( auto& buffer : moved ) { if ( buffer.aliased ) continue; - shader.buffers.emplace_back( buffer ); + shader.aliasBuffer( buffer ); buffer.aliased = true; } - shader.buffers.emplace_back( storage.buffers.drawCommands.alias() ); - shader.buffers.emplace_back( storage.buffers.instance.alias() ); - shader.buffers.emplace_back( storage.buffers.instanceAddresses.alias() ); - shader.buffers.emplace_back( storage.buffers.material.alias() ); - shader.buffers.emplace_back( storage.buffers.texture.alias() ); - shader.buffers.emplace_back( storage.buffers.light.alias() ); + shader.aliasBuffer( storage.buffers.drawCommands ); + shader.aliasBuffer( storage.buffers.instance ); + shader.aliasBuffer( storage.buffers.instanceAddresses ); + shader.aliasBuffer( storage.buffers.material ); + shader.aliasBuffer( storage.buffers.texture ); + shader.aliasBuffer( storage.buffers.light ); } */ } + + return rebuild; } void uf::graph::render() { if ( uf::graph::globalStorage ) return uf::graph::render( uf::graph::storage ); @@ -2359,10 +2439,13 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) { if ( exists ) { auto& graphic = entity.getComponent(); bool rebuild = graphic.updateMesh( mesh ); - if ( rebuild ) { - // uf::renderer::states::rebuild = true; - } + // update texture descriptors ::bindTextures( graphic ); + // update buffers if any of them were resized (because my aliasing system is weak) + if ( rebuild ) { + ::bindBuffers( graphic, mesh ); + uf::renderer::states::rebuild = true; + } } else { uf::graph::initializeGraphics( graph, entity, mesh ); } diff --git a/engine/src/ext/opengl/buffer.cpp b/engine/src/ext/opengl/buffer.cpp index a64ca1fc..79540460 100644 --- a/engine/src/ext/opengl/buffer.cpp +++ b/engine/src/ext/opengl/buffer.cpp @@ -12,15 +12,14 @@ ext::opengl::Buffer ext::opengl::Buffer::alias() const { return buffer; } void ext::opengl::Buffer::aliasBuffer( const ext::opengl::Buffer& buffer ) { - *this = { - .device = NULL, - .buffer = buffer.buffer, - .descriptor = buffer.descriptor, - .size = buffer.size, - .alignment = buffer.alignment, - .usage = buffer.usage, - .allocationInfo = buffer.allocationInfo, - }; + this->aliased = true; + this->device = NULL; + this->buffer = buffer.buffer; + this->descriptor = buffer.descriptor; + this->size = buffer.size; + this->alignment = buffer.alignment; + this->usage = buffer.usage; + this->allocationInfo = buffer.allocationInfo; } void* ext::opengl::Buffer::map( GLsizeiptr size, GLsizeiptr offset ) { @@ -52,9 +51,18 @@ void ext::opengl::Buffer::initialize( const void* data, GLsizeiptr length, GLenu } ); if ( !alias ) this->update( data, length ); } -bool ext::opengl::Buffer::update( const void* data, GLsizeiptr len ) const { +bool ext::opengl::Buffer::update( const void* data, GLsizeiptr length ) const { if ( !buffer || !data ) return false; - if ( len >= size ) len = size; + if ( length > allocationInfo.size ) { + UF_MSG_WARNING("Buffer update of {} exceeds buffer size of {}", length, allocationInfo.size); + + auto savedUsage = usage; + Buffer& self = const_cast(*this); + self.destroy(true); + self.initialize(data, length, savedUsage); + + return true; + } #if !UF_USE_OPENGL_FIXED_FUNCTION // GPU-bound buffer GLenum usage; @@ -74,12 +82,12 @@ bool ext::opengl::Buffer::update( const void* data, GLsizeiptr len ) const { if ( usage & enums::Buffer::COPY ) usage = GL_DYNAMIC_COPY; } GL_ERROR_CHECK(glBindBuffer(GL_COPY_WRITE_BUFFER, buffer)); - GL_ERROR_CHECK(glBufferData(GL_COPY_WRITE_BUFFER, len, data, usage)); + GL_ERROR_CHECK(glBufferData(GL_COPY_WRITE_BUFFER, length, data, usage)); GL_ERROR_CHECK(glBindBuffer(GL_COPY_WRITE_BUFFER, 0)); #else // CPU-bound buffer void* pointer = device->getBuffer( buffer ); - if ( pointer && pointer != data ) memcpy( pointer, data, len ); + if ( pointer && pointer != data ) memcpy( pointer, data, length ); #endif return false; } @@ -113,7 +121,15 @@ void ext::opengl::Buffer::initialize( Device& device ) { this->device = &device; } void ext::opengl::Buffer::destroy( bool defer ) { - if ( device && buffer ) device->destroyBuffer( buffer ); + if ( !device || aliased ) return; + + if ( defer ) { + ext::opengl::mutex.lock(); + device->transient.buffers.emplace_back(*this); + ext::opengl::mutex.unlock(); + } else if ( buffer ) { + device->destroyBuffer( buffer ); + } device = NULL; buffer = NULL; } diff --git a/engine/src/ext/opengl/opengl.cpp b/engine/src/ext/opengl/opengl.cpp index 57900c76..0dbe6528 100644 --- a/engine/src/ext/opengl/opengl.cpp +++ b/engine/src/ext/opengl/opengl.cpp @@ -410,6 +410,7 @@ void ext::opengl::render(){ #if !UF_ENV_DREAMCAST ext::opengl::device.activateContext(); #endif + auto transient = std::move(ext::opengl::device.transient); { auto& scene = uf::scene::getCurrentScene(); @@ -470,6 +471,11 @@ void ext::opengl::render(){ #if UF_USE_OPENVR // if ( ext::openvr::context ) ext::openvr::postSubmit(); #endif + + // cleanup in-flight buffers + for ( auto& buffer : transient.buffers ) buffer.destroy(false); + transient.buffers.clear(); + ext::opengl::mutex.unlock(); } void ext::opengl::destroy() { diff --git a/engine/src/ext/opengl/shader.cpp b/engine/src/ext/opengl/shader.cpp index 681d5d2e..13f759b5 100644 --- a/engine/src/ext/opengl/shader.cpp +++ b/engine/src/ext/opengl/shader.cpp @@ -116,6 +116,19 @@ bool ext::opengl::Shader::hasUniform( const uf::stl::string& name ) const { // return metadata.definitions.uniforms.count(name) > 0; return true; } + +void ext::opengl::Shader::aliasBuffer( const ext::opengl::Shader::Metadata::BufferDescriptor& descriptor ) { + metadata.aliases.buffers.emplace_back(descriptor); +} +// aliases by name +void ext::opengl::Shader::aliasBuffer( const uf::stl::string& name, const ext::opengl::Buffer& fallback, const ext::opengl::RenderMode* renderMode, GLenum flags ) { + return aliasBuffer({ name, fallback.alias(), renderMode, flags }); +} +// aliases directly +void ext::opengl::Shader::aliasBuffer( const ext::opengl::Buffer& buffer ) { + return aliasBuffer({ "", buffer.alias(), NULL, buffer.usage }); +} + ext::opengl::Buffer& ext::opengl::Shader::getUniformBuffer( const uf::stl::string& name ) { UF_ASSERT( hasUniform(name) ); size_t uniformIndex = 0; // metadata.definitions.uniforms[name].index; diff --git a/engine/src/ext/vulkan/buffer.cpp b/engine/src/ext/vulkan/buffer.cpp index e2987d20..a8e7d1a1 100644 --- a/engine/src/ext/vulkan/buffer.cpp +++ b/engine/src/ext/vulkan/buffer.cpp @@ -195,14 +195,11 @@ bool ext::vulkan::Buffer::update( const void* data, VkDeviceSize length, bool st auto savedMemProps = memoryProperties; auto savedAlignment = alignment; - Buffer& oldBuffer = *const_cast(this); - Buffer newBuffer = {}; + Buffer& self = const_cast(*this); - oldBuffer.swap(newBuffer); - newBuffer.destroy(true); - - oldBuffer.initialize(*device, savedAlignment); - oldBuffer.initialize(data, length, savedUsage, savedMemProps, stage); + self.destroy(true); + self.initialize(*device, savedAlignment); + self.initialize(data, length, savedUsage, savedMemProps, stage); return true; } diff --git a/engine/src/ext/vulkan/graphic.cpp b/engine/src/ext/vulkan/graphic.cpp index 6a3c9d28..037a63da 100644 --- a/engine/src/ext/vulkan/graphic.cpp +++ b/engine/src/ext/vulkan/graphic.cpp @@ -516,6 +516,7 @@ void ext::vulkan::Pipeline::update( const Graphic& graphic, const GraphicDescrip auto& infos = INFOS.emplace_back(); uf::stl::vector types; + // add aliased-by-name buffers for ( auto& descriptor : shader->metadata.aliases.buffers ) { auto matches = uf::string::match(descriptor.name, R"(/^(.+?)\[(\d+)\]$/)"); auto name = matches.size() == 2 ? matches[0] : descriptor.name; diff --git a/engine/src/ext/vulkan/shader.cpp b/engine/src/ext/vulkan/shader.cpp index 0e364260..4d16f496 100644 --- a/engine/src/ext/vulkan/shader.cpp +++ b/engine/src/ext/vulkan/shader.cpp @@ -881,9 +881,14 @@ bool ext::vulkan::Shader::hasBuffer( const uf::stl::string& name ) { void ext::vulkan::Shader::aliasBuffer( const ext::vulkan::Shader::Metadata::BufferDescriptor& descriptor ) { metadata.aliases.buffers.emplace_back(descriptor); } +// aliases by name void ext::vulkan::Shader::aliasBuffer( const uf::stl::string& name, const ext::vulkan::Buffer& fallback, const ext::vulkan::RenderMode* renderMode, VkBufferUsageFlags flags ) { return aliasBuffer({ name, fallback.alias(), renderMode, flags }); } +// aliases directly +void ext::vulkan::Shader::aliasBuffer( const ext::vulkan::Buffer& buffer ) { + return aliasBuffer({ "", buffer.alias(), NULL, buffer.usage }); +} bool ext::vulkan::Shader::hasUniform( const uf::stl::string& name ) const { return metadata.definitions.uniforms.count(name) > 0;