TraceDump: Support capturing Vulkan traces
This commit is contained in:
parent
6bbd399cfd
commit
070e7bf33d
|
@ -44,6 +44,8 @@ class GraphicsSystem {
|
||||||
ui::Window* target_window);
|
ui::Window* target_window);
|
||||||
virtual void Shutdown();
|
virtual void Shutdown();
|
||||||
|
|
||||||
|
virtual std::unique_ptr<xe::ui::RawImage> Capture() { return nullptr; }
|
||||||
|
|
||||||
RegisterFile* register_file() { return ®ister_file_; }
|
RegisterFile* register_file() { return ®ister_file_; }
|
||||||
CommandProcessor* command_processor() const {
|
CommandProcessor* command_processor() const {
|
||||||
return command_processor_.get();
|
return command_processor_.get();
|
||||||
|
|
|
@ -194,7 +194,7 @@ void TraceDump::Run() {
|
||||||
loop_->PostDelayed(
|
loop_->PostDelayed(
|
||||||
[&]() {
|
[&]() {
|
||||||
// Capture.
|
// Capture.
|
||||||
auto raw_image = window_->context()->Capture();
|
auto raw_image = graphics_system_->Capture();
|
||||||
if (!raw_image) {
|
if (!raw_image) {
|
||||||
// Failed to capture anything.
|
// Failed to capture anything.
|
||||||
capture_fence.Signal();
|
capture_fence.Signal();
|
||||||
|
|
|
@ -20,12 +20,16 @@
|
||||||
#include "xenia/gpu/vulkan/vulkan_gpu_flags.h"
|
#include "xenia/gpu/vulkan/vulkan_gpu_flags.h"
|
||||||
#include "xenia/ui/vulkan/vulkan_provider.h"
|
#include "xenia/ui/vulkan/vulkan_provider.h"
|
||||||
#include "xenia/ui/vulkan/vulkan_swap_chain.h"
|
#include "xenia/ui/vulkan/vulkan_swap_chain.h"
|
||||||
|
#include "xenia/ui/vulkan/vulkan_util.h"
|
||||||
#include "xenia/ui/window.h"
|
#include "xenia/ui/window.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace gpu {
|
namespace gpu {
|
||||||
namespace vulkan {
|
namespace vulkan {
|
||||||
|
|
||||||
|
using xe::ui::vulkan::CheckResult;
|
||||||
|
using xe::ui::RawImage;
|
||||||
|
|
||||||
VulkanGraphicsSystem::VulkanGraphicsSystem() {}
|
VulkanGraphicsSystem::VulkanGraphicsSystem() {}
|
||||||
VulkanGraphicsSystem::~VulkanGraphicsSystem() = default;
|
VulkanGraphicsSystem::~VulkanGraphicsSystem() = default;
|
||||||
|
|
||||||
|
@ -42,11 +46,189 @@ X_STATUS VulkanGraphicsSystem::Setup(cpu::Processor* processor,
|
||||||
|
|
||||||
display_context_ = reinterpret_cast<xe::ui::vulkan::VulkanContext*>(
|
display_context_ = reinterpret_cast<xe::ui::vulkan::VulkanContext*>(
|
||||||
target_window->context());
|
target_window->context());
|
||||||
|
device_ = display_context_->device();
|
||||||
|
|
||||||
|
// Create our own command pool we can use for captures.
|
||||||
|
VkCommandPoolCreateInfo create_info = {
|
||||||
|
VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, nullptr,
|
||||||
|
VK_COMMAND_POOL_CREATE_TRANSIENT_BIT |
|
||||||
|
VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
|
||||||
|
device_->queue_family_index(),
|
||||||
|
};
|
||||||
|
auto status =
|
||||||
|
vkCreateCommandPool(*device_, &create_info, nullptr, &command_pool_);
|
||||||
|
CheckResult(status, "vkCreateCommandPool");
|
||||||
|
|
||||||
return X_STATUS_SUCCESS;
|
return X_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanGraphicsSystem::Shutdown() { GraphicsSystem::Shutdown(); }
|
void VulkanGraphicsSystem::Shutdown() {
|
||||||
|
GraphicsSystem::Shutdown();
|
||||||
|
|
||||||
|
vkDestroyCommandPool(*device_, command_pool_, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<RawImage> VulkanGraphicsSystem::Capture() {
|
||||||
|
auto& swap_state = command_processor_->swap_state();
|
||||||
|
std::lock_guard<std::mutex> lock(swap_state.mutex);
|
||||||
|
if (!swap_state.front_buffer_texture) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkResult status = VK_SUCCESS;
|
||||||
|
|
||||||
|
VkCommandBufferAllocateInfo alloc_info = {
|
||||||
|
VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
||||||
|
nullptr,
|
||||||
|
command_pool_,
|
||||||
|
VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
||||||
|
1,
|
||||||
|
};
|
||||||
|
|
||||||
|
VkCommandBuffer cmd;
|
||||||
|
status = vkAllocateCommandBuffers(*device_, &alloc_info, &cmd);
|
||||||
|
CheckResult(status, "vkAllocateCommandBuffers");
|
||||||
|
|
||||||
|
VkCommandBufferBeginInfo begin_info = {
|
||||||
|
VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, nullptr,
|
||||||
|
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, nullptr,
|
||||||
|
};
|
||||||
|
vkBeginCommandBuffer(cmd, &begin_info);
|
||||||
|
|
||||||
|
auto front_buffer =
|
||||||
|
reinterpret_cast<VkImage>(swap_state.front_buffer_texture);
|
||||||
|
|
||||||
|
CreateCaptureBuffer(cmd, {swap_state.width, swap_state.height});
|
||||||
|
|
||||||
|
VkImageMemoryBarrier barrier;
|
||||||
|
std::memset(&barrier, 0, sizeof(VkImageMemoryBarrier));
|
||||||
|
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||||
|
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||||
|
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
||||||
|
barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||||
|
barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||||
|
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
|
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
|
barrier.image = front_buffer;
|
||||||
|
barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
|
||||||
|
vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||||||
|
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0,
|
||||||
|
nullptr, 1, &barrier);
|
||||||
|
|
||||||
|
// Copy front buffer into capture image.
|
||||||
|
VkBufferImageCopy region = {
|
||||||
|
0, 0,
|
||||||
|
0, {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1},
|
||||||
|
{0, 0, 0}, {swap_state.width, swap_state.height, 1},
|
||||||
|
};
|
||||||
|
|
||||||
|
vkCmdCopyImageToBuffer(cmd, front_buffer, VK_IMAGE_LAYOUT_GENERAL,
|
||||||
|
capture_buffer_, 1, ®ion);
|
||||||
|
|
||||||
|
VkBufferMemoryBarrier memory_barrier = {
|
||||||
|
VK_STRUCTURE_TYPE_MEMORY_BARRIER,
|
||||||
|
nullptr,
|
||||||
|
VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||||
|
VK_ACCESS_HOST_READ_BIT | VK_ACCESS_MEMORY_READ_BIT,
|
||||||
|
VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
capture_buffer_,
|
||||||
|
0,
|
||||||
|
VK_WHOLE_SIZE,
|
||||||
|
};
|
||||||
|
vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
||||||
|
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 1,
|
||||||
|
&memory_barrier, 0, nullptr);
|
||||||
|
|
||||||
|
vkEndCommandBuffer(cmd);
|
||||||
|
|
||||||
|
// Submit commands and wait.
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex>(device_->primary_queue_mutex());
|
||||||
|
VkSubmitInfo submit_info = {
|
||||||
|
VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||||
|
nullptr,
|
||||||
|
0,
|
||||||
|
nullptr,
|
||||||
|
nullptr,
|
||||||
|
1,
|
||||||
|
&cmd,
|
||||||
|
0,
|
||||||
|
nullptr,
|
||||||
|
};
|
||||||
|
status = vkQueueSubmit(device_->primary_queue(), 1, &submit_info, nullptr);
|
||||||
|
CheckResult(status, "vkQueueSubmit");
|
||||||
|
|
||||||
|
if (status == VK_SUCCESS) {
|
||||||
|
status = vkQueueWaitIdle(device_->primary_queue());
|
||||||
|
CheckResult(status, "vkQueueWaitIdle");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vkFreeCommandBuffers(*device_, command_pool_, 1, &cmd);
|
||||||
|
|
||||||
|
void* data;
|
||||||
|
status =
|
||||||
|
vkMapMemory(*device_, capture_buffer_memory_, 0, VK_WHOLE_SIZE, 0, &data);
|
||||||
|
CheckResult(status, "vkMapMemory");
|
||||||
|
if (status == VK_SUCCESS) {
|
||||||
|
std::unique_ptr<RawImage> raw_image(new RawImage());
|
||||||
|
raw_image->width = swap_state.width;
|
||||||
|
raw_image->height = swap_state.height;
|
||||||
|
raw_image->stride = swap_state.width * 4;
|
||||||
|
raw_image->data.resize(raw_image->stride * raw_image->height);
|
||||||
|
|
||||||
|
std::memcpy(raw_image->data.data(), data,
|
||||||
|
raw_image->stride * raw_image->height);
|
||||||
|
|
||||||
|
vkUnmapMemory(*device_, capture_buffer_memory_);
|
||||||
|
DestroyCaptureBuffer();
|
||||||
|
return raw_image;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkResult VulkanGraphicsSystem::CreateCaptureBuffer(VkCommandBuffer cmd,
|
||||||
|
VkExtent2D extents) {
|
||||||
|
VkBufferCreateInfo buffer_info = {
|
||||||
|
VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||||
|
nullptr,
|
||||||
|
0,
|
||||||
|
extents.width * extents.height * 4,
|
||||||
|
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||||||
|
VK_SHARING_MODE_EXCLUSIVE,
|
||||||
|
0,
|
||||||
|
nullptr,
|
||||||
|
};
|
||||||
|
auto status =
|
||||||
|
vkCreateBuffer(*device_, &buffer_info, nullptr, &capture_buffer_);
|
||||||
|
|
||||||
|
capture_buffer_size_ = extents.width * extents.height * 4;
|
||||||
|
|
||||||
|
// Bind memory to buffer.
|
||||||
|
VkMemoryRequirements mem_requirements;
|
||||||
|
vkGetBufferMemoryRequirements(*device_, capture_buffer_, &mem_requirements);
|
||||||
|
capture_buffer_memory_ =
|
||||||
|
device_->AllocateMemory(mem_requirements,
|
||||||
|
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
|
||||||
|
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
|
||||||
|
assert_not_null(capture_buffer_memory_);
|
||||||
|
|
||||||
|
status =
|
||||||
|
vkBindBufferMemory(*device_, capture_buffer_, capture_buffer_memory_, 0);
|
||||||
|
CheckResult(status, "vkBindImageMemory");
|
||||||
|
|
||||||
|
return VK_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanGraphicsSystem::DestroyCaptureBuffer() {
|
||||||
|
vkDestroyBuffer(*device_, capture_buffer_, nullptr);
|
||||||
|
vkFreeMemory(*device_, capture_buffer_memory_, nullptr);
|
||||||
|
capture_buffer_ = nullptr;
|
||||||
|
capture_buffer_memory_ = nullptr;
|
||||||
|
capture_buffer_size_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<CommandProcessor>
|
std::unique_ptr<CommandProcessor>
|
||||||
VulkanGraphicsSystem::CreateCommandProcessor() {
|
VulkanGraphicsSystem::CreateCommandProcessor() {
|
||||||
|
|
|
@ -28,12 +28,23 @@ class VulkanGraphicsSystem : public GraphicsSystem {
|
||||||
ui::Window* target_window) override;
|
ui::Window* target_window) override;
|
||||||
void Shutdown() override;
|
void Shutdown() override;
|
||||||
|
|
||||||
private:
|
std::unique_ptr<xe::ui::RawImage> Capture() override;
|
||||||
std::unique_ptr<CommandProcessor> CreateCommandProcessor() override;
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
VkResult CreateCaptureBuffer(VkCommandBuffer cmd, VkExtent2D extents);
|
||||||
|
void DestroyCaptureBuffer();
|
||||||
|
|
||||||
|
std::unique_ptr<CommandProcessor> CreateCommandProcessor() override;
|
||||||
void Swap(xe::ui::UIEvent* e) override;
|
void Swap(xe::ui::UIEvent* e) override;
|
||||||
|
|
||||||
|
xe::ui::vulkan::VulkanDevice* device_ = nullptr;
|
||||||
xe::ui::vulkan::VulkanContext* display_context_ = nullptr;
|
xe::ui::vulkan::VulkanContext* display_context_ = nullptr;
|
||||||
|
|
||||||
|
VkCommandPool command_pool_ = nullptr;
|
||||||
|
|
||||||
|
VkBuffer capture_buffer_ = nullptr;
|
||||||
|
VkDeviceMemory capture_buffer_memory_ = nullptr;
|
||||||
|
VkDeviceSize capture_buffer_size_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace vulkan
|
} // namespace vulkan
|
||||||
|
|
Loading…
Reference in New Issue