[GPU] GraphicsSystem now owns swap_state
Swapping in Qt windows now
This commit is contained in:
parent
8bcd7637e5
commit
0f2c8a4e64
|
@ -28,6 +28,71 @@ DEFINE_bool(fullscreen, false, "Toggles fullscreen");
|
|||
namespace xe {
|
||||
namespace app {
|
||||
|
||||
class VulkanWindow : public QVulkanWindow {
|
||||
public:
|
||||
VulkanWindow(EmulatorWindow* parent) : window_(parent) {}
|
||||
QVulkanWindowRenderer* createRenderer() override;
|
||||
|
||||
private:
|
||||
EmulatorWindow* window_;
|
||||
};
|
||||
|
||||
class VulkanRenderer : public QVulkanWindowRenderer {
|
||||
public:
|
||||
VulkanRenderer(VulkanWindow* window, xe::Emulator* emulator)
|
||||
: window_(window), emulator_(emulator) {}
|
||||
|
||||
void startNextFrame() override {
|
||||
// Copy the graphics frontbuffer to our backbuffer.
|
||||
auto swap_state = emulator_->graphics_system()->swap_state();
|
||||
|
||||
auto cmd = window_->currentCommandBuffer();
|
||||
auto src = reinterpret_cast<VkImage>(swap_state->front_buffer_texture);
|
||||
auto dest = window_->swapChainImage(window_->currentSwapChainImageIndex());
|
||||
auto dest_size = window_->swapChainImageSize();
|
||||
|
||||
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 = src;
|
||||
barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
|
||||
vkCmdPipelineBarrier(cmd, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0,
|
||||
nullptr, 1, &barrier);
|
||||
|
||||
VkImageBlit region;
|
||||
region.srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
|
||||
region.srcOffsets[0] = {0, 0, 0};
|
||||
region.srcOffsets[1] = {static_cast<int32_t>(swap_state->width),
|
||||
static_cast<int32_t>(swap_state->height), 1};
|
||||
|
||||
region.dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
|
||||
region.dstOffsets[0] = {0, 0, 0};
|
||||
region.dstOffsets[1] = {static_cast<int32_t>(dest_size.width()),
|
||||
static_cast<int32_t>(dest_size.height()), 1};
|
||||
vkCmdBlitImage(cmd, src, VK_IMAGE_LAYOUT_GENERAL, dest,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion,
|
||||
VK_FILTER_LINEAR);
|
||||
|
||||
swap_state->pending = false;
|
||||
window_->frameReady();
|
||||
}
|
||||
|
||||
private:
|
||||
xe::Emulator* emulator_;
|
||||
VulkanWindow* window_;
|
||||
};
|
||||
|
||||
QVulkanWindowRenderer* VulkanWindow::createRenderer() {
|
||||
return new VulkanRenderer(this, window_->emulator());
|
||||
}
|
||||
|
||||
EmulatorWindow::EmulatorWindow() {}
|
||||
|
||||
bool EmulatorWindow::Setup() {
|
||||
|
@ -52,8 +117,10 @@ bool EmulatorWindow::Setup() {
|
|||
return std::unique_ptr<gpu::vulkan::VulkanGraphicsSystem>(nullptr);
|
||||
}
|
||||
|
||||
graphics->SetSwapCallback(
|
||||
[&]() { this->graphics_window_->requestUpdate(); });
|
||||
graphics->SetSwapCallback([&]() {
|
||||
QMetaObject::invokeMethod(this->graphics_window_.get(), "requestUpdate",
|
||||
Qt::QueuedConnection);
|
||||
});
|
||||
return graphics;
|
||||
};
|
||||
|
||||
|
@ -72,7 +139,7 @@ bool EmulatorWindow::InitializeVulkan() {
|
|||
return false;
|
||||
}
|
||||
|
||||
graphics_window_ = std::make_unique<QVulkanWindow>();
|
||||
graphics_window_ = std::make_unique<VulkanWindow>(this);
|
||||
graphics_window_->setVulkanInstance(vulkan_instance_.get());
|
||||
|
||||
// Now set the graphics window as our central widget.
|
||||
|
@ -83,6 +150,8 @@ bool EmulatorWindow::InitializeVulkan() {
|
|||
return true;
|
||||
}
|
||||
|
||||
void EmulatorWindow::SwapVulkan() {}
|
||||
|
||||
bool EmulatorWindow::Launch(const std::wstring& path) {
|
||||
return emulator_->LaunchPath(path) == X_STATUS_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,9 @@
|
|||
namespace xe {
|
||||
namespace app {
|
||||
|
||||
class VulkanWindow;
|
||||
class VulkanRenderer;
|
||||
|
||||
class EmulatorWindow : public QMainWindow {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -29,8 +32,11 @@ class EmulatorWindow : public QMainWindow {
|
|||
|
||||
bool Setup();
|
||||
bool InitializeVulkan();
|
||||
void SwapVulkan();
|
||||
bool Launch(const std::wstring& path);
|
||||
|
||||
xe::Emulator* emulator() { return emulator_.get(); }
|
||||
|
||||
protected:
|
||||
// Events
|
||||
|
||||
|
|
|
@ -189,7 +189,7 @@ void CommandProcessor::Pause() {
|
|||
});
|
||||
|
||||
// HACK - Prevents a hang in IssueSwap()
|
||||
swap_state_.pending = false;
|
||||
graphics_system_->swap_state()->pending = false;
|
||||
|
||||
fence.Wait();
|
||||
}
|
||||
|
@ -377,10 +377,11 @@ void CommandProcessor::IssueSwap(uint32_t frontbuffer_ptr,
|
|||
// This prevents the display from pulling the backbuffer out from under us.
|
||||
// If we skip a lot then we may need to buffer more, but as the display
|
||||
// thread should be fairly idle that shouldn't happen.
|
||||
auto swap_state = graphics_system_->swap_state();
|
||||
if (!FLAGS_vsync) {
|
||||
std::lock_guard<std::mutex> lock(swap_state_.mutex);
|
||||
if (swap_state_.pending) {
|
||||
swap_state_.pending = false;
|
||||
std::lock_guard<std::mutex> lock(swap_state->mutex);
|
||||
if (swap_state->pending) {
|
||||
swap_state->pending = false;
|
||||
// TODO(benvanik): frame skip counter.
|
||||
XELOGW("Skipped frame!");
|
||||
}
|
||||
|
@ -388,8 +389,8 @@ void CommandProcessor::IssueSwap(uint32_t frontbuffer_ptr,
|
|||
// Spin until no more pending swap.
|
||||
while (worker_running_) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(swap_state_.mutex);
|
||||
if (!swap_state_.pending) {
|
||||
std::lock_guard<std::mutex> lock(swap_state->mutex);
|
||||
if (!swap_state->pending) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -401,8 +402,8 @@ void CommandProcessor::IssueSwap(uint32_t frontbuffer_ptr,
|
|||
|
||||
{
|
||||
// Set pending so that the display will swap the next time it can.
|
||||
std::lock_guard<std::mutex> lock(swap_state_.mutex);
|
||||
swap_state_.pending = true;
|
||||
std::lock_guard<std::mutex> lock(swap_state->mutex);
|
||||
swap_state->pending = true;
|
||||
}
|
||||
|
||||
// Notify the display a swap is pending so that our changes are picked up.
|
||||
|
@ -799,7 +800,7 @@ bool CommandProcessor::ExecutePacketType3_XE_SWAP(RingBuffer* reader,
|
|||
uint32_t frontbuffer_height = reader->ReadAndSwap<uint32_t>();
|
||||
reader->AdvanceRead((count - 4) * sizeof(uint32_t));
|
||||
|
||||
if (swap_mode_ == SwapMode::kNormal) {
|
||||
if (graphics_system_->swap_mode() == SwapMode::kNormal) {
|
||||
IssueSwap(frontbuffer_ptr, frontbuffer_width, frontbuffer_height);
|
||||
}
|
||||
|
||||
|
|
|
@ -37,27 +37,6 @@ namespace gpu {
|
|||
class GraphicsSystem;
|
||||
class Shader;
|
||||
|
||||
struct SwapState {
|
||||
// Lock must be held when changing data in this structure.
|
||||
std::mutex mutex;
|
||||
// Dimensions of the framebuffer textures. Should match window size.
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
// Current front buffer, being drawn to the screen.
|
||||
uintptr_t front_buffer_texture = 0;
|
||||
// Current back buffer, being updated by the CP.
|
||||
uintptr_t back_buffer_texture = 0;
|
||||
// Backend data
|
||||
void* backend_data = nullptr;
|
||||
// Whether the back buffer is dirty and a swap is pending.
|
||||
bool pending = false;
|
||||
};
|
||||
|
||||
enum class SwapMode {
|
||||
kNormal,
|
||||
kIgnored,
|
||||
};
|
||||
|
||||
enum class GammaRampType {
|
||||
kUnknown = 0,
|
||||
kNormal,
|
||||
|
@ -121,8 +100,6 @@ class CommandProcessor {
|
|||
|
||||
virtual void ClearCaches();
|
||||
|
||||
SwapState& swap_state() { return swap_state_; }
|
||||
void set_swap_mode(SwapMode swap_mode) { swap_mode_ = swap_mode; }
|
||||
void IssueSwap(uint32_t frontbuffer_ptr, uint32_t frontbuffer_width,
|
||||
uint32_t frontbuffer_height);
|
||||
|
||||
|
@ -256,8 +233,6 @@ class CommandProcessor {
|
|||
kernel::object_ref<kernel::XHostThread> worker_thread_;
|
||||
|
||||
std::unique_ptr<ui::GraphicsContext> context_;
|
||||
SwapMode swap_mode_ = SwapMode::kNormal;
|
||||
SwapState swap_state_;
|
||||
std::function<void()> swap_request_handler_;
|
||||
std::queue<std::function<void()>> pending_fns_;
|
||||
|
||||
|
|
|
@ -29,6 +29,27 @@ class Emulator;
|
|||
namespace xe {
|
||||
namespace gpu {
|
||||
|
||||
struct SwapState {
|
||||
// Lock must be held when changing data in this structure.
|
||||
std::mutex mutex;
|
||||
// Dimensions of the framebuffer textures. Should match window size.
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
// Current front buffer, being drawn to the screen.
|
||||
uintptr_t front_buffer_texture = 0;
|
||||
// Current back buffer, being updated by the CP.
|
||||
uintptr_t back_buffer_texture = 0;
|
||||
// Backend data
|
||||
void* backend_data = nullptr;
|
||||
// Whether the back buffer is dirty and a swap is pending.
|
||||
bool pending = false;
|
||||
};
|
||||
|
||||
enum class SwapMode {
|
||||
kNormal,
|
||||
kIgnored,
|
||||
};
|
||||
|
||||
class GraphicsSystem {
|
||||
public:
|
||||
virtual ~GraphicsSystem();
|
||||
|
@ -62,7 +83,10 @@ class GraphicsSystem {
|
|||
virtual void ClearCaches();
|
||||
|
||||
void SetSwapCallback(std::function<void()> fn);
|
||||
gpu::SwapState& swap_state() { return command_processor_->swap_state(); }
|
||||
virtual SwapState* swap_state() = 0;
|
||||
|
||||
SwapMode swap_mode() { return swap_mode_; }
|
||||
void set_swap_mode(SwapMode swap_mode) { swap_mode_ = swap_mode; }
|
||||
|
||||
void RequestFrameTrace();
|
||||
void BeginTracing();
|
||||
|
@ -98,6 +122,8 @@ class GraphicsSystem {
|
|||
uint32_t interrupt_callback_ = 0;
|
||||
uint32_t interrupt_callback_data_ = 0;
|
||||
|
||||
SwapMode swap_mode_ = SwapMode::kNormal;
|
||||
|
||||
std::atomic<bool> vsync_worker_running_;
|
||||
kernel::object_ref<kernel::XHostThread> vsync_worker_thread_;
|
||||
|
||||
|
|
|
@ -39,9 +39,8 @@ void NullGraphicsSystem::Swap(xe::ui::UIEvent* e) {
|
|||
return;
|
||||
}
|
||||
|
||||
auto& swap_state = command_processor_->swap_state();
|
||||
std::lock_guard<std::mutex> lock(swap_state.mutex);
|
||||
swap_state.pending = false;
|
||||
std::lock_guard<std::mutex> lock(swap_state_.mutex);
|
||||
swap_state_.pending = false;
|
||||
}
|
||||
|
||||
} // namespace null
|
||||
|
|
|
@ -25,6 +25,7 @@ class NullGraphicsSystem : public GraphicsSystem {
|
|||
~NullGraphicsSystem() override;
|
||||
|
||||
std::wstring name() const override { return L"null"; }
|
||||
SwapState* swap_state() override { return &swap_state_; }
|
||||
|
||||
X_STATUS Setup(cpu::Processor* processor, kernel::KernelState* kernel_state,
|
||||
std::unique_ptr<ui::GraphicsContext> context) override;
|
||||
|
@ -34,6 +35,7 @@ class NullGraphicsSystem : public GraphicsSystem {
|
|||
std::unique_ptr<CommandProcessor> CreateCommandProcessor() override;
|
||||
|
||||
void Swap(xe::ui::UIEvent* e) override;
|
||||
SwapState swap_state_ = {};
|
||||
};
|
||||
|
||||
} // namespace null
|
||||
|
|
|
@ -103,7 +103,7 @@ void TracePlayer::PlayTraceOnThread(const uint8_t* trace_data,
|
|||
command_processor->ClearCaches();
|
||||
}
|
||||
|
||||
command_processor->set_swap_mode(SwapMode::kIgnored);
|
||||
graphics_system_->set_swap_mode(SwapMode::kIgnored);
|
||||
playback_percent_ = 0;
|
||||
auto trace_end = trace_data + trace_size;
|
||||
|
||||
|
@ -200,7 +200,7 @@ void TracePlayer::PlayTraceOnThread(const uint8_t* trace_data,
|
|||
}
|
||||
|
||||
playing_trace_ = false;
|
||||
command_processor->set_swap_mode(SwapMode::kNormal);
|
||||
graphics_system_->set_swap_mode(SwapMode::kNormal);
|
||||
command_processor->IssueSwap(0, 1280, 720);
|
||||
|
||||
playback_event_->Set();
|
||||
|
|
|
@ -128,11 +128,6 @@ bool VulkanCommandProcessor::SetupContext() {
|
|||
void VulkanCommandProcessor::ShutdownContext() {
|
||||
// TODO(benvanik): wait until idle.
|
||||
|
||||
if (swap_state_.front_buffer_texture) {
|
||||
// Free swap chain image.
|
||||
DestroySwapImage();
|
||||
}
|
||||
|
||||
buffer_cache_.reset();
|
||||
pipeline_cache_.reset();
|
||||
render_cache_.reset();
|
||||
|
@ -229,105 +224,6 @@ void VulkanCommandProcessor::WriteRegister(uint32_t index, uint32_t value) {
|
|||
}
|
||||
}
|
||||
|
||||
void VulkanCommandProcessor::CreateSwapImage(VkCommandBuffer setup_buffer,
|
||||
VkExtent2D extents) {
|
||||
VkImageCreateInfo image_info;
|
||||
std::memset(&image_info, 0, sizeof(VkImageCreateInfo));
|
||||
image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
image_info.imageType = VK_IMAGE_TYPE_2D;
|
||||
image_info.format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||
image_info.extent = {extents.width, extents.height, 1};
|
||||
image_info.mipLevels = 1;
|
||||
image_info.arrayLayers = 1;
|
||||
image_info.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
image_info.usage =
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
image_info.queueFamilyIndexCount = 0;
|
||||
image_info.pQueueFamilyIndices = nullptr;
|
||||
image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
|
||||
VkImage image_fb;
|
||||
auto status = vkCreateImage(*device_, &image_info, nullptr, &image_fb);
|
||||
CheckResult(status, "vkCreateImage");
|
||||
|
||||
// Bind memory to image.
|
||||
VkMemoryRequirements mem_requirements;
|
||||
vkGetImageMemoryRequirements(*device_, image_fb, &mem_requirements);
|
||||
fb_memory_ = device_->AllocateMemory(mem_requirements, 0);
|
||||
assert_not_null(fb_memory_);
|
||||
|
||||
status = vkBindImageMemory(*device_, image_fb, fb_memory_, 0);
|
||||
CheckResult(status, "vkBindImageMemory");
|
||||
|
||||
std::lock_guard<std::mutex> lock(swap_state_.mutex);
|
||||
swap_state_.front_buffer_texture = reinterpret_cast<uintptr_t>(image_fb);
|
||||
|
||||
VkImageViewCreateInfo view_create_info = {
|
||||
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
nullptr,
|
||||
0,
|
||||
image_fb,
|
||||
VK_IMAGE_VIEW_TYPE_2D,
|
||||
VK_FORMAT_R8G8B8A8_UNORM,
|
||||
{VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B,
|
||||
VK_COMPONENT_SWIZZLE_A},
|
||||
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1},
|
||||
};
|
||||
status =
|
||||
vkCreateImageView(*device_, &view_create_info, nullptr, &fb_image_view_);
|
||||
CheckResult(status, "vkCreateImageView");
|
||||
|
||||
VkFramebufferCreateInfo framebuffer_create_info = {
|
||||
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
||||
nullptr,
|
||||
0,
|
||||
blitter_->GetRenderPass(VK_FORMAT_R8G8B8A8_UNORM, true),
|
||||
1,
|
||||
&fb_image_view_,
|
||||
extents.width,
|
||||
extents.height,
|
||||
1,
|
||||
};
|
||||
status = vkCreateFramebuffer(*device_, &framebuffer_create_info, nullptr,
|
||||
&fb_framebuffer_);
|
||||
CheckResult(status, "vkCreateFramebuffer");
|
||||
|
||||
// Transition image to general layout.
|
||||
VkImageMemoryBarrier barrier;
|
||||
std::memset(&barrier, 0, sizeof(VkImageMemoryBarrier));
|
||||
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
barrier.srcAccessMask = 0;
|
||||
barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barrier.image = image_fb;
|
||||
barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
|
||||
|
||||
vkCmdPipelineBarrier(setup_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0,
|
||||
nullptr, 0, nullptr, 1, &barrier);
|
||||
}
|
||||
|
||||
void VulkanCommandProcessor::DestroySwapImage() {
|
||||
vkDestroyFramebuffer(*device_, fb_framebuffer_, nullptr);
|
||||
vkDestroyImageView(*device_, fb_image_view_, nullptr);
|
||||
|
||||
std::lock_guard<std::mutex> lock(swap_state_.mutex);
|
||||
vkDestroyImage(*device_,
|
||||
reinterpret_cast<VkImage>(swap_state_.front_buffer_texture),
|
||||
nullptr);
|
||||
vkFreeMemory(*device_, fb_memory_, nullptr);
|
||||
|
||||
swap_state_.front_buffer_texture = 0;
|
||||
fb_memory_ = nullptr;
|
||||
fb_framebuffer_ = nullptr;
|
||||
fb_image_view_ = nullptr;
|
||||
}
|
||||
|
||||
void VulkanCommandProcessor::BeginFrame() {
|
||||
assert_false(frame_open_);
|
||||
|
||||
|
@ -422,10 +318,46 @@ void VulkanCommandProcessor::PerformSwap(uint32_t frontbuffer_ptr,
|
|||
frontbuffer_ptr = last_copy_base_;
|
||||
}
|
||||
|
||||
if (!swap_state_.front_buffer_texture) {
|
||||
CreateSwapImage(copy_commands, {frontbuffer_width, frontbuffer_height});
|
||||
auto swap_state =
|
||||
reinterpret_cast<VulkanGraphicsSystem*>(graphics_system_)->swap_state();
|
||||
|
||||
// Create a framebuffer if it isn't already created.
|
||||
if (!swap_state->fb_framebuffer_) {
|
||||
VkFramebufferCreateInfo framebuffer_create_info = {
|
||||
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
||||
nullptr,
|
||||
0,
|
||||
blitter_->GetRenderPass(VK_FORMAT_R8G8B8A8_UNORM, true),
|
||||
1,
|
||||
&swap_state->fb_image_view_,
|
||||
swap_state->width,
|
||||
swap_state->height,
|
||||
1,
|
||||
};
|
||||
status = vkCreateFramebuffer(*device_, &framebuffer_create_info, nullptr,
|
||||
&swap_state->fb_framebuffer_);
|
||||
CheckResult(status, "vkCreateFramebuffer");
|
||||
}
|
||||
|
||||
auto swap_fb = reinterpret_cast<VkImage>(swap_state->front_buffer_texture);
|
||||
if (swap_state->fb_image_layout_ == VK_IMAGE_LAYOUT_UNDEFINED) {
|
||||
// Transition image to general layout.
|
||||
VkImageMemoryBarrier barrier;
|
||||
std::memset(&barrier, 0, sizeof(VkImageMemoryBarrier));
|
||||
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
barrier.srcAccessMask = 0;
|
||||
barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
barrier.image = swap_fb;
|
||||
barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
|
||||
|
||||
vkCmdPipelineBarrier(copy_commands, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0,
|
||||
nullptr, 0, nullptr, 1, &barrier);
|
||||
}
|
||||
auto swap_fb = reinterpret_cast<VkImage>(swap_state_.front_buffer_texture);
|
||||
|
||||
auto& regs = *register_file_;
|
||||
int r = XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0;
|
||||
|
@ -490,8 +422,8 @@ void VulkanCommandProcessor::PerformSwap(uint32_t frontbuffer_ptr,
|
|||
texture_cache_->DemandView(texture, 0x688)->view, src_rect,
|
||||
{texture->texture_info.width + 1, texture->texture_info.height + 1},
|
||||
VK_FORMAT_R8G8B8A8_UNORM, dst_rect,
|
||||
{frontbuffer_width, frontbuffer_height}, fb_framebuffer_, viewport,
|
||||
scissor, VK_FILTER_LINEAR, true, true);
|
||||
{frontbuffer_width, frontbuffer_height}, swap_state->fb_framebuffer_,
|
||||
viewport, scissor, VK_FILTER_LINEAR, true, true);
|
||||
|
||||
std::swap(barrier.oldLayout, barrier.newLayout);
|
||||
barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
|
@ -500,9 +432,9 @@ void VulkanCommandProcessor::PerformSwap(uint32_t frontbuffer_ptr,
|
|||
copy_commands, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
|
||||
|
||||
std::lock_guard<std::mutex> lock(swap_state_.mutex);
|
||||
swap_state_.width = frontbuffer_width;
|
||||
swap_state_.height = frontbuffer_height;
|
||||
std::lock_guard<std::mutex> lock(swap_state->mutex);
|
||||
swap_state->width = frontbuffer_width;
|
||||
swap_state->height = frontbuffer_height;
|
||||
}
|
||||
|
||||
status = vkEndCommandBuffer(copy_commands);
|
||||
|
|
|
@ -68,9 +68,6 @@ class VulkanCommandProcessor : public CommandProcessor {
|
|||
void BeginFrame();
|
||||
void EndFrame();
|
||||
|
||||
void CreateSwapImage(VkCommandBuffer setup_buffer, VkExtent2D extents);
|
||||
void DestroySwapImage();
|
||||
|
||||
void PerformSwap(uint32_t frontbuffer_ptr, uint32_t frontbuffer_width,
|
||||
uint32_t frontbuffer_height) override;
|
||||
|
||||
|
@ -96,11 +93,6 @@ class VulkanCommandProcessor : public CommandProcessor {
|
|||
|
||||
xe::ui::vulkan::VulkanDevice* device_ = nullptr;
|
||||
|
||||
// front buffer / back buffer memory
|
||||
VkDeviceMemory fb_memory_ = nullptr;
|
||||
VkImageView fb_image_view_ = nullptr;
|
||||
VkFramebuffer fb_framebuffer_ = nullptr;
|
||||
|
||||
uint64_t dirty_float_constants_ = 0; // Dirty float constants in blocks of 4
|
||||
uint8_t dirty_bool_constants_ = 0;
|
||||
uint32_t dirty_loop_constants_ = 0;
|
||||
|
|
|
@ -56,19 +56,21 @@ X_STATUS VulkanGraphicsSystem::Setup(
|
|||
vkCreateCommandPool(*device_, &create_info, nullptr, &command_pool_);
|
||||
CheckResult(status, "vkCreateCommandPool");
|
||||
|
||||
// TODO(DrChat): Don't hardcode this resolution.
|
||||
CreateSwapImage({1280, 720});
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
void VulkanGraphicsSystem::Shutdown() {
|
||||
GraphicsSystem::Shutdown();
|
||||
|
||||
vkDestroyCommandPool(*device_, command_pool_, nullptr);
|
||||
DestroySwapImage();
|
||||
VK_SAFE_DESTROY(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) {
|
||||
std::lock_guard<std::mutex> lock(swap_state_.mutex);
|
||||
if (!swap_state_.front_buffer_texture) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -98,9 +100,9 @@ std::unique_ptr<RawImage> VulkanGraphicsSystem::Capture() {
|
|||
vkBeginCommandBuffer(cmd, &begin_info);
|
||||
|
||||
auto front_buffer =
|
||||
reinterpret_cast<VkImage>(swap_state.front_buffer_texture);
|
||||
reinterpret_cast<VkImage>(swap_state_.front_buffer_texture);
|
||||
|
||||
status = CreateCaptureBuffer(cmd, {swap_state.width, swap_state.height});
|
||||
status = CreateCaptureBuffer(cmd, {swap_state_.width, swap_state_.height});
|
||||
if (status != VK_SUCCESS) {
|
||||
vkFreeCommandBuffers(*device_, command_pool_, 1, &cmd);
|
||||
return nullptr;
|
||||
|
@ -125,7 +127,7 @@ std::unique_ptr<RawImage> VulkanGraphicsSystem::Capture() {
|
|||
VkBufferImageCopy region = {
|
||||
0, 0,
|
||||
0, {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1},
|
||||
{0, 0, 0}, {swap_state.width, swap_state.height, 1},
|
||||
{0, 0, 0}, {swap_state_.width, swap_state_.height, 1},
|
||||
};
|
||||
|
||||
vkCmdCopyImageToBuffer(cmd, front_buffer, VK_IMAGE_LAYOUT_GENERAL,
|
||||
|
@ -182,9 +184,9 @@ std::unique_ptr<RawImage> VulkanGraphicsSystem::Capture() {
|
|||
|
||||
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->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,
|
||||
|
@ -247,6 +249,75 @@ void VulkanGraphicsSystem::DestroyCaptureBuffer() {
|
|||
capture_buffer_size_ = 0;
|
||||
}
|
||||
|
||||
void VulkanGraphicsSystem::CreateSwapImage(VkExtent2D extents) {
|
||||
VkImageCreateInfo image_info;
|
||||
std::memset(&image_info, 0, sizeof(VkImageCreateInfo));
|
||||
image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
image_info.imageType = VK_IMAGE_TYPE_2D;
|
||||
image_info.format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||
image_info.extent = {extents.width, extents.height, 1};
|
||||
image_info.mipLevels = 1;
|
||||
image_info.arrayLayers = 1;
|
||||
image_info.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
|
||||
image_info.usage =
|
||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||
image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
image_info.queueFamilyIndexCount = 0;
|
||||
image_info.pQueueFamilyIndices = nullptr;
|
||||
image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
|
||||
VkImage image_fb;
|
||||
auto status = vkCreateImage(*device_, &image_info, nullptr, &image_fb);
|
||||
CheckResult(status, "vkCreateImage");
|
||||
|
||||
// Bind memory to image.
|
||||
VkMemoryRequirements mem_requirements;
|
||||
vkGetImageMemoryRequirements(*device_, image_fb, &mem_requirements);
|
||||
swap_state_.fb_memory_ = device_->AllocateMemory(mem_requirements, 0);
|
||||
assert_not_null(swap_state_.fb_memory_);
|
||||
|
||||
status = vkBindImageMemory(*device_, image_fb, swap_state_.fb_memory_, 0);
|
||||
CheckResult(status, "vkBindImageMemory");
|
||||
|
||||
std::lock_guard<std::mutex> lock(swap_state_.mutex);
|
||||
swap_state_.front_buffer_texture = reinterpret_cast<uintptr_t>(image_fb);
|
||||
swap_state_.width = extents.width;
|
||||
swap_state_.height = extents.height;
|
||||
|
||||
VkImageViewCreateInfo view_create_info = {
|
||||
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
nullptr,
|
||||
0,
|
||||
image_fb,
|
||||
VK_IMAGE_VIEW_TYPE_2D,
|
||||
VK_FORMAT_R8G8B8A8_UNORM,
|
||||
{VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B,
|
||||
VK_COMPONENT_SWIZZLE_A},
|
||||
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1},
|
||||
};
|
||||
status = vkCreateImageView(*device_, &view_create_info, nullptr,
|
||||
&swap_state_.fb_image_view_);
|
||||
CheckResult(status, "vkCreateImageView");
|
||||
}
|
||||
|
||||
void VulkanGraphicsSystem::DestroySwapImage() {
|
||||
VK_SAFE_DESTROY(vkDestroyFramebuffer, *device_, swap_state_.fb_framebuffer_,
|
||||
nullptr);
|
||||
VK_SAFE_DESTROY(vkDestroyImageView, *device_, swap_state_.fb_image_view_,
|
||||
nullptr);
|
||||
|
||||
std::lock_guard<std::mutex> lock(swap_state_.mutex);
|
||||
VK_SAFE_DESTROY(vkFreeMemory, *device_, swap_state_.fb_memory_, nullptr);
|
||||
if (swap_state_.front_buffer_texture) {
|
||||
vkDestroyImage(*device_,
|
||||
reinterpret_cast<VkImage>(swap_state_.front_buffer_texture),
|
||||
nullptr);
|
||||
}
|
||||
|
||||
swap_state_.front_buffer_texture = 0;
|
||||
}
|
||||
|
||||
std::unique_ptr<CommandProcessor>
|
||||
VulkanGraphicsSystem::CreateCommandProcessor() {
|
||||
return std::make_unique<VulkanCommandProcessor>(this, kernel_state_);
|
||||
|
@ -258,23 +329,22 @@ void VulkanGraphicsSystem::Swap(xe::ui::UIEvent* e) {
|
|||
}
|
||||
|
||||
// Check for pending swap.
|
||||
auto& swap_state = command_processor_->swap_state();
|
||||
if (display_context_->WasLost()) {
|
||||
// We're crashing. Cheese it.
|
||||
swap_state.pending = false;
|
||||
swap_state_.pending = false;
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(swap_state.mutex);
|
||||
if (!swap_state.pending) {
|
||||
std::lock_guard<std::mutex> lock(swap_state_.mutex);
|
||||
if (!swap_state_.pending) {
|
||||
// return;
|
||||
}
|
||||
|
||||
swap_state.pending = false;
|
||||
swap_state_.pending = false;
|
||||
}
|
||||
|
||||
if (!swap_state.front_buffer_texture) {
|
||||
if (!swap_state_.front_buffer_texture) {
|
||||
// Not yet ready.
|
||||
return;
|
||||
}
|
||||
|
@ -282,7 +352,7 @@ void VulkanGraphicsSystem::Swap(xe::ui::UIEvent* e) {
|
|||
auto swap_chain = display_context_->swap_chain();
|
||||
auto copy_cmd_buffer = swap_chain->copy_cmd_buffer();
|
||||
auto front_buffer =
|
||||
reinterpret_cast<VkImage>(swap_state.front_buffer_texture);
|
||||
reinterpret_cast<VkImage>(swap_state_.front_buffer_texture);
|
||||
|
||||
VkImageMemoryBarrier barrier;
|
||||
std::memset(&barrier, 0, sizeof(VkImageMemoryBarrier));
|
||||
|
@ -302,8 +372,8 @@ void VulkanGraphicsSystem::Swap(xe::ui::UIEvent* e) {
|
|||
VkImageBlit region;
|
||||
region.srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
|
||||
region.srcOffsets[0] = {0, 0, 0};
|
||||
region.srcOffsets[1] = {static_cast<int32_t>(swap_state.width),
|
||||
static_cast<int32_t>(swap_state.height), 1};
|
||||
region.srcOffsets[1] = {static_cast<int32_t>(swap_state_.width),
|
||||
static_cast<int32_t>(swap_state_.height), 1};
|
||||
|
||||
region.dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1};
|
||||
region.dstOffsets[0] = {0, 0, 0};
|
||||
|
|
|
@ -19,12 +19,21 @@ namespace xe {
|
|||
namespace gpu {
|
||||
namespace vulkan {
|
||||
|
||||
struct SwapState : public gpu::SwapState {
|
||||
// front buffer / back buffer memory
|
||||
VkDeviceMemory fb_memory_ = nullptr;
|
||||
VkImageView fb_image_view_ = nullptr;
|
||||
VkImageLayout fb_image_layout_ = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
VkFramebuffer fb_framebuffer_ = nullptr; // Used and created by CP.
|
||||
};
|
||||
|
||||
class VulkanGraphicsSystem : public GraphicsSystem {
|
||||
public:
|
||||
VulkanGraphicsSystem();
|
||||
~VulkanGraphicsSystem() override;
|
||||
|
||||
std::wstring name() const override { return L"Vulkan"; }
|
||||
SwapState* swap_state() override { return &swap_state_; }
|
||||
|
||||
X_STATUS Setup(cpu::Processor* processor, kernel::KernelState* kernel_state,
|
||||
std::unique_ptr<ui::GraphicsContext> context) override;
|
||||
|
@ -36,11 +45,16 @@ class VulkanGraphicsSystem : public GraphicsSystem {
|
|||
VkResult CreateCaptureBuffer(VkCommandBuffer cmd, VkExtent2D extents);
|
||||
void DestroyCaptureBuffer();
|
||||
|
||||
void CreateSwapImage(VkExtent2D extents);
|
||||
void DestroySwapImage();
|
||||
|
||||
std::unique_ptr<CommandProcessor> CreateCommandProcessor() override;
|
||||
void Swap(xe::ui::UIEvent* e) override;
|
||||
|
||||
xe::ui::vulkan::VulkanDevice* device_ = nullptr;
|
||||
xe::ui::vulkan::VulkanContext* display_context_ = nullptr;
|
||||
ui::vulkan::VulkanDevice* device_ = nullptr;
|
||||
ui::vulkan::VulkanContext* display_context_ = nullptr;
|
||||
|
||||
SwapState swap_state_;
|
||||
|
||||
VkCommandPool command_pool_ = nullptr;
|
||||
|
||||
|
|
Loading…
Reference in New Issue