lots of changes (physics tweaks, actually use dynamic buffers for UBOs, perf bottleneck fixed, bug fixes, etc)

This commit is contained in:
ecker 2026-04-20 17:33:22 -05:00
parent 012cddd37f
commit 42aaf444ff
38 changed files with 568 additions and 645 deletions

View File

@ -9,8 +9,8 @@
"max": 32, "max": 32,
"shadows": { "shadows": {
"enabled": true, "enabled": true,
"update": 4, "update": 8,
"max": 8, "max": 32,
"samples": 2 "samples": 2
}, },
"bloom": { "bloom": {
@ -101,7 +101,7 @@
"experimental": { "experimental": {
"rebuild on tick begin": false, "rebuild on tick begin": false,
"batch queue submissions": true, "batch queue submissions": true,
"dedicated thread": false, "dedicated thread": false, // mostly works
"memory budget": false, "memory budget": false,
"register render modes": true, "register render modes": true,
"skip render on rebuild": false "skip render on rebuild": false
@ -115,7 +115,7 @@
"pipelines": { "pipelines": {
"deferred": true, "deferred": true,
"gui": true, "gui": true,
"vsync": false, // vsync on vulkan side rather than engine-side "vsync": true, // vsync on vulkan side rather than engine-side
"hdr": true, "hdr": true,
"vxgi": true, "vxgi": true,
"culling": true, "culling": true,

View File

@ -8,8 +8,8 @@
"physics": { "physics": {
"mass": 0, "mass": 0,
"inertia": false, "inertia": false,
// "type": "bounding box" "type": "bounding box"
"type": "mesh" // "type": "mesh"
} }
} }
} }

View File

@ -37,7 +37,7 @@
"dialogue": "/gui/dialogue/main.json" "dialogue": "/gui/dialogue/main.json"
}, },
"light": { "light": {
"enabled": true, "0-enabled": true,
"ambient": [ 0.1, 0.1, 0.1 ], "ambient": [ 0.1, 0.1, 0.1 ],
@ -49,7 +49,7 @@
"size": 8, "size": 8,
"smoothness": 0.5 "smoothness": 0.5
}, },
"shadows": { "0-shadows": {
"enabled": true "enabled": true
} }
}, },

View File

@ -5,7 +5,7 @@
#extension GL_EXT_samplerless_texture_functions : enable #extension GL_EXT_samplerless_texture_functions : enable
layout (constant_id = 0) const uint PASSES = 6; layout (constant_id = 0) const uint PASSES = 6;
layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in; layout (local_size_x = 32, local_size_y = 1, local_size_z = 1) in;
#define COMPUTE 1 #define COMPUTE 1
#define QUERY_MIPMAPS 1 #define QUERY_MIPMAPS 1
@ -90,14 +90,9 @@ bool frustumCull( uint id ) {
if ( drawCommand.indices == 0 || drawCommand.vertices == 0 ) return false; if ( drawCommand.indices == 0 || drawCommand.vertices == 0 ) return false;
bool visible = false; bool visible = true;
for ( uint pass = 0; pass < PushConstant.passes; ++pass ) { for ( uint pass = 0; pass < PushConstant.passes; ++pass ) {
#if 0
vec4 sphere = aabbToSphere( instance.bounds );
vec3 center = vec3( camera.viewport[pass].view * object.model * vec4( ) );
#else
mat4 mat = camera.viewport[pass].projection * camera.viewport[pass].view * object.model; mat4 mat = camera.viewport[pass].projection * camera.viewport[pass].view * object.model;
#if 1
vec4 planes[6]; { vec4 planes[6]; {
for (int i = 0; i < 3; ++i) for (int i = 0; i < 3; ++i)
for (int j = 0; j < 2; ++j) { for (int j = 0; j < 2; ++j) {
@ -108,48 +103,18 @@ bool frustumCull( uint id ) {
planes[i*2+j] = normalizePlane( planes[i*2+j] ); planes[i*2+j] = normalizePlane( planes[i*2+j] );
} }
} }
bool insideFrustum = true;
for ( uint p = 0; p < 6; ++p ) { for ( uint p = 0; p < 6; ++p ) {
float d = max(instance.bounds.min.x * planes[p].x, instance.bounds.max.x * planes[p].x) float d = max(instance.bounds.min.x * planes[p].x, instance.bounds.max.x * planes[p].x)
+ max(instance.bounds.min.y * planes[p].y, instance.bounds.max.y * planes[p].y) + max(instance.bounds.min.y * planes[p].y, instance.bounds.max.y * planes[p].y)
+ max(instance.bounds.min.z * planes[p].z, instance.bounds.max.z * planes[p].z); + max(instance.bounds.min.z * planes[p].z, instance.bounds.max.z * planes[p].z);
if ( d > -planes[p].w ) return true;
if (d < -planes[p].w) {
visible = false;
break;
}
} }
#else if ( !visible ) break;
vec4 corners[8] = {
vec4( instance.bounds.min.x, instance.bounds.min.y, instance.bounds.min.z, 1.0 ),
vec4( instance.bounds.max.x, instance.bounds.min.y, instance.bounds.min.z, 1.0 ),
vec4( instance.bounds.max.x, instance.bounds.max.y, instance.bounds.min.z, 1.0 ),
vec4( instance.bounds.min.x, instance.bounds.max.y, instance.bounds.min.z, 1.0 ),
vec4( instance.bounds.min.x, instance.bounds.min.y, instance.bounds.max.z, 1.0 ),
vec4( instance.bounds.max.x, instance.bounds.min.y, instance.bounds.max.z, 1.0 ),
vec4( instance.bounds.max.x, instance.bounds.max.y, instance.bounds.max.z, 1.0 ),
vec4( instance.bounds.min.x, instance.bounds.max.y, instance.bounds.max.z, 1.0 ),
};
vec4 planes[6]; {
#pragma unroll 3
for (int i = 0; i < 3; ++i)
#pragma unroll 2
for (int j = 0; j < 2; ++j) {
planes[i*2+j].x = mat[0][3] + (j == 0 ? mat[0][i] : -mat[0][i]);
planes[i*2+j].y = mat[1][3] + (j == 0 ? mat[1][i] : -mat[1][i]);
planes[i*2+j].z = mat[2][3] + (j == 0 ? mat[2][i] : -mat[2][i]);
planes[i*2+j].w = mat[3][3] + (j == 0 ? mat[3][i] : -mat[3][i]);
planes[i*2+j] = normalizePlane( planes[i*2+j] );
}
}
#pragma unroll 8
for ( uint p = 0; p < 8; ++p ) corners[p] = mat * corners[p];
#pragma unroll 6
for ( uint p = 0; p < 6; ++p ) {
#pragma unroll 8
for ( uint q = 0; q < 8; ++q ) {
if ( dot( corners[q], planes[p] ) > 0 ) return true;
}
return false;
}
#endif
#endif
} }
return visible; return visible;
} }
@ -163,12 +128,11 @@ bool occlusionCull( uint id ) {
bool visible = true; bool visible = true;
for ( uint pass = 0; pass < PushConstant.passes; ++pass ) { for ( uint pass = 0; pass < PushConstant.passes; ++pass ) {
#if 1
vec4 aabb; vec4 aabb;
vec4 sphere = aabbToSphere( instance.bounds ); vec4 sphere = aabbToSphere( instance.bounds );
vec3 center = (camera.viewport[pass].view * object.model * vec4(sphere.xyz, 1)).xyz; vec3 center = (camera.viewport[pass].view * object.model * vec4(sphere.xyz, 1)).xyz;
float radius = (object.model * vec4(sphere.w, 0, 0, 0)).x; float radius = (object.model * vec4(sphere.w, 0, 0, 0)).x;
// center.y *= -1;
mat4 proj = camera.viewport[pass].projection; mat4 proj = camera.viewport[pass].projection;
float znear = proj[3][2]; float znear = proj[3][2];
float P00 = proj[0][0]; float P00 = proj[0][0];
@ -197,87 +161,6 @@ bool occlusionCull( uint id ) {
//if the depth of the sphere is in front of the depth pyramid value, then the object is visible //if the depth of the sphere is in front of the depth pyramid value, then the object is visible
visible = visible && depthSphere >= depth - DEPTH_BIAS; visible = visible && depthSphere >= depth - DEPTH_BIAS;
} }
#else
mat4 mat = camera.viewport[pass].projection * camera.viewport[pass].view * object.model;
vec3 boundsSize = instance.bounds.max - instance.bounds.min;
vec3 points[8] = {
instance.bounds.min.xyz,
instance.bounds.min.xyz + vec3(boundsSize.x,0,0),
instance.bounds.min.xyz + vec3(0, boundsSize.y,0),
instance.bounds.min.xyz + vec3(0, 0, boundsSize.z),
instance.bounds.min.xyz + vec3(boundsSize.xy,0),
instance.bounds.min.xyz + vec3(0, boundsSize.yz),
instance.bounds.min.xyz + vec3(boundsSize.x, 0, boundsSize.z),
instance.bounds.min.xyz + boundsSize.xyz,
};
vec2 minXY = vec2(1);
vec2 maxXY = vec2(0);
float minZ = 1;
float maxZ = 0;
#pragma unroll 8
for ( uint i = 0; i < 8; ++i ) {
vec4 clip = mat * vec4( points[i], 1 );
clip.xyz /= clip.w;
clip.xy = clip.xy * 0.5 + 0.5;
minXY.x = min(minXY.x, clip.x);
minXY.y = min(minXY.y, clip.y);
maxXY.x = max(maxXY.x, clip.x);
maxXY.y = max(maxXY.y, clip.y);
#if INVERSE
clip.z = 1.0 - clip.z;
maxZ = max(maxZ, clip.z);
#else
minZ = min(minZ, clip.z);
#endif
}
if ( maxXY.x <= 0 || maxXY.y <= 0 ) return false;
if ( minXY.x >= 1 || minXY.y >= 1 ) return false;
ivec2 depthSize = textureSize( samplerDepth, 0 );
float mips = mipLevels( depthSize );
vec4 uv = vec4(minXY, maxXY);
ivec2 clipSize = ivec2(maxXY - minXY) * depthSize;
float mip = mipLevels( clipSize );
mip = clamp( mip, 0, mips );
if ( mip == 0 ) {
mip = 1;
} else {
float lower = max(mip - 1, 0);
float scale = exp2(-lower);
vec2 a = floor(uv.xy * scale);
vec2 b = ceil(uv.zw * scale);
vec2 dims = b - a;
// Use the lower level if we only touch <= 2 texels in both dimensions
if (dims.x <= 2 && dims.y <= 2) mip = lower;
}
float depths[4] = {
textureLod( samplerDepth, uv.xy, mip ).r,
textureLod( samplerDepth, uv.zy, mip ).r,
textureLod( samplerDepth, uv.xw, mip ).r,
textureLod( samplerDepth, uv.zw, mip ).r,
};
#if INVERSE
float minDepth = 1.0 - min(min(min(depths[0], depths[1]), depths[2]), depths[3]);
#else
float maxDepth = max(max(max(depths[0], depths[1]), depths[2]), depths[3]);
#endif
instances[drawCommand.instanceID].bounds.padding1 = minZ;
instances[drawCommand.instanceID].bounds.padding2 = maxDepth;
return minZ <= maxDepth;
#endif
} }
return visible; return visible;
} }
@ -288,90 +171,5 @@ void main() {
bool visible = frustumCull( gID ); bool visible = frustumCull( gID );
// if ( visible ) visible = occlusionCull( gID ); // if ( visible ) visible = occlusionCull( gID );
// bool visible = occlusionCull( gID );
drawCommands[gID].instances = visible ? 1 : 0; drawCommands[gID].instances = visible ? 1 : 0;
} }
/*
Frustum frustum;
for (int i = 0; i < 3; ++i)
for (int j = 0; j < 2; ++j) {
frustum.planes[i*2+j].x = mat[0][3] + (j == 0 ? mat[0][i] : -mat[0][i]);
frustum.planes[i*2+j].y = mat[1][3] + (j == 0 ? mat[1][i] : -mat[1][i]);
frustum.planes[i*2+j].z = mat[2][3] + (j == 0 ? mat[2][i] : -mat[2][i]);
frustum.planes[i*2+j].w = mat[3][3] + (j == 0 ? mat[3][i] : -mat[3][i]);
frustum.planes[i*2+j]*= length(frustum.planes[i*2+j].xyz);
}
for ( uint i = 0; i < 6; ++i ) {
vec4 plane = frustum.planes[i];
float d = dot(instance.bounds.center, plane.xyz);
float r = dot(instance.bounds.extent, abs(plane.xyz));
bool inside = d + r > -plane.w;
if ( !inside ) return 0;
}
return true;
*/
/*
vec4 plane;
vec4 center = vec4( (max + min) * 0.5, 1 );
vec4 extent = vec4( (max - min) * 0.5, 1 );
center = mat * center;
extent = mat * extent;
center.xyz /= center.w;
extent.xyz /= extent.w;
for (int i = 0; i < 4; ++i ) plane[i] = mat[i][3] + mat[i][0]; // left
visible = dot(center.xyz + extent.xyz * sign(plane.xyz), plane.xyz ) > -plane.w;
if ( visible ) return true;
for (int i = 0; i < 4; ++i ) plane[i] = mat[i][3] - mat[i][0]; // right
visible = dot(center.xyz + extent.xyz * sign(plane.xyz), plane.xyz ) > -plane.w;
if ( visible ) return true;
for (int i = 0; i < 4; ++i ) plane[i] = mat[i][3] + mat[i][1]; // bottom
visible = dot(center.xyz + extent.xyz * sign(plane.xyz), plane.xyz ) > -plane.w;
if ( visible ) return true;
for (int i = 0; i < 4; ++i ) plane[i] = mat[i][3] - mat[i][1]; // top
visible = dot(center.xyz + extent.xyz * sign(plane.xyz), plane.xyz ) > -plane.w;
if ( visible ) return true;
for (int i = 0; i < 4; ++i ) plane[i] = mat[i][3] + mat[i][2]; // near
visible = dot(center.xyz + extent.xyz * sign(plane.xyz), plane.xyz ) > -plane.w;
if ( visible ) return true;
for (int i = 0; i < 4; ++i ) plane[i] = mat[i][3] - mat[i][2]; // far
visible = dot(center.xyz + extent.xyz * sign(plane.xyz), plane.xyz ) > -plane.w;
if ( visible ) return true;
*/
/*
for ( uint p = 0; p < 8; ++p ) {
vec4 t = corners[p];
float w = abs(t.w);
visible = -w <= t.x && t.x <= w && -w <= t.y && t.y <= w && 0 <= t.z && t.z <= w; // && -w <= t.z && t.z <= w;
}
*/
/*
mat4 convert( mat4 proj ) {
float f = -proj[1][1];
float raidou = f / proj[0][0];
float zNear = proj[3][2];
float zFar = 32;
float range = zNear - zFar;
float Sx = f * raidou;
float Sy = f;
float Sz = (-zNear - zFar) / range;
float Pz = 2 * zFar * zNear / range;
mat4 new = mat4(1.0);
new[0][0] = Sx;
new[1][1] = -Sy;
new[2][2] = Sz;
new[3][2] = Pz;
new[2][3] = 1;
return new;
}
*/

View File

@ -90,6 +90,8 @@ namespace ext {
void UF_API terminate( uf::Object& ); void UF_API terminate( uf::Object& );
extern UF_API float timescale; extern UF_API float timescale;
extern UF_API bool async;
extern UF_API bool interpolate; extern UF_API bool interpolate;
extern UF_API bool shared; extern UF_API bool shared;
extern UF_API bool globalStorage; extern UF_API bool globalStorage;

View File

@ -18,8 +18,9 @@ namespace ext {
0 0
}; };
VkDeviceSize alignment = 0; VkDeviceSize alignment = 0;
size_t address = {}; mutable size_t address = {};
void* mapped = nullptr; void* mapped = nullptr;
int32_t count = 1;
VkBufferUsageFlags usage = 0; VkBufferUsageFlags usage = 0;
VkMemoryPropertyFlags memoryProperties = 0; VkMemoryPropertyFlags memoryProperties = 0;
@ -29,25 +30,19 @@ namespace ext {
void* map( VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0 ); void* map( VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0 );
void unmap(); void unmap();
// void* map( VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0 ) const;
// void unmap() const;
// VkResult bind( VkDeviceSize offset = 0 );
// void copyTo( void* data, VkDeviceSize size );
// VkResult flush( VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0 ) const;
// VkResult invalidate( VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0 );
void updateDescriptor( VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0 ); void updateDescriptor( VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0 );
void allocate( VkBufferCreateInfo ); void allocate( VkBufferCreateInfo );
uint64_t getAddress();
uint64_t getAddress() const; uint64_t getAddress() const;
VkDeviceSize getLength() const; // returns the aligned length for the entire buffer
VkDeviceSize getOffset( size_t = 0 ) const; // returns the offset / stride / length of one object within the buffer
// RAII
~Buffer(); ~Buffer();
void initialize( ext::vulkan::Device& device, size_t = {} ); 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 ); 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; bool update( const void*, VkDeviceSize, bool = VK_DEFAULT_STAGE_BUFFERS ) const; // returns true if a reallocation occurred (to signal rebuilding command buffers)
void destroy(bool = VK_DEFAULT_DEFER_BUFFER_DESTROY); void destroy(bool = VK_DEFAULT_DEFER_BUFFER_DESTROY);
void swap( Buffer& ); void swap( Buffer& );

View File

@ -38,12 +38,14 @@ namespace ext {
void initialize( const Graphic& graphic, const GraphicDescriptor& descriptor ); void initialize( const Graphic& graphic, const GraphicDescriptor& descriptor );
void update( const Graphic& graphic ); void update( const Graphic& graphic );
void update( const Graphic& graphic, const GraphicDescriptor& descriptor ); void update( const Graphic& graphic, const GraphicDescriptor& descriptor );
void record( const Graphic& graphic, VkCommandBuffer, size_t = 0, size_t = 0 ) const; void record( const Graphic& graphic, VkCommandBuffer, size_t = 0, size_t = 0, size_t = 0 ) const;
void record( const Graphic& graphic, const GraphicDescriptor& descriptor, VkCommandBuffer, size_t = 0, size_t = 0 ) const; void record( const Graphic& graphic, const GraphicDescriptor& descriptor, VkCommandBuffer, size_t = 0, size_t = 0, size_t = 0 ) const;
void destroy(); void destroy();
uf::stl::vector<Shader*> getShaders( uf::stl::vector<Shader>& ); uf::stl::vector<Shader*> getShaders( uf::stl::vector<Shader>& );
uf::stl::vector<const Shader*> getShaders( const uf::stl::vector<Shader>& ) const; uf::stl::vector<const Shader*> getShaders( const uf::stl::vector<Shader>& ) const;
void collectBuffers( const Shader& shader, const RenderMode& renderMode, const Graphic& graphic, const std::function<void(const Buffer&)>& lambda ) const;
}; };
struct UF_API Material { struct UF_API Material {
@ -114,8 +116,9 @@ namespace ext {
void updatePipelines(); void updatePipelines();
void record( VkCommandBuffer commandBuffer, size_t pass = 0, size_t draw = 0 ) const; void record( VkCommandBuffer commandBuffer, size_t pass = 0, size_t draw = 0, size_t offset = 0 ) const;
void record( VkCommandBuffer commandBuffer, const GraphicDescriptor& descriptor, size_t pass = 0, size_t draw = 0 ) const; void record( VkCommandBuffer commandBuffer, const GraphicDescriptor& descriptor, size_t pass = 0, size_t draw = 0, size_t offset = 0 ) const;
}; };
} }
} }

View File

