blob: b588b1dc5542b5ec593240565ecf37236a417f84 [file] [log] [blame]
// Copyright 2017 The Fuchsia 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 "garnet/examples/ui/shadertoy/service/renderer.h"
#include <trace/event.h>
#include "garnet/examples/ui/shadertoy/service/compiler.h"
#include "lib/escher/geometry/tessellation.h"
#include "lib/escher/impl/command_buffer.h"
#include "lib/escher/impl/mesh_manager.h"
#include "lib/escher/renderer/batch_gpu_uploader.h"
#include "lib/escher/util/image_utils.h"
#include "lib/escher/vk/framebuffer.h"
#include "lib/escher/vk/image.h"
#include "lib/escher/vk/image_factory.h"
#include "lib/escher/vk/texture.h"
namespace shadertoy {
static vk::RenderPass CreateRenderPass(vk::Device device,
vk::Format framebuffer_format) {
constexpr uint32_t kAttachmentCount = 1;
const uint32_t kColorAttachment = 0;
vk::AttachmentDescription attachments[kAttachmentCount];
auto& color_attachment = attachments[kColorAttachment];
color_attachment.format = framebuffer_format;
color_attachment.samples = vk::SampleCountFlagBits::e1;
color_attachment.loadOp = vk::AttachmentLoadOp::eDontCare;
color_attachment.storeOp = vk::AttachmentStoreOp::eStore;
color_attachment.initialLayout = vk::ImageLayout::eUndefined;
color_attachment.finalLayout = vk::ImageLayout::eColorAttachmentOptimal;
vk::AttachmentReference color_reference;
color_reference.attachment = kColorAttachment;
color_reference.layout = vk::ImageLayout::eColorAttachmentOptimal;
// Every vk::RenderPass needs at least one subpass.
vk::SubpassDescription subpass;
subpass.pipelineBindPoint = vk::PipelineBindPoint::eGraphics;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &color_reference;
subpass.inputAttachmentCount = 0; // no other subpasses to sample from
// Even though we have a single subpass, we need to declare dependencies to
// support the layout transitions specified by the attachment references.
constexpr uint32_t kDependencyCount = 2;
vk::SubpassDependency dependencies[kDependencyCount];
auto& input_dependency = dependencies[0];
auto& output_dependency = dependencies[1];
// The first dependency transitions from the final layout from the previous
// render pass, to the initial layout of this one.
input_dependency.srcSubpass = VK_SUBPASS_EXTERNAL; // not in vulkan.hpp ?!?
input_dependency.dstSubpass = 0;
input_dependency.srcStageMask = vk::PipelineStageFlagBits::eBottomOfPipe;
input_dependency.dstStageMask =
vk::PipelineStageFlagBits::eColorAttachmentOutput;
input_dependency.srcAccessMask = vk::AccessFlagBits::eMemoryRead;
input_dependency.dstAccessMask = vk::AccessFlagBits::eColorAttachmentRead |
vk::AccessFlagBits::eColorAttachmentWrite;
input_dependency.dependencyFlags = vk::DependencyFlagBits::eByRegion;
// The second dependency describes the transition from the initial to final
// layout.
output_dependency.srcSubpass = 0; // our sole subpass
output_dependency.dstSubpass = VK_SUBPASS_EXTERNAL;
output_dependency.srcStageMask =
vk::PipelineStageFlagBits::eColorAttachmentOutput;
output_dependency.dstStageMask = vk::PipelineStageFlagBits::eBottomOfPipe;
output_dependency.srcAccessMask = vk::AccessFlagBits::eColorAttachmentRead |
vk::AccessFlagBits::eColorAttachmentWrite;
output_dependency.dstAccessMask = vk::AccessFlagBits::eMemoryRead;
output_dependency.dependencyFlags = vk::DependencyFlagBits::eByRegion;
// Create the render-pass.
vk::RenderPassCreateInfo info;
info.attachmentCount = kAttachmentCount;
info.pAttachments = attachments;
info.subpassCount = 1;
info.pSubpasses = &subpass;
info.dependencyCount = kDependencyCount;
info.pDependencies = dependencies;
auto result = device.createRenderPass(info);
if (result.result != vk::Result::eSuccess) {
FXL_LOG(ERROR) << "Failed to create Vulkan RenderPass.";
}
return result.value;
}
Renderer::Renderer(escher::EscherWeakPtr weak_escher,
vk::Format framebuffer_format)
: escher::Renderer(std::move(weak_escher)),
device_(escher()->vulkan_context().device),
framebuffer_format_(framebuffer_format),
render_pass_(CreateRenderPass(device_, framebuffer_format)),
full_screen_(NewFullScreenMesh(escher()->mesh_manager())),
white_texture_(CreateWhiteTexture()),
descriptor_set_pool_(escher()->GetWeakPtr(),
Compiler::GetDescriptorSetLayoutCreateInfo()) {}
escher::Texture* Renderer::GetChannelTexture(const escher::FramePtr& frame,
escher::Texture* texture_or_null) {
if (!texture_or_null) {
return white_texture_.get();
}
frame->command_buffer()->KeepAlive(texture_or_null);
return texture_or_null;
}
vk::DescriptorSet Renderer::GetUpdatedDescriptorSet(
const escher::FramePtr& frame, escher::Texture* channel0,
escher::Texture* channel1, escher::Texture* channel2,
escher::Texture* channel3) {
TRACE_DURATION(
"gfx", "fuchsia::examples::shadertoy::Renderer::GetUpdatedDescriptorSet");
constexpr uint32_t kChannelCount = 4;
vk::DescriptorImageInfo channel_image_info[kChannelCount];
vk::WriteDescriptorSet writes[kChannelCount];
escher::Texture* textures[kChannelCount] = {channel0, channel1, channel2,
channel3};
auto descriptor_set =
descriptor_set_pool_.Allocate(1, frame->command_buffer())->get(0);
for (uint32_t i = 0; i < kChannelCount; ++i) {
auto channel_texture = GetChannelTexture(frame, textures[i]);
channel_image_info[i].imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
channel_image_info[i].imageView = channel_texture->vk_image_view();
channel_image_info[i].sampler = channel_texture->vk_sampler();
writes[i].dstSet = descriptor_set;
writes[i].dstArrayElement = 0;
writes[i].descriptorType = vk::DescriptorType::eCombinedImageSampler;
writes[i].descriptorCount = 1;
writes[i].dstBinding = i;
writes[i].pImageInfo = channel_image_info + i;
}
device_.updateDescriptorSets(kChannelCount, writes, 0, nullptr);
return descriptor_set;
}
void Renderer::DrawFrame(const escher::FramebufferPtr& framebuffer,
const PipelinePtr& pipeline, const Params& params,
escher::Texture* channel0, escher::Texture* channel1,
escher::Texture* channel2, escher::Texture* channel3,
escher::SemaphorePtr framebuffer_ready,
escher::SemaphorePtr frame_done) {
TRACE_DURATION("gfx", "fuchsia::examples::shadertoy::Renderer::DrawFrame");
auto frame = escher()->NewFrame("Shadertoy Renderer", ++frame_number_);
auto command_buffer = frame->command_buffer();
auto vk_command_buffer = frame->vk_command_buffer();
command_buffer->KeepAlive(framebuffer);
command_buffer->AddWaitSemaphore(
std::move(framebuffer_ready),
vk::PipelineStageFlagBits::eColorAttachmentOutput);
vk::Viewport viewport;
viewport.width = framebuffer->width();
viewport.height = framebuffer->height();
vk_command_buffer.setViewport(0, 1, &viewport);
auto descriptor_set =
GetUpdatedDescriptorSet(frame, channel0, channel1, channel2, channel3);
command_buffer->BeginRenderPass(
render_pass_, framebuffer, {},
escher::Camera::Viewport().vk_rect_2d(framebuffer->width(),
framebuffer->height()));
vk_command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics,
pipeline->vk_pipeline());
vk_command_buffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
pipeline->vk_pipeline_layout(), 0, 1,
&descriptor_set, 0, nullptr);
vk_command_buffer.pushConstants(pipeline->vk_pipeline_layout(),
vk::ShaderStageFlagBits::eFragment, 0,
sizeof(Params), &params);
command_buffer->DrawMesh(full_screen_);
command_buffer->EndRenderPass();
command_buffer->TransitionImageLayout(
framebuffer->get_image(0), vk::ImageLayout::eColorAttachmentOptimal,
vk::ImageLayout::ePresentSrcKHR);
frame->EndFrame(frame_done, nullptr);
}
escher::TexturePtr Renderer::CreateWhiteTexture() {
uint8_t channels[4];
channels[0] = channels[1] = channels[2] = channels[3] = 255;
escher::ImageFactoryAdapter image_factory(escher()->gpu_allocator(),
escher()->resource_recycler());
escher::BatchGpuUploader uploader =
escher::BatchGpuUploader(escher()->GetWeakPtr(), /* frame_number= */ 0);
auto image = escher::image_utils::NewRgbaImage(&image_factory, &uploader, 1,
1, channels);
uploader.Submit();
return fxl::MakeRefCounted<escher::Texture>(
escher()->resource_recycler(), std::move(image), vk::Filter::eNearest);
}
Renderer::Params::Params()
: iResolution(0.f),
iTime(0.f),
iTimeDelta(0.f),
iFrame(0),
iChannelTime{0.f, 0.f, 0.f, 0.f},
iChannelResolution{glm::vec3(0), glm::vec3(0), glm::vec3(0),
glm::vec3(0)},
iMouse(0.f),
iDate(0.f),
iSampleRate(0.f) {}
} // namespace shadertoy