Skip to content

Commit

Permalink
Enable trimming to queue submission range
Browse files Browse the repository at this point in the history
Enable trimming based on a range of queue submissions--vkQueueSubmit for
Vulkan, ExecuteCommandLists for DX12. Trimming is activated immediately
before the first submission and deactivated immediately after the last
submission in the range(s) specified.
  • Loading branch information
davidd-lunarg committed Aug 14, 2023
1 parent 92837c5 commit 891c9d5
Show file tree
Hide file tree
Showing 9 changed files with 154 additions and 33 deletions.
3 changes: 2 additions & 1 deletion USAGE_desktop_D3D12.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ Option | Environment Variable | Type | Description
Capture File Name | GFXRECON_CAPTURE_FILE | STRING | Path to use when creating the capture file. Default is: `gfxrecon_capture.gfxr`
Capture Specific Frames | GFXRECON_CAPTURE_FRAMES | STRING | Specify one or more comma-separated frame ranges to capture. Each range will be written to its own file. A frame range can be specified as a single value, to specify a single frame to capture, or as two hyphenated values, to specify the first and last frame to capture. Frame ranges should be specified in ascending order and cannot overlap. Note that frame numbering is 1-based (i.e. the first frame is frame 1). Example: `200,301-305` will create two capture files, one containing a single frame and one containing five frames. Default is: Empty string (all frames are captured).
Hotkey Capture Trigger | GFXRECON_CAPTURE_TRIGGER | STRING | Specify a hotkey (any one of F1-F12, TAB, CONTROL) that will be used to start/stop capture. Example: `F3` will set the capture trigger to F3 hotkey. One capture file will be generated for each pair of start/stop hotkey presses. Default is: Empty string (hotkey capture trigger is disabled).
Hotkey Capture Trigger | GFXRECON_CAPTURE_TRIGGER_FRAMES | STRING | Specify a limit on the number of frames to be captured via hotkey. Example: `1` will capture exactly one frame when the trigger key is pressed. Default is: Empty string (no limit)
Hotkey Capture Trigger Frames | GFXRECON_CAPTURE_TRIGGER_FRAMES | STRING | Specify a limit on the number of frames to be captured via hotkey. Example: `1` will capture exactly one frame when the trigger key is pressed. Default is: Empty string (no limit)
Capture Specific GPU Queue Submits | GFXRECON_CAPTURE_QUEUE_SUBMITS | STRING | Specify one or more comma-separated GPU queue submit call ranges to capture. Queue submit calls are `vkQueueSubmit` for Vulkan and `ID3D12CommandQueue::ExecuteCommandLists` for DX12. Queue submit ranges work as described above in `GFXRECON_CAPTURE_FRAMES` but on GPU queue submit calls instead of frames. Default is: Empty string (all queue submits are captured).
Capture File Compression Type | GFXRECON_CAPTURE_COMPRESSION_TYPE | STRING | Compression format to use with the capture file. Valid values are: `LZ4`, `ZLIB`, `ZSTD`, and `NONE`. Default is: `LZ4`
Capture File Timestamp | GFXRECON_CAPTURE_FILE_TIMESTAMP | BOOL | Add a timestamp to the capture file as described by [Timestamps](#timestamps). Default is: `true`
Capture File Flush After Write | GFXRECON_CAPTURE_FILE_FLUSH | BOOL | Flush output stream after each packet is written to the capture file. Default is: `false`
Expand Down
3 changes: 2 additions & 1 deletion USAGE_desktop_Vulkan.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,8 @@ Option | Environment Variable | Type | Description
Capture File Name | GFXRECON_CAPTURE_FILE | STRING | Path to use when creating the capture file. Default is: `gfxrecon_capture.gfxr`
Capture Specific Frames | GFXRECON_CAPTURE_FRAMES | STRING | Specify one or more comma-separated frame ranges to capture. Each range will be written to its own file. A frame range can be specified as a single value, to specify a single frame to capture, or as two hyphenated values, to specify the first and last frame to capture. Frame ranges should be specified in ascending order and cannot overlap. Note that frame numbering is 1-based (i.e. the first frame is frame 1). Example: `200,301-305` will create two capture files, one containing a single frame and one containing five frames. Default is: Empty string (all frames are captured).
Hotkey Capture Trigger | GFXRECON_CAPTURE_TRIGGER | STRING | Specify a hotkey (any one of F1-F12, TAB, CONTROL) that will be used to start/stop capture. Example: `F3` will set the capture trigger to F3 hotkey. One capture file will be generated for each pair of start/stop hotkey presses. Default is: Empty string (hotkey capture trigger is disabled).
Hotkey Capture Trigger | GFXRECON_CAPTURE_TRIGGER_FRAMES | STRING | Specify a limit on the number of frames to be captured via hotkey. Example: `1` will capture exactly one frame when the trigger key is pressed. Default is: Empty string (no limit)
Hotkey Capture Trigger Frames | GFXRECON_CAPTURE_TRIGGER_FRAMES | STRING | Specify a limit on the number of frames to be captured via hotkey. Example: `1` will capture exactly one frame when the trigger key is pressed. Default is: Empty string (no limit)
Capture Specific GPU Queue Submits | GFXRECON_CAPTURE_QUEUE_SUBMITS | STRING | Specify one or more comma-separated GPU queue submit call ranges to capture. Queue submit calls are `vkQueueSubmit` for Vulkan and `ID3D12CommandQueue::ExecuteCommandLists` for DX12. Queue submit ranges work as described above in `GFXRECON_CAPTURE_FRAMES` but on GPU queue submit calls instead of frames. Default is: Empty string (all queue submits are captured).
Capture File Compression Type | GFXRECON_CAPTURE_COMPRESSION_TYPE | STRING | Compression format to use with the capture file. Valid values are: `LZ4`, `ZLIB`, `ZSTD`, and `NONE`. Default is: `LZ4`
Capture File Timestamp | GFXRECON_CAPTURE_FILE_TIMESTAMP | BOOL | Add a timestamp to the capture file as described by [Timestamps](#timestamps). Default is: `true`
Capture File Flush After Write | GFXRECON_CAPTURE_FILE_FLUSH | BOOL | Flush output stream after each packet is written to the capture file. Default is: `false`
Expand Down
99 changes: 76 additions & 23 deletions framework/encode/capture_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,9 @@ CaptureManager::CaptureManager(format::ApiFamilyId api_family) :
api_family_(api_family), force_file_flush_(false), timestamp_filename_(true),
memory_tracking_mode_(CaptureSettings::MemoryTrackingMode::kPageGuard), page_guard_align_buffer_sizes_(false),
page_guard_track_ahb_memory_(false), page_guard_unblock_sigsegv_(false), page_guard_signal_handler_watcher_(false),
page_guard_memory_mode_(kMemoryModeShadowInternal), trim_enabled_(false), trim_current_range_(0),
current_frame_(kFirstFrame), capture_mode_(kModeWrite), previous_hotkey_state_(false),
page_guard_memory_mode_(kMemoryModeShadowInternal), trim_enabled_(false),
trim_boundary_(CaptureSettings::TrimBoundary::kUnknown), trim_current_range_(0), current_frame_(kFirstFrame),
queue_submit_count_(0), capture_mode_(kModeWrite), previous_hotkey_state_(false),
previous_runtime_trigger_state_(CaptureSettings::RuntimeTriggerState::kNotUsed), debug_layer_(false),
debug_device_lost_(false), screenshot_prefix_(""), screenshots_enabled_(false), disable_dxr_(false),
accel_struct_padding_(0), iunknown_wrapping_(false), force_command_serialization_(false), queue_zero_only_(false),
Expand Down Expand Up @@ -321,15 +322,22 @@ bool CaptureManager::Initialize(std::string base_filename, const CaptureSettings
}
else
{
GFXRECON_ASSERT(trace_settings.trim_boundary != CaptureSettings::TrimBoundary::kUnknown);

// Override default kModeWrite capture mode.
trim_enabled_ = true;
trim_enabled_ = true;
trim_boundary_ = trace_settings.trim_boundary;

// Determine if trim starts at the first frame
// Check if trim ranges were specified.
if (!trace_settings.trim_ranges.empty())
{
GFXRECON_ASSERT((trim_boundary_ == CaptureSettings::TrimBoundary::kFrames) ||
(trim_boundary_ == CaptureSettings::TrimBoundary::kQueueSubmits));

trim_ranges_ = trace_settings.trim_ranges;

if (trim_ranges_[0].first == current_frame_)
// Determine if trim starts at the first frame
if ((trim_boundary_ == CaptureSettings::TrimBoundary::kFrames) && (trim_ranges_[0].first == current_frame_))
{
// When capturing from the first frame, state tracking only needs to be enabled if there is more than
// one capture range.
Expand All @@ -345,10 +353,13 @@ bool CaptureManager::Initialize(std::string base_filename, const CaptureSettings
capture_mode_ = kModeTrack;
}
}
// Check if trim is enabled by hot-key trigger at the first frame
// Check if trim is enabled by hot-key trigger at the first frame.
else if (!trace_settings.trim_key.empty() ||
trace_settings.runtime_capture_trigger != CaptureSettings::RuntimeTriggerState::kNotUsed)
{
// Capture key/trigger only support frames as trim boundaries.
GFXRECON_ASSERT(trim_boundary_ == CaptureSettings::TrimBoundary::kFrames);

trim_key_ = trace_settings.trim_key;
trim_key_frames_ = trace_settings.trim_key_frames;
previous_runtime_trigger_state_ = trace_settings.runtime_capture_trigger;
Expand All @@ -369,7 +380,10 @@ bool CaptureManager::Initialize(std::string base_filename, const CaptureSettings
}
else
{
capture_mode_ = kModeTrack;
// if/else blocks above should have covered all "else" cases from the parent conditional.
GFXRECON_ASSERT(false);
trim_boundary_ = CaptureSettings::TrimBoundary::kUnknown;
capture_mode_ = kModeTrack;
}
}

Expand Down Expand Up @@ -592,11 +606,11 @@ bool CaptureManager::RuntimeTriggerDisabled()
return result;
}

void CaptureManager::CheckContinueCaptureForWriteMode()
void CaptureManager::CheckContinueCaptureForWriteMode(uint32_t current_boundary_count)
{
if (!trim_ranges_.empty())
{
if (current_frame_ == (trim_ranges_[trim_current_range_].last + 1))
if (current_boundary_count == (trim_ranges_[trim_current_range_].last + 1))
{
// Stop recording and close file.
DeactivateTrimming();
Expand All @@ -606,16 +620,17 @@ void CaptureManager::CheckContinueCaptureForWriteMode()
++trim_current_range_;
if (trim_current_range_ >= trim_ranges_.size())
{
// No more frames to capture. Capture can be disabled and resources can be released.
trim_enabled_ = false;
capture_mode_ = kModeDisabled;
// No more trim ranges to capture. Capture can be disabled and resources can be released.
trim_enabled_ = false;
trim_boundary_ = CaptureSettings::TrimBoundary::kUnknown;
capture_mode_ = kModeDisabled;
DestroyStateTracker();
compressor_ = nullptr;
}
else if (trim_ranges_[trim_current_range_].first == current_frame_)
else if (trim_ranges_[trim_current_range_].first == current_boundary_count)
{
// Trimming was configured to capture two consecutive frames, so we need to start a new capture
// file for the current frame.
// Trimming was configured to capture two consecutive ranges, so we need to start a new capture
// file for the current range.
const auto& trim_range = trim_ranges_[trim_current_range_];
bool success = CreateCaptureFile(CreateTrimFilename(base_filename_, trim_range));
if (success)
Expand All @@ -632,7 +647,7 @@ void CaptureManager::CheckContinueCaptureForWriteMode()
}
}
else if (IsTrimHotkeyPressed() ||
((trim_key_frames_ > 0) && (current_frame_ >= (trim_key_first_frame_ + trim_key_frames_))) ||
((trim_key_frames_ > 0) && (current_boundary_count >= (trim_key_first_frame_ + trim_key_frames_))) ||
RuntimeTriggerDisabled())
{
// Stop recording and close file.
Expand All @@ -641,11 +656,11 @@ void CaptureManager::CheckContinueCaptureForWriteMode()
}
}

void CaptureManager::CheckStartCaptureForTrackMode()
void CaptureManager::CheckStartCaptureForTrackMode(uint32_t current_boundary_count)
{
if (!trim_ranges_.empty())
{
if (current_frame_ == trim_ranges_[trim_current_range_].first)
if (current_boundary_count == trim_ranges_[trim_current_range_].first)
{
const auto& trim_range = trim_ranges_[trim_current_range_];
bool success = CreateCaptureFile(CreateTrimFilename(base_filename_, trim_range));
Expand All @@ -667,7 +682,7 @@ void CaptureManager::CheckStartCaptureForTrackMode()
if (success)
{

trim_key_first_frame_ = current_frame_;
trim_key_first_frame_ = current_boundary_count;
ActivateTrimming();
}
else
Expand Down Expand Up @@ -728,19 +743,19 @@ void CaptureManager::EndFrame()

++current_frame_;

if (trim_enabled_)
if (trim_enabled_ && (trim_boundary_ == CaptureSettings::TrimBoundary::kFrames))
{
if ((capture_mode_ & kModeWrite) == kModeWrite)
{
// Currently capturing a frame range.
// Check for end of range or hotkey trigger to stop capture.
CheckContinueCaptureForWriteMode();
CheckContinueCaptureForWriteMode(current_frame_);
}
else if ((capture_mode_ & kModeTrack) == kModeTrack)
{
// Capture is not active.
// Check for start of capture frame range or hotkey trigger to start capture
CheckStartCaptureForTrackMode();
CheckStartCaptureForTrackMode(current_frame_);
}
}

Expand All @@ -751,14 +766,52 @@ void CaptureManager::EndFrame()
}
}

void CaptureManager::PreQueueSubmit()
{
++queue_submit_count_;

if (trim_enabled_ && (trim_boundary_ == CaptureSettings::TrimBoundary::kQueueSubmits))
{
if (((capture_mode_ & kModeWrite) != kModeWrite) && ((capture_mode_ & kModeTrack) == kModeTrack))
{
// Capture is not active, check for start of capture frame range.
CheckStartCaptureForTrackMode(queue_submit_count_);
}
}
}

void CaptureManager::PostQueueSubmit()
{
if (trim_enabled_ && (trim_boundary_ == CaptureSettings::TrimBoundary::kQueueSubmits))
{
if ((capture_mode_ & kModeWrite) == kModeWrite)
{
// Currently capturing a queue submit range, check for end of range.
CheckContinueCaptureForWriteMode(queue_submit_count_);
}
}
}

std::string CaptureManager::CreateTrimFilename(const std::string& base_filename, const util::UintRange& trim_range)
{
GFXRECON_ASSERT(trim_range.last >= trim_range.first);

std::string range_string = "_";

uint32_t total = trim_range.last - trim_range.first + 1;
const char* boundary_str = total > 1 ? "frames_" : "frame_";
const char* boundary_str = "";
switch (trim_boundary_)
{
case CaptureSettings::TrimBoundary::kFrames:
boundary_str = total > 1 ? "frames_" : "frame_";
break;
case CaptureSettings::TrimBoundary::kQueueSubmits:
boundary_str = total > 1 ? "queue_submits_" : "queue_submit_";
break;
default:
GFXRECON_ASSERT(false);
break;
}

range_string += boundary_str;
range_string += std::to_string(trim_range.first);
Expand Down
11 changes: 9 additions & 2 deletions framework/encode/capture_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,18 @@ class CaptureManager

void EndFrame();

// Pre/PostQueueSubmit to be called immediately before and after work is submitted to the GPU by vkQueueSubmit for
// Vulkan or by ID3D12CommandQueue::ExecuteCommandLists for DX12.
void PreQueueSubmit();
void PostQueueSubmit();

bool ShouldTriggerScreenshot();

util::ScreenshotFormat GetScreenshotFormat() { return screenshot_format_; }

void CheckContinueCaptureForWriteMode();
void CheckContinueCaptureForWriteMode(uint32_t current_boundary_count);

void CheckStartCaptureForTrackMode();
void CheckStartCaptureForTrackMode(uint32_t current_boundary_count);

bool IsTrimHotkeyPressed();

Expand Down Expand Up @@ -318,12 +323,14 @@ class CaptureManager
bool page_guard_signal_handler_watcher_;
PageGuardMemoryMode page_guard_memory_mode_;
bool trim_enabled_;
CaptureSettings::TrimBoundary trim_boundary_;
std::vector<util::UintRange> trim_ranges_;
std::string trim_key_;
uint32_t trim_key_frames_;
uint32_t trim_key_first_frame_;
size_t trim_current_range_;
uint32_t current_frame_;
uint32_t queue_submit_count_;
CaptureMode capture_mode_;
bool previous_hotkey_state_;
CaptureSettings::RuntimeTriggerState previous_runtime_trigger_state_;
Expand Down
Loading

0 comments on commit 891c9d5

Please sign in to comment.