validation errors squishing, finally bothered to seamlessly handle N-buffered UBOs, fixed shadowmapping not require a constant rebuild every time the lights changed (by instead binding all shadow textures and not-so-clever tricks), some other things

This commit is contained in:
ecker 2026-05-13 22:14:46 -05:00
parent 226f1fb77e
commit b3df72bfab
20 changed files with 286 additions and 232 deletions

View File

@ -110,7 +110,7 @@
"invariant": {
"default stage buffers": true,
"default defer buffer destroy": true,
"default command buffer immediate": false,
"default command buffer immediate": true,
"multithreaded recording": true
},
"pipelines": {
@ -118,9 +118,9 @@
"gui": true,
"vsync": true, // vsync on vulkan side rather than engine-side
"hdr": true,
"vxgi": true,
"vxgi": false, // to-do: fix issues
"culling": false,
"bloom": false,
"bloom": true,
"dof": false,
"rt": false,
"fsr": false,
@ -175,6 +175,7 @@
"features": [
// 1.1
"multiview"
,"multiviewGeometryShader"
//
,"nullDescriptor"
,"fragmentStoresAndAtomics"
@ -192,6 +193,12 @@
,"shaderSampledImageArrayNonUniformIndexing"
,"shaderStorageImageArrayNonUniformIndexing"
,"descriptorBindingAccelerationStructureUpdateAfterBind"
,"descriptorBindingSampledImageUpdateAfterBind"
,"descriptorBindingStorageImageUpdateAfterBind"
,"descriptorBindingStorageBufferUpdateAfterBind"
,"descriptorBindingUniformBufferUpdateAfterBind"
,"descriptorBindingPartiallyBound"
,"descriptorIndexing"
,"bufferDeviceAddress"
],

View File

@ -111,9 +111,11 @@ local fpsCounter = {
time = 0,
freq = 0.1
}
--[[
text:callHook( "gui:UpdateText.%UID%", {
string = ""
} )
]]
ent:bind( "tick", function(self)
--[[
@ -126,6 +128,7 @@ ent:bind( "tick", function(self)
end
]]
--[[
if fpsCounter["time"] > fpsCounter["freq"] then
-- update text
text:callHook( "gui:UpdateText.%UID%", {
@ -138,6 +141,7 @@ ent:bind( "tick", function(self)
fpsCounter["frames"] = fpsCounter["frames"] + 1
fpsCounter["time"] = fpsCounter["time"] + time.delta()
end
]]
local controllerTransform = controller:getComponent("Transform")

View File

@ -1,7 +1,7 @@
{
"name": "HUD Overlay",
"type": "Gui",
"ignore": false,
"ignore": true,
"assets": [
],

View File

@ -26,7 +26,7 @@
"bias": {
"constant": 1.25,
"slope": 1.75,
"shader": 0.00005 // 0.000005 //0.000000005
"shader": 0.000005 // 0.000005 //0.000000005
},
"radius": [0.5, 0],
"resolution": 1024,

View File

@ -28,7 +28,7 @@ void lambert() {
// skip if attenuation factor is too low
// if ( Lattenuation <= LIGHT_POWER_CUTOFF ) continue;
// ray cast if our surface is occluded from the light
const float Lshadow = ( shadows++ < MAX_SHADOWS ) ? shadowFactor( lights[i], 0.0 ) : 1;
const float Lshadow = shadowFactor( lights[i], 0.0 );
// skip if our shadow factor is too low
// if ( Lshadow <= LIGHT_POWER_CUTOFF ) continue; // in case of any divergence
// light radiance

View File

@ -35,7 +35,7 @@ void pbr() {
// skip if attenuation factor is too low
// if ( Lattenuation <= LIGHT_POWER_CUTOFF ) continue;
// ray cast if our surface is occluded from the light
const float Lshadow = ( shadows++ < MAX_SHADOWS ) ? shadowFactor( lights[i], 0.0 ) : 1;
const float Lshadow = shadowFactor( lights[i], 0.0 );
// skip if our shadow factor is too low
// if ( Lshadow <= LIGHT_POWER_CUTOFF ) continue; // in case of any divergence
// light radiance
@ -58,7 +58,7 @@ void pbr() {
// final lighting
const vec3 diffuse = mix(vec3(1.0) - F, vec3(0), surface.material.metallic) * surface.material.albedo.rgb;
const vec3 specular = ( shadows < MAX_SHADOWS ) ? ((F * D * G) / max(EPSILON, 4.0 * cosLi * cosLo)) : vec3(0);
const vec3 specular = (F * D * G) / max(EPSILON, 4.0 * cosLi * cosLo);
surface.light.rgb += (diffuse + specular) * Lr * cosLi;
surface.light.a += lights[i].power * Lattenuation * Lshadow;

View File

@ -21,6 +21,7 @@ namespace ext {
mutable size_t address = {};
void* mapped = nullptr;
int32_t count = 1;
mutable bool written = false;
VkBufferUsageFlags usage = 0;
VkMemoryPropertyFlags memoryProperties = 0;
@ -41,8 +42,8 @@ namespace ext {
~Buffer();
void initialize( ext::vulkan::Device& device, size_t = {} );
void initialize( const void*, VkDeviceSize, VkBufferUsageFlags, VkMemoryPropertyFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, bool = VK_DEFAULT_STAGE_BUFFERS );
bool update( const void*, VkDeviceSize, bool = VK_DEFAULT_STAGE_BUFFERS ) const; // returns true if a reallocation occurred (to signal rebuilding command buffers)
void initialize( const void*, VkDeviceSize, VkBufferUsageFlags, VkMemoryPropertyFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT );
bool update( const void*, VkDeviceSize ) const; // returns true if a reallocation occurred (to signal rebuilding command buffers)
void destroy(bool = VK_DEFAULT_DEFER_BUFFER_DESTROY);
void swap( Buffer& );
@ -60,9 +61,9 @@ namespace ext {
void destroy(bool = VK_DEFAULT_DEFER_BUFFER_DESTROY);
//
size_t initializeBuffer( const void*, VkDeviceSize, VkBufferUsageFlags, VkMemoryPropertyFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, bool = VK_DEFAULT_STAGE_BUFFERS );
bool updateBuffer( const void*, VkDeviceSize, const Buffer&, bool = VK_DEFAULT_STAGE_BUFFERS ) const;
inline bool updateBuffer( const void* data, VkDeviceSize length, size_t index = 0, bool stage = VK_DEFAULT_STAGE_BUFFERS ) const { return updateBuffer( data, length, buffers.at(index), stage ); }
size_t initializeBuffer( const void*, VkDeviceSize, VkBufferUsageFlags, VkMemoryPropertyFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT );
bool updateBuffer( const void*, VkDeviceSize, const Buffer& ) const;
inline bool updateBuffer( const void* data, VkDeviceSize length, size_t index = 0 ) const { return updateBuffer( data, length, buffers.at(index) ); }
};
struct AccelerationStructure {

View File

@ -139,12 +139,12 @@ namespace ext {
uint32_t getQueueFamilyIndex( VkQueueFlagBits queueFlags );
uint32_t getMemoryType( uint32_t typeBits, VkMemoryPropertyFlags properties, VkBool32 *memTypeFound = nullptr );
VkCommandBuffer createCommandBuffer( VkCommandBufferLevel level, QueueEnum queue, bool begin = true );
VkCommandBuffer createCommandBuffer( VkCommandBufferLevel level, QueueEnum queue, bool begin = true, bool singleton = true );
void flushCommandBuffer( VkCommandBuffer commandBuffer, QueueEnum queue, bool wait = VK_DEFAULT_COMMAND_BUFFER_IMMEDIATE );
pod::Checkpoint* markCommandBuffer( VkCommandBuffer commandBuffer, pod::Checkpoint::Type type, const uf::stl::string& name, const uf::stl::string& info );
CommandBuffer fetchCommandBuffer( QueueEnum queue, bool waits = VK_DEFAULT_COMMAND_BUFFER_IMMEDIATE );
CommandBuffer fetchCommandBuffer( QueueEnum queue, VkCommandBufferLevel, bool waits = VK_DEFAULT_COMMAND_BUFFER_IMMEDIATE );
CommandBuffer fetchCommandBuffer( QueueEnum queue, bool immediate = VK_DEFAULT_COMMAND_BUFFER_IMMEDIATE );
CommandBuffer fetchCommandBuffer( QueueEnum queue, VkCommandBufferLevel, bool immediate = VK_DEFAULT_COMMAND_BUFFER_IMMEDIATE );
void flushCommandBuffer( CommandBuffer& commandBuffer );
pod::Checkpoint* markCommandBuffer( CommandBuffer& commandBuffer, pod::Checkpoint::Type type, const uf::stl::string& name, const uf::stl::string& info );

View File

@ -22,7 +22,7 @@
#define VK_DEFAULT_STAGE_BUFFERS ext::vulkan::settings::defaultStageBuffers
#define VK_DEFAULT_DEFER_BUFFER_DESTROY ext::vulkan::settings::defaultDeferBufferDestroy
#define VK_DEFAULT_COMMAND_BUFFER_IMMEDIATE ext::vulkan::settings::defaultCommandBufferImmediate
#define VK_UBO_USE_N_BUFFERS 0
#define VK_UBO_USE_N_BUFFERS 1
#define VK_USE_MULTIVIEW 1
namespace ext {

View File

@ -578,8 +578,13 @@ void ext::ExtSceneBehavior::tick( uf::Object& self ) {
.global = metadata.global,
});
}
uf::stl::vector<size_t> indices(entities.size());
std::iota(indices.begin(), indices.end(), 0);
// prioritize closer lights; it would be nice to also prioritize lights in view, but because of VXGI it's not really something to do
std::sort( entities.begin(), entities.end(), [&]( LightInfo& l, LightInfo& r ){
std::sort(indices.begin(), indices.end(), [&](size_t a, size_t b) {
auto& l = entities[a];
auto& r = entities[b];
if ( l.global && !r.global ) return true;
if ( !l.global && r.global ) return false;
return l.distance < r.distance;
@ -589,63 +594,89 @@ void ext::ExtSceneBehavior::tick( uf::Object& self ) {
int32_t shadowCount = metadata.shadow.max; // how many shadow maps we should pass, based on range
if ( shadowCount <= 0 ) shadowCount = std::numeric_limits<int32_t>::max();
// disable shadows if that light is outside our threshold
for ( auto& info : entities ) if ( info.shadows && shadowCount-- <= 0 ) info.shadows = false;
// iterate over sorted indices
for ( auto idx : indices ) {
auto& info = entities[idx];
if ( !info.shadows ) continue;
// outside of top-k, disable
if ( shadowCount-- <= 0 ) info.shadows = false;
else {
// enable if also in-range
auto* entity = info.entity;
if ( entity->hasComponent<uf::renderer::RenderTargetRenderMode>() ) {
auto& lightMetadata = entity->getComponent<ext::LightBehavior::Metadata>();
// bind lighting and requested shadow maps
for ( uint32_t i = 0; i < entities.size() && storage.lights.size() < metadata.light.max; ++i ) {
if ( lightMetadata.renderer.mode == "in-range" && shadowUpdateThreshold-- > 0 ) {
auto& renderMode = entity->getComponent<uf::renderer::RenderTargetRenderMode>();
renderMode.execute = true;
renderMode.metadata.limiter.execute = true;
}
}
}
}
constexpr uint32_t MODE_SPLIT = 0;
constexpr uint32_t MODE_CUBEMAP = 1;
constexpr uint32_t MODE_SEPARATE_2DS = 2;
for ( uint32_t i = 0; i < entities.size(); ++i ) {
auto& info = entities[i];
uf::Entity* entity = info.entity;
if ( !info.shadows ) {
storage.lights.emplace_back(pod::Light{
.view = uf::matrix::identity(),
.projection = uf::matrix::identity(),
.position = info.position,
.range = info.range,
.color = info.color,
.intensity = info.intensity,
.type = info.type,
.typeMap = 0,
.indexMap = -1,
.depthBias = info.bias,
});
} else {
int32_t boundIndexMap = -1;
int32_t boundTypeMap = 0;
uint32_t views = 1;
bool hasRT = entity->hasComponent<uf::renderer::RenderTargetRenderMode>();
if ( hasRT ) {
auto& renderMode = entity->getComponent<uf::renderer::RenderTargetRenderMode>();
auto& lightCamera = entity->getComponent<uf::Camera>();
auto& lightMetadata = entity->getComponent<ext::LightBehavior::Metadata>();
views = renderMode.renderTarget.views;
lightMetadata.renderer.rendered = true;
// activate our shadow mapper if it's range-basedd
if ( lightMetadata.renderer.mode == "in-range" && shadowUpdateThreshold-- > 0 ) {
renderMode.execute = true;
renderMode.metadata.limiter.execute = true;
}
constexpr uint32_t MODE_SPLIT = 0;
constexpr uint32_t MODE_CUBEMAP = 1;
constexpr uint32_t MODE_SEPARATE_2DS = 2;
// if point light, and combining is requested
if ( metadata.shadow.typeMap > MODE_SPLIT && renderMode.renderTarget.views == 6 ) {
int32_t index = -1;
// separated texture2Ds
if ( metadata.shadow.typeMap > MODE_SPLIT && views == 6 ) {
// split cubemap (shouldn't actually get used)
if ( metadata.shadow.typeMap == MODE_SEPARATE_2DS ) {
UF_MSG_WARNING("deprecated feature used: separate Texture2Ds for shadow maps");
index = storage.shadow2Ds.size();
boundIndexMap = storage.shadow2Ds.size();
boundTypeMap = MODE_SEPARATE_2DS;
for ( auto& attachment : renderMode.renderTarget.attachments ) {
if ( !(attachment.descriptor.usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ) continue;
for ( size_t view = 0; view < renderMode.renderTarget.views; ++view ) {
if (!(attachment.descriptor.usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) continue;
for ( size_t view = 0; view < views; ++view ) {
storage.shadow2Ds.emplace_back().aliasAttachment(attachment, view);
}
break;
}
// cubemapped
// cubemap
} else if ( metadata.shadow.typeMap == MODE_CUBEMAP ) {
index = storage.shadowCubes.size();
boundIndexMap = storage.shadowCubes.size();
boundTypeMap = MODE_CUBEMAP;
for ( auto& attachment : renderMode.renderTarget.attachments ) {
if ( !(attachment.descriptor.usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ) continue;
if (!(attachment.descriptor.usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) continue;
storage.shadowCubes.emplace_back().aliasAttachment(attachment);
break;
}
}
} else {
// separate 2D maps
boundIndexMap = storage.shadow2Ds.size();
for ( auto& attachment : renderMode.renderTarget.attachments ) {
if ( !(attachment.descriptor.usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) continue;
if (attachment.descriptor.layout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) continue;
for (size_t view = 0; view < views; ++view) {
storage.shadow2Ds.emplace_back().aliasAttachment(attachment, view);
}
}
}
}
if ( storage.lights.size() < metadata.light.max ) {
auto& lightCamera = entity->getComponent<uf::Camera>();
if ( !info.shadows ) boundIndexMap = -1;
if ( boundTypeMap > MODE_SPLIT && views == 6 ) {
storage.lights.emplace_back(pod::Light{
.view = lightCamera.getView(0),
.projection = lightCamera.getProjection(0),
@ -654,30 +685,26 @@ void ext::ExtSceneBehavior::tick( uf::Object& self ) {
.color = info.color,
.intensity = info.intensity,
.type = info.type,
.typeMap = metadata.shadow.typeMap,
.indexMap = index,
.typeMap = boundTypeMap,
.indexMap = boundIndexMap,
.depthBias = info.bias,
});
// any other shadowing light, even point lights, are split by shadow maps
} else {
for ( auto& attachment : renderMode.renderTarget.attachments ) {
if ( !(attachment.descriptor.usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ) continue;
if ( attachment.descriptor.layout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR ) continue;
for ( size_t view = 0; view < renderMode.renderTarget.views; ++view ) {
storage.lights.emplace_back(pod::Light{
.view = lightCamera.getView(view),
.projection = lightCamera.getProjection(view),
.position = info.position,
.range = info.range,
.color = info.color,
.intensity = info.intensity,
.type = info.type,
.typeMap = 0,
.indexMap = storage.shadow2Ds.size(),
.depthBias = info.bias,
});
storage.shadow2Ds.emplace_back().aliasAttachment(attachment, view);
}
for ( size_t view = 0; view < views; ++view ) {
if ( storage.lights.size() >= metadata.light.max ) break;
storage.lights.emplace_back(pod::Light{
.view = hasRT ? lightCamera.getView(view) : uf::matrix::identity(),
.projection = hasRT ? lightCamera.getProjection(view) : uf::matrix::identity(),
.position = info.position,
.range = info.range,
.color = info.color,
.intensity = info.intensity,
.type = info.type,
.typeMap = 0,
.indexMap = info.shadows ? (boundIndexMap == -1 ? -1 : boundIndexMap + view) : -1,
.depthBias = info.bias,
});
}
}
}
@ -1248,9 +1275,9 @@ void ext::ExtSceneBehavior::bindBuffers( uf::Object& self, uf::renderer::Graphic
}
if ( shouldUpdate ) {
// graphic.updatePipelines();
graphic.update();
// graphic.update();
renderMode.rebuild = true;
metadata.shader.invalidated = false;
// metadata.shader.invalidated = false;
}
if ( !graphic.material.hasShader(shaderType, shaderPipeline) ) {

View File

@ -235,11 +235,7 @@ void ext::VoxelizerSceneBehavior::initialize( uf::Object& self ) {
if ( blitter.initialized ) {
auto descriptor = blitter.descriptor;
//descriptor.pipeline = "lighting";
auto& pipeline = blitter.getPipeline( descriptor );
auto& descriptorSet = blitter.getDescriptorSet( descriptor );
pipeline.record( blitter, commandBuffer );
descriptorSet.record( blitter, commandBuffer );
blitter.record( commandBuffer, descriptor );
}
// generate mipmaps
@ -266,10 +262,7 @@ void ext::VoxelizerSceneBehavior::initialize( uf::Object& self ) {
auto descriptor = blitter.descriptor;
descriptor.pipeline = "mipmap";
auto& pipeline = blitter.getPipeline( descriptor );
auto& descriptorSet = blitter.getDescriptorSet( descriptor );
pipeline.record( blitter, commandBuffer );
descriptorSet.record( blitter, commandBuffer );
blitter.record( commandBuffer, descriptor );
}
}
#else

View File

@ -1961,7 +1961,7 @@ void uf::graph::reload( pod::Graph& graph, pod::Node& node ) {
// necessary for OpenGL because recorded descriptors have invalidated pointers
// Vulkan doesn't care about the CPU-side mesh data
#if UF_USE_OPENGL
#if 1 || UF_USE_OPENGL
uf::renderer::states::rebuild = true;
#endif

View File

@ -553,10 +553,12 @@ void ext::gltf::load( pod::Graph& graph, const uf::stl::string& filename, const
auto& mesh = storage.meshes[keyName];
auto& primitives = storage.primitives[keyName];
UF_MSG_DEBUG("Optimizing mesh at level {}: {}", level, keyName);
if ( !ext::meshopt::optimize( mesh, simplify, level, print ) ) {
UF_MSG_ERROR("Mesh optimization failed: {}", keyName );
if ( level ) {
UF_MSG_DEBUG("Optimizing mesh at level {}: {}", level, keyName);
if ( !ext::meshopt::optimize( mesh, simplify, level, print ) ) {
UF_MSG_ERROR("Mesh optimization failed: {}", keyName );
}
}
if ( lods ) {
auto factors = ext::meshopt::computeLODs( mesh.index.count );

View File

@ -23,6 +23,9 @@ namespace {
}
bool ext::meshopt::optimize( uf::Mesh& mesh, float simplify, size_t o, bool verbose ) {
if ( o == 0 ) {
return false; // true, since theres no error technically?
}
if ( mesh.isInterleaved() ) {
UF_MSG_ERROR("Optimization of interleaved meshes is currently not supported.");
return false;

View File

@ -21,6 +21,8 @@ void ext::vulkan::Buffer::swap( ext::vulkan::Buffer& buffer ) {
std::swap(this->memoryProperties, buffer.memoryProperties);
std::swap(this->allocation, buffer.allocation);
std::swap(this->allocationInfo, buffer.allocationInfo);
std::swap(this->count, buffer.count);
std::swap(this->written, buffer.written);
}
ext::vulkan::Buffer ext::vulkan::Buffer::alias() const {
ext::vulkan::Buffer buffer;
@ -40,6 +42,8 @@ void ext::vulkan::Buffer::aliasBuffer( const ext::vulkan::Buffer& buffer ) {
this->memoryProperties = buffer.memoryProperties;
this->allocation = buffer.allocation;
this->allocationInfo = buffer.allocationInfo;
this->count = buffer.count;
this->written = buffer.written;
}
void* ext::vulkan::Buffer::map( VkDeviceSize size, VkDeviceSize offset ) {
@ -116,10 +120,12 @@ void ext::vulkan::Buffer::destroy(bool defer) {
// this->memoryProperties = {};
this->allocation = {};
this->allocationInfo = {};
this->count = 1;
this->written = false;
}
void ext::vulkan::Buffer::initialize( const void* data, VkDeviceSize length, VkBufferUsageFlags usage, VkMemoryPropertyFlags memoryProperties, bool stage ) {
void ext::vulkan::Buffer::initialize( const void* data, VkDeviceSize length, VkBufferUsageFlags usage, VkMemoryPropertyFlags memoryProperties ) {
if ( !device ) device = &ext::vulkan::device;
if ( stage ) usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; // implicitly set properties
if ( VK_DEFAULT_STAGE_BUFFERS ) usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; // implicitly set properties
// assume all UBOs are dynamic
auto totalLength = length;
@ -130,56 +136,16 @@ void ext::vulkan::Buffer::initialize( const void* data, VkDeviceSize length, VkB
totalLength = ALIGNED_SIZE( length, this->alignment ) * this->count;
}
#endif
VK_CHECK_RESULT(device->createBuffer( nullptr, totalLength, usage, memoryProperties, *this ));
VK_CHECK_RESULT(device->createBuffer(
nullptr,
totalLength,
usage,
memoryProperties,
*this
));
if ( length != totalLength ) {
this->updateDescriptor( length, 0 );
}
if ( data && length ) update( data, length, stage );
/*
{
uf::stl::string type;
if ( usage & VK_BUFFER_USAGE_TRANSFER_SRC_BIT ) type += "transfer_src,";
if ( usage & VK_BUFFER_USAGE_TRANSFER_DST_BIT ) type += "transfer_dst,";
if ( usage & VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT ) type += "uniform_texel,";
if ( usage & VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT ) type += "storage_texel,";
if ( usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT ) type += "uniform,";
if ( usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT ) type += "storage,";
if ( usage & VK_BUFFER_USAGE_INDEX_BUFFER_BIT ) type += "index,";
if ( usage & VK_BUFFER_USAGE_VERTEX_BUFFER_BIT ) type += "vertex,";
if ( usage & VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT ) type += "indirect,";
if ( usage & VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR ) type += "acceleration_structure,";
if ( usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT ) type += "address,";
if ( usage & VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR ) type += "binding_table,";
UF_MSG_DEBUG("CREATING BUFFER: {}: {}({})", fmt::ptr(this->buffer), type, usage);
}
*/
this->usage = usage;
if ( length != totalLength ) this->updateDescriptor( length, 0 );
if ( data && length ) update( data, length );
}
bool ext::vulkan::Buffer::update( const void* data, VkDeviceSize length, bool stage ) const {
bool ext::vulkan::Buffer::update( const void* data, VkDeviceSize length ) const {
// if ( !data || !length ) return false;
if ( !length ) return false;
if ( !buffer ) return false;
VkDeviceSize offset = 0;
#if VK_UBO_USE_N_BUFFERS
if ( this->count == ext::vulkan::swapchain.buffers ) {
offset = this->getOffset( states::currentBuffer );
}
#endif
// 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);
@ -192,15 +158,40 @@ bool ext::vulkan::Buffer::update( const void* data, VkDeviceSize length, bool st
self.destroy(true);
self.initialize(*device, savedAlignment);
self.initialize(data, length, savedUsage, savedMemProps, stage);
self.initialize(data, length, savedUsage, savedMemProps);
return true;
}
if ( !data ) return false;
if ( !stage ) {
bool broadcast = !this->written;
this->written = true;
STATIC_THREAD_LOCAL(uf::stl::vector<VkBufferCopy>, regions);
if ( broadcast && this->count > 1 ) {
for ( size_t i = 0; i < this->count; i++ ) {
auto& region = regions.emplace_back();
region.size = length;
region.srcOffset = 0;
region.dstOffset = this->getOffset( i );
}
} else {
auto& region = regions.emplace_back();
region.size = length;
region.srcOffset = 0;
region.dstOffset = 0;
#if VK_UBO_USE_N_BUFFERS
if ( this->count == ext::vulkan::swapchain.buffers ) {
region.dstOffset = this->getOffset( states::currentBuffer );
}
#endif
}
if ( !VK_DEFAULT_STAGE_BUFFERS ) {
auto* self = const_cast<ext::vulkan::Buffer*>(this);
void* map = self->map(length, offset);
memcpy(map, data, length);
void* map = self->map(length);
for ( const auto& region : regions ) memcpy(static_cast<char*>(map) + region.dstOffset, data, length);
self->unmap();
return false;
}
@ -208,18 +199,12 @@ bool ext::vulkan::Buffer::update( const void* data, VkDeviceSize length, bool st
ext::vulkan::Device* device = this->device ? this->device : &ext::vulkan::device;
Buffer staging = device->fetchTransientBuffer(
// Buffer staging = device->createBuffer(
data,
length,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
data, length,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
);
auto commandBuffer = device->fetchCommandBuffer(QueueEnum::TRANSFER); // waits on finish
VkBufferCopy region = {};
region.size = length;
region.dstOffset = offset;
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "copyBuffer" );
vkCmdCopyBuffer(commandBuffer, staging.buffer, buffer, 1, &region);
vkCmdCopyBuffer(commandBuffer, staging.buffer, buffer, regions.size(), regions.data());
device->flushCommandBuffer(commandBuffer);
return false;
}
@ -240,14 +225,14 @@ void ext::vulkan::Buffers::destroy(bool defer) {
buffers.clear();
}
size_t ext::vulkan::Buffers::initializeBuffer( const void* data, VkDeviceSize length, VkBufferUsageFlags usage, VkMemoryPropertyFlags memoryProperties, bool stage ) {
size_t ext::vulkan::Buffers::initializeBuffer( const void* data, VkDeviceSize length, VkBufferUsageFlags usage, VkMemoryPropertyFlags memoryProperties ) {
size_t index = buffers.size();
auto& buffer = buffers.emplace_back();
buffer.initialize( *device, requestedAlignment );
buffer.initialize( data, length, usage, memoryProperties, stage );
buffer.initialize( data, length, usage, memoryProperties );
return index;
}
bool ext::vulkan::Buffers::updateBuffer( const void* data, VkDeviceSize length, const Buffer& buffer, bool stage ) const {
return buffer.update( data, length, stage );
bool ext::vulkan::Buffers::updateBuffer( const void* data, VkDeviceSize length, const Buffer& buffer ) const {
return buffer.update( data, length );
}
#endif

View File

@ -609,7 +609,7 @@ uint32_t ext::vulkan::Device::getMemoryType( uint32_t typeBits, VkMemoryProperty
UF_EXCEPTION("Vulkan error: could not find a matching memory type");
}
VkCommandBuffer ext::vulkan::Device::createCommandBuffer( VkCommandBufferLevel level, QueueEnum queue, bool begin ){
VkCommandBuffer ext::vulkan::Device::createCommandBuffer( VkCommandBufferLevel level, QueueEnum queue, bool begin, bool singleton ){
VkCommandBufferAllocateInfo cmdBufAllocateInfo = ext::vulkan::initializers::commandBufferAllocateInfo( getCommandPool(queue), level, 1 );
VkCommandBuffer commandBuffer;
@ -617,6 +617,8 @@ VkCommandBuffer ext::vulkan::Device::createCommandBuffer( VkCommandBufferLevel l
// If requested, also start recording for the new command buffer
if ( begin ) {
VkCommandBufferBeginInfo cmdBufInfo = ext::vulkan::initializers::commandBufferBeginInfo();
if ( singleton ) cmdBufInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
VK_CHECK_RESULT( vkBeginCommandBuffer( commandBuffer, &cmdBufInfo ) );
UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::BEGIN, "begin" );
}
@ -674,7 +676,7 @@ ext::vulkan::CommandBuffer ext::vulkan::Device::fetchCommandBuffer( ext::vulkan:
return {
.immediate = immediate,
.queueType = queueType,
.handle = this->createCommandBuffer( level, queueType, true ),
.handle = this->createCommandBuffer( level, queueType, true, true ),
.threadId = std::this_thread::get_id(),
};
}
@ -1346,6 +1348,7 @@ void ext::vulkan::Device::initialize() {
accelerationStructureFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR;
accelerationStructureFeatures.accelerationStructure = VK_TRUE;
// accelerationStructureFeatures.accelerationStructureHostCommands = VK_TRUE;
accelerationStructureFeatures.descriptorBindingAccelerationStructureUpdateAfterBind = VK_TRUE;
chain.push( &accelerationStructureFeatures );
VK_VALIDATION_MESSAGE("Enabled feature chain: {}", "accelerationStructureFeatures" );
}
@ -1698,7 +1701,7 @@ void ext::vulkan::DescriptorAllocator::destroy() {
VkDescriptorPool ext::vulkan::DescriptorAllocator::createPool() {
VkDescriptorPoolCreateInfo poolInfo = {};
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
poolInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
poolInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT | VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT;
poolInfo.maxSets = 1000;
poolInfo.poolSizeCount = (uint32_t) poolSizes.size();
poolInfo.pPoolSizes = poolSizes.data();

View File

@ -48,8 +48,6 @@ void ext::vulkan::Pipeline::initialize( const Graphic& graphic, const GraphicDes
auto shaders = graphic.material.getShaders( descriptor.pipeline );
assert( shaders.size() > 0 );
uint32_t subpass = descriptor.subpass;
uf::stl::vector<VkDescriptorSetLayoutBinding> descriptorSetLayoutBindings;
uf::stl::vector<VkPushConstantRange> pushConstantRanges;
@ -61,22 +59,41 @@ void ext::vulkan::Pipeline::initialize( const Graphic& graphic, const GraphicDes
uf::stl::vector<VkSpecializationInfo> shaderSpecializationInfos;
uf::stl::vector<VkVertexInputBindingDescription> inputBindingDescriptions;
uf::stl::vector<VkVertexInputAttributeDescription> attributeDescriptions;
{
for ( auto* shaderPointer : shaders ) {
auto& shader = *shaderPointer;
descriptorSetLayoutBindings.insert( descriptorSetLayoutBindings.end(), shader.descriptorSetLayoutBindings.begin(), shader.descriptorSetLayoutBindings.end() );
{
uf::stl::vector<VkDescriptorBindingFlags> bindingFlags;
bool hasDynamicBuffer = false;
for ( auto* shader : shaders ) {
for ( auto& binding : shader->descriptorSetLayoutBindings ) {
switch ( binding.descriptorType ) {
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
hasDynamicBuffer = true;
break;
}
}
}
for ( auto* shader : shaders ) {
for ( auto& binding : shader->descriptorSetLayoutBindings ) {
descriptorSetLayoutBindings.emplace_back( binding );
auto& flags = bindingFlags.emplace_back();
flags |= VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT;
if ( !hasDynamicBuffer ) flags |= VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT;
}
size_t offset = 0;
for ( auto& pushConstant : shader.pushConstants ) {
for ( auto& pushConstant : shader->pushConstants ) {
size_t len = pushConstant.data().len;
if ( len <= 0 || len > device.properties.limits.maxPushConstantsSize ) {
VK_DEBUG_VALIDATION_MESSAGE("Invalid push constant length of {} for shader {}", len, shader.filename);
VK_DEBUG_VALIDATION_MESSAGE("Invalid push constant length of {} for shader {}", len, shader->filename);
// goto PIPELINE_INITIALIZATION_INVALID;
len = device.properties.limits.maxPushConstantsSize;
}
pushConstantRanges.emplace_back(ext::vulkan::initializers::pushConstantRange(
shader.descriptor.stage,
shader->descriptor.stage,
len,
offset
));
@ -84,10 +101,19 @@ void ext::vulkan::Pipeline::initialize( const Graphic& graphic, const GraphicDes
}
}
VkDescriptorSetLayoutBindingFlagsCreateInfo layoutBindingFlags = {};
layoutBindingFlags.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO;
layoutBindingFlags.bindingCount = bindingFlags.size();
layoutBindingFlags.pBindingFlags = bindingFlags.data();
VkDescriptorSetLayoutCreateInfo descriptorLayout = ext::vulkan::initializers::descriptorSetLayoutCreateInfo(
descriptorSetLayoutBindings.data(),
descriptorSetLayoutBindings.size()
);
descriptorLayout.flags |= VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT;
descriptorLayout.pNext = &layoutBindingFlags;
VK_CHECK_RESULT(vkCreateDescriptorSetLayout( device, &descriptorLayout, nullptr, &descriptorSetLayout ));
VK_REGISTER_HANDLE( descriptorSetLayout );
@ -189,16 +215,15 @@ void ext::vulkan::Pipeline::initialize( const Graphic& graphic, const GraphicDes
}
// Compute
for ( auto* shaderPointer : shaders ) {
auto& shader = *shaderPointer;
if ( shader.descriptor.stage != VK_SHADER_STAGE_COMPUTE_BIT ) continue;
for ( auto* shader : shaders ) {
if ( shader->descriptor.stage != VK_SHADER_STAGE_COMPUTE_BIT ) continue;
// Create compute shader pipelines
VkComputePipelineCreateInfo computePipelineCreateInfo = ext::vulkan::initializers::computePipelineCreateInfo(
pipelineLayout,
0
);
computePipelineCreateInfo.stage = shader.descriptor;
computePipelineCreateInfo.stage = shader->descriptor;
VK_CHECK_RESULT(vkCreateComputePipelines(device, device.pipelineCache, 1, &computePipelineCreateInfo, nullptr, &pipeline));
VK_REGISTER_HANDLE( pipeline );
@ -242,8 +267,6 @@ void ext::vulkan::Pipeline::initialize( const Graphic& graphic, const GraphicDes
}
}
} else {
subpass = 0;
VkPipelineColorBlendAttachmentState blendAttachmentState = ext::vulkan::initializers::pipelineColorBlendAttachmentState(
descriptor.blend.colorWriteMask,
descriptor.blend.enabled ? VK_TRUE : VK_FALSE
@ -507,7 +530,9 @@ void ext::vulkan::DescriptorSet::record( const Graphic& graphic, const GraphicDe
else continue;
}
#if VK_UBO_USE_N_BUFFERS
dynamicOffsets.insert( dynamicOffsets.end(), shader->metadata.dynamicRanges.begin(), shader->metadata.dynamicRanges.end() );
#endif
}
for ( auto& dynamicOffset : dynamicOffsets ) {
@ -520,44 +545,7 @@ void ext::vulkan::DescriptorSet::record( const Graphic& graphic, const GraphicDe
}
// Bind descriptor sets describing shader binding points
#if VK_UBO_USE_N_BUFFERS
vkCmdBindDescriptorSets(commandBuffer, (VkPipelineBindPoint) descriptor.bind.point, pipeline.pipelineLayout, 0, 1, &descriptorSet, dynamicOffsets.size(), dynamicOffsets.data());
#else
vkCmdBindDescriptorSets(commandBuffer, (VkPipelineBindPoint) descriptor.bind.point, pipeline.pipelineLayout, 0, 1, &descriptorSet, 0, nullptr);
#endif
uint32_t width = descriptor.bind.width ? descriptor.bind.width : ext::vulkan::settings::width;
uint32_t height = descriptor.bind.height ? descriptor.bind.height : ext::vulkan::settings::height;
uint32_t depth = descriptor.bind.depth ? descriptor.bind.depth : 1;
if ( descriptor.bind.point == VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR ) {
uf::renderer::vkCmdTraceRaysKHR(
commandBuffer,
&pipeline.sbtEntries[0],
&pipeline.sbtEntries[1],
&pipeline.sbtEntries[2],
&pipeline.sbtEntries[3],
width,
height,
1
);
} else if ( descriptor.bind.point == VK_PIPELINE_BIND_POINT_COMPUTE ) {
for ( auto* shader : shaders ) {
if ( shader->descriptor.stage != VK_SHADER_STAGE_COMPUTE_BIT ) continue;
auto& localSize = shader->metadata.definitions.localSize;
auto dispatch = pod::Vector3ui{
std::ceil( (float) width / localSize.x ),
std::ceil( (float) height / localSize.y ),
std::ceil( (float) depth / localSize.z ),
};
vkCmdDispatch(commandBuffer,
dispatch.x,
dispatch.y,
dispatch.z
);
}
}
}
void ext::vulkan::DescriptorSet::update( const Graphic& graphic ) {
return this->update( graphic, descriptor );
@ -1637,24 +1625,32 @@ void ext::vulkan::Graphic::generateTopAccelerationStructure( const uf::stl::vect
size_t tlasBufferIndex{};
size_t tlasBackBufferIndex{};
// do not stage, because apparently vkQueueWaitIdle doesn't actually wait for the transfer to complete
// manually copy because I can't be assed to expose an un-staged API now
if ( !update ) {
// do not stage, because apparently vkQueueWaitIdle doesn't actually wait for the transfer to complete
this->requestedAlignment = 16;
instanceIndex = this->initializeBuffer(
instanceIndex = this->buffers.size();
auto& buffer = this->buffers.emplace_back();
buffer.alignment = 16;
device.createBuffer(
(const void*) instancesVK.data(), instancesVK.size() * sizeof(VkAccelerationStructureInstanceKHR),
VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR, false
VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
buffer
);
this->requestedAlignment = 0;
this->metadata.buffers["tlasInstance"] = instanceIndex;
} else {
if ( this->metadata.buffers.count("tlasInstance") > 0 ) {
instanceIndex = this->metadata.buffers["tlasInstance"];
} else UF_EXCEPTION("Buffers not found: {}", "tlasInstance");
/*rebuild = rebuild ||*/ this->updateBuffer( (const void*) instancesVK.data(), instancesVK.size() * sizeof(VkAccelerationStructureInstanceKHR), instanceIndex, false );
auto& buffer = this->buffers.at(instanceIndex);
void* map = buffer.map();
memcpy(map, instancesVK.data(), instancesVK.size() * sizeof(VkAccelerationStructureInstanceKHR));
buffer.unmap();
}
size_t instanceBufferAddress = this->buffers[instanceIndex].getAddress();
auto& tlas = this->accelerationStructures.tops[0];
auto& tlas = this->accelerationStructures.tops[0];
{
VkBuildAccelerationStructureFlagsKHR flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR | VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR;
@ -1930,6 +1926,42 @@ void ext::vulkan::Graphic::record( VkCommandBuffer commandBuffer, const GraphicD
descriptorSet.record( *this, descriptor, commandBuffer, pass, draw, offset );
auto shaders = material.getShaders( descriptor.pipeline );
uint32_t width = descriptor.bind.width ? descriptor.bind.width : ext::vulkan::settings::width;
uint32_t height = descriptor.bind.height ? descriptor.bind.height : ext::vulkan::settings::height;
uint32_t depth = descriptor.bind.depth ? descriptor.bind.depth : 1;
if ( descriptor.bind.point == VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR ) {
uf::renderer::vkCmdTraceRaysKHR(
commandBuffer,
&pipeline.sbtEntries[0],
&pipeline.sbtEntries[1],
&pipeline.sbtEntries[2],
&pipeline.sbtEntries[3],
width,
height,
1
);
return;
}
if ( descriptor.bind.point == VK_PIPELINE_BIND_POINT_COMPUTE ) {
for ( auto* shader : shaders ) {
if ( shader->descriptor.stage != VK_SHADER_STAGE_COMPUTE_BIT ) continue;
auto& localSize = shader->metadata.definitions.localSize;
auto dispatch = pod::Vector3ui{
std::ceil( (float) width / localSize.x ),
std::ceil( (float) height / localSize.y ),
std::ceil( (float) depth / localSize.z ),
};
vkCmdDispatch(commandBuffer,
dispatch.x,
dispatch.y,
dispatch.z
);
}
return;
}
for ( auto* shader : shaders ) {
if ( shader->descriptor.stage == VK_SHADER_STAGE_COMPUTE_BIT ) return;
if (

View File

@ -118,10 +118,10 @@ void ext::vulkan::BaseRenderMode::render() {
//lockMutex( this->mostRecentCommandPoolId );
auto& commands = getCommands( this->mostRecentCommandPoolId );
VK_CHECK_RESULT(swapchain.acquireNextImage(&states::currentBuffer, swapchain.presentCompleteSemaphores[0]));
uint32_t imageIndex;
VK_CHECK_RESULT(vkWaitForFences(*device, 1, &fences[states::currentBuffer], VK_TRUE, VK_DEFAULT_FENCE_TIMEOUT));
VK_CHECK_RESULT(swapchain.acquireNextImage(&imageIndex, swapchain.presentCompleteSemaphores[states::currentBuffer]));
VK_CHECK_RESULT(vkResetFences(*device, 1, &fences[states::currentBuffer]));
VkPipelineStageFlags waitStageMask[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
@ -129,7 +129,7 @@ void ext::vulkan::BaseRenderMode::render() {
VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.pWaitDstStageMask = waitStageMask;
submitInfo.pWaitSemaphores = &swapchain.presentCompleteSemaphores[0];
submitInfo.pWaitSemaphores = &swapchain.presentCompleteSemaphores[states::currentBuffer];
submitInfo.waitSemaphoreCount = 1;
submitInfo.pSignalSemaphores = &renderCompleteSemaphores[states::currentBuffer];
submitInfo.signalSemaphoreCount = 1;
@ -142,8 +142,7 @@ void ext::vulkan::BaseRenderMode::render() {
VK_CHECK_QUEUE_CHECKPOINT( queue, res );
}
VK_CHECK_RESULT(swapchain.queuePresent(device->getQueue( QueueEnum::PRESENT ), states::currentBuffer, renderCompleteSemaphores[states::currentBuffer]));
VK_CHECK_RESULT(swapchain.queuePresent(device->getQueue( QueueEnum::PRESENT ), imageIndex, renderCompleteSemaphores[states::currentBuffer]));
#if 0
{
VkQueue queue = device->getQueue( QueueEnum::PRESENT );
@ -152,6 +151,7 @@ void ext::vulkan::BaseRenderMode::render() {
}
#endif
states::currentBuffer = (states::currentBuffer + 1) % ext::vulkan::swapchain.buffers;
this->executed = true;
//unlockMutex( this->mostRecentCommandPoolId );
@ -183,9 +183,8 @@ void ext::vulkan::BaseRenderMode::createCommandBuffers( const uf::stl::vector<ex
float width = windowSize.x; //this->width > 0 ? this->width : windowSize.x;
float height = windowSize.y; //this->height > 0 ? this->height : windowSize.y;
VkCommandBufferBeginInfo cmdBufInfo = {};
cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
cmdBufInfo.pNext = nullptr;
VkCommandBufferBeginInfo cmdBufInfo = ext::vulkan::initializers::commandBufferBeginInfo();
cmdBufInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
VkImageMemoryBarrier imageMemoryBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; // ext::vulkan::device.queueFamilyIndices.graphics; //VK_QUEUE_FAMILY_IGNORED

View File

@ -695,9 +695,8 @@ void ext::vulkan::DeferredRenderMode::createCommandBuffers( const uf::stl::vecto
uint32_t width = this->width > 0 ? this->width : (ext::vulkan::settings::width * this->scale);
uint32_t height = this->height > 0 ? this->height : (ext::vulkan::settings::height * this->scale);
VkCommandBufferBeginInfo cmdBufInfo = {};
cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
cmdBufInfo.pNext = nullptr;
VkCommandBufferBeginInfo cmdBufInfo = ext::vulkan::initializers::commandBufferBeginInfo();
cmdBufInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
VkImageMemoryBarrier imageMemoryBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER };
imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; // ext::vulkan::device.queueFamilyIndices.graphics; //VK_QUEUE_FAMILY_IGNORED

View File

@ -312,9 +312,8 @@ void ext::vulkan::RenderTargetRenderMode::createCommandBuffers( const uf::stl::v
uint32_t width = this->width > 0 ? this->width : (ext::vulkan::settings::width * this->scale);
uint32_t height = this->height > 0 ? this->height : (ext::vulkan::settings::height * this->scale);
VkCommandBufferBeginInfo cmdBufInfo = {};
cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
cmdBufInfo.pNext = nullptr;
VkCommandBufferBeginInfo cmdBufInfo = ext::vulkan::initializers::commandBufferBeginInfo();
cmdBufInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
auto& scene = uf::scene::getCurrentScene();
auto& sceneMetadataJson = scene.getComponent<uf::Serializer>();