blob: 5f4d6125b0c1b000c5648ddb94611b352dbec12e [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/ozone/demo/vulkan_overlay_renderer.h"
#include <vulkan/vulkan.h>
#include "base/location.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/trace_event.h"
#include "gpu/vulkan/init/vulkan_factory.h"
#include "gpu/vulkan/vulkan_command_buffer.h"
#include "gpu/vulkan/vulkan_command_pool.h"
#include "gpu/vulkan/vulkan_device_queue.h"
#include "gpu/vulkan/vulkan_function_pointers.h"
#include "gpu/vulkan/vulkan_implementation.h"
#include "gpu/vulkan/vulkan_surface.h"
#include "gpu/vulkan/vulkan_swap_chain.h"
#include "ui/display/types/display_snapshot.h"
#include "ui/gfx/native_pixmap.h"
#include "ui/ozone/public/overlay_plane.h"
#include "ui/ozone/public/overlay_surface.h"
#include "ui/ozone/public/ozone_platform.h"
#include "ui/ozone/public/surface_factory_ozone.h"
namespace ui {
namespace {
// If we only submit one buffer, it'll never be released back to us for reuse.
constexpr int kMinimumBuffersForForwardProgress = 2;
} // namespace
VulkanOverlayRenderer::VulkanOverlayRenderer(
std::unique_ptr<OverlaySurface> overlay_surface,
SurfaceFactoryOzone* surface_factory_ozone,
gpu::VulkanImplementation* vulkan_implementation,
gfx::AcceleratedWidget widget,
const gfx::Size& size)
: RendererBase(widget, size),
surface_factory_ozone_(surface_factory_ozone),
vulkan_implementation_(vulkan_implementation),
overlay_surface_(std::move(overlay_surface)),
weak_ptr_factory_(this) {}
VulkanOverlayRenderer::~VulkanOverlayRenderer() {
DestroyBuffers();
DestroyRenderPass();
command_pool_->Destroy();
command_pool_.reset();
device_queue_->Destroy();
device_queue_.reset();
}
bool VulkanOverlayRenderer::Initialize() {
TRACE_EVENT1("ozone", "VulkanOverlayRenderer::Initialize", "widget", widget_);
device_queue_ = gpu::CreateVulkanDeviceQueue(
vulkan_implementation_, gpu::VulkanDeviceQueue::GRAPHICS_QUEUE_FLAG);
if (!device_queue_) {
LOG(FATAL) << "Failed to init device queue";
}
VkAttachmentDescription render_pass_attachments[] = {{
/* .flags = */ 0,
/* .format = */ VK_FORMAT_B8G8R8A8_SRGB,
/* .samples = */ VK_SAMPLE_COUNT_1_BIT,
/* .loadOp = */ VK_ATTACHMENT_LOAD_OP_CLEAR,
/* .storeOp = */ VK_ATTACHMENT_STORE_OP_STORE,
/* .stencilLoadOp = */ VK_ATTACHMENT_LOAD_OP_DONT_CARE,
/* .stencilStoreOp = */ VK_ATTACHMENT_STORE_OP_DONT_CARE,
/* .initialLayout = */ VK_IMAGE_LAYOUT_UNDEFINED,
/* .finalLayout = */ VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
}};
VkAttachmentReference color_attachment_references[] = {
{/* .attachment = */ 0,
/* .layout = */ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}};
VkSubpassDescription render_pass_subpasses[] = {{
/* .flags = */ 0,
/* .pipelineBindPoint = */ VK_PIPELINE_BIND_POINT_GRAPHICS,
/* .inputAttachmentCount = */ 0,
/* .pInputAttachments = */ nullptr,
/* .colorAttachmentCount = */ base::size(color_attachment_references),
/* .pColorAttachments = */ color_attachment_references,
/* .pResolveAttachments = */ nullptr,
/* .pDepthStencilAttachment = */ nullptr,
/* .preserveAttachmentCount = */ 0,
/* .pPreserveAttachments = */ nullptr,
}};
VkRenderPassCreateInfo render_pass_create_info = {
/* .sType = */ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
/* .pNext = */ nullptr,
/* .flags = */ 0,
/* .attachmentCount = */ base::size(render_pass_attachments),
/* .pAttachments = */ render_pass_attachments,
/* .subpassCount = */ base::size(render_pass_subpasses),
/* .pSubpasses = */ render_pass_subpasses,
/* .dependencyCount = */ 0,
/* .pDependencies = */ nullptr,
};
CHECK_EQ(vkCreateRenderPass(device_queue_->GetVulkanDevice(),
&render_pass_create_info, nullptr, &render_pass_),
VK_SUCCESS);
command_pool_ = std::make_unique<gpu::VulkanCommandPool>(device_queue_.get());
CHECK(command_pool_->Initialize());
RecreateBuffers();
// Schedule the initial render.
PostRenderFrameTask();
return true;
}
void VulkanOverlayRenderer::DestroyRenderPass() {
if (render_pass_ == VK_NULL_HANDLE)
return;
vkDestroyRenderPass(device_queue_->GetVulkanDevice(), render_pass_, nullptr);
render_pass_ = VK_NULL_HANDLE;
}
void VulkanOverlayRenderer::DestroyBuffers() {
VkDevice vk_device = device_queue_->GetVulkanDevice();
VkResult result = vkQueueWaitIdle(device_queue_->GetVulkanQueue());
CHECK_EQ(result, VK_SUCCESS);
for (std::unique_ptr<Buffer>& buffer : buffers_) {
if (!buffer)
continue;
vkDestroyFence(vk_device, buffer->fence(), nullptr);
buffer->command_buffer()->Destroy();
vkDestroyFramebuffer(vk_device, buffer->vk_framebuffer(), nullptr);
vkDestroyImageView(vk_device, buffer->vk_image_view(), nullptr);
vkDestroyImage(vk_device, buffer->vk_image(), nullptr);
vkFreeMemory(vk_device, buffer->vk_device_memory(), nullptr);
buffer.reset();
}
}
void VulkanOverlayRenderer::RecreateBuffers() {
TRACE_EVENT0("ozone", "VulkanOverlayRenderer::RecreateBuffers");
DestroyBuffers();
for (auto& buffer : buffers_) {
buffer = Buffer::Create(surface_factory_ozone_, vulkan_implementation_,
device_queue_.get(), command_pool_.get(),
render_pass_, widget_, size_);
CHECK(buffer);
}
}
void VulkanOverlayRenderer::RenderFrame() {
TRACE_EVENT0("ozone", "VulkanOverlayRenderer::RenderFrame");
VkClearValue clear_value = {
/* .color = */ {/* .float32 = */ {.5f, 1.f - NextFraction(), .5f, 1.f}}};
const Buffer& buffer = *buffers_[next_buffer_];
next_buffer_++;
next_buffer_ %= base::size(buffers_);
++in_use_buffers_;
DCHECK_LE(in_use_buffers_, base::size(buffers_));
gpu::VulkanCommandBuffer& command_buffer = *buffer.command_buffer();
{
gpu::ScopedSingleUseCommandBufferRecorder recorder(command_buffer);
VkRenderPassBeginInfo begin_info = {
/* .sType = */ VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
/* .pNext = */ nullptr,
/* .renderPass = */ render_pass_,
/* .framebuffer = */ buffer.vk_framebuffer(),
/* .renderArea = */
{
/* .offset = */ {
/* .x = */ 0,
/* .y = */ 0,
},
/* .extent = */
{
/* .width = */ buffer.size().width(),
/* .height = */ buffer.size().height(),
},
},
/* .clearValueCount = */ 1,
/* .pClearValues = */ &clear_value,
};
vkCmdBeginRenderPass(recorder.handle(), &begin_info,
VK_SUBPASS_CONTENTS_INLINE);
vkCmdEndRenderPass(recorder.handle());
}
CHECK(command_buffer.Submit(0, nullptr, 0, nullptr));
SubmitFrame(&buffer);
}
std::unique_ptr<gfx::GpuFence> VulkanOverlayRenderer::SubmitFence(
VkFence fence) {
VkResult result;
VkFence fences[] = {fence};
result = vkResetFences(device_queue_->GetVulkanDevice(), base::size(fences),
fences);
CHECK_EQ(result, VK_SUCCESS);
result = vkQueueSubmit(device_queue_->GetVulkanQueue(), 0, nullptr, fence);
CHECK_EQ(result, VK_SUCCESS);
std::unique_ptr<gfx::GpuFence> gpu_fence =
vulkan_implementation_->ExportVkFenceToGpuFence(
device_queue_->GetVulkanDevice(), fence);
if (!gpu_fence)
LOG(FATAL) << "Unable to export VkFence to gfx::GpuFence";
return gpu_fence;
}
void VulkanOverlayRenderer::SubmitFrame(
const VulkanOverlayRenderer::Buffer* buffer) {
std::unique_ptr<gfx::GpuFence> gpu_fence = SubmitFence(buffer->fence());
ui::OverlayPlane primary_plane;
primary_plane.pixmap = buffer->native_pixmap();
primary_plane.display_bounds = gfx::Rect(buffer->size());
primary_plane.gpu_fence = std::move(gpu_fence);
std::vector<ui::OverlayPlane> overlay_planes;
overlay_planes.push_back(std::move(primary_plane));
uint64_t frame_sequence = ++frame_sequence_;
overlay_surface_->SubmitFrame(
std::move(overlay_planes),
base::BindOnce(&VulkanOverlayRenderer::OnFrameSubmitted,
weak_ptr_factory_.GetWeakPtr(), frame_sequence),
base::BindOnce(&VulkanOverlayRenderer::OnFramePresented,
weak_ptr_factory_.GetWeakPtr(), frame_sequence),
base::BindOnce(&VulkanOverlayRenderer::OnFrameReleased,
weak_ptr_factory_.GetWeakPtr(), frame_sequence));
}
void VulkanOverlayRenderer::PostRenderFrameTask() {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&VulkanOverlayRenderer::RenderFrame,
weak_ptr_factory_.GetWeakPtr()));
}
void VulkanOverlayRenderer::OnFrameSubmitted(uint64_t frame_sequence,
gfx::SwapResult swap_result) {
TRACE_EVENT1("ozone", "VulkanOverlayRenderer::OnFrameSubmitted", "frame",
frame_sequence);
CHECK_NE(swap_result, gfx::SwapResult::SWAP_FAILED);
if (swap_result == gfx::SwapResult::SWAP_NAK_RECREATE_BUFFERS)
RecreateBuffers();
if (in_use_buffers_ < kMinimumBuffersForForwardProgress)
PostRenderFrameTask();
}
void VulkanOverlayRenderer::OnFramePresented(
uint64_t frame_sequence,
const gfx::PresentationFeedback& feedback) {
TRACE_EVENT1("ozone", "VulkanOverlayRenderer::OnFramePresented", "frame",
frame_sequence);
}
void VulkanOverlayRenderer::OnFrameReleased(uint64_t frame_sequence) {
TRACE_EVENT1("ozone", "VulkanOverlayRenderer::OnFrameReleased", "frame",
frame_sequence);
--in_use_buffers_;
PostRenderFrameTask();
}
VulkanOverlayRenderer::Buffer::Buffer(
const gfx::Size& size,
scoped_refptr<gfx::NativePixmap> native_pixmap,
VkDeviceMemory vk_device_memory,
VkImage vk_image,
VkImageView vk_image_view,
VkFramebuffer vk_framebuffer,
std::unique_ptr<gpu::VulkanCommandBuffer> command_buffer,
VkFence fence)
: native_pixmap_(std::move(native_pixmap)),
size_(size),
vk_device_memory_(vk_device_memory),
vk_image_(vk_image),
vk_image_view_(vk_image_view),
vk_framebuffer_(vk_framebuffer),
command_buffer_(std::move(command_buffer)),
fence_(fence) {}
VulkanOverlayRenderer::Buffer::~Buffer() {}
std::unique_ptr<VulkanOverlayRenderer::Buffer>
VulkanOverlayRenderer::Buffer::Create(
SurfaceFactoryOzone* surface_factory_ozone,
gpu::VulkanImplementation* vulkan_implementation,
gpu::VulkanDeviceQueue* vulkan_device_queue,
gpu::VulkanCommandPool* vulkan_command_pool,
VkRenderPass vk_render_pass,
gfx::AcceleratedWidget widget,
const gfx::Size& size) {
gfx::BufferFormat format = gfx::BufferFormat::BGRA_8888;
VkDevice vk_device = vulkan_device_queue->GetVulkanDevice();
VkImage vk_image = VK_NULL_HANDLE;
VkDeviceMemory vk_device_memory = VK_NULL_HANDLE;
scoped_refptr<gfx::NativePixmap> native_pixmap =
surface_factory_ozone->CreateNativePixmapForVulkan(
widget, size, format, gfx::BufferUsage::SCANOUT, vk_device,
&vk_device_memory, &vk_image);
if (!native_pixmap) {
LOG(FATAL)
<< "Failed to create a presentable buffer for rendering with vulkan";
}
VkImageViewCreateInfo vk_image_view_create_info = {
/* .sType = */ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
/* .pNext = */ nullptr,
/* .flags = */ 0,
/* .image = */ vk_image,
/* .viewType = */ VK_IMAGE_VIEW_TYPE_2D,
/* .format = */ VK_FORMAT_B8G8R8A8_SRGB,
/* .components = */
{
/* .r = */ VK_COMPONENT_SWIZZLE_IDENTITY,
/* .b = */ VK_COMPONENT_SWIZZLE_IDENTITY,
/* .g = */ VK_COMPONENT_SWIZZLE_IDENTITY,
/* .a = */ VK_COMPONENT_SWIZZLE_IDENTITY,
},
/* .subresourceRange = */
{
/* .aspectMask = */ VK_IMAGE_ASPECT_COLOR_BIT,
/* .baseMipLevel = */ 0,
/* .levelCount = */ 1,
/* .baseArrayLayer = */ 0,
/* .layerCount = */ 1,
},
};
VkResult result;
VkImageView vk_image_view = VK_NULL_HANDLE;
result = vkCreateImageView(vk_device, &vk_image_view_create_info, nullptr,
&vk_image_view);
if (result != VK_SUCCESS) {
LOG(FATAL) << "Failed to create a Vulkan image view.";
}
VkFramebufferCreateInfo vk_framebuffer_create_info = {
/* .sType = */ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
/* .pNext = */ nullptr,
/* .flags = */ 0,
/* .renderPass = */ vk_render_pass,
/* .attachmentCount = */ 1,
/* .pAttachments = */ &vk_image_view,
/* .width = */ size.width(),
/* .height = */ size.height(),
/* .layers = */ 1,
};
VkFramebuffer vk_framebuffer = VK_NULL_HANDLE;
result = vkCreateFramebuffer(vk_device, &vk_framebuffer_create_info, nullptr,
&vk_framebuffer);
if (result != VK_SUCCESS) {
LOG(FATAL) << "Failed to create a Vulkan framebuffer.";
}
auto command_buffer = std::make_unique<gpu::VulkanCommandBuffer>(
vulkan_device_queue, vulkan_command_pool, true /* primary */);
CHECK(command_buffer->Initialize());
VkFence fence = vulkan_implementation->CreateVkFenceForGpuFence(vk_device);
if (fence == VK_NULL_HANDLE)
LOG(FATAL) << "Failed to create VkFence";
return std::make_unique<VulkanOverlayRenderer::Buffer>(
size, native_pixmap, vk_device_memory, vk_image, vk_image_view,
vk_framebuffer, std::move(command_buffer), fence);
}
} // namespace ui