blob: ce29a299de8454640fc8bb5f3ffae1d7763c8104 [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 "lib/escher/impl/model_render_pass.h"
#include "lib/escher/impl/model_data.h"
#include "lib/escher/impl/vulkan_utils.h"
#include "lib/escher/resources/resource_recycler.h"
namespace escher {
namespace impl {
static constexpr uint32_t kColorAttachmentCount = 1;
static constexpr uint32_t kDepthAttachmentCount = 1;
static constexpr uint32_t kAttachmentReferenceCount = 2;
static constexpr uint32_t kSubpassCount = 1;
static constexpr uint32_t kSubpassDependencyCount = 2;
static constexpr uint32_t kSoleSubpassIndex = 0;
ModelRenderPass::ModelRenderPass(ResourceRecycler* recycler,
vk::Format color_format,
vk::Format depth_format, uint32_t sample_count)
: RenderPass(recycler, kColorAttachmentCount, kDepthAttachmentCount,
kAttachmentReferenceCount, kSubpassCount,
kSubpassDependencyCount),
sample_count_(sample_count) {
// Sanity check that these indices correspond to the first color and depth
// attachments, respectively.
FXL_DCHECK(kColorAttachmentIndex == color_attachment_index(0));
FXL_DCHECK(kDepthAttachmentIndex == depth_attachment_index(0));
vk::AttachmentDescription* color_attachment =
attachment(kColorAttachmentIndex);
vk::AttachmentDescription* depth_attachment =
attachment(kDepthAttachmentIndex);
vk::AttachmentReference* color_reference =
attachment_reference(kColorAttachmentIndex);
vk::AttachmentReference* depth_reference =
attachment_reference(kDepthAttachmentIndex);
vk::SubpassDescription* single_subpass =
subpass_description(kSoleSubpassIndex);
vk::SubpassDependency* input_dependency = subpass_dependency(0);
vk::SubpassDependency* output_dependency = subpass_dependency(1);
// Common to all subclasses.
color_attachment->format = color_format;
color_attachment->samples = SampleCountFlagBitsFromInt(sample_count);
depth_attachment->format = depth_format;
depth_attachment->samples = SampleCountFlagBitsFromInt(sample_count);
depth_attachment->stencilLoadOp = vk::AttachmentLoadOp::eClear;
depth_attachment->stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
color_reference->attachment = kColorAttachmentIndex;
color_reference->layout = vk::ImageLayout::eColorAttachmentOptimal;
depth_reference->attachment = kDepthAttachmentIndex;
depth_reference->layout = vk::ImageLayout::eDepthStencilAttachmentOptimal;
single_subpass->pipelineBindPoint = vk::PipelineBindPoint::eGraphics;
single_subpass->colorAttachmentCount = 1;
single_subpass->pColorAttachments = color_reference;
single_subpass->pDepthStencilAttachment = depth_reference;
// No other subpasses to sample from.
single_subpass->inputAttachmentCount = 0;
// 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 = kSoleSubpassIndex;
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 = kSoleSubpassIndex;
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;
}
void ModelRenderPass::CreateRenderPassAndPipelineCache(
ModelDataPtr model_data) {
CreateRenderPass();
// TODO: ModelPipelineCache doesn't need to be a resource if this render pass
// is one.
pipeline_cache_ = fxl::MakeRefCounted<ModelPipelineCache>(
static_cast<ResourceRecycler*>(owner()), std::move(model_data), this);
}
static constexpr char kVertexShaderPreamble[] = R"GLSL(
#version 450
#extension GL_ARB_separate_shader_objects : enable
out gl_PerVertex {
vec4 gl_Position;
};
layout(set = 0, binding = 0) uniform PerModel {
vec2 frag_coord_to_uv_multiplier;
float time;
vec3 ambient_light_intensity;
vec3 direct_light_intensity;
};
// Use binding 2 to avoid potential collision with PerModelSampler
layout(set = 0, binding = 2) uniform ViewProjection {
mat4 vp_matrix;
};
// Attribute locations must match constants in model_data.h
)GLSL";
static constexpr char kVertexShaderPosition[] = R"GLSL(
layout(set = 1, binding = 0) uniform PerObject {
mat4 model_transform;
mat4 light_transform;
vec4 color;
};
vec4 ComputeVertexPosition() {
return vec4(inPosition, 1);
}
)GLSL";
static constexpr char kVertexShaderWobblePosition[] = R"GLSL(
// TODO: unused. See discussion in PerObject struct, below.
struct SineParams {
float speed;
float amplitude;
float frequency;
};
const int kNumSineParams = 3;
float EvalSineParams(SineParams params) {
float arg = params.frequency * inPerimeter + params.speed * time;
return params.amplitude * sin(arg);
}
layout(set = 1, binding = 0) uniform PerObject {
mat4 model_transform;
mat4 light_transform;
vec4 color;
// Corresponds to ModifierWobble::SineParams[0].
float speed_0;
float amplitude_0;
float frequency_0;
// Corresponds to ModifierWobble::SineParams[1].
float speed_1;
float amplitude_1;
float frequency_1;
// Corresponds to ModifierWobble::SineParams[2].
float speed_2;
float amplitude_2;
float frequency_2;
// TODO: for some reason, I can't say:
// SineParams sine_params[kNumSineParams];
// nor:
// SineParams sine_params_0;
// SineParams sine_params_1;
// SineParams sine_params_2;
// ... if I try, the GLSL compiler produces SPIR-V, but the "SC"
// validation layer complains when trying to create a vk::ShaderModule
// from that SPIR-V. Note: if we ignore the warning and proceed, nothing
// explodes. Nevertheless, we'll leave it this way for now, to be safe.
};
// TODO: workaround. See discussion in PerObject struct, above.
float EvalSineParams_0() {
float arg = frequency_0 * inPerimeter + speed_0 * time;
return amplitude_0 * sin(arg);
}
float EvalSineParams_1() {
float arg = frequency_1 * inPerimeter + speed_1 * time;
return amplitude_1 * sin(arg);
}
float EvalSineParams_2() {
float arg = frequency_2 * inPerimeter + speed_2 * time;
return amplitude_2 * sin(arg);
}
vec4 ComputeVertexPosition() {
// TODO: workaround. See discussion in PerObject struct, above.
// float scale = EvalSineParams(sine_params_0) +
// EvalSineParams(sine_params_1) +
// EvalSineParams(sine_params_2);
float offset_scale = EvalSineParams_0() + EvalSineParams_1() + EvalSineParams_2();
return vec4(inPosition + offset_scale * inPositionOffset, 1);
}
)GLSL";
std::string ModelRenderPass::GetVertexShaderSourceCode(
const ModelPipelineSpec& spec) {
std::ostringstream src;
src << kVertexShaderPreamble;
if (spec.mesh_spec.has_attribute(0, MeshAttribute::kPosition2D) ||
spec.mesh_spec.has_attribute(0, MeshAttribute::kPosition3D)) {
// NOTE: this shader works with both 2D and 3D meshes. In the former case,
// Vulkan fills the Z-coordinate of |inPosition| with the default value: 0.
// See section 20.2 of the Vulkan spec (as of Vulkan 1.0.57).
src << "layout(location = 0) in vec3 inPosition;\n";
}
if (spec.mesh_spec.has_attribute(0, MeshAttribute::kPositionOffset)) {
src << "layout(location = 1) in vec3 inPositionOffset;\n";
}
if (spec.mesh_spec.has_attribute(0, MeshAttribute::kUV)) {
src << "layout(location = 2) in vec2 inUV;\n";
}
if (spec.mesh_spec.has_attribute(0, MeshAttribute::kPerimeterPos)) {
src << "layout(location = 3) in float inPerimeter;\n";
}
if (spec.shape_modifiers & ShapeModifier::kWobble) {
src << kVertexShaderWobblePosition;
} else {
src << kVertexShaderPosition;
}
src << GetVertexShaderMainSourceCode() << std::endl;
return src.str();
}
} // namespace impl
} // namespace escher