diff --git a/frame/vulkan/device.cpp b/frame/vulkan/device.cpp index df34f40a..83744309 100644 --- a/frame/vulkan/device.cpp +++ b/frame/vulkan/device.cpp @@ -32,6 +32,7 @@ #include "frame/vulkan/build_level.h" #include "frame/vulkan/command_resources.h" #include "frame/vulkan/command_queue.h" +#include "frame/vulkan/frame_profiler.h" #include "frame/vulkan/gpu_memory_manager.h" #include "frame/vulkan/mesh_resources.h" #include "frame/vulkan/mesh_utils.h" @@ -2732,10 +2733,12 @@ void Device::UpdateRaytraceBuffers() if (!level_ || !buffer_resources_ || (!use_compute_raytracing_ && !use_raytracing_pipeline_)) - { + { return; } + VulkanProfileScope profile_scope("raytrace.update_buffers"); + auto& animated_rt_stats = GetAnimatedRaytraceTimingStats(); const auto total_update_start = SteadyClock::now(); @@ -2964,6 +2967,21 @@ void Device::UpdateRaytraceBuffers() // Re-arm transfer->compute visibility barrier after dynamic SSBO writes. storage_buffers_ready_ = false; } + RecordVulkanProfileCounter( + "raytrace.updated_cpu_storage_buffers", + updated_buffer_count); + if (updated_aggregate_scene) + { + RecordVulkanProfileCounter("raytrace.updated_aggregate_scenes"); + } + if (updated_hardware_transforms) + { + RecordVulkanProfileCounter("raytrace.updated_hardware_transforms"); + } + if (animated_triangle_updated) + { + RecordVulkanProfileCounter("raytrace.updated_animated_triangles"); + } if (animated_triangle_updated) { ++animated_rt_stats.frame_count; @@ -2983,6 +3001,8 @@ void Device::UpdateRaytraceBuffers() bool Device::UpdateAggregateRaytracingSceneBuffers(bool build_software_bvh) { + VulkanProfileScope profile_scope("raytrace.aggregate_scene_update"); + if (!level_ || !buffer_resources_ || !active_program_info_) { return false; @@ -3173,6 +3193,9 @@ bool Device::UpdateHardwareRaytracingAggregateSceneBuffers( const std::vector& updated_source_triangle_buffer_ids, const std::vector& prepared_source_geometries) { + VulkanProfileScope profile_scope( + "raytrace.hardware_aggregate_scene_update"); + if (!level_ || !buffer_resources_ || !active_program_info_ || !hardware_raytracing_uses_source_instances_ || updated_source_triangle_buffer_ids.empty()) @@ -3322,6 +3345,8 @@ bool Device::UpdateHardwareRaytracingDynamicGeometry( return false; } + VulkanProfileScope profile_scope("raytrace.blas_update"); + auto& animated_rt_stats = GetAnimatedRaytraceTimingStats(); const auto build_scratch_address = @@ -3495,6 +3520,7 @@ bool Device::UpdateHardwareRaytracingDynamicGeometry( { return false; } + RecordVulkanProfileCounter("raytrace.blas_updates", pending_updates.size()); for (auto& pending : pending_updates) { @@ -3503,44 +3529,48 @@ bool Device::UpdateHardwareRaytracingDynamicGeometry( } const auto submit_start = SteadyClock::now(); - command_queue_->SubmitOneTime( - [&](vk::CommandBuffer command_buffer) { - std::vector copy_barriers = {}; - copy_barriers.reserve(pending_updates.size()); - for (const auto& pending : pending_updates) - { - command_buffer.copyBuffer( - *pending.staging_buffer, - *pending.geometry->vertex_buffer, - vk::BufferCopy(0, 0, pending.upload_size)); - copy_barriers.emplace_back( - vk::AccessFlagBits::eTransferWrite, - vk::AccessFlagBits::eAccelerationStructureReadKHR, - VK_QUEUE_FAMILY_IGNORED, - VK_QUEUE_FAMILY_IGNORED, - *pending.geometry->vertex_buffer, - 0, - pending.upload_size); - } - if (!copy_barriers.empty()) - { - command_buffer.pipelineBarrier( - vk::PipelineStageFlagBits::eTransfer, - vk::PipelineStageFlagBits::eAccelerationStructureBuildKHR, - {}, - nullptr, - copy_barriers, - nullptr); - } - for (const auto& pending : pending_updates) + { + VulkanProfileScope submit_scope("raytrace.blas_update_submit_wait"); + command_queue_->SubmitOneTime( + [&](vk::CommandBuffer command_buffer) { - const vk::AccelerationStructureBuildRangeInfoKHR* range_infos[] = { - &pending.range_info}; - command_buffer.buildAccelerationStructuresKHR( - pending.build_info, - range_infos); - } - }); + std::vector copy_barriers = {}; + copy_barriers.reserve(pending_updates.size()); + for (const auto& pending : pending_updates) + { + command_buffer.copyBuffer( + *pending.staging_buffer, + *pending.geometry->vertex_buffer, + vk::BufferCopy(0, 0, pending.upload_size)); + copy_barriers.emplace_back( + vk::AccessFlagBits::eTransferWrite, + vk::AccessFlagBits::eAccelerationStructureReadKHR, + VK_QUEUE_FAMILY_IGNORED, + VK_QUEUE_FAMILY_IGNORED, + *pending.geometry->vertex_buffer, + 0, + pending.upload_size); + } + if (!copy_barriers.empty()) + { + command_buffer.pipelineBarrier( + vk::PipelineStageFlagBits::eTransfer, + vk::PipelineStageFlagBits::eAccelerationStructureBuildKHR, + {}, + nullptr, + copy_barriers, + nullptr); + } + for (const auto& pending : pending_updates) + { + const vk::AccelerationStructureBuildRangeInfoKHR* + range_infos[] = {&pending.range_info}; + command_buffer.buildAccelerationStructuresKHR( + pending.build_info, + range_infos); + } + }); + } animated_rt_stats.dynamic_geometry_submit_ms += ElapsedMilliseconds(submit_start); @@ -3553,6 +3583,9 @@ bool Device::UpdateHardwareRaytracingDynamicGeometry( void Device::UpdateHardwareRaytracingScene() { + VulkanProfileScope profile_scope("raytrace.hardware_scene_rebuild"); + RecordVulkanProfileCounter("raytrace.hardware_scene_rebuilds"); + CreateHardwareRaytracingScene(); UpdateHardwareRaytracingDescriptor(); } @@ -3570,6 +3603,9 @@ bool Device::UpdateHardwareRaytracingTransforms(bool force_tlas_update) return false; } + VulkanProfileScope profile_scope("raytrace.hardware_transform_update"); + RecordVulkanProfileCounter("raytrace.hardware_transform_checks"); + auto& animated_rt_stats = GetAnimatedRaytraceTimingStats(); const auto total_start = SteadyClock::now(); @@ -3618,6 +3654,7 @@ bool Device::UpdateHardwareRaytracingTransforms(bool force_tlas_update) { if (!force_tlas_update) { + RecordVulkanProfileCounter("raytrace.hardware_transform_unchanged"); animated_rt_stats.transform_total_ms += ElapsedMilliseconds(total_start); return storage_buffer_changed; @@ -3673,6 +3710,7 @@ bool Device::UpdateHardwareRaytracingTransforms(bool force_tlas_update) vk_unique_device_->unmapMemory(*hardware_raytracing_instance_memory_); hardware_raytracing_instance_bytes_ = bytes; animated_rt_stats.transform_upload_ms += ElapsedMilliseconds(upload_start); + RecordVulkanProfileCounter("raytrace.hardware_transform_uploads"); RebuildHardwareRaytracingTlas(); animated_rt_stats.transform_total_ms += ElapsedMilliseconds(total_start); return true; @@ -3694,6 +3732,10 @@ void Device::RebuildHardwareRaytracingTlas() return; } + VulkanProfileScope profile_scope("raytrace.tlas_update"); + RecordVulkanProfileCounter("raytrace.tlas_updates"); + RecordVulkanProfileCounter("raytrace.tlas_instances", instance_count); + const auto build_scratch_address = [&](vk::DeviceSize size, vk::UniqueBuffer& scratch_buffer, @@ -3749,12 +3791,16 @@ void Device::RebuildHardwareRaytracingTlas() const vk::AccelerationStructureBuildRangeInfoKHR* tlas_ranges[] = { &tlas_range_info}; const auto rebuild_start = SteadyClock::now(); - command_queue_->SubmitOneTime( - [&](vk::CommandBuffer command_buffer) { - command_buffer.buildAccelerationStructuresKHR( - tlas_build_info, - tlas_ranges); - }); + { + VulkanProfileScope submit_scope("raytrace.tlas_update_submit_wait"); + command_queue_->SubmitOneTime( + [&](vk::CommandBuffer command_buffer) + { + command_buffer.buildAccelerationStructuresKHR( + tlas_build_info, + tlas_ranges); + }); + } GetAnimatedRaytraceTimingStats().tlas_rebuild_ms += ElapsedMilliseconds(rebuild_start); } @@ -4007,6 +4053,8 @@ void Device::RecreateSwapchain() SceneState Device::BuildFrameSceneState(vk::Extent2D extent) const { + VulkanProfileScope profile_scope("scene.build_frame_state"); + const bool has_raytrace_source_meshes = level_ && HasRaytracingSourceMeshes(*level_); const bool use_shared_transform_hardware_scene = @@ -4538,6 +4586,8 @@ std::vector Device::UpdateGpuSkinnedMeshes() return {}; } + VulkanProfileScope profile_scope("raytrace.gpu_skinning_update"); + auto find_storage_buffer_resource = [&](EntityId buffer_id) -> const BufferResource* { if (!buffer_resources_ || buffer_id == NullId) @@ -4755,6 +4805,9 @@ std::vector Device::UpdateGpuSkinnedMeshes() { return {}; } + RecordVulkanProfileCounter( + "raytrace.gpu_skinning_dispatches", + pending_dispatches.size()); for (auto& pending : pending_dispatches) { @@ -4766,130 +4819,136 @@ std::vector Device::UpdateGpuSkinnedMeshes() } const auto submit_start = SteadyClock::now(); - command_queue_->SubmitOneTime( - [&](vk::CommandBuffer command_buffer) { - command_buffer.bindPipeline( - vk::PipelineBindPoint::eCompute, - *gpu_skinning_pipeline_); - - for (const auto& pending : pending_dispatches) + { + VulkanProfileScope submit_scope("raytrace.gpu_skinning_submit_wait"); + command_queue_->SubmitOneTime( + [&](vk::CommandBuffer command_buffer) { - command_buffer.bindDescriptorSets( + command_buffer.bindPipeline( vk::PipelineBindPoint::eCompute, - *gpu_skinning_pipeline_layout_, - 0, - pending.resource->descriptor_set, - {}); - - GpuSkinningPushConstants push_constants = {}; - push_constants.output_vertex_count = - pending.resource->output_vertex_count; - push_constants.color_multiplier = - pending.resource->color_multiplier; - push_constants.atlas_uv_bounds = - pending.resource->atlas_uv_bounds; - command_buffer.pushConstants( - *gpu_skinning_pipeline_layout_, - vk::ShaderStageFlagBits::eCompute, - 0, - sizeof(GpuSkinningPushConstants), - &push_constants); - - const std::uint32_t group_count = - (pending.resource->output_vertex_count + - kGpuSkinningWorkgroupSize - 1u) / - kGpuSkinningWorkgroupSize; - command_buffer.dispatch(group_count, 1, 1); - } - - std::vector output_barriers = {}; - output_barriers.reserve(pending_dispatches.size()); - for (const auto& pending : pending_dispatches) - { - output_barriers.emplace_back( - vk::AccessFlagBits::eShaderWrite, - vk::AccessFlagBits::eTransferRead, - VK_QUEUE_FAMILY_IGNORED, - VK_QUEUE_FAMILY_IGNORED, - *pending.resource->output_buffer, - 0, - pending.resource->output_buffer_size); - } - command_buffer.pipelineBarrier( - vk::PipelineStageFlagBits::eComputeShader, - vk::PipelineStageFlagBits::eTransfer, - {}, - nullptr, - output_barriers, - nullptr); + *gpu_skinning_pipeline_); - std::vector geometry_copy_barriers = {}; - geometry_copy_barriers.reserve(pending_dispatches.size()); - for (const auto& pending : pending_dispatches) - { - if (pending.aggregate_buffer && pending.aggregate_buffer->buffer) + for (const auto& pending : pending_dispatches) { - const vk::DeviceSize dst_offset = - static_cast(pending.resource->triangle_offset) * - static_cast( - kRaytraceTriangleVertexStrideBytes * 3u); - if (dst_offset + pending.resource->output_buffer_size <= - pending.aggregate_buffer->size) - { - command_buffer.copyBuffer( - *pending.resource->output_buffer, - *pending.aggregate_buffer->buffer, - vk::BufferCopy( - 0, - dst_offset, - pending.resource->output_buffer_size)); - } + command_buffer.bindDescriptorSets( + vk::PipelineBindPoint::eCompute, + *gpu_skinning_pipeline_layout_, + 0, + pending.resource->descriptor_set, + {}); + + GpuSkinningPushConstants push_constants = {}; + push_constants.output_vertex_count = + pending.resource->output_vertex_count; + push_constants.color_multiplier = + pending.resource->color_multiplier; + push_constants.atlas_uv_bounds = + pending.resource->atlas_uv_bounds; + command_buffer.pushConstants( + *gpu_skinning_pipeline_layout_, + vk::ShaderStageFlagBits::eCompute, + 0, + sizeof(GpuSkinningPushConstants), + &push_constants); + + const std::uint32_t group_count = + (pending.resource->output_vertex_count + + kGpuSkinningWorkgroupSize - 1u) / + kGpuSkinningWorkgroupSize; + command_buffer.dispatch(group_count, 1, 1); } - if (pending.hardware_geometry) + std::vector output_barriers = {}; + output_barriers.reserve(pending_dispatches.size()); + for (const auto& pending : pending_dispatches) { - command_buffer.copyBuffer( - *pending.resource->output_buffer, - *pending.hardware_geometry->vertex_buffer, - vk::BufferCopy( - 0, - 0, - pending.resource->output_buffer_size)); - geometry_copy_barriers.emplace_back( - vk::AccessFlagBits::eTransferWrite, - vk::AccessFlagBits::eAccelerationStructureReadKHR, + output_barriers.emplace_back( + vk::AccessFlagBits::eShaderWrite, + vk::AccessFlagBits::eTransferRead, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, - *pending.hardware_geometry->vertex_buffer, + *pending.resource->output_buffer, 0, pending.resource->output_buffer_size); } - } - - if (!geometry_copy_barriers.empty()) - { command_buffer.pipelineBarrier( + vk::PipelineStageFlagBits::eComputeShader, vk::PipelineStageFlagBits::eTransfer, - vk::PipelineStageFlagBits::eAccelerationStructureBuildKHR, {}, nullptr, - geometry_copy_barriers, + output_barriers, nullptr); - } - for (const auto& pending : pending_dispatches) - { - if (!pending.hardware_geometry) + std::vector geometry_copy_barriers = {}; + geometry_copy_barriers.reserve(pending_dispatches.size()); + for (const auto& pending : pending_dispatches) { - continue; + if (pending.aggregate_buffer && + pending.aggregate_buffer->buffer) + { + const vk::DeviceSize dst_offset = + static_cast( + pending.resource->triangle_offset) * + static_cast( + kRaytraceTriangleVertexStrideBytes * 3u); + if (dst_offset + pending.resource->output_buffer_size <= + pending.aggregate_buffer->size) + { + command_buffer.copyBuffer( + *pending.resource->output_buffer, + *pending.aggregate_buffer->buffer, + vk::BufferCopy( + 0, + dst_offset, + pending.resource->output_buffer_size)); + } + } + + if (pending.hardware_geometry) + { + command_buffer.copyBuffer( + *pending.resource->output_buffer, + *pending.hardware_geometry->vertex_buffer, + vk::BufferCopy( + 0, + 0, + pending.resource->output_buffer_size)); + geometry_copy_barriers.emplace_back( + vk::AccessFlagBits::eTransferWrite, + vk::AccessFlagBits::eAccelerationStructureReadKHR, + VK_QUEUE_FAMILY_IGNORED, + VK_QUEUE_FAMILY_IGNORED, + *pending.hardware_geometry->vertex_buffer, + 0, + pending.resource->output_buffer_size); + } } - const vk::AccelerationStructureBuildRangeInfoKHR* range_infos[] = { - &pending.range_info}; - command_buffer.buildAccelerationStructuresKHR( - pending.build_info, - range_infos); - } - }); + + if (!geometry_copy_barriers.empty()) + { + command_buffer.pipelineBarrier( + vk::PipelineStageFlagBits::eTransfer, + vk::PipelineStageFlagBits::eAccelerationStructureBuildKHR, + {}, + nullptr, + geometry_copy_barriers, + nullptr); + } + + for (const auto& pending : pending_dispatches) + { + if (!pending.hardware_geometry) + { + continue; + } + const vk::AccelerationStructureBuildRangeInfoKHR* + range_infos[] = {&pending.range_info}; + command_buffer.buildAccelerationStructuresKHR( + pending.build_info, + range_infos); + } + }); + } animated_rt_stats.dynamic_geometry_submit_ms += ElapsedMilliseconds(submit_start); @@ -4904,6 +4963,15 @@ std::vector Device::UpdateGpuSkinnedMeshes() [](const PendingGpuDispatch& pending) { return pending.hardware_geometry != nullptr; }); + const std::size_t blas_update_count = static_cast(std::count_if( + pending_dispatches.begin(), + pending_dispatches.end(), + [](const PendingGpuDispatch& pending) { + return pending.hardware_geometry != nullptr; + })); + RecordVulkanProfileCounter( + "raytrace.gpu_skinning_blas_updates", + blas_update_count); if (has_blas_updates) { const auto transform_start = SteadyClock::now(); diff --git a/frame/vulkan/frame_profiler.h b/frame/vulkan/frame_profiler.h new file mode 100644 index 00000000..5c78639b --- /dev/null +++ b/frame/vulkan/frame_profiler.h @@ -0,0 +1,299 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "frame/logger.h" + +namespace frame::vulkan +{ + +namespace detail +{ + +using VulkanProfileClock = std::chrono::steady_clock; + +inline std::string GetVulkanProfileEnvValue() +{ +#if defined(_WIN32) + char* value = nullptr; + std::size_t size = 0; + if (_dupenv_s(&value, &size, "FRAME_VULKAN_PROFILE") != 0 || + value == nullptr) + { + return {}; + } + std::string result(value); + std::free(value); + return result; +#else + const char* value = std::getenv("FRAME_VULKAN_PROFILE"); + return value ? std::string(value) : std::string{}; +#endif +} + +inline bool IsVulkanFrameProfilerEnabled() +{ + static const bool enabled = [] + { + std::string value = GetVulkanProfileEnvValue(); + std::transform( + value.begin(), + value.end(), + value.begin(), + [](unsigned char character) + { + return static_cast(std::tolower(character)); + }); + return !value.empty() && value != "0" && value != "false" && + value != "off"; + }(); + return enabled; +} + +struct VulkanProfileSample +{ + double total_ms = 0.0; + double max_ms = 0.0; + std::size_t count = 0; +}; + +} // namespace detail + +class VulkanFrameProfiler +{ + public: + void RecordSample(const char* label, double elapsed_ms) + { + if (!detail::IsVulkanFrameProfilerEnabled()) + { + return; + } + + std::lock_guard lock(mutex_); + auto& sample = samples_[label]; + sample.total_ms += elapsed_ms; + sample.max_ms = std::max(sample.max_ms, elapsed_ms); + ++sample.count; + } + + void RecordCounter(const char* label, std::size_t value) + { + if (!detail::IsVulkanFrameProfilerEnabled() || value == 0) + { + return; + } + + std::lock_guard lock(mutex_); + counters_[label] += value; + } + + void EndFrame(const frame::Logger& logger) + { + if (!detail::IsVulkanFrameProfilerEnabled()) + { + return; + } + + std::vector> + samples; + std::vector> counters; + std::size_t frame_count = 0; + double wall_ms = 0.0; + + { + std::lock_guard lock(mutex_); + ++frame_count_; + if (frame_count_ < kLogFrameInterval) + { + return; + } + + frame_count = frame_count_; + wall_ms = std::chrono::duration_cast< + std::chrono::duration>( + detail::VulkanProfileClock::now() - window_start_) + .count(); + samples.assign(samples_.begin(), samples_.end()); + counters.assign(counters_.begin(), counters_.end()); + ResetLocked(); + } + + std::sort( + samples.begin(), + samples.end(), + [](const auto& lhs, const auto& rhs) + { + return lhs.second.total_ms > rhs.second.total_ms; + }); + std::sort(counters.begin(), counters.end()); + + const double fps = + wall_ms > 0.0 + ? static_cast(frame_count) * 1000.0 / wall_ms + : 0.0; + logger->warn( + "Vulkan frame profile avg over {} frames ({:.1f} fps): {}", + frame_count, + fps, + FormatSamples(samples, frame_count)); + if (!counters.empty()) + { + logger->warn( + "Vulkan frame profile counters over {} frames: {}", + frame_count, + FormatCounters(counters, frame_count)); + } + } + + private: + static constexpr std::size_t kLogFrameInterval = 120; + + void ResetLocked() + { + samples_.clear(); + counters_.clear(); + frame_count_ = 0; + window_start_ = detail::VulkanProfileClock::now(); + } + + static std::string FormatSamples( + const std::vector>& + samples, + std::size_t frame_count) + { + if (samples.empty()) + { + return "no samples"; + } + + std::ostringstream stream; + stream << std::fixed << std::setprecision(2); + for (std::size_t i = 0; i < samples.size(); ++i) + { + if (i > 0) + { + stream << ", "; + } + const auto& [label, sample] = samples[i]; + const double average_per_frame = + sample.total_ms / static_cast(frame_count); + const double calls_per_frame = + static_cast(sample.count) / + static_cast(frame_count); + const double average_per_call = + sample.count > 0 + ? sample.total_ms / static_cast(sample.count) + : 0.0; + stream << label << "=" << average_per_frame << "ms/frame"; + if (sample.count != frame_count) + { + stream << " (" << calls_per_frame << " calls/frame, " + << average_per_call << "ms/call)"; + } + stream << " max=" << sample.max_ms << "ms"; + } + return stream.str(); + } + + static std::string FormatCounters( + const std::vector>& counters, + std::size_t frame_count) + { + std::ostringstream stream; + stream << std::fixed << std::setprecision(2); + for (std::size_t i = 0; i < counters.size(); ++i) + { + if (i > 0) + { + stream << ", "; + } + const auto& [label, value] = counters[i]; + stream << label << "=" + << static_cast(value) / + static_cast(frame_count) + << "/frame"; + } + return stream.str(); + } + + private: + std::mutex mutex_; + std::unordered_map samples_; + std::unordered_map counters_; + std::size_t frame_count_ = 0; + detail::VulkanProfileClock::time_point window_start_ = + detail::VulkanProfileClock::now(); +}; + +inline VulkanFrameProfiler& GetVulkanFrameProfiler() +{ + static VulkanFrameProfiler profiler; + return profiler; +} + +class VulkanProfileScope +{ + public: + explicit VulkanProfileScope(const char* label) + : enabled_(detail::IsVulkanFrameProfilerEnabled()), + label_(label), + start_(detail::VulkanProfileClock::now()) + { + } + + ~VulkanProfileScope() + { + if (!enabled_) + { + return; + } + const double elapsed_ms = + std::chrono::duration_cast>( + detail::VulkanProfileClock::now() - start_) + .count(); + GetVulkanFrameProfiler().RecordSample(label_, elapsed_ms); + } + + private: + bool enabled_ = false; + const char* label_ = ""; + detail::VulkanProfileClock::time_point start_; +}; + +class VulkanProfileFrame +{ + public: + explicit VulkanProfileFrame(const frame::Logger& logger) + : enabled_(detail::IsVulkanFrameProfilerEnabled()), logger_(logger) + { + } + + ~VulkanProfileFrame() + { + if (enabled_) + { + GetVulkanFrameProfiler().EndFrame(logger_); + } + } + + private: + bool enabled_ = false; + const frame::Logger& logger_; +}; + +inline void RecordVulkanProfileCounter(const char* label, std::size_t value = 1) +{ + GetVulkanFrameProfiler().RecordCounter(label, value); +} + +} // namespace frame::vulkan diff --git a/frame/vulkan/raytrace_scene_renderer.cpp b/frame/vulkan/raytrace_scene_renderer.cpp index 21a0b381..c24e231c 100644 --- a/frame/vulkan/raytrace_scene_renderer.cpp +++ b/frame/vulkan/raytrace_scene_renderer.cpp @@ -5,6 +5,7 @@ #include "frame/vulkan/device.h" #include "frame/vulkan/buffer_resources.h" +#include "frame/vulkan/frame_profiler.h" #include "frame/vulkan/output_image_resources.h" #include "frame/vulkan/pipeline_resources.h" #include "frame/vulkan/scene_state.h" @@ -18,6 +19,8 @@ RaytraceSceneRenderer::RaytraceSceneRenderer(Device& device) : device_(device) void RaytraceSceneRenderer::UpdateUniformBuffer(const SceneState& state) { + VulkanProfileScope scope("raytrace.update_uniform_buffer"); + if (!device_.buffer_resources_) { return; @@ -34,6 +37,8 @@ void RaytraceSceneRenderer::Render( vk::CommandBuffer command_buffer, vk::Extent2D extent) { + VulkanProfileScope scope("raytrace.render_record"); + auto transition_output = [&](vk::ImageLayout old_layout, vk::ImageLayout new_layout, vk::PipelineStageFlags src_stage, @@ -73,6 +78,8 @@ void RaytraceSceneRenderer::Render( if (!device_.storage_buffers_ready_ && device_.buffer_resources_) { + VulkanProfileScope barrier_scope( + "raytrace.storage_buffer_barrier_record"); const auto& storage_buffers = device_.buffer_resources_->GetStorageBuffers(); std::vector buffer_barriers; @@ -109,6 +116,8 @@ void RaytraceSceneRenderer::Render( if (device_.output_image_resources_->IsComputeOutputInShaderRead()) { + VulkanProfileScope transition_scope( + "raytrace.output_to_general_record"); transition_output( vk::ImageLayout::eShaderReadOnlyOptimal, vk::ImageLayout::eGeneral, @@ -125,6 +134,7 @@ void RaytraceSceneRenderer::Render( device_.pipeline_resources_->HasRaytracingPipeline() && device_.pipeline_resources_->GetRaytracingPipelineLayout()) { + VulkanProfileScope dispatch_scope("raytrace.trace_rays_record"); command_buffer.bindPipeline( vk::PipelineBindPoint::eRayTracingKHR, device_.pipeline_resources_->GetRaytracingPipeline()); @@ -142,11 +152,13 @@ void RaytraceSceneRenderer::Render( extent.width, extent.height, 1); + RecordVulkanProfileCounter("raytrace.trace_rays_dispatches"); } else if (device_.pipeline_resources_ && device_.pipeline_resources_->HasComputePipeline() && device_.pipeline_resources_->GetComputePipelineLayout()) { + VulkanProfileScope dispatch_scope("raytrace.compute_dispatch_record"); command_buffer.bindPipeline( vk::PipelineBindPoint::eCompute, device_.pipeline_resources_->GetComputePipeline()); @@ -159,17 +171,22 @@ void RaytraceSceneRenderer::Render( const std::uint32_t group_x = (extent.width + 7) / 8; const std::uint32_t group_y = (extent.height + 7) / 8; command_buffer.dispatch(group_x, group_y, 1); + RecordVulkanProfileCounter("raytrace.compute_dispatches"); } - transition_output( - vk::ImageLayout::eGeneral, - vk::ImageLayout::eShaderReadOnlyOptimal, - device_.use_raytracing_pipeline_ - ? vk::PipelineStageFlagBits::eRayTracingShaderKHR - : vk::PipelineStageFlagBits::eComputeShader, - vk::PipelineStageFlagBits::eFragmentShader, - vk::AccessFlagBits::eShaderWrite, - vk::AccessFlagBits::eShaderRead); + { + VulkanProfileScope transition_scope( + "raytrace.output_to_shader_read_record"); + transition_output( + vk::ImageLayout::eGeneral, + vk::ImageLayout::eShaderReadOnlyOptimal, + device_.use_raytracing_pipeline_ + ? vk::PipelineStageFlagBits::eRayTracingShaderKHR + : vk::PipelineStageFlagBits::eComputeShader, + vk::PipelineStageFlagBits::eFragmentShader, + vk::AccessFlagBits::eShaderWrite, + vk::AccessFlagBits::eShaderRead); + } device_.output_image_resources_->SetComputeOutputInShaderRead(true); } diff --git a/frame/vulkan/renderer.cpp b/frame/vulkan/renderer.cpp index 4db45786..18631393 100644 --- a/frame/vulkan/renderer.cpp +++ b/frame/vulkan/renderer.cpp @@ -12,6 +12,7 @@ #include "frame/camera.h" #include "frame/vulkan/device.h" #include "frame/vulkan/command_resources.h" +#include "frame/vulkan/frame_profiler.h" #include "frame/vulkan/mesh_resources.h" #include "frame/vulkan/output_image_resources.h" #include "frame/vulkan/pipeline_resources.h" @@ -38,13 +39,22 @@ void Renderer::Display(double dt) return; } + VulkanProfileFrame profile_frame(device_.logger_); + VulkanProfileScope display_scope("renderer.display"); + device_.elapsed_time_seconds_ += static_cast(dt); if (device_.level_) { - device_.level_->UpdateLights( - static_cast(device_.elapsed_time_seconds_)); - device_.UpdateRaytraceBuffers(); + { + VulkanProfileScope scope("renderer.update_lights"); + device_.level_->UpdateLights( + static_cast(device_.elapsed_time_seconds_)); + } + { + VulkanProfileScope scope("renderer.update_raytrace_buffers"); + device_.UpdateRaytraceBuffers(); + } } if (!device_.vk_unique_device_ || !device_.swapchain_resources_ || @@ -81,12 +91,16 @@ void Renderer::Display(double dt) const vk::Fence fence = device_.sync_resources_->GetInFlightFence(current_frame_); const VkFence fence_handle = static_cast(fence); - const VkResult wait_result = vkWaitForFences( - static_cast(*device_.vk_unique_device_), - 1, - &fence_handle, - VK_TRUE, - std::numeric_limits::max()); + const VkResult wait_result = [&]() + { + VulkanProfileScope scope("renderer.wait_fence"); + return vkWaitForFences( + static_cast(*device_.vk_unique_device_), + 1, + &fence_handle, + VK_TRUE, + std::numeric_limits::max()); + }(); if (wait_result != VK_SUCCESS) { device_.logger_->error( @@ -100,11 +114,15 @@ void Renderer::Display(double dt) } const auto& swapchain = device_.swapchain_resources_->GetSwapchain(); - auto acquire = device_.vk_unique_device_->acquireNextImageKHR( - *swapchain, - std::numeric_limits::max(), - device_.sync_resources_->GetImageAvailable(current_frame_), - nullptr); + auto acquire = [&]() + { + VulkanProfileScope scope("renderer.acquire_image"); + return device_.vk_unique_device_->acquireNextImageKHR( + *swapchain, + std::numeric_limits::max(), + device_.sync_resources_->GetImageAvailable(current_frame_), + nullptr); + }(); if (acquire.result == vk::Result::eErrorOutOfDateKHR) { @@ -125,10 +143,14 @@ void Renderer::Display(double dt) } const std::uint32_t image_index = acquire.value; - const VkResult reset_result = vkResetFences( - static_cast(*device_.vk_unique_device_), - 1, - &fence_handle); + const VkResult reset_result = [&]() + { + VulkanProfileScope scope("renderer.reset_fence"); + return vkResetFences( + static_cast(*device_.vk_unique_device_), + 1, + &fence_handle); + }(); if (reset_result != VK_SUCCESS) { device_.logger_->error( @@ -143,8 +165,14 @@ void Renderer::Display(double dt) vk::CommandBuffer command_buffer = device_.command_resources_->GetBuffer(current_frame_); - command_buffer.reset(); - RecordCommandBuffer(command_buffer, image_index); + { + VulkanProfileScope scope("renderer.reset_command_buffer"); + command_buffer.reset(); + } + { + VulkanProfileScope scope("renderer.record_command_buffer"); + RecordCommandBuffer(command_buffer, image_index); + } const vk::Semaphore wait_semaphores[] = { device_.sync_resources_->GetImageAvailable(current_frame_)}; @@ -163,11 +191,15 @@ void Renderer::Display(double dt) signal_semaphores); const VkSubmitInfo submit_info_c = submit_info; - const VkResult submit_result = vkQueueSubmit( - static_cast(device_.graphics_queue_), - 1, - &submit_info_c, - fence); + const VkResult submit_result = [&]() + { + VulkanProfileScope scope("renderer.queue_submit"); + return vkQueueSubmit( + static_cast(device_.graphics_queue_), + 1, + &submit_info_c, + fence); + }(); if (submit_result != VK_SUCCESS) { device_.logger_->error( @@ -187,8 +219,11 @@ void Renderer::Display(double dt) &swapchain.get(), &image_index); - const vk::Result present_result = - device_.present_queue_.presentKHR(present_info); + const vk::Result present_result = [&]() + { + VulkanProfileScope scope("renderer.present"); + return device_.present_queue_.presentKHR(present_info); + }(); if (present_result == vk::Result::eErrorOutOfDateKHR || present_result == vk::Result::eSuboptimalKHR) { @@ -218,8 +253,13 @@ void Renderer::RecordCommandBuffer( vk::CommandBuffer command_buffer, std::uint32_t image_index) { + VulkanProfileScope record_scope("renderer.record_body"); + vk::CommandBufferBeginInfo begin_info; - command_buffer.begin(begin_info); + { + VulkanProfileScope scope("renderer.command_begin"); + command_buffer.begin(begin_info); + } const auto extent = device_.swapchain_resources_->GetExtent(); const auto& images = device_.swapchain_resources_->GetImages(); @@ -230,15 +270,26 @@ void Renderer::RecordCommandBuffer( const auto& gui_framebuffers = device_.swapchain_resources_->GetGuiFramebuffers(); - const SceneState scene_state = device_.BuildFrameSceneState(extent); - raytrace_scene_renderer_->UpdateUniformBuffer(scene_state); - raytrace_scene_renderer_->Render(command_buffer, extent); + const SceneState scene_state = [&]() + { + VulkanProfileScope scope("renderer.build_scene_state"); + return device_.BuildFrameSceneState(extent); + }(); + { + VulkanProfileScope scope("renderer.update_uniforms"); + raytrace_scene_renderer_->UpdateUniformBuffer(scene_state); + } + { + VulkanProfileScope scope("renderer.raytrace_render_record"); + raytrace_scene_renderer_->Render(command_buffer, extent); + } std::array clear_values{}; clear_values[0].color = vk::ClearColorValue( std::array{0.1f, 0.1f, 0.1f, 1.0f}); auto draw_scene = [&]() -> bool { + VulkanProfileScope scope("renderer.draw_scene_record"); if (!device_.pipeline_resources_ || !device_.pipeline_resources_->HasGraphicsPipeline()) { @@ -399,6 +450,7 @@ void Renderer::RecordCommandBuffer( bool scene_content_rendered = false; if (render_pass && image_index < framebuffers.size()) { + VulkanProfileScope scope("renderer.graphics_pass_record"); vk::RenderPassBeginInfo render_pass_info( *render_pass, *framebuffers[image_index], @@ -441,6 +493,7 @@ void Renderer::RecordCommandBuffer( image_index < images.size() && extent.width > 0 && extent.height > 0) { + VulkanProfileScope scope("renderer.preview_copy_record"); const vk::Image swapchain_image = images[image_index]; std::array to_copy_barriers = { @@ -524,6 +577,7 @@ void Renderer::RecordCommandBuffer( if (device_.gui_render_callback_ && gui_render_pass && image_index < gui_framebuffers.size()) { + VulkanProfileScope scope("renderer.gui_record"); vk::RenderPassBeginInfo gui_pass_info( *gui_render_pass, *gui_framebuffers[image_index], @@ -564,7 +618,10 @@ void Renderer::RecordCommandBuffer( to_present); } - command_buffer.end(); + { + VulkanProfileScope scope("renderer.command_end"); + command_buffer.end(); + } } } // namespace frame::vulkan