@ -7,6 +7,14 @@
#include <uf/ext/vulkan/texture.h> #include <uf/ext/vulkan/texture.h>
#include <uf/ext/vulkan/graphic.h> #include <uf/ext/vulkan/graphic.h>
#define VK_COMMAND_BUFFER_CALLBACK( pass, commandBuffer, i, f ) {\
auto it = commandBufferCallbacks.find(pass);\
if ( it != commandBufferCallbacks.end() ) {\
commandBufferCallbacks[pass]( commandBuffer, i );\
f;\
}\
}
namespace ext { namespace ext {
namespace vulkan { namespace vulkan {
struct Graphic; struct Graphic;
@ -54,8 +62,9 @@ namespace ext {
Device* device = VK_NULL_HANDLE; Device* device = VK_NULL_HANDLE;
RenderTarget renderTarget; RenderTarget renderTarget;
VkSemaphore renderCompleteSemaphore; uf::stl::vector<VkSemaphore> renderCompleteSemaphores;
uf::stl::vector<VkFence> fences; uf::stl::vector<VkFence> fences;
uf::renderer::QueueEnum queueEnum = {};
typedef uf::stl::vector<VkCommandBuffer> commands_container_t; typedef uf::stl::vector<VkCommandBuffer> commands_container_t;
std::thread::id mostRecentCommandPoolId; std::thread::id mostRecentCommandPoolId;

View File

@ -124,6 +124,8 @@ namespace ext {
uf::stl::vector<AttachmentDescriptor> attachments; uf::stl::vector<AttachmentDescriptor> attachments;
uf::stl::vector<BufferDescriptor> buffers; uf::stl::vector<BufferDescriptor> buffers;
} aliases; } aliases;
uf::stl::vector<uint32_t> dynamicRanges;
} metadata; } metadata;
ext::vulkan::userdata_t specializationConstants; ext::vulkan::userdata_t specializationConstants;
@ -177,7 +179,6 @@ namespace ext {
void setSpecializationConstants( const uf::stl::unordered_map<uf::stl::string, uint32_t>& values ); void setSpecializationConstants( const uf::stl::unordered_map<uf::stl::string, uint32_t>& values );
void setDescriptorCounts( const uf::stl::unordered_map<uf::stl::string, uint32_t>& values ); void setDescriptorCounts( const uf::stl::unordered_map<uf::stl::string, uint32_t>& values );
/* /*
uf::Serializer getUniformJson( const uf::stl::string& name, bool cache = true ); uf::Serializer getUniformJson( const uf::stl::string& name, bool cache = true );
bool updateUniform( const uf::stl::string& name, const ext::json::Value& payload ); bool updateUniform( const uf::stl::string& name, const ext::json::Value& payload );

View File

@ -13,11 +13,10 @@ namespace ext {
bool initialized = false; bool initialized = false;
uint32_t buffers = {}; uint32_t buffers = {};
VkSemaphore presentCompleteSemaphore; uf::stl::vector<VkSemaphore> presentCompleteSemaphores;
VkSemaphore renderCompleteSemaphore;
// helpers // helpers
VkResult acquireNextImage( uint32_t* imageIndex, VkSemaphore ); VkResult acquireNextImage( uint32_t* imageIndex, VkSemaphore, VkFence = nullptr );
VkResult queuePresent( VkQueue queue, uint32_t imageIndex, VkSemaphore waitSemaphore = VK_NULL_HANDLE ); VkResult queuePresent( VkQueue queue, uint32_t imageIndex, VkSemaphore waitSemaphore = VK_NULL_HANDLE );
// RAII // RAII

View File

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

View File

@ -227,6 +227,50 @@ namespace pod {
pod::BVH::pairs_t pairs; pod::BVH::pairs_t pairs;
}; };
struct PhysicsSettings {
bool warmupSolver = true; // cache manifold data to warm up the solver
bool blockContactSolver = true; // use BlockNxN solvers (where N = number of contacts for a manifold)
bool psgContactSolver = true; // use PSG contact solver
bool useGjk = false; // currently don't have a way to broadphase mesh => narrowphase tri via GJK
bool fixedStep = true; // run physics simulation with a fixed delta time (with accumulation), rather than rely on actual engine deltatime
uint32_t substeps = 4; // number of substeps per frame tick
uint32_t reserveCount = 32; // amount of elements to reserve for vectors used in this system, to-do: have it tie to a memory pool allocator
// increasing these make things lag for reasons I can imagine why
uint32_t broadphaseBvhCapacity = 4; // number of bodies per leaf node
uint32_t meshBvhCapacity = 4; // number of triangles per leaf node
// additionally flattens a BVH for linear iteration, rather than a recursive / stack-based traversal
bool flattenBvhBodies = true;
bool flattenBvhMeshes = true;
// use surface area heuristics for building the BVH, rather than naive splits
bool useBvhSahBodies = true; // it actually seems slower to use these......
bool useBvhSahMeshes = true;
bool useSplitBvhs = true; // creates separate BVHs for static / dynamic objects
// to-do: find possibly better values for this
uint32_t solverIterations = 10;
float baumgarteCorrectionPercent = 0.4f;
float baumgarteCorrectionSlop = 0.01f;
uf::stl::unordered_map<size_t, pod::Manifold> manifoldsCache;
uint32_t manifoldCacheLifetime = 6; // to-do: find a good value for this
uint32_t frameCounter = 0;
// to-do: tweak this to not be annoying
pod::BVH::UpdatePolicy bvhUpdatePolicy = {
.displacementThreshold = 0.25f,
.overlapThreshold = 2.0f,
.dirtyRatioThreshold = 0.3f,
.maxFramesBeforeRebuild = 60, // * 10, // 10 seconds
};
float groundedThreshold = 0.7f; // threshold before marking a body as grounded
};
struct World { struct World {
uf::stl::vector<pod::PhysicsBody*> bodies; uf::stl::vector<pod::PhysicsBody*> bodies;
@ -240,11 +284,14 @@ namespace uf {
namespace physics { namespace physics {
namespace impl { namespace impl {
extern UF_API float timescale; extern UF_API float timescale;
extern UF_API bool async;
extern UF_API bool interpolate; extern UF_API bool interpolate;
extern UF_API bool shared; extern UF_API bool shared;
extern UF_API bool globalStorage; extern UF_API bool globalStorage;
extern UF_API pod::World world; extern UF_API pod::World world;
extern UF_API pod::PhysicsSettings settings;
void UF_API initialize(); void UF_API initialize();
void UF_API initialize( uf::Object& ); void UF_API initialize( uf::Object& );
@ -272,7 +319,7 @@ namespace uf {
void UF_API updateInertia( pod::PhysicsBody& body ); void UF_API updateInertia( pod::PhysicsBody& body );
void UF_API applyForce( pod::PhysicsBody& body, const pod::Vector3f& force ); void UF_API applyForce( pod::PhysicsBody& body, const pod::Vector3f& force );
void UF_API applyForceAtPoint( pod::PhysicsBody body, const pod::Vector3f& force, const pod::Vector3f& point ); void UF_API applyForceAtPoint( pod::PhysicsBody& body, const pod::Vector3f& force, const pod::Vector3f& point );
void UF_API applyImpulse( pod::PhysicsBody& body, const pod::Vector3f& impulse ); void UF_API applyImpulse( pod::PhysicsBody& body, const pod::Vector3f& impulse );
void UF_API applyTorque( pod::PhysicsBody& body, const pod::Vector3f& torque ); void UF_API applyTorque( pod::PhysicsBody& body, const pod::Vector3f& torque );

View File

@ -40,6 +40,7 @@ namespace pod {
std::condition_variable queued; std::condition_variable queued;
std::condition_variable finished; std::condition_variable finished;
} conditions; } conditions;
std::thread thread; std::thread thread;
pod::Thread::queue_t queue; pod::Thread::queue_t queue;
@ -47,6 +48,7 @@ namespace pod {
uf::Timer<long long> timer; uf::Timer<long long> timer;
uint affinity = 0; uint affinity = 0;
std::atomic<int> pending{0};
struct UF_API Tasks { struct UF_API Tasks {
uf::stl::string name = uf::thread::workerThreadName; uf::stl::string name = uf::thread::workerThreadName;
@ -112,7 +114,11 @@ namespace uf {
// schedules to named thread // schedules to named thread
inline void queue( const uf::stl::string& name, const pod::Thread::function_t& fun ) { return uf::thread::queue( uf::thread::get(name), fun ); } inline void queue( const uf::stl::string& name, const pod::Thread::function_t& fun ) { return uf::thread::queue( uf::thread::get(name), fun ); }
inline void add( const uf::stl::string& name, const pod::Thread::function_t& fun ) { return uf::thread::add( uf::thread::get(name), fun ); } inline void add( const uf::stl::string& name, const pod::Thread::function_t& fun ) { return uf::thread::add( uf::thread::get(name), fun ); }
/*
template<typename F>
inline void queue( const uf::stl::string& name, const F& fun ) { return uf::thread::queue( uf::thread::get(name), [=](){ fun(); } ); }
*/
void UF_API process( pod::Thread& ); void UF_API process( pod::Thread& );
void UF_API wait( pod::Thread& ); void UF_API wait( pod::Thread& );

View File

@ -753,6 +753,8 @@ void UF_API uf::initialize() {
} }
void UF_API uf::tick() { void UF_API uf::tick() {
++uf::time::frame;
#if 1 #if 1
if ( /*global*/::sceneTransition.phase >= 0 ) { if ( /*global*/::sceneTransition.phase >= 0 ) {
auto target = /*global*/::sceneTransition.payload["scene"].as<uf::stl::string>(); auto target = /*global*/::sceneTransition.payload["scene"].as<uf::stl::string>();
@ -837,13 +839,14 @@ void UF_API uf::tick() {
lMetadata["light"]["color"][2] = (rand() % 100) / 100.0; lMetadata["light"]["color"][2] = (rand() % 100) / 100.0;
} }
auto& sMetadata = scene.getComponent<uf::Serializer>(); auto& sMetadata = scene.getComponent<uf::Serializer>();
sMetadata["light"]["should"] = true; sMetadata["light"]["enabled"] = true;
} }
} }
} }
#endif #endif
/* Update physics timer */ { /* Update physics timer */ {
// uf::physics::tick(); // to-do: add setting to either run in main thread or defer to a background thread
uf::physics::tick();
} }
/* Update entities */ { /* Update entities */ {
uf::scene::tick(); uf::scene::tick();

View File

@ -40,6 +40,7 @@ void ext::LightBehavior::initialize( uf::Object& self ) {
if ( ++::roundRobin.current >= ::roundRobin.lights.size() ) ::roundRobin.current = 0; if ( ++::roundRobin.current >= ::roundRobin.lights.size() ) ::roundRobin.current = 0;
}); });
} }
/* /*
if ( !metadataJson["light"]["bias"]["shader"].is<float>() ) metadataJson["light"]["bias"]["shader"] = 0.000000005f; if ( !metadataJson["light"]["bias"]["shader"].is<float>() ) metadataJson["light"]["bias"]["shader"] = 0.000000005f;
*/ */
@ -56,7 +57,7 @@ void ext::LightBehavior::initialize( uf::Object& self ) {
#if UF_USE_OPENGL #if UF_USE_OPENGL
metadataJson["light"]["shadows"] = false; metadataJson["light"]["shadows"] = false;
#endif #endif
if ( !sceneMetadataJson["lights"]["shadows"]["enabled"].as<bool>(true) ) { if ( !sceneMetadataJson["light"]["shadows"]["enabled"].as<bool>(true) ) {
metadataJson["light"]["shadows"] = false; metadataJson["light"]["shadows"] = false;
} }
if ( metadataJson["light"]["shadows"].as<bool>() ) { if ( metadataJson["light"]["shadows"].as<bool>() ) {

View File

@ -767,8 +767,7 @@ void ext::ExtSceneBehavior::destroy( uf::Object& self ) {
} }
} }
void ext::ExtSceneBehavior::Metadata::serialize( uf::Object& self, uf::Serializer& serializer ) { void ext::ExtSceneBehavior::Metadata::serialize( uf::Object& self, uf::Serializer& serializer ) {
serializer["light"]["should"] = /*this->*/light.enabled; serializer["light"]["enabled"] = /*this->*/light.enabled;
serializer["light"]["ambient"] = uf::vector::encode( /*this->*/light.ambient ); serializer["light"]["ambient"] = uf::vector::encode( /*this->*/light.ambient );
serializer["light"]["exposure"] = /*this->*/light.exposure; serializer["light"]["exposure"] = /*this->*/light.exposure;
serializer["light"]["gamma"] = /*this->*/light.gamma; serializer["light"]["gamma"] = /*this->*/light.gamma;
@ -834,7 +833,7 @@ void ext::ExtSceneBehavior::Metadata::deserialize( uf::Object& self, uf::Seriali
/*this->*/shadow.update = serializer["light"]["shadows"]["update"].as(/*this->*/shadow.update); /*this->*/shadow.update = serializer["light"]["shadows"]["update"].as(/*this->*/shadow.update);
/*this->*/shadow.typeMap = serializer["light"]["shadows"]["map type"].as(/*this->*/shadow.typeMap); /*this->*/shadow.typeMap = serializer["light"]["shadows"]["map type"].as(/*this->*/shadow.typeMap);
/*this->*/light.enabled = serializer["light"]["enabled"].as(/*this->*/light.enabled) && serializer["light"]["should"].as(/*this->*/light.enabled); /*this->*/light.enabled = serializer["light"]["enabled"].as(/*this->*/light.enabled) && serializer["light"]["enabled"].as(/*this->*/light.enabled);
/*this->*/light.max = serializer["light"]["max"].as(/*this->*/light.max); /*this->*/light.max = serializer["light"]["max"].as(/*this->*/light.max);
/*this->*/light.ambient = uf::vector::decode( serializer["light"]["ambient"], /*this->*/light.ambient); /*this->*/light.ambient = uf::vector::decode( serializer["light"]["ambient"], /*this->*/light.ambient);
@ -1228,6 +1227,9 @@ void ext::ExtSceneBehavior::bindBuffers( uf::Object& self, uf::renderer::Graphic
auto& shader = graphic.material.getShader(shaderType, shaderPipeline); auto& shader = graphic.material.getShader(shaderType, shaderPipeline);
if ( !shader.hasUniform("UBO") ) return; if ( !shader.hasUniform("UBO") ) return;
//UF_MSG_DEBUG( "{}: {} {} // {}", uf::string::toString( self ), shaderType, shaderPipeline, uf::string::toString( uf::scene::getCurrentScene() ) ); //UF_MSG_DEBUG( "{}: {} {} // {}", uf::string::toString( self ), shaderType, shaderPipeline, uf::string::toString( uf::scene::getCurrentScene() ) );
// if ( controller.getName() == "Player" ) UF_MSG_DEBUG("frame={}, camera={}", uf::time::frame, uf::matrix::toString( uniforms.matrices[0].view ));
shader.updateBuffer( (const void*) &uniforms, sizeof(uniforms), shader.getUniformBuffer("UBO") ); shader.updateBuffer( (const void*) &uniforms, sizeof(uniforms), shader.getUniformBuffer("UBO") );
bool shouldUpdate2 = !uf::matrix::equals( uniforms.matrices[0].view, previousUniforms.matrices[0].view, 0.0001f ); bool shouldUpdate2 = !uf::matrix::equals( uniforms.matrices[0].view, previousUniforms.matrices[0].view, 0.0001f );

View File

@ -1494,7 +1494,9 @@ void uf::graph::render( uf::Object& object ) {
} }
void uf::graph::render( pod::Graph::Storage& storage ) { void uf::graph::render( pod::Graph::Storage& storage ) {
auto* renderMode = uf::renderer::getCurrentRenderMode(); auto* renderMode = uf::renderer::getCurrentRenderMode();
if ( renderMode->getName() == "Gui" ) return;
auto& scene = uf::scene::getCurrentScene(); auto& scene = uf::scene::getCurrentScene();
auto& controller = scene.getController(); auto& controller = scene.getController();
auto& camera = scene.getCamera( controller ); auto& camera = scene.getCamera( controller );
@ -1509,6 +1511,8 @@ void uf::graph::render( pod::Graph::Storage& storage ) {
} }
#endif #endif
// if ( controller.getName() == "Player" ) UF_MSG_DEBUG("frame={}, camera={}, renderMode={}, {}", uf::time::frame, uf::matrix::toString( viewport.matrices[0].view ), renderMode->getName(), renderMode->getType() );
storage.buffers.camera.update( (const void*) &viewport, sizeof(pod::Camera::Viewports) ); storage.buffers.camera.update( (const void*) &viewport, sizeof(pod::Camera::Viewports) );
#if UF_USE_VULKAN #if UF_USE_VULKAN

View File

@ -140,7 +140,6 @@ void uf::ObjectBehavior::initialize( uf::Object& self ) {
pod::Vector3f min = uf::vector::decode( metadataJsonPhysics["min"], pod::Vector3f{-0.5f, -0.5f, -0.5f} ); pod::Vector3f min = uf::vector::decode( metadataJsonPhysics["min"], pod::Vector3f{-0.5f, -0.5f, -0.5f} );
pod::Vector3f max = uf::vector::decode( metadataJsonPhysics["max"], pod::Vector3f{0.5f, 0.5f, 0.5f} ); pod::Vector3f max = uf::vector::decode( metadataJsonPhysics["max"], pod::Vector3f{0.5f, 0.5f, 0.5f} );
UF_MSG_DEBUG("entity={}, min={}, max={}", uf::string::toString( *this ), uf::vector::toString( min ), uf::vector::toString( max ));
#if UF_USE_REACTPHYSICS #if UF_USE_REACTPHYSICS
auto center = ( max + min ) * 0.5f; auto center = ( max + min ) * 0.5f;
if ( metadataJsonPhysics["recenter"].as<bool>(true) ) offset = (center - transform.position); if ( metadataJsonPhysics["recenter"].as<bool>(true) ) offset = (center - transform.position);

View File

@ -6,6 +6,7 @@
#include <uf/utils/math/physics.h> #include <uf/utils/math/physics.h>
#include <uf/utils/renderer/renderer.h> #include <uf/utils/renderer/renderer.h>
#include <uf/utils/io/fmt.h> #include <uf/utils/io/fmt.h>
#include <uf/engine/ext.h>
#include <regex> #include <regex>
UF_OBJECT_REGISTER_BEGIN(uf::Scene) UF_OBJECT_REGISTER_BEGIN(uf::Scene)
@ -268,7 +269,7 @@ void uf::scene::tick() {
auto& scene = uf::scene::getCurrentScene(); auto& scene = uf::scene::getCurrentScene();
auto/*&*/ graph = scene.getGraph(true); auto/*&*/ graph = scene.getGraph(true);
uf::physics::tick( scene ); // uf::physics::tick( scene );
#if !UF_SCENE_GLOBAL_GRAPH #if !UF_SCENE_GLOBAL_GRAPH
auto& metadata = scene.getComponent<uf::SceneBehavior::Metadata>(); auto& metadata = scene.getComponent<uf::SceneBehavior::Metadata>();

View File

@ -44,37 +44,13 @@ void ext::vulkan::Buffer::aliasBuffer( const ext::vulkan::Buffer& buffer ) {
void* ext::vulkan::Buffer::map( VkDeviceSize size, VkDeviceSize offset ) { void* ext::vulkan::Buffer::map( VkDeviceSize size, VkDeviceSize offset ) {
if ( !mapped ) VK_CHECK_RESULT(vmaMapMemory( allocator, allocation, &mapped )); if ( !mapped ) VK_CHECK_RESULT(vmaMapMemory( allocator, allocation, &mapped ));
return mapped; return static_cast<char*>(mapped) + offset;;
} }
void ext::vulkan::Buffer::unmap() { void ext::vulkan::Buffer::unmap() {
if ( !mapped ) return; if ( !mapped ) return;
vmaUnmapMemory( allocator, allocation ); vmaUnmapMemory( allocator, allocation );
mapped = nullptr; mapped = nullptr;
} }
/*
void* ext::vulkan::Buffer::map( VkDeviceSize size, VkDeviceSize offset ) const {
void* mapped{};
VK_CHECK_RESULT(vmaMapMemory( allocator, allocation, &mapped ));
return mapped;
}
void ext::vulkan::Buffer::unmap() const {
vmaUnmapMemory( allocator, allocation );
}
VkResult ext::vulkan::Buffer::bind( VkDeviceSize offset ) {
return VK_SUCCESS;
}
VkResult ext::vulkan::Buffer::flush( VkDeviceSize size, VkDeviceSize offset ) const {
return VK_SUCCESS;
}
VkResult ext::vulkan::Buffer::invalidate( VkDeviceSize size, VkDeviceSize offset ) {
return VK_SUCCESS;
}
void ext::vulkan::Buffer::copyTo( void* data, VkDeviceSize size ) {
assert(mapped);
memcpy(mapped, data, size);
}
*/
void ext::vulkan::Buffer::updateDescriptor( VkDeviceSize size, VkDeviceSize offset ) { void ext::vulkan::Buffer::updateDescriptor( VkDeviceSize size, VkDeviceSize offset ) {
descriptor.offset = offset; descriptor.offset = offset;
@ -95,7 +71,7 @@ void ext::vulkan::Buffer::allocate( VkBufferCreateInfo bufferCreateInfo ) {
VK_REGISTER_HANDLE( buffer ); VK_REGISTER_HANDLE( buffer );
} }
size_t ext::vulkan::Buffer::getAddress() { size_t ext::vulkan::Buffer::getAddress() const {
// if ( !(usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT) ) UF_MSG_DEBUG("CALLING GETADDRESS ON BUFFER WITHOUT ADDRESS BIT: {}", fmt::ptr(this->buffer)); // if ( !(usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT) ) UF_MSG_DEBUG("CALLING GETADDRESS ON BUFFER WITHOUT ADDRESS BIT: {}", fmt::ptr(this->buffer));
if ( this->address ) return this->address; if ( this->address ) return this->address;
@ -104,14 +80,12 @@ size_t ext::vulkan::Buffer::getAddress() {
info.buffer = buffer; info.buffer = buffer;
return (this->address = vkGetBufferDeviceAddressKHR(this->device ? *this->device : ext::vulkan::device, &info)); return (this->address = vkGetBufferDeviceAddressKHR(this->device ? *this->device : ext::vulkan::device, &info));
} }
size_t ext::vulkan::Buffer::getAddress() const {
// if ( !(usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT) ) UF_MSG_DEBUG("CALLING GETADDRESS ON BUFFER WITHOUT ADDRESS BIT: {}", fmt::ptr(this->buffer));
if ( this->address ) return this->address;
VkBufferDeviceAddressInfoKHR info{}; size_t ext::vulkan::Buffer::getLength( ) const {
info.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO; return allocationInfo.size;
info.buffer = buffer; }
return vkGetBufferDeviceAddressKHR(this->device ? *this->device : ext::vulkan::device, &info); size_t ext::vulkan::Buffer::getOffset( size_t i ) const {
return this->getLength() / this->count * i;
} }
ext::vulkan::Buffer::~Buffer() { ext::vulkan::Buffer::~Buffer() {
@ -147,16 +121,28 @@ void ext::vulkan::Buffer::initialize( const void* data, VkDeviceSize length, VkB
if ( !device ) device = &ext::vulkan::device; if ( !device ) device = &ext::vulkan::device;
if ( stage ) usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; // implicitly set properties if ( stage ) usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; // implicitly set properties
// if ( usage != VK_BUFFER_USAGE_TRANSFER_SRC_BIT ) usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; // assume all UBOs are dynamic
auto totalLength = length;
#if VK_UBO_USE_N_BUFFERS
if ( usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT ) {
this->count = ext::vulkan::swapchain.buffers;
this->alignment = device->properties.limits.minUniformBufferOffsetAlignment;
totalLength = ALIGNED_SIZE( length, this->alignment ) * this->count;
}
#endif
VK_CHECK_RESULT(device->createBuffer( VK_CHECK_RESULT(device->createBuffer(
nullptr, nullptr,
length, totalLength,
usage, usage,
memoryProperties, memoryProperties,
*this *this
)); ));
if ( length != totalLength ) {
this->updateDescriptor( length, 0 );
}
if ( data && length ) update( data, length, stage ); if ( data && length ) update( data, length, stage );
/* /*
@ -187,6 +173,13 @@ bool ext::vulkan::Buffer::update( const void* data, VkDeviceSize length, bool st
if ( !length ) return false; if ( !length ) return false;
if ( !buffer ) 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 // to-do: fix this because it's a thorn in my side when a mesh needs to update
if ( length > allocationInfo.size ) { if ( length > allocationInfo.size ) {
UF_MSG_WARNING("Buffer update of {} exceeds buffer size of {}", length, allocationInfo.size); UF_MSG_WARNING("Buffer update of {} exceeds buffer size of {}", length, allocationInfo.size);
@ -206,7 +199,7 @@ bool ext::vulkan::Buffer::update( const void* data, VkDeviceSize length, bool st
if ( !data ) return false; if ( !data ) return false;
if ( !stage ) { if ( !stage ) {
auto* self = const_cast<ext::vulkan::Buffer*>(this); auto* self = const_cast<ext::vulkan::Buffer*>(this);
void* map = self->map(); void* map = self->map(length, offset);
memcpy(map, data, length); memcpy(map, data, length);
self->unmap(); self->unmap();
return false; return false;
@ -224,6 +217,7 @@ bool ext::vulkan::Buffer::update( const void* data, VkDeviceSize length, bool st
auto commandBuffer = device->fetchCommandBuffer(QueueEnum::TRANSFER); // waits on finish auto commandBuffer = device->fetchCommandBuffer(QueueEnum::TRANSFER); // waits on finish
VkBufferCopy region = {}; VkBufferCopy region = {};
region.size = length; region.size = length;
region.dstOffset = offset;
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "copyBuffer" ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "copyBuffer" );
vkCmdCopyBuffer(commandBuffer, staging.buffer, buffer, 1, &region); vkCmdCopyBuffer(commandBuffer, staging.buffer, buffer, 1, &region);
device->flushCommandBuffer(commandBuffer); device->flushCommandBuffer(commandBuffer);

View File

@ -984,7 +984,7 @@ void ext::vulkan::Device::initialize() {
} }
auto& deviceInfo = deviceInfos[bestDeviceIndex]; auto& deviceInfo = deviceInfos[bestDeviceIndex];
this->physicalDevice = deviceInfo.handle; this->physicalDevice = deviceInfo.handle;
VK_VALIDATION_MESSAGE("Usind device #{}: (score: {} | device ID: {} | vendor ID: {} | API version: {} | driver version: {})", bestDeviceIndex, deviceInfo.properties.deviceName, deviceInfo.score, deviceInfo.properties.deviceID, deviceInfo.properties.vendorID, deviceInfo.properties.apiVersion, deviceInfo.properties.driverVersion ); VK_VALIDATION_MESSAGE("Using device #{}: (score: {} | device ID: {} | vendor ID: {} | API version: {} | driver version: {})", bestDeviceIndex, deviceInfo.properties.deviceName, deviceInfo.score, deviceInfo.properties.deviceID, deviceInfo.properties.vendorID, deviceInfo.properties.apiVersion, deviceInfo.properties.driverVersion );
/* /*
VK_VALIDATION_MESSAGE("Using device #" << bestDeviceIndex << " (" VK_VALIDATION_MESSAGE("Using device #" << bestDeviceIndex << " ("
"score: " << deviceInfo.score << " | " "score: " << deviceInfo.score << " | "

View File

@ -393,12 +393,18 @@ PIPELINE_INITIALIZATION_INVALID:
}); });
return; return;
} }
void ext::vulkan::Pipeline::record( const Graphic& graphic, VkCommandBuffer commandBuffer, size_t pass, size_t draw ) const { void ext::vulkan::Pipeline::record( const Graphic& graphic, VkCommandBuffer commandBuffer, size_t pass, size_t draw, size_t offset ) const {
return record( graphic, descriptor, commandBuffer, pass, draw ); return record( graphic, descriptor, commandBuffer, pass, draw, offset );
} }
void ext::vulkan::Pipeline::record( const Graphic& graphic, const GraphicDescriptor& descriptor, VkCommandBuffer commandBuffer, size_t pass, size_t draw ) const { void ext::vulkan::Pipeline::record( const Graphic& graphic, const GraphicDescriptor& descriptor, VkCommandBuffer commandBuffer, size_t pass, size_t draw, size_t offset ) const {
auto shaders = getShaders( graphic.material.shaders ); auto shaders = getShaders( graphic.material.shaders );
// create dynamic offset ranges
static thread_local uf::stl::vector<uint32_t> dynamicOffsets;
dynamicOffsets.clear();
RenderMode& renderMode = ext::vulkan::getRenderMode(descriptor.renderMode, true);
bool bound = false; bool bound = false;
for ( auto* shader : shaders ) { for ( auto* shader : shaders ) {
// compute shaders // compute shaders
@ -435,13 +441,23 @@ void ext::vulkan::Pipeline::record( const Graphic& graphic, const GraphicDescrip
vkCmdPushConstants( commandBuffer, pipelineLayout, shader->descriptor.stage, 0, size, data ); vkCmdPushConstants( commandBuffer, pipelineLayout, shader->descriptor.stage, 0, size, data );
} }
} }
dynamicOffsets.insert( dynamicOffsets.end(), shader->metadata.dynamicRanges.begin(), shader->metadata.dynamicRanges.end() );
}
for ( auto& dynamicOffset : dynamicOffsets ) {
dynamicOffset *= offset;
} }
// no matching bind point for shaders, skip // no matching bind point for shaders, skip
if ( !bound ) return; if ( !bound ) return;
// Bind descriptor sets describing shader binding points // Bind descriptor sets describing shader binding points
vkCmdBindDescriptorSets(commandBuffer, (VkPipelineBindPoint)descriptor.bind.point, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr); #if VK_UBO_USE_N_BUFFERS
vkCmdBindDescriptorSets(commandBuffer, (VkPipelineBindPoint) descriptor.bind.point, pipelineLayout, 0, 1, &descriptorSet, dynamicOffsets.size(), dynamicOffsets.data());
#else
vkCmdBindDescriptorSets(commandBuffer, (VkPipelineBindPoint) descriptor.bind.point, pipelineLayout, 0, 1, &descriptorSet, 0, nullptr);
#endif
// Bind the rendering pipeline // Bind the rendering pipeline
// The pipeline (state object) contains all states of the rendering pipeline, binding it will set all the states specified at pipeline creation time // The pipeline (state object) contains all states of the rendering pipeline, binding it will set all the states specified at pipeline creation time
vkCmdBindPipeline(commandBuffer, (VkPipelineBindPoint)descriptor.bind.point, pipeline); vkCmdBindPipeline(commandBuffer, (VkPipelineBindPoint)descriptor.bind.point, pipeline);
@ -516,50 +532,10 @@ void ext::vulkan::Pipeline::update( const Graphic& graphic, const GraphicDescrip
auto& infos = INFOS.emplace_back(); auto& infos = INFOS.emplace_back();
uf::stl::vector<ext::vulkan::enums::Image::viewType_t> types; uf::stl::vector<ext::vulkan::enums::Image::viewType_t> types;
// add aliased-by-name buffers this->collectBuffers( *shader, renderMode, graphic, [&]( const Buffer& buffer ){
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;
auto view = matches.size() == 2 ? stoi(matches[1]) : -1;
const ext::vulkan::Buffer* buffer = &descriptor.fallback;
if ( descriptor.renderMode ) {
if ( descriptor.renderMode->hasBuffer(name) )
buffer = &descriptor.renderMode->getBuffer(name);
} else if ( renderMode.hasBuffer(name) ) {
buffer = &renderMode.getBuffer(name);
}
if ( !buffer ) continue;
if ( buffer->usage & uf::renderer::enums::Buffer::UNIFORM ) infos.uniform.emplace_back(buffer->descriptor);
if ( buffer->usage & uf::renderer::enums::Buffer::STORAGE ) infos.storage.emplace_back(buffer->descriptor);
}
#if 0
// add per-rendermode buffers
for ( auto& buffer : renderMode.buffers ) {
if ( buffer.usage & uf::renderer::enums::Buffer::UNIFORM ) infos.uniform.emplace_back(buffer.descriptor); if ( buffer.usage & uf::renderer::enums::Buffer::UNIFORM ) infos.uniform.emplace_back(buffer.descriptor);
if ( buffer.usage & uf::renderer::enums::Buffer::STORAGE ) infos.storage.emplace_back(buffer.descriptor); if ( buffer.usage & uf::renderer::enums::Buffer::STORAGE ) infos.storage.emplace_back(buffer.descriptor);
// if ( buffer.usage & VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR ) infos.accelerationStructure.emplace_back(buffer.descriptor); } );
}
#endif
// add per-shader buffers
for ( auto& buffer : shader->buffers ) {
if ( buffer.usage & uf::renderer::enums::Buffer::UNIFORM ) infos.uniform.emplace_back(buffer.descriptor);
if ( buffer.usage & uf::renderer::enums::Buffer::STORAGE ) infos.storage.emplace_back(buffer.descriptor);
// if ( buffer.usage & VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR ) infos.accelerationStructure.emplace_back(buffer.descriptor);
}
// add per-pipeline buffers
for ( auto& buffer : this->buffers ) {
if ( buffer.usage & uf::renderer::enums::Buffer::UNIFORM ) infos.uniform.emplace_back(buffer.descriptor);
if ( buffer.usage & uf::renderer::enums::Buffer::STORAGE ) infos.storage.emplace_back(buffer.descriptor);
// if ( buffer.usage & VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR ) infos.accelerationStructure.emplace_back(buffer.descriptor);
}
// add per-graphics buffers
for ( auto& buffer : graphic.buffers ) {
if ( buffer.usage & uf::renderer::enums::Buffer::UNIFORM ) infos.uniform.emplace_back(buffer.descriptor);
if ( buffer.usage & uf::renderer::enums::Buffer::STORAGE ) infos.storage.emplace_back(buffer.descriptor);
// if ( buffer.usage & VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR ) infos.accelerationStructure.emplace_back(buffer.descriptor);
}
if ( descriptor.subpass < renderTarget.passes.size() ) { if ( descriptor.subpass < renderTarget.passes.size() ) {
auto& subpass = renderTarget.passes[descriptor.subpass]; auto& subpass = renderTarget.passes[descriptor.subpass];
@ -796,7 +772,8 @@ void ext::vulkan::Pipeline::update( const Graphic& graphic, const GraphicDescrip
)); ));
samplerInfo += layout.descriptorCount; samplerInfo += layout.descriptorCount;
} break; } break;
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: { case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: {
UF_ASSERT_BREAK_MSG( uniformBufferInfo != infos.uniform.end(), "Filename: {}\tCount: {}", shader->filename, layout.descriptorCount ) UF_ASSERT_BREAK_MSG( uniformBufferInfo != infos.uniform.end(), "Filename: {}\tCount: {}", shader->filename, layout.descriptorCount )
writeDescriptorSets.emplace_back(ext::vulkan::initializers::writeDescriptorSet( writeDescriptorSets.emplace_back(ext::vulkan::initializers::writeDescriptorSet(
descriptorSet, descriptorSet,
@ -807,7 +784,8 @@ void ext::vulkan::Pipeline::update( const Graphic& graphic, const GraphicDescrip
)); ));
uniformBufferInfo += layout.descriptorCount; uniformBufferInfo += layout.descriptorCount;
} break; } break;
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: { case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: {
UF_ASSERT_BREAK_MSG( storageBufferInfo != infos.storage.end(), "Filename: {}\tCount: {}", shader->filename, layout.descriptorCount ) UF_ASSERT_BREAK_MSG( storageBufferInfo != infos.storage.end(), "Filename: {}\tCount: {}", shader->filename, layout.descriptorCount )
writeDescriptorSets.emplace_back(ext::vulkan::initializers::writeDescriptorSet( writeDescriptorSets.emplace_back(ext::vulkan::initializers::writeDescriptorSet(
descriptorSet, descriptorSet,
@ -908,6 +886,35 @@ PIPELINE_UPDATE_INVALID:
}); });
return; return;
} }
void ext::vulkan::Pipeline::collectBuffers( const Shader& shader, const RenderMode& renderMode, const Graphic& graphic, const std::function<void(const Buffer&)>& lambda ) const {
// 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;
auto view = matches.size() == 2 ? stoi(matches[1]) : -1;
const ext::vulkan::Buffer* buffer = &descriptor.fallback;
if ( descriptor.renderMode ) {
if ( descriptor.renderMode->hasBuffer(name) )
buffer = &descriptor.renderMode->getBuffer(name);
} else if ( renderMode.hasBuffer(name) ) {
buffer = &renderMode.getBuffer(name);
}
if ( !buffer ) continue;
lambda( *buffer );
}
#if 0
// add per-rendermode buffers
for ( auto& buffer : renderMode.buffers ) lambda( buffer );
#endif
// add per-shader buffers
for ( auto& buffer : shader.buffers ) lambda( buffer );
// add per-pipeline buffers
for ( auto& buffer : this->buffers ) lambda( buffer );
// add per-graphics buffers
for ( auto& buffer : graphic.buffers ) lambda( buffer );
}
void ext::vulkan::Pipeline::destroy() { void ext::vulkan::Pipeline::destroy() {
if ( aliased ) return; if ( aliased ) return;
@ -1826,10 +1833,10 @@ const ext::vulkan::Pipeline& ext::vulkan::Graphic::getPipeline( const GraphicDes
void ext::vulkan::Graphic::updatePipelines() { void ext::vulkan::Graphic::updatePipelines() {
for ( auto pair : this->pipelines ) pair.second.update( *this ); for ( auto pair : this->pipelines ) pair.second.update( *this );
} }
void ext::vulkan::Graphic::record( VkCommandBuffer commandBuffer, size_t pass, size_t draw ) const { void ext::vulkan::Graphic::record( VkCommandBuffer commandBuffer, size_t pass, size_t draw, size_t offset ) const {
return this->record( commandBuffer, descriptor, pass, draw ); return this->record( commandBuffer, descriptor, pass, draw, offset );
} }
void ext::vulkan::Graphic::record( VkCommandBuffer commandBuffer, const GraphicDescriptor& descriptor, size_t pass, size_t draw ) const { void ext::vulkan::Graphic::record( VkCommandBuffer commandBuffer, const GraphicDescriptor& descriptor, size_t pass, size_t draw, size_t offset ) const {
if ( !process ) return; if ( !process ) return;
if ( !this->hasPipeline( descriptor ) ) { if ( !this->hasPipeline( descriptor ) ) {
VK_DEBUG_VALIDATION_MESSAGE(this << ": has no valid pipeline ({} {})", descriptor.renderMode, descriptor.renderTarget); VK_DEBUG_VALIDATION_MESSAGE(this << ": has no valid pipeline ({} {})", descriptor.renderMode, descriptor.renderTarget);
@ -1842,7 +1849,7 @@ void ext::vulkan::Graphic::record( VkCommandBuffer commandBuffer, const GraphicD
return; return;
} }
if ( !pipeline.metadata.process ) return; if ( !pipeline.metadata.process ) return;
pipeline.record(*this, descriptor, commandBuffer, pass, draw); pipeline.record(*this, descriptor, commandBuffer, pass, draw, offset);
auto shaders = pipeline.getShaders( material.shaders ); auto shaders = pipeline.getShaders( material.shaders );
for ( auto* shader : shaders ) { for ( auto* shader : shaders ) {

View File

@ -221,9 +221,9 @@ ext::vulkan::GraphicDescriptor ext::vulkan::RenderMode::bindGraphicDescriptor( c
} }
void ext::vulkan::RenderMode::createCommandBuffers() { void ext::vulkan::RenderMode::createCommandBuffers() {
this->execute = true; static thread_local uf::stl::vector<ext::vulkan::Graphic*> graphics;
graphics.clear();
uf::stl::vector<ext::vulkan::Graphic*> graphics;
auto& scene = uf::scene::getCurrentScene(); auto& scene = uf::scene::getCurrentScene();
auto/*&*/ graph = scene.getGraph(); auto/*&*/ graph = scene.getGraph();
for ( auto entity : graph ) { for ( auto entity : graph ) {
@ -243,6 +243,7 @@ void ext::vulkan::RenderMode::createCommandBuffers() {
this->mostRecentCommandPoolId = std::this_thread::get_id(); this->mostRecentCommandPoolId = std::this_thread::get_id();
this->rebuild = false; this->rebuild = false;
this->rerecord = false; this->rerecord = false;
this->execute = true;
} }
ext::vulkan::RenderMode::commands_container_t& ext::vulkan::RenderMode::getCommands( std::thread::id id ) { ext::vulkan::RenderMode::commands_container_t& ext::vulkan::RenderMode::getCommands( std::thread::id id ) {
bool exists = this->commands.has(id); //this->commands.count(id) > 0; bool exists = this->commands.has(id); //this->commands.count(id) > 0;
@ -251,7 +252,7 @@ ext::vulkan::RenderMode::commands_container_t& ext::vulkan::RenderMode::getComma
commands.resize( swapchain.buffers ); commands.resize( swapchain.buffers );
VkCommandBufferAllocateInfo cmdBufAllocateInfo = ext::vulkan::initializers::commandBufferAllocateInfo( VkCommandBufferAllocateInfo cmdBufAllocateInfo = ext::vulkan::initializers::commandBufferAllocateInfo(
device->getCommandPool(this->getType() == "Compute" ? QueueEnum::COMPUTE : QueueEnum::GRAPHICS), device->getCommandPool(this->queueEnum),
VK_COMMAND_BUFFER_LEVEL_PRIMARY, VK_COMMAND_BUFFER_LEVEL_PRIMARY,
static_cast<uint32_t>(commands.size()) static_cast<uint32_t>(commands.size())
); );
@ -277,7 +278,6 @@ void ext::vulkan::RenderMode::cleanupAllCommands() {
for ( auto& pair : container ) { for ( auto& pair : container ) {
if ( pair.second.empty() ) continue; if ( pair.second.empty() ) continue;
auto queueEnum = this->getType() == "Compute" ? QueueEnum::COMPUTE : QueueEnum::GRAPHICS;
VkQueue queue = device->getQueue( queueEnum, pair.first ); VkQueue queue = device->getQueue( queueEnum, pair.first );
VkResult res = vkWaitForFences( *device, fences.size(), fences.data(), VK_TRUE, VK_DEFAULT_FENCE_TIMEOUT ); VkResult res = vkWaitForFences( *device, fences.size(), fences.data(), VK_TRUE, VK_DEFAULT_FENCE_TIMEOUT );
VK_CHECK_QUEUE_CHECKPOINT( queue, res ); VK_CHECK_QUEUE_CHECKPOINT( queue, res );
@ -299,7 +299,6 @@ void ext::vulkan::RenderMode::cleanupCommands( std::thread::id id ) {
if ( pair.first == id ) continue; if ( pair.first == id ) continue;
if ( pair.second.empty() ) continue; if ( pair.second.empty() ) continue;
auto queueEnum = this->getType() == "Compute" ? QueueEnum::COMPUTE : QueueEnum::GRAPHICS;
VkQueue queue = device->getQueue( queueEnum, pair.first ); VkQueue queue = device->getQueue( queueEnum, pair.first );
VkResult res = vkWaitForFences( *device, fences.size(), fences.data(), VK_TRUE, VK_DEFAULT_FENCE_TIMEOUT ); VkResult res = vkWaitForFences( *device, fences.size(), fences.data(), VK_TRUE, VK_DEFAULT_FENCE_TIMEOUT );
VK_CHECK_QUEUE_CHECKPOINT( queue, res ); VK_CHECK_QUEUE_CHECKPOINT( queue, res );
@ -319,8 +318,6 @@ void ext::vulkan::RenderMode::createCommandBuffers( const uf::stl::vector<ext::v
} }
void ext::vulkan::RenderMode::bindPipelines() { void ext::vulkan::RenderMode::bindPipelines() {
this->execute = true;
uf::stl::vector<ext::vulkan::Graphic*> graphics; uf::stl::vector<ext::vulkan::Graphic*> graphics;
auto& scene = uf::scene::getCurrentScene(); auto& scene = uf::scene::getCurrentScene();
auto/*&*/ graph = scene.getGraph(); auto/*&*/ graph = scene.getGraph();
@ -333,6 +330,7 @@ void ext::vulkan::RenderMode::bindPipelines() {
} }
this->synchronize(); this->synchronize();
this->bindPipelines( graphics ); this->bindPipelines( graphics );
this->execute = true;
} }
void ext::vulkan::RenderMode::bindPipelines( const uf::stl::vector<ext::vulkan::Graphic*>& graphics ) { void ext::vulkan::RenderMode::bindPipelines( const uf::stl::vector<ext::vulkan::Graphic*>& graphics ) {
//lockMutex(); //lockMutex();
@ -382,14 +380,16 @@ void ext::vulkan::RenderMode::initialize( Device& device ) {
// this->width = 0; //ext::vulkan::width; // this->width = 0; //ext::vulkan::width;
// this->height = 0; //ext::vulkan::height; // this->height = 0; //ext::vulkan::height;
if ( this->scale == 0 ) this->scale = 1; if ( this->scale == 0 ) this->scale = 1;
{ {
if ( this->width > 0 ) renderTarget.width = this->width; if ( this->width > 0 ) renderTarget.width = this->width;
if ( this->height > 0 ) renderTarget.height = this->height; if ( this->height > 0 ) renderTarget.height = this->height;
if ( this->scale > 0 ) renderTarget.scale = this->scale; if ( this->scale > 0 ) renderTarget.scale = this->scale;
} }
// set enum type
this->queueEnum = this->getType() == "Compute" ? QueueEnum::COMPUTE : QueueEnum::GRAPHICS;
// Set sync objects // Set sync objects
{ {
// Fences (Used to check draw command buffer completion) // Fences (Used to check draw command buffer completion)
@ -403,7 +403,8 @@ void ext::vulkan::RenderMode::initialize( Device& device ) {
VK_REGISTER_HANDLE( fence ); VK_REGISTER_HANDLE( fence );
} }
// Set sync objects // Set sync objects
{ for ( auto i = 0; i < ext::vulkan::swapchain.buffers; ++i ) {
auto& renderCompleteSemaphore = renderCompleteSemaphores.emplace_back();
// Semaphores (Used for correct command ordering) // Semaphores (Used for correct command ordering)
VkSemaphoreCreateInfo semaphoreCreateInfo = {}; VkSemaphoreCreateInfo semaphoreCreateInfo = {};
semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
@ -425,8 +426,10 @@ void ext::vulkan::RenderMode::initialize( Device& device ) {
void ext::vulkan::RenderMode::tick() { void ext::vulkan::RenderMode::tick() {
if ( ext::vulkan::states::resized || uf::renderer::states::rebuild || rebuild ) { if ( ext::vulkan::states::resized || uf::renderer::states::rebuild || rebuild ) {
if ( device ) vkDeviceWaitIdle(*device);
cleanupAllCommands(); cleanupAllCommands();
} }
this->synchronize(); this->synchronize();
if ( metadata.limiter.frequency > 0 ) { if ( metadata.limiter.frequency > 0 ) {
@ -445,26 +448,28 @@ void ext::vulkan::RenderMode::render() {
} }
void ext::vulkan::RenderMode::destroy() { void ext::vulkan::RenderMode::destroy() {
if ( device ) vkDeviceWaitIdle(*device);
this->synchronize(); this->synchronize();
renderTarget.destroy(); renderTarget.destroy();
for ( auto& pair : this->commands.container() ) { for ( auto& pair : this->commands.container() ) {
if ( !pair.second.empty() ) { if ( !pair.second.empty() ) {
vkFreeCommandBuffers( *device, device->getCommandPool(this->getType() == "Compute" ? QueueEnum::COMPUTE : QueueEnum::GRAPHICS, pair.first), static_cast<uint32_t>(pair.second.size()), pair.second.data()); vkFreeCommandBuffers( *device, device->getCommandPool(this->queueEnum, pair.first), static_cast<uint32_t>(pair.second.size()), pair.second.data());
} }
pair.second.clear(); pair.second.clear();
} }
if ( renderCompleteSemaphore != VK_NULL_HANDLE ) { for ( auto& renderCompleteSemaphore : renderCompleteSemaphores ) {
vkDestroySemaphore( *device, renderCompleteSemaphore, nullptr); vkDestroySemaphore( *device, renderCompleteSemaphore, nullptr);
VK_UNREGISTER_HANDLE( renderCompleteSemaphore ); VK_UNREGISTER_HANDLE( renderCompleteSemaphore );
renderCompleteSemaphore = VK_NULL_HANDLE;
} }
for ( auto& fence : fences ) { for ( auto& fence : fences ) {
vkDestroyFence( *device, fence, nullptr); vkDestroyFence( *device, fence, nullptr);
VK_UNREGISTER_HANDLE( fence ); VK_UNREGISTER_HANDLE( fence );
} }
renderCompleteSemaphores.clear();
fences.clear(); fences.clear();
blitter.destroy(); blitter.destroy();
ext::vulkan::Buffers::destroy(); ext::vulkan::Buffers::destroy();
@ -474,9 +479,9 @@ void ext::vulkan::RenderMode::synchronize( uint64_t timeout ) {
if ( fences.empty() ) return; if ( fences.empty() ) return;
lockMutex(); lockMutex();
auto queueEnum = this->getType() == "Compute" ? QueueEnum::COMPUTE : QueueEnum::GRAPHICS;
VkQueue queue = device->getQueue( queueEnum, this->mostRecentCommandPoolId ); VkQueue queue = device->getQueue( queueEnum, this->mostRecentCommandPoolId );
VkResult res = vkWaitForFences( *device, fences.size(), fences.data(), VK_TRUE, timeout ); VkResult res = vkWaitForFences( *device, fences.size(), fences.data(), VK_TRUE, timeout );
// VkResult res = vkWaitForFences(*device, 1, &fences[states::currentBuffer], VK_TRUE, timeout);
VK_CHECK_QUEUE_CHECKPOINT( queue, res ); VK_CHECK_QUEUE_CHECKPOINT( queue, res );
unlockMutex(); unlockMutex();

View File

@ -90,10 +90,10 @@ void ext::vulkan::BaseRenderMode::createCommandBuffers( const uf::stl::vector<ex
scissor.offset.x = 0; scissor.offset.x = 0;
scissor.offset.y = 0; scissor.offset.y = 0;
for (size_t i = 0; i < commands.size(); ++i) { for (size_t frame = 0; frame < commands.size(); ++frame) {
auto& commandBuffer = commands[i]; auto& commandBuffer = commands[frame];
renderPassBeginInfo.framebuffer = renderTarget.framebuffers[i]; renderPassBeginInfo.framebuffer = renderTarget.framebuffers[frame];
VK_CHECK_RESULT(vkBeginCommandBuffer(commandBuffer, &cmdBufInfo)); VK_CHECK_RESULT(vkBeginCommandBuffer(commandBuffer, &cmdBufInfo));
@ -106,34 +106,33 @@ void ext::vulkan::BaseRenderMode::createCommandBuffers( const uf::stl::vector<ex
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
imageMemoryBarrier.srcAccessMask = 0; imageMemoryBarrier.srcAccessMask = 0;
imageMemoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; imageMemoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
imageMemoryBarrier.oldLayout = renderTarget.attachments[i].descriptor.layout; imageMemoryBarrier.oldLayout = renderTarget.attachments[frame].descriptor.layout;
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
// explicitly transfer queue-ownership // explicitly transfer queue-ownership
if ( ext::vulkan::device.queueFamilyIndices.graphics != ext::vulkan::device.queueFamilyIndices.present ) { if ( ext::vulkan::device.queueFamilyIndices.graphics != ext::vulkan::device.queueFamilyIndices.present ) {
imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; // ext::vulkan::device.queueFamilyIndices.present;
imageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; // ext::vulkan::device.queueFamilyIndices.graphics;
} else {
imageMemoryBarrier.srcQueueFamilyIndex = ext::vulkan::device.queueFamilyIndices.present; imageMemoryBarrier.srcQueueFamilyIndex = ext::vulkan::device.queueFamilyIndices.present;
imageMemoryBarrier.dstQueueFamilyIndex = ext::vulkan::device.queueFamilyIndices.graphics; imageMemoryBarrier.dstQueueFamilyIndex = ext::vulkan::device.queueFamilyIndices.graphics;
} else {
imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; // ext::vulkan::device.queueFamilyIndices.present;
imageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; // ext::vulkan::device.queueFamilyIndices.graphics;
} }
imageMemoryBarrier.image = renderTarget.attachments[i].image; imageMemoryBarrier.image = renderTarget.attachments[frame].image;
imageMemoryBarrier.subresourceRange.baseMipLevel = 0; imageMemoryBarrier.subresourceRange.baseMipLevel = 0;
imageMemoryBarrier.subresourceRange.levelCount = 1; imageMemoryBarrier.subresourceRange.levelCount = 1;
imageMemoryBarrier.subresourceRange.baseArrayLayer = 0; imageMemoryBarrier.subresourceRange.baseArrayLayer = 0;
imageMemoryBarrier.subresourceRange.layerCount = 1; imageMemoryBarrier.subresourceRange.layerCount = 1;
imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
renderTarget.attachments[i].descriptor.layout = imageMemoryBarrier.newLayout; renderTarget.attachments[frame].descriptor.layout = imageMemoryBarrier.newLayout;
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "setImageLayout" ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "setImageLayout" );
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
} }
// pre-renderpass commands // pre-renderpass commands
if ( commandBufferCallbacks.count(CALLBACK_BEGIN) > 0 ) { VK_COMMAND_BUFFER_CALLBACK( CALLBACK_BEGIN, commandBuffer, frame, {
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "callback[begin]" ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "callback[begin]" );
commandBufferCallbacks[CALLBACK_BEGIN]( commandBuffer, i ); } );
}
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::BEGIN, "renderPass[begin]" ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::BEGIN, "renderPass[begin]" );
vkCmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); vkCmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
@ -151,17 +150,16 @@ void ext::vulkan::BaseRenderMode::createCommandBuffers( const uf::stl::vector<ex
if ( !blitter.initialized || !blitter.process || blitter.descriptor.subpass != currentPass || blitter.descriptor.renderMode != this->getName() ) continue; if ( !blitter.initialized || !blitter.process || blitter.descriptor.subpass != currentPass || blitter.descriptor.renderMode != this->getName() ) continue;
ext::vulkan::GraphicDescriptor descriptor = blitter.descriptor; // bindGraphicDescriptor(blitter.descriptor, currentSubpass); ext::vulkan::GraphicDescriptor descriptor = blitter.descriptor; // bindGraphicDescriptor(blitter.descriptor, currentSubpass);
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, ::fmt::format("blitter[{}: {}]", layer->getName(), layer->getType()) ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, ::fmt::format("blitter[{}: {}]", layer->getName(), layer->getType()) );
blitter.record(commandBuffer, descriptor); blitter.record(commandBuffer, descriptor, 0, 0, frame);
} }
} }
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::END, "renderPass[end]" ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::END, "renderPass[end]" );
vkCmdEndRenderPass(commandBuffer); vkCmdEndRenderPass(commandBuffer);
// post-renderpass commands // post-renderpass commands
if ( commandBufferCallbacks.count(CALLBACK_END) > 0 ) { VK_COMMAND_BUFFER_CALLBACK( CALLBACK_END, commandBuffer, frame, {
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "callback[end]" ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "callback[end]" );
commandBufferCallbacks[CALLBACK_END]( commandBuffer, i ); } );
}
// need to transfer it back, if they differ // need to transfer it back, if they differ
if ( ext::vulkan::device.queueFamilyIndices.graphics != ext::vulkan::device.queueFamilyIndices.present ) { if ( ext::vulkan::device.queueFamilyIndices.graphics != ext::vulkan::device.queueFamilyIndices.present ) {
@ -169,18 +167,18 @@ void ext::vulkan::BaseRenderMode::createCommandBuffers( const uf::stl::vector<ex
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
imageMemoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; imageMemoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
imageMemoryBarrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; imageMemoryBarrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
imageMemoryBarrier.oldLayout = renderTarget.attachments[i].descriptor.layout; imageMemoryBarrier.oldLayout = renderTarget.attachments[frame].descriptor.layout;
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
imageMemoryBarrier.srcQueueFamilyIndex = ext::vulkan::device.queueFamilyIndices.graphics; imageMemoryBarrier.srcQueueFamilyIndex = ext::vulkan::device.queueFamilyIndices.graphics;
imageMemoryBarrier.dstQueueFamilyIndex = ext::vulkan::device.queueFamilyIndices.present; imageMemoryBarrier.dstQueueFamilyIndex = ext::vulkan::device.queueFamilyIndices.present;
imageMemoryBarrier.image = renderTarget.attachments[i].image; imageMemoryBarrier.image = renderTarget.attachments[frame].image;
imageMemoryBarrier.subresourceRange.baseMipLevel = 0; imageMemoryBarrier.subresourceRange.baseMipLevel = 0;
imageMemoryBarrier.subresourceRange.levelCount = 1; imageMemoryBarrier.subresourceRange.levelCount = 1;
imageMemoryBarrier.subresourceRange.baseArrayLayer = 0; imageMemoryBarrier.subresourceRange.baseArrayLayer = 0;
imageMemoryBarrier.subresourceRange.layerCount = 1; imageMemoryBarrier.subresourceRange.layerCount = 1;
imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
renderTarget.attachments[i].descriptor.layout = imageMemoryBarrier.newLayout; renderTarget.attachments[frame].descriptor.layout = imageMemoryBarrier.newLayout;
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "setImageLayout" ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "setImageLayout" );
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
@ -205,10 +203,13 @@ void ext::vulkan::BaseRenderMode::render() {
// if ( ext::vulkan::renderModes.size() > 1 ) return; // if ( ext::vulkan::renderModes.size() > 1 ) return;
// if ( ext::vulkan::renderModes.back() != this ) return; // if ( ext::vulkan::renderModes.back() != this ) return;
if ( this->commands.container().empty() ) return;
//lockMutex( this->mostRecentCommandPoolId ); //lockMutex( this->mostRecentCommandPoolId );
auto& commands = getCommands( this->mostRecentCommandPoolId ); auto& commands = getCommands( this->mostRecentCommandPoolId );
// Get next image in the swap chain (back/front buffer) // Get next image in the swap chain (back/front buffer)
VK_CHECK_RESULT(swapchain.acquireNextImage(&states::currentBuffer, swapchain.presentCompleteSemaphore)); VK_CHECK_RESULT(swapchain.acquireNextImage(&states::currentBuffer, swapchain.presentCompleteSemaphores[0]));
// Use a fence to wait until the command buffer has finished execution before using it again // Use a fence to wait until the command buffer has finished execution before using it again
VK_CHECK_RESULT(vkWaitForFences(*device, 1, &fences[states::currentBuffer], VK_TRUE, VK_DEFAULT_FENCE_TIMEOUT)); VK_CHECK_RESULT(vkWaitForFences(*device, 1, &fences[states::currentBuffer], VK_TRUE, VK_DEFAULT_FENCE_TIMEOUT));
@ -219,32 +220,39 @@ void ext::vulkan::BaseRenderMode::render() {
// The submit info structure specifices a command buffer queue submission batch // The submit info structure specifices a command buffer queue submission batch
VkSubmitInfo submitInfo = {}; VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.pWaitDstStageMask = waitStageMask; // Pointer to the list of pipeline stages that the semaphore waits will occur at submitInfo.pWaitDstStageMask = waitStageMask; // Pointer to the list of pipeline stages that the semaphore waits will occur at
submitInfo.pWaitSemaphores = &swapchain.presentCompleteSemaphore; // Semaphore(s) to wait upon before the submitted command buffer starts executing submitInfo.pWaitSemaphores = &swapchain.presentCompleteSemaphores[0]; // Semaphore(s) to wait upon before the submitted command buffer starts executing
submitInfo.waitSemaphoreCount = 1; // One wait semaphore submitInfo.waitSemaphoreCount = 1; // One wait semaphore
submitInfo.pSignalSemaphores = &renderCompleteSemaphore; // Semaphore(s) to be signaled when command buffers have completed submitInfo.pSignalSemaphores = &renderCompleteSemaphores[states::currentBuffer]; // Semaphore(s) to be signaled when command buffers have completed
submitInfo.signalSemaphoreCount = 1; // One signal semaphore submitInfo.signalSemaphoreCount = 1; // One signal semaphore
submitInfo.pCommandBuffers = &commands[states::currentBuffer]; // Command buffers(s) to execute in this batch (submission) submitInfo.pCommandBuffers = &commands[states::currentBuffer]; // Command buffers(s) to execute in this batch (submission)
submitInfo.commandBufferCount = 1; submitInfo.commandBufferCount = 1;
// Submit to the graphics queue passing a wait fence // Submit to the graphics queue passing a wait fence
// VK_CHECK_RESULT(vkQueueSubmit( device->getQueue( QueueEnum::GRAPHICS ), 1, &submitInfo, fences[states::currentBuffer])); #if 1
VK_CHECK_RESULT(vkQueueSubmit( device->getQueue( QueueEnum::GRAPHICS ), 1, &submitInfo, fences[states::currentBuffer]));
#else
{ {
VkQueue queue = device->getQueue( QueueEnum::GRAPHICS ); VkQueue queue = device->getQueue( QueueEnum::GRAPHICS );
VkResult res = vkQueueSubmit( queue, 1, &submitInfo, fences[states::currentBuffer]); VkResult res = vkQueueSubmit( queue, 1, &submitInfo, fences[states::currentBuffer]);
VK_CHECK_QUEUE_CHECKPOINT( queue, res ); VK_CHECK_QUEUE_CHECKPOINT( queue, res );
} }
#endif
// Present the current buffer to the swap chain // Present the current buffer to the swap chain
// Pass the semaphore signaled by the command buffer submission from the submit info as the wait semaphore for swap chain presentation // Pass the semaphore signaled by the command buffer submission from the submit info as the wait semaphore for swap chain presentation
// This ensures that the image is not presented to the windowing system until all commands have been submitted // This ensures that the image is not presented to the windowing system until all commands have been submitted
VK_CHECK_RESULT(swapchain.queuePresent(device->getQueue( QueueEnum::PRESENT ), states::currentBuffer, renderCompleteSemaphore)); VK_CHECK_RESULT(swapchain.queuePresent(device->getQueue( QueueEnum::PRESENT ), states::currentBuffer, renderCompleteSemaphores[states::currentBuffer]));
// VK_CHECK_RESULT(vkQueueWaitIdle(device->getQueue( QueueEnum::PRESENT )));
#if 1
//VK_CHECK_RESULT(vkQueueWaitIdle(device->getQueue( QueueEnum::PRESENT )));
#else
{ {
VkQueue queue = device->getQueue( QueueEnum::PRESENT ); VkQueue queue = device->getQueue( QueueEnum::PRESENT );
VkResult res = vkQueueWaitIdle(device->getQueue( QueueEnum::PRESENT )); VkResult res = vkQueueWaitIdle(device->getQueue( QueueEnum::PRESENT ));
VK_CHECK_QUEUE_CHECKPOINT( queue, res ); VK_CHECK_QUEUE_CHECKPOINT( queue, res );
} }
#endif
this->executed = true; this->executed = true;
@ -276,7 +284,7 @@ void ext::vulkan::BaseRenderMode::initialize( Device& device ) {
// uint32_t height = windowSize.y; //this->height > 0 ? this->height : windowSize.y; // uint32_t height = windowSize.y; //this->height > 0 ? this->height : windowSize.y;
size_t attachmentIndex = 0; size_t attachmentIndex = 0;
for ( size_t i = 0; i < ext::vulkan::swapchain.buffers; ++i ) { for ( size_t frame = 0; frame < ext::vulkan::swapchain.buffers; ++frame ) {
VkImageViewCreateInfo colorAttachmentView = {}; VkImageViewCreateInfo colorAttachmentView = {};
colorAttachmentView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; colorAttachmentView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
colorAttachmentView.pNext = NULL; colorAttachmentView.pNext = NULL;
@ -294,20 +302,20 @@ void ext::vulkan::BaseRenderMode::initialize( Device& device ) {
colorAttachmentView.subresourceRange.layerCount = 1; colorAttachmentView.subresourceRange.layerCount = 1;
colorAttachmentView.viewType = VK_IMAGE_VIEW_TYPE_2D; colorAttachmentView.viewType = VK_IMAGE_VIEW_TYPE_2D;
colorAttachmentView.flags = 0; colorAttachmentView.flags = 0;
colorAttachmentView.image = images[i]; colorAttachmentView.image = images[frame];
VK_CHECK_RESULT(vkCreateImageView( device, &colorAttachmentView, nullptr, &renderTarget.attachments[i].view)); VK_CHECK_RESULT(vkCreateImageView( device, &colorAttachmentView, nullptr, &renderTarget.attachments[frame].view));
VK_REGISTER_HANDLE( renderTarget.attachments[i].view ); VK_REGISTER_HANDLE( renderTarget.attachments[frame].view );
renderTarget.attachments[i].descriptor.format = ext::vulkan::settings::formats::color; renderTarget.attachments[frame].descriptor.format = ext::vulkan::settings::formats::color;
// renderTarget.attachments[i].descriptor.layout = VK_IMAGE_LAYOUT_UNDEFINED; // renderTarget.attachments[frame].descriptor.layout = VK_IMAGE_LAYOUT_UNDEFINED;
renderTarget.attachments[i].descriptor.layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; renderTarget.attachments[frame].descriptor.layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
renderTarget.attachments[i].descriptor.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; renderTarget.attachments[frame].descriptor.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
renderTarget.attachments[i].descriptor.aliased = true; renderTarget.attachments[frame].descriptor.aliased = true;
renderTarget.attachments[i].image = images[i]; renderTarget.attachments[frame].image = images[frame];
renderTarget.attachments[i].mem = VK_NULL_HANDLE; renderTarget.attachments[frame].mem = VK_NULL_HANDLE;
metadata.attachments["color["+std::to_string((int) i)+"]"] = attachmentIndex++; metadata.attachments["color["+std::to_string((int) frame)+"]"] = attachmentIndex++;
} }
{ {
// Create depth // Create depth
@ -523,10 +531,10 @@ void ext::vulkan::BaseRenderMode::initialize( Device& device ) {
{ {
// Create a frame buffer for every image in the swapchain // Create a frame buffer for every image in the swapchain
renderTarget.framebuffers.resize(images.size()); renderTarget.framebuffers.resize(images.size());
for (size_t i = 0; i < renderTarget.framebuffers.size(); i++) for (size_t frame = 0; frame < renderTarget.framebuffers.size(); frame++)
{ {
std::array<VkImageView, 2> attachments; std::array<VkImageView, 2> attachments;
attachments[0] = renderTarget.attachments[i].view; // Color attachment is the view of the swapchain image attachments[0] = renderTarget.attachments[frame].view; // Color attachment is the view of the swapchain image
attachments[1] = renderTarget.attachments[metadata.attachments["depth"]].view; // Depth/Stencil attachment is the same for all frame buffers attachments[1] = renderTarget.attachments[metadata.attachments["depth"]].view; // Depth/Stencil attachment is the same for all frame buffers
VkFramebufferCreateInfo frameBufferCreateInfo = {}; VkFramebufferCreateInfo frameBufferCreateInfo = {};
@ -539,32 +547,32 @@ void ext::vulkan::BaseRenderMode::initialize( Device& device ) {
frameBufferCreateInfo.height = height; frameBufferCreateInfo.height = height;
frameBufferCreateInfo.layers = 1; frameBufferCreateInfo.layers = 1;
// Create the framebuffer // Create the framebuffer
VK_CHECK_RESULT(vkCreateFramebuffer( device, &frameBufferCreateInfo, nullptr, &renderTarget.framebuffers[i])); VK_CHECK_RESULT(vkCreateFramebuffer( device, &frameBufferCreateInfo, nullptr, &renderTarget.framebuffers[frame]));
VK_REGISTER_HANDLE(renderTarget.framebuffers[i]); VK_REGISTER_HANDLE(renderTarget.framebuffers[frame]);
} }
} }
#if 0 #if 0
if ( true ) { if ( true ) {
auto commandBuffer = device.fetchCommandBuffer(uf::renderer::QueueEnum::TRANSFER); auto commandBuffer = device.fetchCommandBuffer(uf::renderer::QueueEnum::TRANSFER);
for ( size_t i = 0; i < images.size(); ++i ) { for ( size_t frame = 0; frame < images.size(); ++frame ) {
VkImageMemoryBarrier imageMemoryBarrier = {}; VkImageMemoryBarrier imageMemoryBarrier = {};
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
imageMemoryBarrier.srcAccessMask = 0; imageMemoryBarrier.srcAccessMask = 0;
imageMemoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; imageMemoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
imageMemoryBarrier.oldLayout = renderTarget.attachments[i].descriptor.layout; imageMemoryBarrier.oldLayout = renderTarget.attachments[frame].descriptor.layout;
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; // ext::vulkan::device.queueFamilyIndices.present; imageMemoryBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; // ext::vulkan::device.queueFamilyIndices.present;
imageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; // ext::vulkan::device.queueFamilyIndices.graphics; imageMemoryBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; // ext::vulkan::device.queueFamilyIndices.graphics;
imageMemoryBarrier.image = renderTarget.attachments[i].image; imageMemoryBarrier.image = renderTarget.attachments[frame].image;
imageMemoryBarrier.subresourceRange.baseMipLevel = 0; imageMemoryBarrier.subresourceRange.baseMipLevel = 0;
imageMemoryBarrier.subresourceRange.levelCount = 1; imageMemoryBarrier.subresourceRange.levelCount = 1;
imageMemoryBarrier.subresourceRange.baseArrayLayer = 0; imageMemoryBarrier.subresourceRange.baseArrayLayer = 0;
imageMemoryBarrier.subresourceRange.layerCount = 1; imageMemoryBarrier.subresourceRange.layerCount = 1;
imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
renderTarget.attachments[i].descriptor.layout = imageMemoryBarrier.newLayout; renderTarget.attachments[frame].descriptor.layout = imageMemoryBarrier.newLayout;
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier); vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
} }
@ -597,15 +605,16 @@ void ext::vulkan::BaseRenderMode::initialize( Device& device ) {
renderTarget.initialize( device ); renderTarget.initialize( device );
*/ */
// Set sync objects // Set sync objects
{ for ( auto i = 0; i < ext::vulkan::swapchain.buffers; ++i ) {
auto& presentCompleteSemaphore = swapchain.presentCompleteSemaphores.emplace_back();
// Semaphores (Used for correct command ordering) // Semaphores (Used for correct command ordering)
VkSemaphoreCreateInfo semaphoreCreateInfo = {}; VkSemaphoreCreateInfo semaphoreCreateInfo = {};
semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
semaphoreCreateInfo.pNext = nullptr; semaphoreCreateInfo.pNext = nullptr;
// Semaphore used to ensures that image presentation is complete before starting to submit again // Semaphore used to ensures that image presentation is complete before starting to submit again
VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &swapchain.presentCompleteSemaphore)); VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &presentCompleteSemaphore));
VK_REGISTER_HANDLE(swapchain.presentCompleteSemaphore); VK_REGISTER_HANDLE(presentCompleteSemaphore);
} }
} }
@ -616,11 +625,11 @@ void ext::vulkan::BaseRenderMode::destroy() {
renderTarget.renderPass = VK_NULL_HANDLE; renderTarget.renderPass = VK_NULL_HANDLE;
} }
for ( uint32_t i = 0; i < renderTarget.framebuffers.size(); i++ ) { for ( uint32_t frame = 0; frame < renderTarget.framebuffers.size(); frame++ ) {
if ( renderTarget.framebuffers[i] != VK_NULL_HANDLE ) { if ( renderTarget.framebuffers[frame] != VK_NULL_HANDLE ) {
vkDestroyFramebuffer( *device, renderTarget.framebuffers[i], nullptr ); vkDestroyFramebuffer( *device, renderTarget.framebuffers[frame], nullptr );
VK_UNREGISTER_HANDLE( renderTarget.framebuffers[i] ); VK_UNREGISTER_HANDLE( renderTarget.framebuffers[frame] );
renderTarget.framebuffers[i] = VK_NULL_HANDLE; renderTarget.framebuffers[frame] = VK_NULL_HANDLE;
} }
} }
for ( auto& attachment : renderTarget.attachments ) { for ( auto& attachment : renderTarget.attachments ) {
@ -650,10 +659,11 @@ void ext::vulkan::BaseRenderMode::destroy() {
ext::vulkan::RenderMode::destroy(); ext::vulkan::RenderMode::destroy();
if ( swapchain.presentCompleteSemaphore != VK_NULL_HANDLE ) { for ( auto& presentCompleteSemaphore : swapchain.presentCompleteSemaphores ) {
vkDestroySemaphore( *device, swapchain.presentCompleteSemaphore, nullptr); vkDestroySemaphore( *device, presentCompleteSemaphore, nullptr);
VK_UNREGISTER_HANDLE( swapchain.presentCompleteSemaphore ); VK_UNREGISTER_HANDLE( presentCompleteSemaphore );
} }
swapchain.presentCompleteSemaphores.clear();
} }
ext::vulkan::GraphicDescriptor ext::vulkan::BaseRenderMode::bindGraphicDescriptor( const ext::vulkan::GraphicDescriptor& reference, size_t pass ) { ext::vulkan::GraphicDescriptor ext::vulkan::BaseRenderMode::bindGraphicDescriptor( const ext::vulkan::GraphicDescriptor& reference, size_t pass ) {

View File

@ -610,26 +610,30 @@ VkSubmitInfo ext::vulkan::DeferredRenderMode::queue() {
// The submit info structure specifices a command buffer queue submission batch // The submit info structure specifices a command buffer queue submission batch
VkSubmitInfo submitInfo = {}; VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.pWaitDstStageMask = waitStageMask; // Pointer to the list of pipeline stages that the semaphore waits will occur at submitInfo.pWaitDstStageMask = waitStageMask; // Pointer to the list of pipeline stages that the semaphore waits will occur at
submitInfo.pWaitSemaphores = &swapchain.presentCompleteSemaphore; // Semaphore(s) to wait upon before the submitted command buffer starts executing submitInfo.pWaitSemaphores = &swapchain.presentCompleteSemaphores[states::currentBuffer]; // Semaphore(s) to wait upon before the submitted command buffer starts executing
submitInfo.waitSemaphoreCount = 1; // One wait semaphore submitInfo.waitSemaphoreCount = 1; // One wait semaphore
submitInfo.pSignalSemaphores = &renderCompleteSemaphore; // Semaphore(s) to be signaled when command buffers have completed submitInfo.pSignalSemaphores = &renderCompleteSemaphores[states::currentBuffer]; // Semaphore(s) to be signaled when command buffers have completed
submitInfo.signalSemaphoreCount = 1; // One signal semaphore submitInfo.signalSemaphoreCount = 1; // One signal semaphore
submitInfo.pCommandBuffers = &commands[states::currentBuffer]; // Command buffers(s) to execute in this batch (submission) submitInfo.pCommandBuffers = &commands[states::currentBuffer]; // Command buffers(s) to execute in this batch (submission)
submitInfo.commandBufferCount = 1; submitInfo.commandBufferCount = 1;
return submitInfo; return submitInfo;
} }
void ext::vulkan::DeferredRenderMode::render() { void ext::vulkan::DeferredRenderMode::render() {
// if ( this->executed ) return; // if ( this->executed ) return;
if ( commandBufferCallbacks.count(EXECUTE_BEGIN) > 0 ) commandBufferCallbacks[EXECUTE_BEGIN]( VkCommandBuffer{}, 0 );
//lockMutex( this->mostRecentCommandPoolId ); //lockMutex( this->mostRecentCommandPoolId );
if ( this->commands.container().empty() ) return;
auto& commands = getCommands( this->mostRecentCommandPoolId ); auto& commands = getCommands( this->mostRecentCommandPoolId );
VK_COMMAND_BUFFER_CALLBACK( EXECUTE_BEGIN, VkCommandBuffer{}, 0, {} );
// Submit commands // Submit commands
// Use a fence to ensure that command buffer has finished executing before using it again // Use a fence to ensure that command buffer has finished executing before using it again
/*
VK_CHECK_RESULT(vkWaitForFences( *device, 1, &fences[states::currentBuffer], VK_TRUE, VK_DEFAULT_FENCE_TIMEOUT )); VK_CHECK_RESULT(vkWaitForFences( *device, 1, &fences[states::currentBuffer], VK_TRUE, VK_DEFAULT_FENCE_TIMEOUT ));
VK_CHECK_RESULT(vkResetFences( *device, 1, &fences[states::currentBuffer] )); VK_CHECK_RESULT(vkResetFences( *device, 1, &fences[states::currentBuffer] ));
*/
VkSubmitInfo submitInfo = {}; VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
@ -643,9 +647,9 @@ void ext::vulkan::DeferredRenderMode::render() {
// VK_CHECK_RESULT(vkQueueSubmit(device->getQueue( QueueEnum::GRAPHICS ), 1, &submitInfo, fences[states::currentBuffer])); // VK_CHECK_RESULT(vkQueueSubmit(device->getQueue( QueueEnum::GRAPHICS ), 1, &submitInfo, fences[states::currentBuffer]));
VkQueue queue = device->getQueue( QueueEnum::GRAPHICS ); VkQueue queue = device->getQueue( QueueEnum::GRAPHICS );
VkResult res = vkQueueSubmit( queue, 1, &submitInfo, fences[states::currentBuffer]); VkResult res = vkQueueSubmit( queue, 1, &submitInfo, VK_NULL_HANDLE/*fences[states::currentBuffer]*/);
VK_CHECK_QUEUE_CHECKPOINT( queue, res ); VK_CHECK_QUEUE_CHECKPOINT( queue, res );
if ( commandBufferCallbacks.count(EXECUTE_END) > 0 ) commandBufferCallbacks[EXECUTE_END]( VkCommandBuffer{}, 0 ); VK_COMMAND_BUFFER_CALLBACK( EXECUTE_END, VkCommandBuffer{}, 0, {} );
this->executed = true; this->executed = true;
//unlockMutex( this->mostRecentCommandPoolId ); //unlockMutex( this->mostRecentCommandPoolId );
@ -701,8 +705,8 @@ void ext::vulkan::DeferredRenderMode::createCommandBuffers( const uf::stl::vecto
} }
} }
bool shouldRecord = true; // ( settings::pipelines::rt && !uf::config["engine"]["scenes"]["rt"]["full"].as<bool>() ) || !settings::pipelines::rt; bool shouldRecord = true; // ( settings::pipelines::rt && !uf::config["engine"]["scenes"]["rt"]["full"].as<bool>() ) || !settings::pipelines::rt;
for (size_t i = 0; i < commands.size(); ++i) { for (size_t frame = 0; frame < commands.size(); ++frame) {
auto commandBuffer = commands[i]; auto commandBuffer = commands[frame];
VK_CHECK_RESULT( vkBeginCommandBuffer(commandBuffer, &cmdBufInfo) ); VK_CHECK_RESULT( vkBeginCommandBuffer(commandBuffer, &cmdBufInfo) );
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::BEGIN, "begin" ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::BEGIN, "begin" );
@ -718,7 +722,7 @@ void ext::vulkan::DeferredRenderMode::createCommandBuffers( const uf::stl::vecto
renderPassBeginInfo.clearValueCount = clearValues.size(); renderPassBeginInfo.clearValueCount = clearValues.size();
renderPassBeginInfo.pClearValues = &clearValues[0]; renderPassBeginInfo.pClearValues = &clearValues[0];
renderPassBeginInfo.renderPass = renderTarget.renderPass; renderPassBeginInfo.renderPass = renderTarget.renderPass;
renderPassBeginInfo.framebuffer = renderTarget.framebuffers[i]; renderPassBeginInfo.framebuffer = renderTarget.framebuffers[frame];
// Update dynamic viewport state // Update dynamic viewport state
VkViewport viewport = {}; VkViewport viewport = {};
@ -780,15 +784,14 @@ void ext::vulkan::DeferredRenderMode::createCommandBuffers( const uf::stl::vecto
descriptor.bind.point = VK_PIPELINE_BIND_POINT_COMPUTE; descriptor.bind.point = VK_PIPELINE_BIND_POINT_COMPUTE;
} }
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, ::fmt::format("graphic[{}]", pipeline) ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, ::fmt::format("graphic[{}]", pipeline) );
graphic->record( commandBuffer, descriptor, 0, metadata.eyes ); graphic->record( commandBuffer, descriptor, 0, metadata.eyes, frame );
} }
} }
// pre-renderpass commands // pre-renderpass commands
if ( commandBufferCallbacks.count(CALLBACK_BEGIN) > 0 ) { VK_COMMAND_BUFFER_CALLBACK( CALLBACK_BEGIN, commandBuffer, frame, {
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "callback[begin]" ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "callback[begin]" );
commandBufferCallbacks[CALLBACK_BEGIN]( commandBuffer, i ); } );
}
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::BEGIN, "renderPass[begin]" ) ; device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::BEGIN, "renderPass[begin]" ) ;
vkCmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); vkCmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
@ -803,7 +806,7 @@ void ext::vulkan::DeferredRenderMode::createCommandBuffers( const uf::stl::vecto
if ( graphic->descriptor.renderMode != this->getName() ) continue; if ( graphic->descriptor.renderMode != this->getName() ) continue;
ext::vulkan::GraphicDescriptor descriptor = bindGraphicDescriptor(graphic->descriptor, currentSubpass); ext::vulkan::GraphicDescriptor descriptor = bindGraphicDescriptor(graphic->descriptor, currentSubpass);
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, ::fmt::format("graphic[{}]", currentDraw) ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, ::fmt::format("graphic[{}]", currentDraw) );
graphic->record( commandBuffer, descriptor, eye, currentDraw++ ); graphic->record( commandBuffer, descriptor, eye, currentDraw++, frame );
} }
if ( eye + 1 < metadata.eyes ) { if ( eye + 1 < metadata.eyes ) {
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "nextSubpass" ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "nextSubpass" );
@ -821,7 +824,7 @@ void ext::vulkan::DeferredRenderMode::createCommandBuffers( const uf::stl::vecto
descriptor.subpass = currentSubpass; descriptor.subpass = currentSubpass;
descriptor.bind.point = VK_PIPELINE_BIND_POINT_GRAPHICS; descriptor.bind.point = VK_PIPELINE_BIND_POINT_GRAPHICS;
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "deferred" ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "deferred" );
blitter.record(commandBuffer, descriptor, eye, currentDraw++); blitter.record(commandBuffer, descriptor, eye, currentDraw++, frame);
} }
if ( eye + 1 < metadata.eyes ) { if ( eye + 1 < metadata.eyes ) {
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "nextSubpass" ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "nextSubpass" );
@ -849,7 +852,7 @@ void ext::vulkan::DeferredRenderMode::createCommandBuffers( const uf::stl::vecto
// dispatch compute shader // dispatch compute shader
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "deferred" ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "deferred" );
blitter.record(commandBuffer, descriptor, 0, 0); blitter.record(commandBuffer, descriptor, 0, 0, frame);
// transition attachments back to shader read layouts // transition attachments back to shader read layouts
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "setImageLayout" ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "setImageLayout" );
@ -877,15 +880,15 @@ void ext::vulkan::DeferredRenderMode::createCommandBuffers( const uf::stl::vecto
auto& attachmentScratch = this->getAttachment("scratch"); // pingpong auto& attachmentScratch = this->getAttachment("scratch"); // pingpong
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "bloom[1]" ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "bloom[1]" );
blitter.record(commandBuffer, descriptor, 0, 1); blitter.record( commandBuffer, descriptor, 0, 1 );
cmdImageBarrier( commandBuffer, attachmentScratch.image, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL ); cmdImageBarrier( commandBuffer, attachmentScratch.image, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL );
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "bloom[2]" ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "bloom[2]" );
blitter.record(commandBuffer, descriptor, 0, 2); blitter.record( commandBuffer, descriptor, 0, 2 );
cmdImageBarrier( commandBuffer, attachmentBright.image, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL ); cmdImageBarrier( commandBuffer, attachmentBright.image, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL );
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "bloom[3]" ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "bloom[3]" );
blitter.record(commandBuffer, descriptor, 0, 3); blitter.record( commandBuffer, descriptor, 0, 3 );
// transition attachments back to shader read layouts // transition attachments back to shader read layouts
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "setImageLayout" ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "setImageLayout" );
@ -934,7 +937,7 @@ void ext::vulkan::DeferredRenderMode::createCommandBuffers( const uf::stl::vecto
if ( descriptor.bind.width < 1 ) descriptor.bind.width = 1; if ( descriptor.bind.width < 1 ) descriptor.bind.width = 1;
if ( descriptor.bind.height < 1 ) descriptor.bind.height = 1; if ( descriptor.bind.height < 1 ) descriptor.bind.height = 1;
blitter.record(commandBuffer, descriptor, 0, i); blitter.record(commandBuffer, descriptor, 0, i, frame);
vkCmdPipelineBarrier( commandBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_FLAGS_NONE, 1, &memoryBarrier, 0, NULL, 0, NULL ); vkCmdPipelineBarrier( commandBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_FLAGS_NONE, 1, &memoryBarrier, 0, NULL, 0, NULL );
} }
@ -945,10 +948,9 @@ void ext::vulkan::DeferredRenderMode::createCommandBuffers( const uf::stl::vecto
} }
#endif #endif
// post-renderpass commands // post-renderpass commands
if ( commandBufferCallbacks.count(CALLBACK_END) > 0 ) { VK_COMMAND_BUFFER_CALLBACK( CALLBACK_END, commandBuffer, frame, {
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "callback[end]" ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "callback[end]" );
commandBufferCallbacks[CALLBACK_END]( commandBuffer, i ); } );
}
#if 0 #if 0
if ( this->hasAttachment("depth") ) { if ( this->hasAttachment("depth") ) {

View File

@ -419,15 +419,19 @@ void ext::vulkan::RenderTargetRenderMode::destroy() {
void ext::vulkan::RenderTargetRenderMode::render() { void ext::vulkan::RenderTargetRenderMode::render() {
// if ( this->executed ) return; // if ( this->executed ) return;
if ( this->commands.container().empty() ) return;
if ( commandBufferCallbacks.count(EXECUTE_BEGIN) > 0 ) commandBufferCallbacks[EXECUTE_BEGIN]( VkCommandBuffer{}, 0 );
//lockMutex( this->mostRecentCommandPoolId ); //lockMutex( this->mostRecentCommandPoolId );
auto& commands = getCommands( this->mostRecentCommandPoolId ); auto& commands = getCommands( this->mostRecentCommandPoolId );
VK_COMMAND_BUFFER_CALLBACK( EXECUTE_BEGIN, VkCommandBuffer{}, 0, {} );
// Submit commands // Submit commands
// Use a fence to ensure that command buffer has finished executing before using it again // Use a fence to ensure that command buffer has finished executing before using it again
/*
VK_CHECK_RESULT(vkWaitForFences( *device, 1, &fences[states::currentBuffer], VK_TRUE, VK_DEFAULT_FENCE_TIMEOUT )); VK_CHECK_RESULT(vkWaitForFences( *device, 1, &fences[states::currentBuffer], VK_TRUE, VK_DEFAULT_FENCE_TIMEOUT ));
VK_CHECK_RESULT(vkResetFences( *device, 1, &fences[states::currentBuffer] )); VK_CHECK_RESULT(vkResetFences( *device, 1, &fences[states::currentBuffer] ));
*/
VkSubmitInfo submitInfo = {}; VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
@ -441,10 +445,9 @@ void ext::vulkan::RenderTargetRenderMode::render() {
// VK_CHECK_RESULT(vkQueueSubmit(device->getQueue( QueueEnum::GRAPHICS ), 1, &submitInfo, fences[states::currentBuffer])); // VK_CHECK_RESULT(vkQueueSubmit(device->getQueue( QueueEnum::GRAPHICS ), 1, &submitInfo, fences[states::currentBuffer]));
VkQueue queue = device->getQueue( QueueEnum::GRAPHICS ); VkQueue queue = device->getQueue( QueueEnum::GRAPHICS );
VkResult res = vkQueueSubmit( queue, 1, &submitInfo, fences[states::currentBuffer]); VkResult res = vkQueueSubmit( queue, 1, &submitInfo, VK_NULL_HANDLE/*fences[states::currentBuffer]*/);
VK_CHECK_QUEUE_CHECKPOINT( queue, res ); VK_CHECK_QUEUE_CHECKPOINT( queue, res );
VK_COMMAND_BUFFER_CALLBACK( EXECUTE_END, VkCommandBuffer{}, 0, {} );
if ( commandBufferCallbacks.count(EXECUTE_END) > 0 ) commandBufferCallbacks[EXECUTE_END]( VkCommandBuffer{}, 0 );
this->executed = true; this->executed = true;
//unlockMutex( this->mostRecentCommandPoolId ); //unlockMutex( this->mostRecentCommandPoolId );
@ -481,8 +484,8 @@ void ext::vulkan::RenderTargetRenderMode::createCommandBuffers( const uf::stl::v
} }
auto& commands = getCommands(); auto& commands = getCommands();
for (size_t i = 0; i < commands.size(); ++i) { for (size_t frame = 0; frame < commands.size(); ++frame) {
auto& commandBuffer = commands[i]; auto& commandBuffer = commands[frame];
VK_CHECK_RESULT(vkBeginCommandBuffer(commandBuffer, &cmdBufInfo)); VK_CHECK_RESULT(vkBeginCommandBuffer(commandBuffer, &cmdBufInfo));
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::BEGIN, "begin" ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::BEGIN, "begin" );
{ {
@ -497,7 +500,7 @@ void ext::vulkan::RenderTargetRenderMode::createCommandBuffers( const uf::stl::v
renderPassBeginInfo.clearValueCount = clearValues.size(); renderPassBeginInfo.clearValueCount = clearValues.size();
renderPassBeginInfo.pClearValues = &clearValues[0]; renderPassBeginInfo.pClearValues = &clearValues[0];
renderPassBeginInfo.renderPass = renderTarget.renderPass; renderPassBeginInfo.renderPass = renderTarget.renderPass;
renderPassBeginInfo.framebuffer = renderTarget.framebuffers[i]; renderPassBeginInfo.framebuffer = renderTarget.framebuffers[frame];
// Update dynamic viewport state // Update dynamic viewport state
VkViewport viewport = {}; VkViewport viewport = {};
@ -517,7 +520,7 @@ void ext::vulkan::RenderTargetRenderMode::createCommandBuffers( const uf::stl::v
size_t currentPass = 0; size_t currentPass = 0;
// //
// this->pipelineBarrier( commands[i], 1 ); // this->pipelineBarrier( commands[frame], 1 );
// VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL // VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
// VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL // VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
@ -540,10 +543,9 @@ void ext::vulkan::RenderTargetRenderMode::createCommandBuffers( const uf::stl::v
#endif #endif
// pre-renderpass commands // pre-renderpass commands
if ( commandBufferCallbacks.count(CALLBACK_BEGIN) > 0 ) { VK_COMMAND_BUFFER_CALLBACK( CALLBACK_BEGIN, commandBuffer, frame, {
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "callback[begin]" ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "callback[begin]" );
commandBufferCallbacks[CALLBACK_BEGIN]( commandBuffer, i ); } );
}
if ( this->getName() == "Compute" ) { if ( this->getName() == "Compute" ) {
for ( auto graphic : graphics ) { for ( auto graphic : graphics ) {
@ -573,7 +575,7 @@ void ext::vulkan::RenderTargetRenderMode::createCommandBuffers( const uf::stl::v
UF_MSG_DEBUG("Aux pipeline: {}", pipeline); UF_MSG_DEBUG("Aux pipeline: {}", pipeline);
} }
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, ::fmt::format("graphic[{}]", pipeline) ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, ::fmt::format("graphic[{}]", pipeline) );
graphic->record( commandBuffer, descriptor, 0, metadata.type == uf::renderer::settings::pipelines::names::vxgi ? 0 : MIN(subpasses,6) ); graphic->record( commandBuffer, descriptor, 0, metadata.type == uf::renderer::settings::pipelines::names::vxgi ? 0 : MIN(subpasses,6), frame );
} }
} }
@ -587,12 +589,13 @@ void ext::vulkan::RenderTargetRenderMode::createCommandBuffers( const uf::stl::v
if ( graphic->descriptor.renderMode != this->getTarget() ) continue; if ( graphic->descriptor.renderMode != this->getTarget() ) continue;
ext::vulkan::GraphicDescriptor descriptor = bindGraphicDescriptor(graphic->descriptor, currentPass); ext::vulkan::GraphicDescriptor descriptor = bindGraphicDescriptor(graphic->descriptor, currentPass);
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, ::fmt::format("graphic[{}]", currentDraw) ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, ::fmt::format("graphic[{}]", currentDraw) );
graphic->record( commandBuffer, descriptor, currentPass, currentDraw++ ); graphic->record( commandBuffer, descriptor, currentPass, currentDraw++, frame );
} }
if ( commandBufferCallbacks.count( currentPass ) > 0 ) {
commandBufferCallbacks[currentPass]( commandBuffer, i ); VK_COMMAND_BUFFER_CALLBACK( currentPass, commandBuffer, frame, {
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, ::fmt::format("callback[{}]", currentPass) ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, ::fmt::format("callback[{}]", currentPass) );
} } );
if ( currentPass + 1 < subpasses ) { if ( currentPass + 1 < subpasses ) {
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "nextSubpass" ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "nextSubpass" );
vkCmdNextSubpass(commandBuffer, VK_SUBPASS_CONTENTS_INLINE); vkCmdNextSubpass(commandBuffer, VK_SUBPASS_CONTENTS_INLINE);
@ -604,12 +607,11 @@ void ext::vulkan::RenderTargetRenderMode::createCommandBuffers( const uf::stl::v
// post-renderpass commands // post-renderpass commands
if ( commandBufferCallbacks.count(CALLBACK_END) > 0 ) { VK_COMMAND_BUFFER_CALLBACK( CALLBACK_END, commandBuffer, frame, {
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "callback[end]" ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "callback[end]" );
commandBufferCallbacks[CALLBACK_END]( commandBuffer, i ); } );
}
// this->pipelineBarrier( commands[i], 1 ); // this->pipelineBarrier( commands[frame], 1 );
} }
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::END, "end" ); device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::END, "end" );
VK_CHECK_RESULT(vkEndCommandBuffer(commandBuffer)); VK_CHECK_RESULT(vkEndCommandBuffer(commandBuffer));

View File

@ -484,18 +484,17 @@ void ext::vulkan::Shader::initialize( ext::vulkan::Device& device, const uf::stl
etype, etype,
}; };
} break; } break;
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: { case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: {
size_t bufferSize = comp.get_declared_struct_size(base_type); size_t bufferSize = comp.get_declared_struct_size(base_type);
if ( bufferSize <= 0 ) break; if ( bufferSize <= 0 ) break;
if ( bufferSize > device.properties.limits.maxUniformBufferRange ) { if ( bufferSize > device.properties.limits.maxUniformBufferRange ) {
VK_DEBUG_VALIDATION_MESSAGE("Invalid uniform buffer length of " << bufferSize << " for shader " << filename); VK_DEBUG_VALIDATION_MESSAGE("Invalid uniform buffer length of " << bufferSize << " for shader " << filename);
bufferSize = device.properties.limits.maxUniformBufferRange; bufferSize = device.properties.limits.maxUniformBufferRange;
} }
size_t misalignment = bufferSize % device.properties.limits.minStorageBufferOffsetAlignment;
if ( misalignment != 0 ) { bufferSize = ALIGNED_SIZE( bufferSize, device.properties.limits.minUniformBufferOffsetAlignment );
VK_DEBUG_VALIDATION_MESSAGE("Invalid uniform buffer alignment of " << misalignment << " for shader " << filename << ", correcting...");
bufferSize += misalignment;
}
{ {
VK_DEBUG_VALIDATION_MESSAGE("Uniform size of " << bufferSize << " for shader " << filename); VK_DEBUG_VALIDATION_MESSAGE("Uniform size of " << bufferSize << " for shader " << filename);
// auto& uniform = uniforms.emplace_back(); // auto& uniform = uniforms.emplace_back();
@ -518,14 +517,20 @@ void ext::vulkan::Shader::initialize( ext::vulkan::Device& device, const uf::stl
binding, binding,
bufferSize, bufferSize,
}; };
if ( descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC ) {
metadata.dynamicRanges.emplace_back( bufferSize );
}
} break; } break;
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: { case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: {
// generate definition to JSON // generate definition to JSON
#if UF_SHADER_PARSE_AS_JSON #if UF_SHADER_PARSE_AS_JSON
{ {
metadata.json["definitions"]["storage"][name]["name"] = name; metadata.json["definitions"]["storage"][name]["name"] = name;
metadata.json["definitions"]["storage"][name]["index"] = index; metadata.json["definitions"]["storage"][name]["index"] = index;
metadata.json["definitions"]["storage"][name]["binding"] = binding; metadata.json["definitions"]["storage"][name]["binding"] = binding;
// metadata.json["definitions"]["storage"][name]["size"] = bufferSize;
metadata.json["definitions"]["storage"][name]["members"] = parseMembers(resource.type_id); metadata.json["definitions"]["storage"][name]["members"] = parseMembers(resource.type_id);
} }
#endif #endif
@ -567,7 +572,11 @@ void ext::vulkan::Shader::initialize( ext::vulkan::Device& device, const uf::stl
LOOP_RESOURCES( storage_images, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE ); LOOP_RESOURCES( storage_images, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE );
LOOP_RESOURCES( separate_samplers, VK_DESCRIPTOR_TYPE_SAMPLER ); LOOP_RESOURCES( separate_samplers, VK_DESCRIPTOR_TYPE_SAMPLER );
LOOP_RESOURCES( subpass_inputs, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT ); LOOP_RESOURCES( subpass_inputs, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT );
#if VK_UBO_USE_N_BUFFERS
LOOP_RESOURCES( uniform_buffers, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC );
#else
LOOP_RESOURCES( uniform_buffers, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ); LOOP_RESOURCES( uniform_buffers, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER );
#endif
LOOP_RESOURCES( storage_buffers, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER ); LOOP_RESOURCES( storage_buffers, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER );
LOOP_RESOURCES( acceleration_structures, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR ); LOOP_RESOURCES( acceleration_structures, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR );
#undef LOOP_RESOURCES #undef LOOP_RESOURCES

View File

@ -5,10 +5,10 @@
#include <uf/ext/vulkan/initializers.h> #include <uf/ext/vulkan/initializers.h>
#include <uf/utils/window/window.h> #include <uf/utils/window/window.h>
VkResult ext::vulkan::Swapchain::acquireNextImage( uint32_t* imageIndex, VkSemaphore presentCompleteSemaphore ) { VkResult ext::vulkan::Swapchain::acquireNextImage( uint32_t* imageIndex, VkSemaphore presentCompleteSemaphore, VkFence acquireFence ) {
// By setting timeout to UINT64_MAX we will always wait until the next image has been acquired or an actual error is thrown // By setting timeout to UINT64_MAX we will always wait until the next image has been acquired or an actual error is thrown
// With that we don't have to handle VK_NOT_READY // With that we don't have to handle VK_NOT_READY
return vkAcquireNextImageKHR( *device, swapChain, VK_DEFAULT_FENCE_TIMEOUT, presentCompleteSemaphore, (VkFence) nullptr, imageIndex ); return vkAcquireNextImageKHR( *device, swapChain, VK_DEFAULT_FENCE_TIMEOUT, presentCompleteSemaphore, acquireFence, imageIndex );
} }
VkResult ext::vulkan::Swapchain::queuePresent( VkQueue queue, uint32_t imageIndex, VkSemaphore waitSemaphore ) { VkResult ext::vulkan::Swapchain::queuePresent( VkQueue queue, uint32_t imageIndex, VkSemaphore waitSemaphore ) {

View File

@ -515,7 +515,7 @@ void ext::vulkan::Texture::fromBuffers(
vkGetPhysicalDeviceFormatProperties(device.physicalDevice, format, &formatProperties); vkGetPhysicalDeviceFormatProperties(device.physicalDevice, format, &formatProperties);
if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) { if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) {
this->mips = 1; this->mips = 1;
VK_VALIDATION_MESSAGE("Texture image format {} does not support linear blitting", format); // VK_VALIDATION_MESSAGE("Texture image format {} does not support linear blitting", format);
} }
} }

View File

@ -476,7 +476,7 @@ void ext::vulkan::initialize( bool soft ) {
auto tasks = uf::thread::schedule( settings::invariant::multithreadedRecording ); auto tasks = uf::thread::schedule( settings::invariant::multithreadedRecording );
for ( auto& renderMode : renderModes ) { if ( !renderMode ) continue; for ( auto& renderMode : renderModes ) { if ( !renderMode ) continue;
tasks.queue([&]{ tasks.queue([renderMode]{
if ( settings::invariant::individualPipelines ) renderMode->bindPipelines(); if ( settings::invariant::individualPipelines ) renderMode->bindPipelines();
renderMode->createCommandBuffers(); renderMode->createCommandBuffers();
}); });
@ -523,11 +523,11 @@ void ext::vulkan::tick() {
auto tasks = uf::thread::schedule( settings::invariant::multithreadedRecording ); auto tasks = uf::thread::schedule( settings::invariant::multithreadedRecording );
for ( auto& renderMode : renderModes ) { if ( !renderMode || (renderMode->executed && !renderMode->execute) ) continue; for ( auto& renderMode : renderModes ) { if ( !renderMode || (renderMode->executed && !renderMode->execute) ) continue;
if ( ext::vulkan::states::rebuild || renderMode->rebuild ) tasks.queue([&]{ if ( ext::vulkan::states::rebuild || renderMode->rebuild ) tasks.queue([renderMode]{
if ( settings::invariant::individualPipelines ) renderMode->bindPipelines(); if ( settings::invariant::individualPipelines ) renderMode->bindPipelines();
renderMode->createCommandBuffers(); renderMode->createCommandBuffers();
}); });
else if ( renderMode->rerecord ) tasks.queue([&]{ else if ( renderMode->rerecord ) tasks.queue([renderMode]{
renderMode->createCommandBuffers(); renderMode->createCommandBuffers();
}); });
} }
@ -597,9 +597,11 @@ void ext::vulkan::render() {
else submitsGraphics.emplace_back(submitInfo); else submitsGraphics.emplace_back(submitInfo);
renderMode->executed = true; renderMode->executed = true;
// tasks.queue([&]{ // tasks.queue([renderMode]{
ext::vulkan::setCurrentRenderMode(renderMode); ext::vulkan::setCurrentRenderMode(renderMode);
uf::scene::render(); if ( renderMode->getType() != "Swapchain" ) {
uf::scene::render();
}
ext::vulkan::setCurrentRenderMode(NULL); ext::vulkan::setCurrentRenderMode(NULL);
// }); // });
} }
@ -617,9 +619,11 @@ void ext::vulkan::render() {
// stuff we can't batch // stuff we can't batch
for ( auto renderMode : specialRenderModes ) { for ( auto renderMode : specialRenderModes ) {
ext::vulkan::setCurrentRenderMode(renderMode); ext::vulkan::setCurrentRenderMode(renderMode);
uf::scene::render(); if ( renderMode->getType() != "Swapchain" ) {
uf::scene::render();
}
#if UF_USE_FFX_FSR #if UF_USE_FFX_FSR
if ( renderMode->getName() == "Swapchain" && settings::pipelines::fsr && ext::fsr::initialized ) { if ( renderMode->getType() == "Swapchain" && settings::pipelines::fsr && ext::fsr::initialized ) {
ext::fsr::tick(); ext::fsr::tick();
ext::fsr::render(); ext::fsr::render();
} }
@ -637,15 +641,16 @@ void ext::vulkan::render() {
if ( !renderMode || !renderMode->execute || !renderMode->metadata.limiter.execute ) continue; if ( !renderMode || !renderMode->execute || !renderMode->metadata.limiter.execute ) continue;
#if UF_USE_FFX_FSR #if UF_USE_FFX_FSR
if ( renderMode->getName() == "Swapchain" && settings::pipelines::fsr && ext::fsr::initialized ) { if ( renderMode->getType() == "Swapchain" && settings::pipelines::fsr && ext::fsr::initialized ) {
ext::fsr::tick(); ext::fsr::tick();
ext::fsr::render(); ext::fsr::render();
} }
#endif #endif
ext::vulkan::setCurrentRenderMode(renderMode); ext::vulkan::setCurrentRenderMode(renderMode);
uf::graph::render(); if ( renderMode->getType() != "Swapchain" ) {
uf::scene::render(); uf::scene::render();
}
renderMode->render(); renderMode->render();
ext::vulkan::setCurrentRenderMode(NULL); ext::vulkan::setCurrentRenderMode(NULL);

View File

@ -22,8 +22,6 @@ void uf::physics::tick( ) {
return uf::physics::tick( uf::scene::getCurrentScene() ); return uf::physics::tick( uf::scene::getCurrentScene() );
} }
void uf::physics::tick( uf::Object& scene ) { void uf::physics::tick( uf::Object& scene ) {
++uf::physics::time::frame;
uf::physics::time::previous = uf::physics::time::current; uf::physics::time::previous = uf::physics::time::current;
uf::physics::time::current = uf::physics::time::timer.elapsed(); uf::physics::time::current = uf::physics::time::timer.elapsed();
@ -31,7 +29,12 @@ void uf::physics::tick( uf::Object& scene ) {
if ( uf::physics::time::delta > uf::physics::time::clamp ) { if ( uf::physics::time::delta > uf::physics::time::clamp ) {
uf::physics::time::delta = uf::physics::time::clamp; uf::physics::time::delta = uf::physics::time::clamp;
} }
uf::physics::impl::tick( scene, uf::physics::time::delta );
if ( uf::physics::impl::async ) {
uf::thread::queue( "Physics", [&](){ uf::physics::impl::tick( scene, uf::physics::time::delta ); });
} else {
uf::physics::impl::tick( scene, uf::physics::time::delta );
}
} }
void uf::physics::terminate( ) { void uf::physics::terminate( ) {
return uf::physics::terminate( uf::scene::getCurrentScene() ); return uf::physics::terminate( uf::scene::getCurrentScene() );

View File

@ -77,7 +77,6 @@ namespace {
constexpr auto numBins = 16; constexpr auto numBins = 16;
static thread_local Bin bins[numBins]; static thread_local Bin bins[numBins];
for ( auto i = 0; i < numBins; i++ ) bins[i].count = 0;
auto extent = bound.max - bound.min; auto extent = bound.max - bound.min;
auto bestAxis = -1, bestSplit = -1; auto bestAxis = -1, bestSplit = -1;
@ -85,6 +84,10 @@ namespace {
for ( auto axis = 0; axis < 3; ++axis ) { for ( auto axis = 0; axis < 3; ++axis ) {
if ( extent[axis] < EPS(1e-6f) ) continue; if ( extent[axis] < EPS(1e-6f) ) continue;
for ( auto i = 0; i < numBins; i++ ) {
bins[i].count = 0;
bins[i].bounds = {};
}
float minC = bound.min[axis]; float minC = bound.min[axis];
float maxC = bound.max[axis]; float maxC = bound.max[axis];
@ -203,10 +206,10 @@ namespace {
if ( bvh.indices.empty() ) return; // inserted nothing if ( bvh.indices.empty() ) return; // inserted nothing
// recursively build BVH from indices // recursively build BVH from indices
if ( ::useBvhSahBodies ) ::buildBVHNode_SAH( bvh, bounds, 0, bvh.indices.size(), capacity ); if ( uf::physics::impl::settings.useBvhSahBodies ) ::buildBVHNode_SAH( bvh, bounds, 0, bvh.indices.size(), capacity );
else ::buildBVHNode( bvh, bounds, 0, bvh.indices.size(), capacity ); else ::buildBVHNode( bvh, bounds, 0, bvh.indices.size(), capacity );
// flatten if requested // flatten if requested
if ( ::flattenBvhBodies ) ::flattenBVH( bvh, 0 ); if ( uf::physics::impl::settings.flattenBvhBodies ) ::flattenBVH( bvh, 0 );
// mark as clean // mark as clean
bvh.dirty = false; bvh.dirty = false;
@ -245,10 +248,10 @@ namespace {
} }
// recursively build BVH from indices // recursively build BVH from indices
if ( ::useBvhSahMeshes ) ::buildBVHNode_SAH( bvh, bounds, 0, bvh.indices.size(), capacity ); if ( uf::physics::impl::settings.useBvhSahMeshes ) ::buildBVHNode_SAH( bvh, bounds, 0, bvh.indices.size(), capacity );
else ::buildBVHNode( bvh, bounds, 0, bvh.indices.size(), capacity ); else ::buildBVHNode( bvh, bounds, 0, bvh.indices.size(), capacity );
// flatten if requested // flatten if requested
if ( ::flattenBvhMeshes ) ::flattenBVH( bvh, 0 ); if ( uf::physics::impl::settings.flattenBvhMeshes ) ::flattenBVH( bvh, 0 );
// mark as clean // mark as clean
bvh.dirty = false; bvh.dirty = false;
@ -316,7 +319,7 @@ namespace {
// update leaf bounds // update leaf bounds
uf::stl::vector<pod::BVH::index_t> leaves; uf::stl::vector<pod::BVH::index_t> leaves;
leaves.reserve(::reserveCount); leaves.reserve(uf::physics::impl::settings.reserveCount);
for ( auto i = 0; i < bvh.nodes.size(); i++ ) { for ( auto i = 0; i < bvh.nodes.size(); i++ ) {
if ( bvh.nodes[i].getCount() == 0 ) continue; if ( bvh.nodes[i].getCount() == 0 ) continue;
leaves.emplace_back(i); leaves.emplace_back(i);
@ -333,7 +336,7 @@ namespace {
} }
// update internal nodes bottom-up // update internal nodes bottom-up
for ( pod::BVH::index_t i = (pod::BVH::index_t) bvh.nodes.size() - 1; i >= 0; i-- ) { for ( int64_t i = (int64_t) bvh.nodes.size() - 1; i >= 0; i-- ) {
auto& node = bvh.nodes[i]; auto& node = bvh.nodes[i];
auto& bound = bvh.bounds[i]; auto& bound = bvh.bounds[i];
// internal node // internal node
@ -534,7 +537,7 @@ namespace {
if ( !bvh.flattened.empty() ) return ::queryFlatOverlaps( bvh, outPairs ); if ( !bvh.flattened.empty() ) return ::queryFlatOverlaps( bvh, outPairs );
if ( bvh.nodes.empty() ) return; if ( bvh.nodes.empty() ) return;
outPairs.reserve(::reserveCount); outPairs.reserve(uf::physics::impl::settings.reserveCount);
::traverseBVH( bvh, 0, outPairs ); ::traverseBVH( bvh, 0, outPairs );
} }
@ -542,7 +545,7 @@ namespace {
if ( !bvhA.flattened.empty() && !bvhB.flattened.empty() ) return ::queryFlatOverlaps( bvhA, bvhB, outPairs ); if ( !bvhA.flattened.empty() && !bvhB.flattened.empty() ) return ::queryFlatOverlaps( bvhA, bvhB, outPairs );
if ( bvhA.nodes.empty() || bvhB.nodes.empty() ) return; if ( bvhA.nodes.empty() || bvhB.nodes.empty() ) return;
outPairs.reserve(::reserveCount); outPairs.reserve(uf::physics::impl::settings.reserveCount);
::traverseNodePair(bvhA, 0, bvhB, 0, outPairs); ::traverseNodePair(bvhA, 0, bvhB, 0, outPairs);
} }
} }
@ -554,7 +557,7 @@ namespace {
if ( !bvh.flattened.empty() ) return ::queryFlatBVH( bvh, bounds, outIndices ); if ( !bvh.flattened.empty() ) return ::queryFlatBVH( bvh, bounds, outIndices );
outIndices.reserve(::reserveCount); outIndices.reserve(uf::physics::impl::settings.reserveCount);
static thread_local uf::stl::stack<pod::BVH::index_t> stack; static thread_local uf::stl::stack<pod::BVH::index_t> stack;
//stack.clear(); // there is no stack.clear(), and the stack should already be cleared by the end of this function //stack.clear(); // there is no stack.clear(), and the stack should already be cleared by the end of this function
@ -581,7 +584,7 @@ namespace {
void queryBVH( const pod::BVH& bvh, const pod::AABB& bounds, uf::stl::vector<pod::BVH::index_t>& outIndices, pod::BVH::index_t nodeID ) { void queryBVH( const pod::BVH& bvh, const pod::AABB& bounds, uf::stl::vector<pod::BVH::index_t>& outIndices, pod::BVH::index_t nodeID ) {
if ( !bvh.flattened.empty() ) return ::queryFlatBVH( bvh, bounds, outIndices ); if ( !bvh.flattened.empty() ) return ::queryFlatBVH( bvh, bounds, outIndices );
if ( nodeID == 0 ) outIndices.reserve(::reserveCount); if ( nodeID == 0 ) outIndices.reserve(uf::physics::impl::settings.reserveCount);
const auto& node = bvh.nodes[nodeID]; const auto& node = bvh.nodes[nodeID];
if ( node.isAsleep() || !::aabbOverlap( bounds, bvh.bounds[nodeID] ) ) return; if ( node.isAsleep() || !::aabbOverlap( bounds, bvh.bounds[nodeID] ) ) return;
@ -601,7 +604,7 @@ namespace {
if ( !bvh.flattened.empty() ) return ::queryFlatBVH( bvh, ray, outIndices, maxDist ); if ( !bvh.flattened.empty() ) return ::queryFlatBVH( bvh, ray, outIndices, maxDist );
if ( bvh.nodes.empty() ) return; if ( bvh.nodes.empty() ) return;
outIndices.reserve(::reserveCount); outIndices.reserve(uf::physics::impl::settings.reserveCount);
static thread_local uf::stl::stack<pod::BVH::index_t> stack; static thread_local uf::stl::stack<pod::BVH::index_t> stack;
//stack.clear(); // there is no stack.clear(), and the stack should already be cleared by the end of this function //stack.clear(); // there is no stack.clear(), and the stack should already be cleared by the end of this function
@ -627,7 +630,7 @@ namespace {
void queryBVH( const pod::BVH& bvh, const pod::Ray& ray, uf::stl::vector<pod::BVH::index_t>& outIndices, pod::BVH::index_t nodeID, float maxDist ) { void queryBVH( const pod::BVH& bvh, const pod::Ray& ray, uf::stl::vector<pod::BVH::index_t>& outIndices, pod::BVH::index_t nodeID, float maxDist ) {
if ( !bvh.flattened.empty() ) return ::queryFlatBVH( bvh, ray, outIndices, maxDist ); if ( !bvh.flattened.empty() ) return ::queryFlatBVH( bvh, ray, outIndices, maxDist );
if ( nodeID == 0 ) outIndices.reserve(::reserveCount); if ( nodeID == 0 ) outIndices.reserve(uf::physics::impl::settings.reserveCount);
const auto& node = bvh.nodes[nodeID]; const auto& node = bvh.nodes[nodeID];
float tMin, tMax; float tMin, tMax;
@ -649,63 +652,81 @@ namespace {
namespace { namespace {
void queryFlatOverlaps( const pod::BVH& bvh, pod::BVH::pairs_t& outPairs ) { void queryFlatOverlaps( const pod::BVH& bvh, pod::BVH::pairs_t& outPairs ) {
auto& nodes = bvh.flattened; auto& nodes = bvh.flattened;
auto& bounds = bvh.flatBounds;
auto& indices = bvh.indices; auto& indices = bvh.indices;
outPairs.reserve(::reserveCount); if ( nodes.empty() ) return;
outPairs.reserve( uf::physics::impl::settings.reserveCount );
for ( auto i = 0; i < nodes.size(); ++i ) { for ( pod::BVH::index_t a = 0; a < nodes.size(); ++a ) {
const auto& nodeA = nodes[i]; const auto& nodeA = nodes[a];
if ( nodeA.getCount() <= 0 || nodeA.isAsleep() ) continue; if ( nodeA.getCount() <= 0 || nodeA.isAsleep() ) continue;
for ( auto j = i + 1; j < nodes.size(); ++j ) { const auto& boundsA = bounds[a];
const auto& nodeB = nodes[j]; pod::BVH::index_t b = a + 1;
if ( nodeB.getCount() <= 0 || nodeB.isAsleep() ) continue; while ( b < nodes.size() ) {
const auto& nodeB = nodes[b];
if ( !::aabbOverlap( bvh.flatBounds[i], bvh.flatBounds[j] ) ) continue; if ( nodeB.isAsleep() || !::aabbOverlap( boundsA, bounds[b] ) ) {
b = nodeB.skipIndex;
continue;
}
for ( auto ia = 0; ia < nodeA.getCount(); ++ia ) { if ( nodeB.getCount() > 0 ) {
for ( auto ib = 0; ib < nodeB.getCount(); ++ib ) { for ( pod::BVH::index_t ia = 0; ia < nodeA.getCount(); ++ia ) {
auto indexA = indices[nodeA.start + ia]; for ( pod::BVH::index_t ib = 0; ib < nodeB.getCount(); ++ib ) {
auto indexB = indices[nodeB.start + ib]; auto indexA = indices[nodeA.start + ia];
auto indexB = indices[nodeB.start + ib];
if ( indexA == indexB ) continue; if ( indexA == indexB ) continue;
if ( indexA > indexB ) std::swap( indexA, indexB ); if ( indexA > indexB ) std::swap(indexA, indexB);
outPairs.emplace( indexA, indexB ); outPairs.emplace( indexA, indexB );
}
} }
} }
++b;
} }
} }
} }
void queryFlatOverlaps( const pod::BVH& bvhA, const pod::BVH& bvhB, pod::BVH::pairs_t& outPairs ) { void queryFlatOverlaps( const pod::BVH& bvhA, const pod::BVH& bvhB, pod::BVH::pairs_t& outPairs ) {
auto& nodesA = bvhA.flattened; auto& nodesA = bvhA.flattened;
auto& boundsA = bvhA.flatBounds;
auto& indicesA = bvhA.indices; auto& indicesA = bvhA.indices;
auto& nodesB = bvhB.flattened; auto& nodesB = bvhB.flattened;
auto& boundsB = bvhB.flatBounds;
auto& indicesB = bvhB.indices; auto& indicesB = bvhB.indices;
if ( nodesA.empty() || nodesB.empty() ) return; if ( nodesA.empty() || nodesB.empty() ) return;
outPairs.reserve(uf::physics::impl::settings.reserveCount);
outPairs.reserve(::reserveCount);
for ( auto i = 0; i < nodesA.size(); ++i ) { for ( pod::BVH::index_t a = 0; a < nodesA.size(); ++a ) {
const auto& nodeA = nodesA[i]; const auto& nodeA = nodesA[a];
if ( nodeA.getCount() <= 0 || nodeA.isAsleep() ) continue; if ( nodeA.getCount() <= 0 || nodeA.isAsleep() ) continue;
for ( auto j = 0; j < nodesB.size(); ++j ) { const auto& bA = boundsA[a];
const auto& nodeB = nodesB[j];
if ( nodeB.getCount() <= 0 || nodeB.isAsleep() ) continue;
if ( !::aabbOverlap( bvhA.flatBounds[i], bvhB.flatBounds[j] ) ) continue; pod::BVH::index_t b = 0;
while ( b < nodesB.size() ) {
const auto& nodeB = nodesB[b];
for ( auto ia = 0; ia < nodeA.getCount(); ++ia ) { if ( nodeB.isAsleep() || !::aabbOverlap(bA, boundsB[b]) ) {
for (auto ib = 0; ib < nodeB.getCount(); ++ib ) { b = nodeB.skipIndex;
auto indexA = indicesA[nodeA.start + ia]; continue;
auto indexB = indicesB[nodeB.start + ib]; }
outPairs.emplace( indexA, indexB ); if ( nodeB.getCount() > 0 ) {
for ( pod::BVH::index_t ia = 0; ia < nodeA.getCount(); ++ia ) {
for ( pod::BVH::index_t ib = 0; ib < nodeB.getCount(); ++ib ) {
auto indexA = indicesA[nodeA.start + ia];
auto indexB = indicesB[nodeB.start + ib];
outPairs.emplace(indexA, indexB);
}
} }
} }
++b;
} }
} }
} }
@ -714,7 +735,7 @@ namespace {
auto& nodes = bvh.flattened; auto& nodes = bvh.flattened;
auto& indices = bvh.indices; auto& indices = bvh.indices;
outIndices.reserve(::reserveCount); outIndices.reserve(uf::physics::impl::settings.reserveCount);
pod::BVH::index_t idx = 0; pod::BVH::index_t idx = 0;
while ( idx < nodes.size() ) { while ( idx < nodes.size() ) {
@ -738,7 +759,7 @@ namespace {
auto& nodes = bvh.flattened; auto& nodes = bvh.flattened;
auto& indices = bvh.indices; auto& indices = bvh.indices;
outIndices.reserve(::reserveCount); outIndices.reserve(uf::physics::impl::settings.reserveCount);
pod::BVH::index_t idx = 0; pod::BVH::index_t idx = 0;
while ( idx < nodes.size() ) { while ( idx < nodes.size() ) {
@ -802,7 +823,8 @@ namespace {
} }
// map root to island index // map root to island index
uf::stl::unordered_map<pod::BVH::index_t, pod::BVH::index_t> rootToIsland; static thread_local uf::stl::unordered_map<pod::BVH::index_t, pod::BVH::index_t> rootToIsland;
rootToIsland.clear();
islands.clear(); islands.clear();
islands.reserve(bodies.size()); islands.reserve(bodies.size());

View File

@ -61,9 +61,10 @@ namespace {
uint64_t lhs = reinterpret_cast<uint64_t>(&a); uint64_t lhs = reinterpret_cast<uint64_t>(&a);
uint64_t rhs = reinterpret_cast<uint64_t>(&b); uint64_t rhs = reinterpret_cast<uint64_t>(&b);
if (lhs > rhs) std::swap(lhs, rhs); if (lhs > rhs) std::swap(lhs, rhs);
size_t seed = 0;
lhs ^= rhs + 0x9e3779b97f4a7c15 + (lhs << 6) + (lhs >> 2); seed ^= std::hash<uint64_t>{}(lhs) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
return lhs; seed ^= std::hash<uint64_t>{}(rhs) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
return seed;
} }
// marks a body as asleep // marks a body as asleep
@ -78,6 +79,7 @@ namespace {
} }
void updateActivity( pod::PhysicsBody& body, float dt ) { void updateActivity( pod::PhysicsBody& body, float dt ) {
// reset grounded state // reset grounded state
bool wasGrounded = body.activity.grounded;
body.activity.grounded = false; body.activity.grounded = false;
// already asleep // already asleep
@ -91,7 +93,7 @@ namespace {
if ( linSpeed < pod::Activity::linearSleepEpsilon && angSpeed < pod::Activity::angularSleepEpsilon ) { if ( linSpeed < pod::Activity::linearSleepEpsilon && angSpeed < pod::Activity::angularSleepEpsilon ) {
body.activity.sleepTimer += dt; body.activity.sleepTimer += dt;
float threshold = pod::Activity::sleepThreshold; float threshold = pod::Activity::sleepThreshold;
if ( body.activity.grounded ) threshold *= 0.25f; if ( wasGrounded ) threshold *= 0.25f;
if ( body.activity.sleepTimer > threshold ) ::sleepBody( body ); if ( body.activity.sleepTimer > threshold ) ::sleepBody( body );
} }
// body is moving, reset timer // body is moving, reset timer
@ -242,19 +244,24 @@ namespace {
// check against AB // check against AB
float t = std::clamp(uf::vector::dot( ao, ab ) / d00, 0.0f, 1.0f); float t = std::clamp(uf::vector::dot( ao, ab ) / d00, 0.0f, 1.0f);
auto pAB = a + (ab * t); auto pAB = a + (ab * t);
float distAB = uf::vector::dot( pAB, pAB ); float distAB = uf::vector::dot( p - pAB, p - pAB );
//float distAB = uf::vector::dot( pAB, pAB );
// check against AC // check against AC
float t2 = std::clamp(uf::vector::dot( ao, ac ) / d11, 0.0f, 1.0f); float t2 = std::clamp(uf::vector::dot( ao, ac ) / d11, 0.0f, 1.0f);
auto pAC = a + (ac * t2); auto pAC = a + (ac * t2);
float distAC = uf::vector::dot( pAC, pAC ); float distAC = uf::vector::dot( p - pAC, p - pAC );
//float distAC = uf::vector::dot( pAC, pAC );
// check against BC // check against BC
auto bc = c - b; auto bc = c - b;
float d22 = uf::vector::dot( bc, bc ); float d22 = uf::vector::dot( bc, bc );
float t3 = std::clamp(uf::vector::dot( -b, bc ) / d22, 0.0f, 1.0f); float t3 = std::clamp(uf::vector::dot( p - b, bc ) / d22, 0.0f, 1.0f);
//float t3 = std::clamp(uf::vector::dot( -b, bc ) / d22, 0.0f, 1.0f);
auto pBC = b + ( bc * t3 ); auto pBC = b + ( bc * t3 );
float distBC = uf::vector::dot( pBC, pBC ); float distBC = uf::vector::dot( p - pBC, p - pBC );
//float distBC = uf::vector::dot( pBC, pBC );
// pick closest edge/vertex // pick closest edge/vertex
if ( distAB <= distAC && distAB <= distBC ) return { 1.0f - t, t, 0.0f }; if ( distAB <= distAC && distAB <= distBC ) return { 1.0f - t, t, 0.0f };

View File

@ -4,50 +4,6 @@
#include <uf/utils/mesh/mesh.h> #include <uf/utils/mesh/mesh.h>
#include <uf/utils/memory/stack.h> #include <uf/utils/memory/stack.h>
namespace {
bool warmupSolver = true; // cache manifold data to warm up the solver
bool blockContactSolver = true; // use BlockNxN solvers (where N = number of contacts for a manifold)
bool psgContactSolver = true; // use PSG contact solver
bool useGjk = false; // currently don't have a way to broadphase mesh => narrowphase tri via GJK
bool fixedStep = false; // run physics simulation with a fixed delta time (with accumulation), rather than rely on actual engine deltatime
uint32_t substeps = 4; // number of substeps per frame tick
uint32_t reserveCount = 32; // amount of elements to reserve for vectors used in this system, to-do: have it tie to a memory pool allocator
// increasing these make things lag for reasons I can imagine why
uint32_t broadphaseBvhCapacity = 4; // number of bodies per leaf node
uint32_t meshBvhCapacity = 4; // number of triangles per leaf node
// additionally flattens a BVH for linear iteration, rather than a recursive / stack-based traversal
bool flattenBvhBodies = true;
bool flattenBvhMeshes = true;
// use surface area heuristics for building the BVH, rather than naive splits
bool useBvhSahBodies = true; // it actually seems slower to use these......
bool useBvhSahMeshes = true;
bool useSplitBvhs = true; // creates separate BVHs for static / dynamic objects
// to-do: find possibly better values for this
uint32_t solverIterations = 10;
float baumgarteCorrectionPercent = 0.4f;
float baumgarteCorrectionSlop = 0.01f;
uf::stl::unordered_map<size_t, pod::Manifold> manifoldsCache;
uint32_t manifoldCacheLifetime = 6; // to-do: find a good value for this
uint32_t frameCounter = 0;
// to-do: tweak this to not be annoying
pod::BVH::UpdatePolicy bvhUpdatePolicy = {
.displacementThreshold = 0.25f,
.overlapThreshold = 2.0f,
.dirtyRatioThreshold = 0.3f,
.maxFramesBeforeRebuild = 60, // * 10, // 10 seconds
};
float groundedThreshold = 0.7f; // threshold before marking a body as grounded
}
#define EPS(x) 1.0e-6f #define EPS(x) 1.0e-6f
#define EPS2 (EPS(1.0e-6) * EPS(1.0e-6)) #define EPS2 (EPS(1.0e-6) * EPS(1.0e-6))
#define ASSERT_COLLIDER_TYPES( A, B ) UF_ASSERT( a.collider.type == pod::ShapeType::A && b.collider.type == pod::ShapeType::B ); #define ASSERT_COLLIDER_TYPES( A, B ) UF_ASSERT( a.collider.type == pod::ShapeType::A && b.collider.type == pod::ShapeType::B );
@ -67,8 +23,12 @@ namespace {
#include "integration.inl" #include "integration.inl"
#include "solvers.inl" #include "solvers.inl"
// unused, as these are from reactphysics pod::PhysicsSettings uf::physics::impl::settings;
float uf::physics::impl::timescale = 1.0f / 60.0f; float uf::physics::impl::timescale = 1.0f / 60.0f;
bool uf::physics::impl::async = false;
// unused, as these are from reactphysics
bool uf::physics::impl::interpolate = false; bool uf::physics::impl::interpolate = false;
bool uf::physics::impl::shared = false; bool uf::physics::impl::shared = false;
bool uf::physics::impl::globalStorage = false; bool uf::physics::impl::globalStorage = false;
@ -90,9 +50,8 @@ void uf::physics::impl::tick( uf::Object& object, float dt ) {
uf::physics::impl::tick( object.getComponent<pod::World>(), dt ); uf::physics::impl::tick( object.getComponent<pod::World>(), dt );
} }
void uf::physics::impl::tick( pod::World& world, float dt ) { void uf::physics::impl::tick( pod::World& world, float dt ) {
if ( !::fixedStep ) { if ( !uf::physics::impl::settings.fixedStep ) {
if ( uf::physics::impl::settings.substeps > 0 ) uf::physics::impl::substep( world, dt, uf::physics::impl::settings.substeps );
if ( ::substeps > 0 ) uf::physics::impl::substep( world, dt, ::substeps );
else uf::physics::impl::step( world, dt ); else uf::physics::impl::step( world, dt );
return; return;
@ -101,7 +60,7 @@ void uf::physics::impl::tick( pod::World& world, float dt ) {
static float accumulator = 0; static float accumulator = 0;
accumulator += dt; accumulator += dt;
while ( accumulator >= uf::physics::impl::timescale ) { while ( accumulator >= uf::physics::impl::timescale ) {
if ( ::substeps > 0 ) uf::physics::impl::substep( world, uf::physics::impl::timescale, ::substeps ); if ( uf::physics::impl::settings.substeps > 0 ) uf::physics::impl::substep( world, uf::physics::impl::timescale, uf::physics::impl::settings.substeps );
else uf::physics::impl::step( world, uf::physics::impl::timescale ); else uf::physics::impl::step( world, uf::physics::impl::timescale );
accumulator -= uf::physics::impl::timescale; accumulator -= uf::physics::impl::timescale;
} }
@ -130,20 +89,20 @@ void uf::physics::impl::step( pod::World& world, float dt ) {
if ( bodies.empty() ) return; if ( bodies.empty() ) return;
++::frameCounter; ++uf::physics::impl::settings.frameCounter;
for ( auto* body : bodies ) { for ( auto* body : bodies ) {
::integrate( *body, dt ); ::integrate( *body, dt );
} }
// rebuild static bvh if dirty // rebuild static bvh if dirty
if ( staticBvh.dirty && ::useSplitBvhs ) { if ( staticBvh.dirty && uf::physics::impl::settings.useSplitBvhs ) {
::buildBroadphaseBVH( staticBvh, bodies, ::broadphaseBvhCapacity, ::useSplitBvhs, true ); // (re)build ::buildBroadphaseBVH( staticBvh, bodies, uf::physics::impl::settings.broadphaseBvhCapacity, uf::physics::impl::settings.useSplitBvhs, true ); // (re)build
} }
switch ( ::decideBVHUpdate( dynamicBvh, bodies, ::bvhUpdatePolicy, ::frameCounter ) ) { switch ( ::decideBVHUpdate( dynamicBvh, bodies, uf::physics::impl::settings.bvhUpdatePolicy, uf::physics::impl::settings.frameCounter ) ) {
case pod::BVH::UpdatePolicy::Decision::REBUILD: { case pod::BVH::UpdatePolicy::Decision::REBUILD: {
::buildBroadphaseBVH( dynamicBvh, bodies, ::broadphaseBvhCapacity, ::useSplitBvhs, false ); // (re)build ::buildBroadphaseBVH( dynamicBvh, bodies, uf::physics::impl::settings.broadphaseBvhCapacity, uf::physics::impl::settings.useSplitBvhs, false ); // (re)build
} break; } break;
case pod::BVH::UpdatePolicy::Decision::REFIT: { case pod::BVH::UpdatePolicy::Decision::REFIT: {
::refitBVH( dynamicBvh, bodies ); // refit ::refitBVH( dynamicBvh, bodies ); // refit
@ -157,7 +116,7 @@ void uf::physics::impl::step( pod::World& world, float dt ) {
// query for overlaps // query for overlaps
pod::BVH::pairs_t pairs; pod::BVH::pairs_t pairs;
::queryOverlaps( dynamicBvh, pairs ); ::queryOverlaps( dynamicBvh, pairs );
if ( ::useSplitBvhs ) { if ( uf::physics::impl::settings.useSplitBvhs ) {
::queryOverlaps( dynamicBvh, staticBvh, pairs ); ::queryOverlaps( dynamicBvh, staticBvh, pairs );
} }
@ -165,13 +124,14 @@ void uf::physics::impl::step( pod::World& world, float dt ) {
uf::stl::vector<pod::Island> islands; uf::stl::vector<pod::Island> islands;
::buildIslands( pairs, bodies, islands ); ::buildIslands( pairs, bodies, islands );
if ( ::warmupSolver ) ::prepareManifoldCache( ::manifoldsCache, islands, bodies ); if ( uf::physics::impl::settings.warmupSolver ) ::prepareManifoldCache( uf::physics::impl::settings.manifoldsCache, islands, bodies );
// iterate islands // iterate islands
#pragma omp parallel for schedule(dynamic) #pragma omp parallel for schedule(dynamic)
for ( auto& island : islands ) { for ( auto& island : islands ) {
uf::stl::vector<pod::Manifold> manifolds; static thread_local uf::stl::vector<pod::Manifold> manifolds;
manifolds.reserve(::reserveCount); manifolds.clear();
manifolds.reserve(uf::physics::impl::settings.reserveCount);
// sleeping island, skip // sleeping island, skip
if ( !::updateIsland( island, bodies, dt ) ) continue; if ( !::updateIsland( island, bodies, dt ) ) continue;
@ -190,9 +150,9 @@ void uf::physics::impl::step( pod::World& world, float dt ) {
for ( auto& c : manifold.points ) c.normal = ::orientNormalToAB( a, b, c.normal ); for ( auto& c : manifold.points ) c.normal = ::orientNormalToAB( a, b, c.normal );
} }
// retrieve accumulated impulses // retrieve accumulated impulses
if ( ::warmupSolver ) { if ( uf::physics::impl::settings.warmupSolver ) {
auto it = ::manifoldsCache.find( ::makePairKey( a, b ) ); auto it = uf::physics::impl::settings.manifoldsCache.find( ::makePairKey( a, b ) );
if ( it != ::manifoldsCache.end() ) ::retrieveContacts( manifold, it->second ); if ( it != uf::physics::impl::settings.manifoldsCache.end() ) ::retrieveContacts( manifold, it->second );
} }
// merge similar contacts from a mesh to ensure continuity // merge similar contacts from a mesh to ensure continuity
if ( a.collider.type == pod::ShapeType::MESH || b.collider.type == pod::ShapeType::MESH ) { if ( a.collider.type == pod::ShapeType::MESH || b.collider.type == pod::ShapeType::MESH ) {
@ -207,7 +167,7 @@ void uf::physics::impl::step( pod::World& world, float dt ) {
if ( b.activity.awake && !a.activity.awake ) ::wakeBody( a ); if ( b.activity.awake && !a.activity.awake ) ::wakeBody( a );
// mark as grounded // mark as grounded
for ( auto& c : manifold.points ) { for ( auto& c : manifold.points ) {
if ( std::fabs(uf::vector::dot(c.normal, pod::Vector3f{0,1,0})) > ::groundedThreshold ) { if ( std::fabs(uf::vector::dot(c.normal, pod::Vector3f{0,1,0})) > uf::physics::impl::settings.groundedThreshold ) {
// only mark if contact point is below body // only mark if contact point is below body
if ( c.point.y < getPosition(a).y ) a.activity.grounded = true; if ( c.point.y < getPosition(a).y ) a.activity.grounded = true;
if ( c.point.y < getPosition(b).y ) b.activity.grounded = true; if ( c.point.y < getPosition(b).y ) b.activity.grounded = true;
@ -223,12 +183,12 @@ void uf::physics::impl::step( pod::World& world, float dt ) {
// do position correction // do position correction
::solvePositions( manifolds, dt ); ::solvePositions( manifolds, dt );
// cache manifold positions // cache manifold positions
if ( ::warmupSolver ) { if ( uf::physics::impl::settings.warmupSolver ) {
::updateManifoldCache( manifolds, ::manifoldsCache ); ::updateManifoldCache( manifolds, uf::physics::impl::settings.manifoldsCache );
} }
} }
if ( ::warmupSolver ) ::pruneManifoldCache( ::manifoldsCache ); if ( uf::physics::impl::settings.warmupSolver ) ::pruneManifoldCache( uf::physics::impl::settings.manifoldsCache );
for ( auto* b : bodies ) { for ( auto* b : bodies ) {
if ( b->isStatic ) continue; if ( b->isStatic ) continue;
@ -381,7 +341,7 @@ void uf::physics::impl::applyForce( pod::PhysicsBody& body, const pod::Vector3f&
if ( body.isStatic ) return; ::wakeBody( body ); if ( body.isStatic ) return; ::wakeBody( body );
body.forceAccumulator += force; body.forceAccumulator += force;
} }
void uf::physics::impl::applyForceAtPoint( pod::PhysicsBody body, const pod::Vector3f& force, const pod::Vector3f& point ) { void uf::physics::impl::applyForceAtPoint( pod::PhysicsBody& body, const pod::Vector3f& force, const pod::Vector3f& point ) {
if ( body.isStatic ) return; ::wakeBody( body ); if ( body.isStatic ) return; ::wakeBody( body );
// linear force // linear force
body.forceAccumulator += force; body.forceAccumulator += force;
@ -483,7 +443,7 @@ pod::PhysicsBody& uf::physics::impl::create( pod::World& world, uf::Object& obje
body.collider.mesh.bvh = new pod::BVH; body.collider.mesh.bvh = new pod::BVH;
auto& bvh = *body.collider.mesh.bvh; auto& bvh = *body.collider.mesh.bvh;
::buildMeshBVH( bvh, mesh, ::meshBvhCapacity ); ::buildMeshBVH( bvh, mesh, uf::physics::impl::settings.meshBvhCapacity );
body.bounds = ::computeAABB( body ); body.bounds = ::computeAABB( body );
uf::physics::impl::updateInertia( body ); uf::physics::impl::updateInertia( body );
@ -562,7 +522,7 @@ pod::RayQuery uf::physics::impl::rayCast( const pod::Ray& ray, const pod::World&
static thread_local uf::stl::vector<pod::BVH::index_t> candidates; static thread_local uf::stl::vector<pod::BVH::index_t> candidates;
candidates.clear(); candidates.clear();
::queryBVH( dynamicBvh, ray, candidates ); ::queryBVH( dynamicBvh, ray, candidates );
if ( ::useSplitBvhs ) ::queryBVH( staticBvh, ray, candidates ); if ( uf::physics::impl::settings.useSplitBvhs ) ::queryBVH( staticBvh, ray, candidates );
for ( auto i : candidates ) { for ( auto i : candidates ) {
auto* b = bodies[i]; auto* b = bodies[i];

View File

@ -24,11 +24,15 @@ namespace {
void applyImpulseTo( pod::PhysicsBody& a, pod::PhysicsBody& b, const pod::Vector3f& rA, const pod::Vector3f& rB, const pod::Vector3f& impulse ) { void applyImpulseTo( pod::PhysicsBody& a, pod::PhysicsBody& b, const pod::Vector3f& rA, const pod::Vector3f& rB, const pod::Vector3f& impulse ) {
if ( !a.isStatic ) { if ( !a.isStatic ) {
a.velocity -= impulse * a.inverseMass; a.velocity -= impulse * a.inverseMass;
a.angularVelocity -= (uf::vector::cross(rA, impulse)) * a.inverseInertiaTensor; //a.angularVelocity -= (uf::vector::cross(rA, impulse)) * a.inverseInertiaTensor;
pod::Matrix3f invIa = computeWorldInverseInertia( a );
a.angularVelocity -= uf::matrix::multiply( invIa, uf::vector::cross(rA, impulse) );
} }
if ( !b.isStatic ) { if ( !b.isStatic ) {
b.velocity += impulse * b.inverseMass; b.velocity += impulse * b.inverseMass;
b.angularVelocity += (uf::vector::cross(rB, impulse)) * b.inverseInertiaTensor; //b.angularVelocity += (uf::vector::cross(rB, impulse)) * b.inverseInertiaTensor;
pod::Matrix3f invIb = computeWorldInverseInertia( b );
a.angularVelocity += uf::matrix::multiply( invIb, uf::vector::cross(rB, impulse) );
} }
} }
@ -65,7 +69,7 @@ namespace {
} }
bool generateContacts( pod::PhysicsBody& a, pod::PhysicsBody& b, pod::Manifold& manifold, float dt ) { bool generateContacts( pod::PhysicsBody& a, pod::PhysicsBody& b, pod::Manifold& manifold, float dt ) {
if ( ::useGjk ) return generateContactsGjk( a, b, manifold, dt ); if ( uf::physics::impl::settings.useGjk ) return generateContactsGjk( a, b, manifold, dt );
::bindManifold( a, b, manifold, dt ); ::bindManifold( a, b, manifold, dt );
#define CHECK_CONTACT( A, B, fun )\ #define CHECK_CONTACT( A, B, fun )\
@ -111,7 +115,8 @@ namespace {
void reduceContacts( pod::Manifold& manifold ) { void reduceContacts( pod::Manifold& manifold ) {
if ( manifold.points.size() <= 4 ) return; if ( manifold.points.size() <= 4 ) return;
uf::stl::vector<pod::Contact> result; static thread_local uf::stl::vector<pod::Contact> result;
result.clear();
result.reserve(4); result.reserve(4);
for ( auto& c : manifold.points ) { for ( auto& c : manifold.points ) {
@ -200,7 +205,7 @@ namespace {
// prune points that are too old // prune points that are too old
for ( auto it = manifold.points.begin(); it != manifold.points.end(); ) { for ( auto it = manifold.points.begin(); it != manifold.points.end(); ) {
if ( it->lifetime > ::manifoldCacheLifetime ) it = manifold.points.erase(it); if ( it->lifetime > uf::physics::impl::settings.manifoldCacheLifetime ) it = manifold.points.erase(it);
else ++it; else ++it;
} }
@ -251,11 +256,11 @@ namespace {
// baumgarte position correction // baumgarte position correction
void positionCorrection( pod::PhysicsBody& a, pod::PhysicsBody& b, const pod::Contact& contact ) { void positionCorrection( pod::PhysicsBody& a, pod::PhysicsBody& b, const pod::Contact& contact ) {
if ( ::baumgarteCorrectionPercent <= 0 ) return; if ( uf::physics::impl::settings.baumgarteCorrectionPercent <= 0 ) return;
if ( a.isStatic && b.isStatic ) return; if ( a.isStatic && b.isStatic ) return;
// penetration depth beyond slop // penetration depth beyond slop
float penetration = std::max( contact.penetration - ::baumgarteCorrectionSlop, 0.0f ); float penetration = std::max( contact.penetration - uf::physics::impl::settings.baumgarteCorrectionSlop, 0.0f );
if ( penetration <= 0.0f ) return; if ( penetration <= 0.0f ) return;
// compute correction magnitude // compute correction magnitude
@ -265,7 +270,7 @@ namespace {
if ( totalInvMass <= EPS(1e-8f) ) return; if ( totalInvMass <= EPS(1e-8f) ) return;
// apply correction vector // apply correction vector
pod::Vector3f correction = contact.normal * (penetration / totalInvMass) * ::baumgarteCorrectionPercent; pod::Vector3f correction = contact.normal * (penetration / totalInvMass) * uf::physics::impl::settings.baumgarteCorrectionPercent;
if ( !a.isStatic ) a.transform->position -= correction * invMassA; if ( !a.isStatic ) a.transform->position -= correction * invMassA;
if ( !b.isStatic ) b.transform->position += correction * invMassB; if ( !b.isStatic ) b.transform->position += correction * invMassB;

View File

@ -37,7 +37,7 @@ namespace {
if ( dist > r ) return false; if ( dist > r ) return false;
float penetration = r - dist; float penetration = r - dist;
auto contact = center - normal * dist - normal * penetration; auto contact = center - normal * r;
manifold.points.emplace_back(pod::Contact{ contact, normal, penetration }); manifold.points.emplace_back(pod::Contact{ contact, normal, penetration });
return true; return true;

View File

@ -26,7 +26,7 @@ namespace {
// normal impulse scalar // normal impulse scalar
float jn = -(1.0f + e) * velAlongNormal; float jn = -(1.0f + e) * velAlongNormal;
jn /= invMassN; jn /= invMassN;
if ( ::warmupSolver ) { if ( uf::physics::impl::settings.warmupSolver ) {
float jnOld = contact.accumulatedNormalImpulse; float jnOld = contact.accumulatedNormalImpulse;
float jnNew = std::max(0.0f, jnOld + jn); float jnNew = std::max(0.0f, jnOld + jn);
float jnDelta = jnNew - jnOld; float jnDelta = jnNew - jnOld;
@ -57,7 +57,7 @@ namespace {
if ( std::fabs(jt) > jn * mu_s) jt = -jn * mu_d; // dynamic friction: resist sliding proportionally if ( std::fabs(jt) > jn * mu_s) jt = -jn * mu_d; // dynamic friction: resist sliding proportionally
if ( ::warmupSolver ) { if ( uf::physics::impl::settings.warmupSolver ) {
float maxFriction = mu_s * contact.accumulatedNormalImpulse; float maxFriction = mu_s * contact.accumulatedNormalImpulse;
float jtOld = contact.accumulatedTangentImpulse; float jtOld = contact.accumulatedTangentImpulse;
float jtNew = std::max(-maxFriction, std::min(jtOld + jt, maxFriction)); float jtNew = std::max(-maxFriction, std::min(jtOld + jt, maxFriction));
@ -83,6 +83,9 @@ namespace {
// precompute inverse masses // precompute inverse masses
float invMassA = ( a.isStatic ? 0.0f : a.inverseMass ); float invMassA = ( a.isStatic ? 0.0f : a.inverseMass );
float invMassB = ( b.isStatic ? 0.0f : b.inverseMass ); float invMassB = ( b.isStatic ? 0.0f : b.inverseMass );
pod::Matrix3f invIa = computeWorldInverseInertia( a );
pod::Matrix3f invIb = computeWorldInverseInertia( b );
auto pA = ::getPosition( a, true ); auto pA = ::getPosition( a, true );
auto pB = ::getPosition( b, true ); auto pB = ::getPosition( b, true );
@ -90,12 +93,11 @@ namespace {
for ( auto i = 0; i < N; i++ ) { for ( auto i = 0; i < N; i++ ) {
pod::Vector3f rA_i = manifold.points[i].point - pA; pod::Vector3f rA_i = manifold.points[i].point - pA;
pod::Vector3f rB_i = manifold.points[i].point - pB; pod::Vector3f rB_i = manifold.points[i].point - pB;
pod::Vector3f n_i = manifold.points[i].normal;
for ( auto j = 0; j < N; j++ ) { for ( auto j = 0; j < N; j++ ) {
pod::Vector3f rA_j = manifold.points[j].point - pA; pod::Vector3f rA_j = manifold.points[j].point - pA;
pod::Vector3f rB_j = manifold.points[j].point - pB; pod::Vector3f rB_j = manifold.points[j].point - pB;
pod::Vector3f n_i = manifold.points[i].normal;
pod::Vector3f n_j = manifold.points[j].normal; pod::Vector3f n_j = manifold.points[j].normal;
float termLinear = (invMassA + invMassB) * uf::vector::dot(n_i, n_j); float termLinear = (invMassA + invMassB) * uf::vector::dot(n_i, n_j);
@ -104,8 +106,6 @@ namespace {
pod::Vector3f raXnj = uf::vector::cross(rA_j, n_j); pod::Vector3f raXnj = uf::vector::cross(rA_j, n_j);
pod::Vector3f rbXnj = uf::vector::cross(rB_j, n_j); pod::Vector3f rbXnj = uf::vector::cross(rB_j, n_j);
pod::Matrix3f invIa = computeWorldInverseInertia( a );
pod::Matrix3f invIb = computeWorldInverseInertia( b );
pod::Vector3f Ia_raXnj = uf::matrix::multiply( invIa, raXnj ); pod::Vector3f Ia_raXnj = uf::matrix::multiply( invIa, raXnj );
pod::Vector3f Ib_rbXnj = uf::matrix::multiply( invIb, rbXnj ); pod::Vector3f Ib_rbXnj = uf::matrix::multiply( invIb, rbXnj );
@ -126,7 +126,7 @@ namespace {
for ( auto i = 0; i < N; i++ ) { for ( auto i = 0; i < N; i++ ) {
float vRel = uf::vector::dot( relVelLinear, manifold.points[i].normal ); float vRel = uf::vector::dot( relVelLinear, manifold.points[i].normal );
float penetrationBias = std::max( manifold.points[i].penetration - ::baumgarteCorrectionSlop, 0.0f ) * ( ::baumgarteCorrectionPercent / dt ); float penetrationBias = std::max( manifold.points[i].penetration - uf::physics::impl::settings.baumgarteCorrectionSlop, 0.0f ) * ( uf::physics::impl::settings.baumgarteCorrectionPercent / dt );
float cDot = vRel + penetrationBias; float cDot = vRel + penetrationBias;
rhs[i] = (cDot < 0.0f) ? -cDot : 0.0f; rhs[i] = (cDot < 0.0f) ? -cDot : 0.0f;
@ -141,7 +141,7 @@ namespace {
float vRel = uf::vector::dot((vB - vA), contact.normal); float vRel = uf::vector::dot((vB - vA), contact.normal);
// penetration bias with clamp // penetration bias with clamp
float penetrationBias = std::max(contact.penetration - ::baumgarteCorrectionSlop, 0.0f) * (::baumgarteCorrectionPercent / dt); float penetrationBias = std::max(contact.penetration - uf::physics::impl::settings.baumgarteCorrectionSlop, 0.0f) * (uf::physics::impl::settings.baumgarteCorrectionPercent / dt);
penetrationBias = std::min(penetrationBias, 2.0f / dt); // clamp penetrationBias = std::min(penetrationBias, 2.0f / dt); // clamp
float maxPenetrationRecovery = 2.0f; // limit to 2 units per second float maxPenetrationRecovery = 2.0f; // limit to 2 units per second
@ -214,7 +214,7 @@ namespace {
// restitution bias + baumgarte // restitution bias + baumgarte
float e = std::min( a.material.restitution, b.material.restitution ); float e = std::min( a.material.restitution, b.material.restitution );
float penetrationBias = std::max( c.penetration - ::baumgarteCorrectionSlop, 0.0f ) * (::baumgarteCorrectionPercent / dt); float penetrationBias = std::max( c.penetration - uf::physics::impl::settings.baumgarteCorrectionSlop, 0.0f ) * (uf::physics::impl::settings.baumgarteCorrectionPercent / dt);
cc.bias = (vn < -1.0f ? -e * vn : 0.0f) + penetrationBias; cc.bias = (vn < -1.0f ? -e * vn : 0.0f) + penetrationBias;
// effective mass (normal) // effective mass (normal)
@ -232,6 +232,7 @@ namespace {
cc.effectiveMassT = ( Kt > 0.0f ) ? ( 1.0f / Kt ) : 0.0f; cc.effectiveMassT = ( Kt > 0.0f ) ? ( 1.0f / Kt ) : 0.0f;
// warm start // warm start
#if 1
cc.accumulatedNormalImpulse = c.accumulatedNormalImpulse; cc.accumulatedNormalImpulse = c.accumulatedNormalImpulse;
cc.accumulatedTangentImpulse = c.accumulatedTangentImpulse; cc.accumulatedTangentImpulse = c.accumulatedTangentImpulse;
@ -239,10 +240,11 @@ namespace {
pod::Vector3f P = cc.normal * cc.accumulatedNormalImpulse + cc.tangent * cc.accumulatedTangentImpulse; pod::Vector3f P = cc.normal * cc.accumulatedNormalImpulse + cc.tangent * cc.accumulatedTangentImpulse;
::applyImpulseTo(a, b, cc.rA, cc.rB, P); ::applyImpulseTo(a, b, cc.rA, cc.rB, P);
#endif
} }
// iterative PGS // iterative PGS
for ( auto iter = 0; iter < ::solverIterations; iter++ ) { for ( auto iter = 0; iter < uf::physics::impl::settings.solverIterations; iter++ ) {
for ( auto i = 0; i < count; i++ ) { for ( auto i = 0; i < count; i++ ) {
auto& cc = cache[i]; auto& cc = cache[i];
@ -280,18 +282,18 @@ namespace {
} }
void resolveManifold( pod::PhysicsBody& a, pod::PhysicsBody& b, pod::Manifold& manifold, float dt ) { void resolveManifold( pod::PhysicsBody& a, pod::PhysicsBody& b, pod::Manifold& manifold, float dt ) {
if ( ::blockContactSolver ) { if ( uf::physics::impl::settings.blockContactSolver ) {
if ( manifold.points.size() == 2 ) return ::block2x2Solver( a, b, manifold, dt ); if ( manifold.points.size() == 2 ) return ::block2x2Solver( a, b, manifold, dt );
if ( manifold.points.size() == 3 ) return ::block3x3Solver( a, b, manifold, dt ); if ( manifold.points.size() == 3 ) return ::block3x3Solver( a, b, manifold, dt );
if ( manifold.points.size() == 4 ) return ::block4x4Solver( a, b, manifold, dt ); if ( manifold.points.size() == 4 ) return ::block4x4Solver( a, b, manifold, dt );
} }
if ( ::psgContactSolver ) return ::blockPGSSolver( a, b, manifold, dt ); if ( uf::physics::impl::settings.psgContactSolver ) return ::blockPGSSolver( a, b, manifold, dt );
for ( auto& contact : manifold.points ) ::iterativeImpulseSolver( a, b, contact, dt ); for ( auto& contact : manifold.points ) ::iterativeImpulseSolver( a, b, contact, dt );
} }
void solveContacts( uf::stl::vector<pod::Manifold>& manifolds, float dt ) { void solveContacts( uf::stl::vector<pod::Manifold>& manifolds, float dt ) {
if ( ::warmupSolver ) for ( auto& manifold : manifolds ) ::warmupManifold( *manifold.a, *manifold.b, manifold, dt ); if ( uf::physics::impl::settings.warmupSolver ) for ( auto& manifold : manifolds ) ::warmupManifold( *manifold.a, *manifold.b, manifold, dt );
for ( auto i = 0; i < ::solverIterations; ++i ) for ( auto& manifold : manifolds ) ::resolveManifold( *manifold.a, *manifold.b, manifold, dt ); for ( auto i = 0; i < uf::physics::impl::settings.solverIterations; ++i ) for ( auto& manifold : manifolds ) ::resolveManifold( *manifold.a, *manifold.b, manifold, dt );
} }
void solvePositions( uf::stl::vector<pod::Manifold>& manifolds, float dt, uint32_t iterations = 2 ) { void solvePositions( uf::stl::vector<pod::Manifold>& manifolds, float dt, uint32_t iterations = 2 ) {

View File

@ -37,11 +37,6 @@ void uf::thread::tick( pod::Thread& thread ) {
thread.timer.start(); thread.timer.start();
while ( thread.running ) { while ( thread.running ) {
std::unique_lock<std::mutex> lock(*thread.mutex);
thread.conditions.queued.wait(lock, [&]{
return (!thread.container.empty() || !thread.queue.empty()) || !thread.running;
});
uf::thread::process( thread ); uf::thread::process( thread );
if ( thread.limiter > 0 ) { if ( thread.limiter > 0 ) {
@ -57,7 +52,7 @@ void uf::thread::tick( pod::Thread& thread ) {
pod::Thread& uf::thread::fetchWorker( const uf::stl::string& name ) { pod::Thread& uf::thread::fetchWorker( const uf::stl::string& name ) {
static int current = 0; static int current = 0;
static int limit = uf::thread::workers; int limit = uf::thread::workers;
int tries = 0; int tries = 0;
while ( tries++ < limit ) { while ( tries++ < limit ) {
@ -153,12 +148,25 @@ void uf::thread::queue( pod::Thread& thread, const pod::Thread::function_t& func
if ( thread.mutex != NULL ) thread.mutex->lock(); if ( thread.mutex != NULL ) thread.mutex->lock();
thread.queue.emplace( function ); thread.queue.emplace( function );
thread.conditions.queued.notify_one(); thread.conditions.queued.notify_one();
thread.pending.fetch_add(1);
if ( thread.mutex != NULL ) thread.mutex->unlock(); if ( thread.mutex != NULL ) thread.mutex->unlock();
} }
void uf::thread::process( pod::Thread& thread ) { if ( !uf::thread::has(uf::thread::uid(thread)) )return; //ops void uf::thread::process( pod::Thread& thread ) { if ( !uf::thread::has(uf::thread::uid(thread)) ) return; // ops
while ( !thread.queue.empty() ) { pod::Thread::queue_t local_queue;
auto& function = thread.queue.front(); pod::Thread::container_t local_container;
if ( function )
{
std::unique_lock<std::mutex> lock(*thread.mutex);
thread.conditions.queued.wait(lock, [&]{
return (!thread.container.empty() || !thread.queue.empty()) || !thread.running;
});
if ( !thread.running ) return;
std::swap( local_queue, thread.queue );
}
while ( !local_queue.empty() ) {
auto& function = local_queue.front();
#if UF_EXCEPTIONS #if UF_EXCEPTIONS
try { try {
#endif #endif
@ -168,10 +176,17 @@ void uf::thread::process( pod::Thread& thread ) { if ( !uf::thread::has(uf::thre
UF_MSG_ERROR("Thread {} (UID: {}) caught exception: {}", thread.name, thread.uid, e.what()); UF_MSG_ERROR("Thread {} (UID: {}) caught exception: {}", thread.name, thread.uid, e.what());
} }
#endif #endif
thread.queue.pop();
local_queue.pop();
thread.pending.fetch_sub(1);
} }
for ( auto function : thread.container ) {
if ( function ) {
std::unique_lock<std::mutex> lock(*thread.mutex);
local_container = thread.container;
}
for ( auto& function : local_container ) {
#if UF_EXCEPTIONS #if UF_EXCEPTIONS
try { try {
#endif #endif
@ -182,7 +197,11 @@ void uf::thread::process( pod::Thread& thread ) { if ( !uf::thread::has(uf::thre
} }
#endif #endif
} }
thread.conditions.finished.notify_one();
{
std::lock_guard<std::mutex> lock(*thread.mutex);
thread.conditions.finished.notify_all();
}
} }
void uf::thread::wait( pod::Thread& thread ) { void uf::thread::wait( pod::Thread& thread ) {
if ( thread.mutex != NULL ) { if ( thread.mutex != NULL ) {