blob: 485ebb686ef73c67dad636772b48b1e9239fc809 [file] [log] [blame]
// Copyright 2018 The Amber Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/vulkan/graphics_pipeline.h"
#include <cassert>
#include <cmath>
#include "src/command.h"
#include "src/make_unique.h"
#include "src/vulkan/command_pool.h"
#include "src/vulkan/device.h"
namespace amber {
namespace vulkan {
namespace {
const VkAttachmentDescription kDefaultAttachmentDesc = {
0, /* flags */
VK_FORMAT_UNDEFINED, /* format */
VK_SAMPLE_COUNT_1_BIT, /* samples */
VK_ATTACHMENT_LOAD_OP_LOAD, /* loadOp */
VK_ATTACHMENT_STORE_OP_STORE, /* storeOp */
VK_ATTACHMENT_LOAD_OP_LOAD, /* stencilLoadOp */
VK_ATTACHMENT_STORE_OP_STORE, /* stencilStoreOp */
VK_IMAGE_LAYOUT_UNDEFINED, /* initialLayout */
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, /* finalLayout */
};
const VkSampleMask kSampleMask = ~0U;
VkPrimitiveTopology ToVkTopology(Topology topology) {
switch (topology) {
case Topology::kPointList:
return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
case Topology::kLineList:
return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
case Topology::kLineStrip:
return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
case Topology::kTriangleList:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
case Topology::kTriangleStrip:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
case Topology::kTriangleFan:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN;
case Topology::kLineListWithAdjacency:
return VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY;
case Topology::kLineStripWithAdjacency:
return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY;
case Topology::kTriangleListWithAdjacency:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY;
case Topology::kTriangleStripWithAdjacency:
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY;
case Topology::kPatchList:
return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
default:
break;
}
assert(false && "Vulkan::Unknown topology");
return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
}
VkStencilOp ToVkStencilOp(StencilOp op) {
switch (op) {
case StencilOp::kKeep:
return VK_STENCIL_OP_KEEP;
case StencilOp::kZero:
return VK_STENCIL_OP_ZERO;
case StencilOp::kReplace:
return VK_STENCIL_OP_REPLACE;
case StencilOp::kIncrementAndClamp:
return VK_STENCIL_OP_INCREMENT_AND_CLAMP;
case StencilOp::kDecrementAndClamp:
return VK_STENCIL_OP_DECREMENT_AND_CLAMP;
case StencilOp::kInvert:
return VK_STENCIL_OP_INVERT;
case StencilOp::kIncrementAndWrap:
return VK_STENCIL_OP_INCREMENT_AND_WRAP;
case StencilOp::kDecrementAndWrap:
return VK_STENCIL_OP_DECREMENT_AND_WRAP;
case StencilOp::kUnknown:
break;
}
assert(false && "Vulkan::Unknown StencilOp");
return VK_STENCIL_OP_KEEP;
}
VkCompareOp ToVkCompareOp(CompareOp op) {
switch (op) {
case CompareOp::kNever:
return VK_COMPARE_OP_NEVER;
case CompareOp::kLess:
return VK_COMPARE_OP_LESS;
case CompareOp::kEqual:
return VK_COMPARE_OP_EQUAL;
case CompareOp::kLessOrEqual:
return VK_COMPARE_OP_LESS_OR_EQUAL;
case CompareOp::kGreater:
return VK_COMPARE_OP_GREATER;
case CompareOp::kNotEqual:
return VK_COMPARE_OP_NOT_EQUAL;
case CompareOp::kGreaterOrEqual:
return VK_COMPARE_OP_GREATER_OR_EQUAL;
case CompareOp::kAlways:
return VK_COMPARE_OP_ALWAYS;
case CompareOp::kUnknown:
break;
}
assert(false && "Vulkan::Unknown CompareOp");
return VK_COMPARE_OP_NEVER;
}
VkPolygonMode ToVkPolygonMode(PolygonMode mode) {
switch (mode) {
case PolygonMode::kFill:
return VK_POLYGON_MODE_FILL;
case PolygonMode::kLine:
return VK_POLYGON_MODE_LINE;
case PolygonMode::kPoint:
return VK_POLYGON_MODE_POINT;
}
assert(false && "Vulkan::Unknown PolygonMode");
return VK_POLYGON_MODE_FILL;
}
VkCullModeFlags ToVkCullMode(CullMode mode) {
switch (mode) {
case CullMode::kNone:
return VK_CULL_MODE_NONE;
case CullMode::kFront:
return VK_CULL_MODE_FRONT_BIT;
case CullMode::kBack:
return VK_CULL_MODE_BACK_BIT;
case CullMode::kFrontAndBack:
return VK_CULL_MODE_FRONT_AND_BACK;
}
assert(false && "Vulkan::Unknown CullMode");
return VK_CULL_MODE_NONE;
}
VkFrontFace ToVkFrontFace(FrontFace front_face) {
return front_face == FrontFace::kClockwise ? VK_FRONT_FACE_CLOCKWISE
: VK_FRONT_FACE_COUNTER_CLOCKWISE;
}
VkLogicOp ToVkLogicOp(LogicOp op) {
switch (op) {
case LogicOp::kClear:
return VK_LOGIC_OP_CLEAR;
case LogicOp::kAnd:
return VK_LOGIC_OP_AND;
case LogicOp::kAndReverse:
return VK_LOGIC_OP_AND_REVERSE;
case LogicOp::kCopy:
return VK_LOGIC_OP_COPY;
case LogicOp::kAndInverted:
return VK_LOGIC_OP_AND_INVERTED;
case LogicOp::kNoOp:
return VK_LOGIC_OP_NO_OP;
case LogicOp::kXor:
return VK_LOGIC_OP_XOR;
case LogicOp::kOr:
return VK_LOGIC_OP_OR;
case LogicOp::kNor:
return VK_LOGIC_OP_NOR;
case LogicOp::kEquivalent:
return VK_LOGIC_OP_EQUIVALENT;
case LogicOp::kInvert:
return VK_LOGIC_OP_INVERT;
case LogicOp::kOrReverse:
return VK_LOGIC_OP_OR_REVERSE;
case LogicOp::kCopyInverted:
return VK_LOGIC_OP_COPY_INVERTED;
case LogicOp::kOrInverted:
return VK_LOGIC_OP_OR_INVERTED;
case LogicOp::kNand:
return VK_LOGIC_OP_NAND;
case LogicOp::kSet:
return VK_LOGIC_OP_SET;
}
assert(false && "Vulkan::Unknown LogicOp");
return VK_LOGIC_OP_CLEAR;
}
VkBlendFactor ToVkBlendFactor(BlendFactor factor) {
switch (factor) {
case BlendFactor::kZero:
return VK_BLEND_FACTOR_ZERO;
case BlendFactor::kOne:
return VK_BLEND_FACTOR_ONE;
case BlendFactor::kSrcColor:
return VK_BLEND_FACTOR_SRC_COLOR;
case BlendFactor::kOneMinusSrcColor:
return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
case BlendFactor::kDstColor:
return VK_BLEND_FACTOR_DST_COLOR;
case BlendFactor::kOneMinusDstColor:
return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;
case BlendFactor::kSrcAlpha:
return VK_BLEND_FACTOR_SRC_ALPHA;
case BlendFactor::kOneMinusSrcAlpha:
return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
case BlendFactor::kDstAlpha:
return VK_BLEND_FACTOR_DST_ALPHA;
case BlendFactor::kOneMinusDstAlpha:
return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
case BlendFactor::kConstantColor:
return VK_BLEND_FACTOR_CONSTANT_COLOR;
case BlendFactor::kOneMinusConstantColor:
return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR;
case BlendFactor::kConstantAlpha:
return VK_BLEND_FACTOR_CONSTANT_ALPHA;
case BlendFactor::kOneMinusConstantAlpha:
return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA;
case BlendFactor::kSrcAlphaSaturate:
return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE;
case BlendFactor::kSrc1Color:
return VK_BLEND_FACTOR_SRC1_COLOR;
case BlendFactor::kOneMinusSrc1Color:
return VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR;
case BlendFactor::kSrc1Alpha:
return VK_BLEND_FACTOR_SRC1_ALPHA;
case BlendFactor::kOneMinusSrc1Alpha:
return VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA;
case BlendFactor::kUnknown:
break;
}
assert(false && "Vulkan::Unknown BlendFactor");
return VK_BLEND_FACTOR_ZERO;
}
VkBlendOp ToVkBlendOp(BlendOp op) {
switch (op) {
case BlendOp::kAdd:
return VK_BLEND_OP_ADD;
case BlendOp::kSubtract:
return VK_BLEND_OP_SUBTRACT;
case BlendOp::kReverseSubtract:
return VK_BLEND_OP_REVERSE_SUBTRACT;
case BlendOp::kMin:
return VK_BLEND_OP_MIN;
case BlendOp::kMax:
return VK_BLEND_OP_MAX;
case BlendOp::kZero:
return VK_BLEND_OP_ZERO_EXT;
case BlendOp::kSrc:
return VK_BLEND_OP_SRC_EXT;
case BlendOp::kDst:
return VK_BLEND_OP_DST_EXT;
case BlendOp::kSrcOver:
return VK_BLEND_OP_SRC_OVER_EXT;
case BlendOp::kDstOver:
return VK_BLEND_OP_DST_OVER_EXT;
case BlendOp::kSrcIn:
return VK_BLEND_OP_SRC_IN_EXT;
case BlendOp::kDstIn:
return VK_BLEND_OP_DST_IN_EXT;
case BlendOp::kSrcOut:
return VK_BLEND_OP_SRC_OUT_EXT;
case BlendOp::kDstOut:
return VK_BLEND_OP_DST_OUT_EXT;
case BlendOp::kSrcAtop:
return VK_BLEND_OP_SRC_ATOP_EXT;
case BlendOp::kDstAtop:
return VK_BLEND_OP_DST_ATOP_EXT;
case BlendOp::kXor:
return VK_BLEND_OP_XOR_EXT;
case BlendOp::kMultiply:
return VK_BLEND_OP_MULTIPLY_EXT;
case BlendOp::kScreen:
return VK_BLEND_OP_SCREEN_EXT;
case BlendOp::kOverlay:
return VK_BLEND_OP_OVERLAY_EXT;
case BlendOp::kDarken:
return VK_BLEND_OP_DARKEN_EXT;
case BlendOp::kLighten:
return VK_BLEND_OP_LIGHTEN_EXT;
case BlendOp::kColorDodge:
return VK_BLEND_OP_COLORDODGE_EXT;
case BlendOp::kColorBurn:
return VK_BLEND_OP_COLORBURN_EXT;
case BlendOp::kHardLight:
return VK_BLEND_OP_HARDLIGHT_EXT;
case BlendOp::kSoftLight:
return VK_BLEND_OP_SOFTLIGHT_EXT;
case BlendOp::kDifference:
return VK_BLEND_OP_DIFFERENCE_EXT;
case BlendOp::kExclusion:
return VK_BLEND_OP_EXCLUSION_EXT;
case BlendOp::kInvert:
return VK_BLEND_OP_INVERT_EXT;
case BlendOp::kInvertRGB:
return VK_BLEND_OP_INVERT_RGB_EXT;
case BlendOp::kLinearDodge:
return VK_BLEND_OP_LINEARDODGE_EXT;
case BlendOp::kLinearBurn:
return VK_BLEND_OP_LINEARBURN_EXT;
case BlendOp::kVividLight:
return VK_BLEND_OP_VIVIDLIGHT_EXT;
case BlendOp::kLinearLight:
return VK_BLEND_OP_LINEARLIGHT_EXT;
case BlendOp::kPinLight:
return VK_BLEND_OP_PINLIGHT_EXT;
case BlendOp::kHardMix:
return VK_BLEND_OP_HARDMIX_EXT;
case BlendOp::kHslHue:
return VK_BLEND_OP_HSL_HUE_EXT;
case BlendOp::kHslSaturation:
return VK_BLEND_OP_HSL_SATURATION_EXT;
case BlendOp::kHslColor:
return VK_BLEND_OP_HSL_COLOR_EXT;
case BlendOp::kHslLuminosity:
return VK_BLEND_OP_HSL_LUMINOSITY_EXT;
case BlendOp::kPlus:
return VK_BLEND_OP_PLUS_EXT;
case BlendOp::kPlusClamped:
return VK_BLEND_OP_PLUS_CLAMPED_EXT;
case BlendOp::kPlusClampedAlpha:
return VK_BLEND_OP_PLUS_CLAMPED_ALPHA_EXT;
case BlendOp::kPlusDarker:
return VK_BLEND_OP_PLUS_DARKER_EXT;
case BlendOp::kMinus:
return VK_BLEND_OP_MINUS_EXT;
case BlendOp::kMinusClamped:
return VK_BLEND_OP_MINUS_CLAMPED_EXT;
case BlendOp::kContrast:
return VK_BLEND_OP_CONTRAST_EXT;
case BlendOp::kInvertOvg:
return VK_BLEND_OP_INVERT_OVG_EXT;
case BlendOp::kRed:
return VK_BLEND_OP_RED_EXT;
case BlendOp::kGreen:
return VK_BLEND_OP_GREEN_EXT;
case BlendOp::kBlue:
return VK_BLEND_OP_BLUE_EXT;
case BlendOp::kUnknown:
break;
}
assert(false && "Vulkan::Unknown BlendOp");
return VK_BLEND_OP_ADD;
}
class RenderPassGuard {
public:
explicit RenderPassGuard(GraphicsPipeline* pipeline) : pipeline_(pipeline) {
auto* frame = pipeline_->GetFrameBuffer();
auto* cmd = pipeline_->GetCommandBuffer();
frame->ChangeFrameToDrawLayout(cmd);
VkRenderPassBeginInfo render_begin_info = VkRenderPassBeginInfo();
render_begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
render_begin_info.renderPass = pipeline_->GetVkRenderPass();
render_begin_info.framebuffer = frame->GetVkFrameBuffer();
render_begin_info.renderArea = {{0, 0},
{frame->GetWidth(), frame->GetHeight()}};
pipeline_->GetDevice()->GetPtrs()->vkCmdBeginRenderPass(
cmd->GetVkCommandBuffer(), &render_begin_info,
VK_SUBPASS_CONTENTS_INLINE);
}
~RenderPassGuard() {
auto* cmd = pipeline_->GetCommandBuffer();
pipeline_->GetDevice()->GetPtrs()->vkCmdEndRenderPass(
cmd->GetVkCommandBuffer());
auto* frame = pipeline_->GetFrameBuffer();
frame->ChangeFrameToProbeLayout(cmd);
}
private:
GraphicsPipeline* pipeline_;
};
} // namespace
GraphicsPipeline::GraphicsPipeline(
Device* device,
const std::vector<amber::Pipeline::BufferInfo>& color_buffers,
amber::Pipeline::BufferInfo depth_stencil_buffer,
const std::vector<amber::Pipeline::BufferInfo>& resolve_targets,
uint32_t fence_timeout_ms,
bool pipeline_runtime_layer_enabled,
const std::vector<VkPipelineShaderStageCreateInfo>& shader_stage_info)
: Pipeline(PipelineType::kGraphics,
device,
fence_timeout_ms,
pipeline_runtime_layer_enabled,
shader_stage_info),
depth_stencil_buffer_(depth_stencil_buffer) {
for (const auto& info : color_buffers)
color_buffers_.push_back(&info);
for (const auto& info : resolve_targets)
resolve_targets_.push_back(&info);
}
GraphicsPipeline::~GraphicsPipeline() {
if (render_pass_) {
device_->GetPtrs()->vkDestroyRenderPass(device_->GetVkDevice(),
render_pass_, nullptr);
}
}
Result GraphicsPipeline::CreateRenderPass() {
VkSubpassDescription subpass_desc = VkSubpassDescription();
subpass_desc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
std::vector<VkAttachmentDescription> attachment_desc;
std::vector<VkAttachmentReference> color_refer;
VkAttachmentReference depth_refer = VkAttachmentReference();
std::vector<VkAttachmentReference> resolve_refer;
for (const auto* info : color_buffers_) {
attachment_desc.push_back(kDefaultAttachmentDesc);
attachment_desc.back().format =
device_->GetVkFormat(*info->buffer->GetFormat());
attachment_desc.back().initialLayout =
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachment_desc.back().finalLayout =
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachment_desc.back().samples =
static_cast<VkSampleCountFlagBits>(info->buffer->GetSamples());
VkAttachmentReference ref = VkAttachmentReference();
ref.attachment = static_cast<uint32_t>(attachment_desc.size() - 1);
ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
color_refer.push_back(ref);
}
subpass_desc.colorAttachmentCount = static_cast<uint32_t>(color_refer.size());
subpass_desc.pColorAttachments = color_refer.data();
if (depth_stencil_buffer_.buffer &&
depth_stencil_buffer_.buffer->GetFormat()->IsFormatKnown()) {
attachment_desc.push_back(kDefaultAttachmentDesc);
attachment_desc.back().format =
device_->GetVkFormat(*depth_stencil_buffer_.buffer->GetFormat());
attachment_desc.back().initialLayout =
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachment_desc.back().finalLayout =
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
depth_refer.attachment = static_cast<uint32_t>(attachment_desc.size() - 1);
depth_refer.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
subpass_desc.pDepthStencilAttachment = &depth_refer;
}
for (const auto* info : resolve_targets_) {
attachment_desc.push_back(kDefaultAttachmentDesc);
attachment_desc.back().format =
device_->GetVkFormat(*info->buffer->GetFormat());
attachment_desc.back().initialLayout =
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachment_desc.back().finalLayout =
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkAttachmentReference ref = VkAttachmentReference();
ref.attachment = static_cast<uint32_t>(attachment_desc.size() - 1);
ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
resolve_refer.push_back(ref);
}
subpass_desc.pResolveAttachments = resolve_refer.data();
VkRenderPassCreateInfo render_pass_info = VkRenderPassCreateInfo();
render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
render_pass_info.attachmentCount =
static_cast<uint32_t>(attachment_desc.size());
render_pass_info.pAttachments = attachment_desc.data();
render_pass_info.subpassCount = 1;
render_pass_info.pSubpasses = &subpass_desc;
if (device_->GetPtrs()->vkCreateRenderPass(device_->GetVkDevice(),
&render_pass_info, nullptr,
&render_pass_) != VK_SUCCESS) {
return Result("Vulkan::Calling vkCreateRenderPass Fail");
}
return {};
}
VkPipelineDepthStencilStateCreateInfo
GraphicsPipeline::GetVkPipelineDepthStencilInfo(
const PipelineData* pipeline_data) {
VkPipelineDepthStencilStateCreateInfo depthstencil_info =
VkPipelineDepthStencilStateCreateInfo();
depthstencil_info.sType =
VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
depthstencil_info.depthTestEnable = pipeline_data->GetEnableDepthTest();
depthstencil_info.depthWriteEnable = pipeline_data->GetEnableDepthWrite();
depthstencil_info.depthCompareOp =
ToVkCompareOp(pipeline_data->GetDepthCompareOp());
depthstencil_info.depthBoundsTestEnable =
pipeline_data->GetEnableDepthBoundsTest();
depthstencil_info.stencilTestEnable = pipeline_data->GetEnableStencilTest();
depthstencil_info.front.failOp =
ToVkStencilOp(pipeline_data->GetFrontFailOp());
depthstencil_info.front.passOp =
ToVkStencilOp(pipeline_data->GetFrontPassOp());
depthstencil_info.front.depthFailOp =
ToVkStencilOp(pipeline_data->GetFrontDepthFailOp());
depthstencil_info.front.compareOp =
ToVkCompareOp(pipeline_data->GetFrontCompareOp());
depthstencil_info.front.compareMask = pipeline_data->GetFrontCompareMask();
depthstencil_info.front.writeMask = pipeline_data->GetFrontWriteMask();
depthstencil_info.front.reference = pipeline_data->GetFrontReference();
depthstencil_info.back.failOp = ToVkStencilOp(pipeline_data->GetBackFailOp());
depthstencil_info.back.passOp = ToVkStencilOp(pipeline_data->GetBackPassOp());
depthstencil_info.back.depthFailOp =
ToVkStencilOp(pipeline_data->GetBackDepthFailOp());
depthstencil_info.back.compareOp =
ToVkCompareOp(pipeline_data->GetBackCompareOp());
depthstencil_info.back.compareMask = pipeline_data->GetBackCompareMask();
depthstencil_info.back.writeMask = pipeline_data->GetBackWriteMask();
depthstencil_info.back.reference = pipeline_data->GetBackReference();
depthstencil_info.minDepthBounds = pipeline_data->GetMinDepthBounds();
depthstencil_info.maxDepthBounds = pipeline_data->GetMaxDepthBounds();
return depthstencil_info;
}
std::vector<VkPipelineColorBlendAttachmentState>
GraphicsPipeline::GetVkPipelineColorBlendAttachmentState(
const PipelineData* pipeline_data) {
std::vector<VkPipelineColorBlendAttachmentState> states;
for (size_t i = 0; i < color_buffers_.size(); ++i) {
VkPipelineColorBlendAttachmentState colorblend_attachment =
VkPipelineColorBlendAttachmentState();
colorblend_attachment.blendEnable = pipeline_data->GetEnableBlend();
colorblend_attachment.srcColorBlendFactor =
ToVkBlendFactor(pipeline_data->GetSrcColorBlendFactor());
colorblend_attachment.dstColorBlendFactor =
ToVkBlendFactor(pipeline_data->GetDstColorBlendFactor());
colorblend_attachment.colorBlendOp =
ToVkBlendOp(pipeline_data->GetColorBlendOp());
colorblend_attachment.srcAlphaBlendFactor =
ToVkBlendFactor(pipeline_data->GetSrcAlphaBlendFactor());
colorblend_attachment.dstAlphaBlendFactor =
ToVkBlendFactor(pipeline_data->GetDstAlphaBlendFactor());
colorblend_attachment.alphaBlendOp =
ToVkBlendOp(pipeline_data->GetAlphaBlendOp());
colorblend_attachment.colorWriteMask = pipeline_data->GetColorWriteMask();
states.push_back(colorblend_attachment);
}
return states;
}
Result GraphicsPipeline::CreateVkGraphicsPipeline(
const PipelineData* pipeline_data,
VkPrimitiveTopology topology,
const VertexBuffer* vertex_buffer,
const VkPipelineLayout& pipeline_layout,
VkPipeline* pipeline) {
if (!pipeline_data) {
return Result(
"Vulkan: GraphicsPipeline::CreateVkGraphicsPipeline PipelineData is "
"null");
}
std::vector<VkVertexInputBindingDescription> vertex_bindings;
std::vector<VkVertexInputAttributeDescription> vertex_attribs;
if (vertex_buffer != nullptr) {
vertex_bindings = vertex_buffer->GetVkVertexInputBinding();
vertex_attribs = vertex_buffer->GetVkVertexInputAttr();
}
VkPipelineVertexInputStateCreateInfo vertex_input_info =
VkPipelineVertexInputStateCreateInfo();
vertex_input_info.sType =
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertex_input_info.vertexBindingDescriptionCount =
static_cast<uint32_t>(vertex_bindings.size());
vertex_input_info.pVertexBindingDescriptions =
vertex_bindings.empty() ? nullptr : vertex_bindings.data();
vertex_input_info.vertexAttributeDescriptionCount =
static_cast<uint32_t>(vertex_attribs.size());
vertex_input_info.pVertexAttributeDescriptions =
vertex_attribs.empty() ? nullptr : vertex_attribs.data();
VkPipelineInputAssemblyStateCreateInfo input_assembly_info =
VkPipelineInputAssemblyStateCreateInfo();
input_assembly_info.sType =
VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
// TODO(jaebaek): Handle the given index if exists.
input_assembly_info.topology = topology;
input_assembly_info.primitiveRestartEnable =
pipeline_data->GetEnablePrimitiveRestart();
VkViewport viewport = {
0, 0, static_cast<float>(frame_width_), static_cast<float>(frame_height_),
0, 1};
if (pipeline_data->HasViewportData()) {
Viewport vp = pipeline_data->GetViewport();
viewport.x = vp.x;
viewport.y = vp.y;
viewport.width = vp.w;
viewport.height = vp.h;
viewport.minDepth = vp.mind;
viewport.maxDepth = vp.maxd;
}
VkRect2D scissor = {{0, 0}, {frame_width_, frame_height_}};
VkPipelineViewportStateCreateInfo viewport_info =
VkPipelineViewportStateCreateInfo();
viewport_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewport_info.viewportCount = 1;
viewport_info.pViewports = &viewport;
viewport_info.scissorCount = 1;
viewport_info.pScissors = &scissor;
auto shader_stage_info = GetVkShaderStageInfo();
bool is_tessellation_needed = false;
for (auto& info : shader_stage_info) {
info.pName = GetEntryPointName(info.stage);
if (info.stage == VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT ||
info.stage == VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) {
is_tessellation_needed = true;
}
}
VkPipelineMultisampleStateCreateInfo multisampleInfo = {
VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, /* sType */
nullptr, /* pNext */
0, /* flags */
kDefaultAttachmentDesc.samples, /* rasterizationSamples */
VK_FALSE, /* sampleShadingEnable */
0, /* minSampleShading */
&kSampleMask, /* pSampleMask */
VK_FALSE, /* alphaToCoverageEnable */
VK_FALSE, /* alphaToOneEnable */
};
// Search for multisampled color buffers and adjust the rasterization samples
// to match.
for (const auto& cb : color_buffers_) {
uint32_t samples = cb->buffer->GetSamples();
assert(static_cast<VkSampleCountFlagBits>(samples) >=
multisampleInfo.rasterizationSamples);
multisampleInfo.rasterizationSamples =
static_cast<VkSampleCountFlagBits>(samples);
}
VkGraphicsPipelineCreateInfo pipeline_info = VkGraphicsPipelineCreateInfo();
pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipeline_info.stageCount = static_cast<uint32_t>(shader_stage_info.size());
pipeline_info.pStages = shader_stage_info.data();
pipeline_info.pVertexInputState = &vertex_input_info;
pipeline_info.pInputAssemblyState = &input_assembly_info;
pipeline_info.pViewportState = &viewport_info;
pipeline_info.pMultisampleState = &multisampleInfo;
VkPipelineRasterizationStateCreateInfo rasterization_info =
VkPipelineRasterizationStateCreateInfo();
rasterization_info.sType =
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterization_info.depthClampEnable = pipeline_data->GetEnableDepthClamp();
rasterization_info.rasterizerDiscardEnable =
pipeline_data->GetEnableRasterizerDiscard();
rasterization_info.polygonMode =
ToVkPolygonMode(pipeline_data->GetPolygonMode());
rasterization_info.cullMode = ToVkCullMode(pipeline_data->GetCullMode());
rasterization_info.frontFace = ToVkFrontFace(pipeline_data->GetFrontFace());
rasterization_info.depthBiasEnable = pipeline_data->GetEnableDepthBias();
rasterization_info.depthBiasConstantFactor =
pipeline_data->GetDepthBiasConstantFactor();
rasterization_info.depthBiasClamp = pipeline_data->GetDepthBiasClamp();
rasterization_info.depthBiasSlopeFactor =
pipeline_data->GetDepthBiasSlopeFactor();
rasterization_info.lineWidth = pipeline_data->GetLineWidth();
pipeline_info.pRasterizationState = &rasterization_info;
VkPipelineTessellationStateCreateInfo tess_info =
VkPipelineTessellationStateCreateInfo();
if (is_tessellation_needed) {
tess_info.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
tess_info.patchControlPoints = patch_control_points_;
pipeline_info.pTessellationState = &tess_info;
}
VkPipelineDepthStencilStateCreateInfo depthstencil_info;
if (depth_stencil_buffer_.buffer &&
depth_stencil_buffer_.buffer->GetFormat()->IsFormatKnown()) {
depthstencil_info = GetVkPipelineDepthStencilInfo(pipeline_data);
pipeline_info.pDepthStencilState = &depthstencil_info;
}
VkPipelineColorBlendStateCreateInfo colorblend_info =
VkPipelineColorBlendStateCreateInfo();
auto colorblend_attachment =
GetVkPipelineColorBlendAttachmentState(pipeline_data);
colorblend_info.sType =
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorblend_info.logicOpEnable = pipeline_data->GetEnableLogicOp();
colorblend_info.logicOp = ToVkLogicOp(pipeline_data->GetLogicOp());
colorblend_info.attachmentCount =
static_cast<uint32_t>(colorblend_attachment.size());
colorblend_info.pAttachments = colorblend_attachment.data();
pipeline_info.pColorBlendState = &colorblend_info;
pipeline_info.layout = pipeline_layout;
pipeline_info.renderPass = render_pass_;
pipeline_info.subpass = 0;
if (device_->GetPtrs()->vkCreateGraphicsPipelines(
device_->GetVkDevice(), VK_NULL_HANDLE, 1, &pipeline_info, nullptr,
pipeline) != VK_SUCCESS) {
return Result("Vulkan::Calling vkCreateGraphicsPipelines Fail");
}
return {};
}
Result GraphicsPipeline::Initialize(uint32_t width,
uint32_t height,
CommandPool* pool) {
Result r = Pipeline::Initialize(pool);
if (!r.IsSuccess())
return r;
r = CreateRenderPass();
if (!r.IsSuccess())
return r;
frame_ =
MakeUnique<FrameBuffer>(device_, color_buffers_, depth_stencil_buffer_,
resolve_targets_, width, height);
r = frame_->Initialize(render_pass_);
if (!r.IsSuccess())
return r;
frame_width_ = width;
frame_height_ = height;
return {};
}
Result GraphicsPipeline::SendVertexBufferDataIfNeeded(
VertexBuffer* vertex_buffer) {
if (!vertex_buffer || vertex_buffer->VertexDataSent())
return {};
return vertex_buffer->SendVertexData(command_.get());
}
Result GraphicsPipeline::SetIndexBuffer(Buffer* buffer) {
if (index_buffer_) {
return Result(
"GraphicsPipeline::SetIndexBuffer must be called once when "
"index_buffer_ is created");
}
index_buffer_ = MakeUnique<IndexBuffer>(device_);
CommandBufferGuard guard(GetCommandBuffer());
if (!guard.IsRecording())
return guard.GetResult();
Result r = index_buffer_->SendIndexData(command_.get(), buffer);
if (!r.IsSuccess())
return r;
return guard.Submit(GetFenceTimeout(), GetPipelineRuntimeLayerEnabled());
}
Result GraphicsPipeline::SetClearColor(float r, float g, float b, float a) {
clear_color_r_ = r;
clear_color_g_ = g;
clear_color_b_ = b;
clear_color_a_ = a;
return {};
}
Result GraphicsPipeline::SetClearStencil(uint32_t stencil) {
if (!depth_stencil_buffer_.buffer ||
!depth_stencil_buffer_.buffer->GetFormat()->IsFormatKnown()) {
return Result(
"Vulkan::ClearStencilCommand No DepthStencil Buffer for FrameBuffer "
"Exists");
}
clear_stencil_ = stencil;
return {};
}
Result GraphicsPipeline::SetClearDepth(float depth) {
if (!depth_stencil_buffer_.buffer ||
!depth_stencil_buffer_.buffer->GetFormat()->IsFormatKnown()) {
return Result(
"Vulkan::ClearStencilCommand No DepthStencil Buffer for FrameBuffer "
"Exists");
}
clear_depth_ = depth;
return {};
}
Result GraphicsPipeline::Clear() {
VkClearValue colour_clear;
colour_clear.color = {
{clear_color_r_, clear_color_g_, clear_color_b_, clear_color_a_}};
CommandBufferGuard cmd_buf_guard(GetCommandBuffer());
if (!cmd_buf_guard.IsRecording())
return cmd_buf_guard.GetResult();
frame_->ChangeFrameToWriteLayout(GetCommandBuffer());
frame_->CopyBuffersToImages();
frame_->TransferImagesToDevice(GetCommandBuffer());
{
RenderPassGuard render_pass_guard(this);
std::vector<VkClearAttachment> clears;
for (size_t i = 0; i < color_buffers_.size(); ++i) {
VkClearAttachment clear_attachment = VkClearAttachment();
clear_attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
clear_attachment.colorAttachment = static_cast<uint32_t>(i);
clear_attachment.clearValue = colour_clear;
clears.push_back(clear_attachment);
}
if (depth_stencil_buffer_.buffer &&
depth_stencil_buffer_.buffer->GetFormat()->IsFormatKnown()) {
VkClearValue depth_stencil_clear;
depth_stencil_clear.depthStencil = {clear_depth_, clear_stencil_};
VkImageAspectFlags aspect = VK_IMAGE_ASPECT_DEPTH_BIT;
if (depth_stencil_buffer_.buffer->GetFormat()->HasStencilComponent())
aspect |= VK_IMAGE_ASPECT_STENCIL_BIT;
VkClearAttachment clear_attachment = VkClearAttachment();
clear_attachment.aspectMask = aspect;
clear_attachment.colorAttachment =
static_cast<uint32_t>(color_buffers_.size());
clear_attachment.clearValue = depth_stencil_clear;
clears.push_back(clear_attachment);
}
VkClearRect clear_rect;
clear_rect.rect = {{0, 0}, {frame_width_, frame_height_}};
clear_rect.baseArrayLayer = 0;
clear_rect.layerCount = 1;
device_->GetPtrs()->vkCmdClearAttachments(
command_->GetVkCommandBuffer(), static_cast<uint32_t>(clears.size()),
clears.data(), 1, &clear_rect);
}
frame_->TransferImagesToHost(command_.get());
Result r = cmd_buf_guard.Submit(GetFenceTimeout(),
GetPipelineRuntimeLayerEnabled());
if (!r.IsSuccess())
return r;
frame_->CopyImagesToBuffers();
return {};
}
Result GraphicsPipeline::Draw(const DrawArraysCommand* command,
VertexBuffer* vertex_buffer) {
Result r = SendDescriptorDataToDeviceIfNeeded();
if (!r.IsSuccess())
return r;
VkPipelineLayout pipeline_layout = VK_NULL_HANDLE;
r = CreateVkPipelineLayout(&pipeline_layout);
if (!r.IsSuccess())
return r;
VkPipeline pipeline = VK_NULL_HANDLE;
r = CreateVkGraphicsPipeline(command->GetPipelineData(),
ToVkTopology(command->GetTopology()),
vertex_buffer, pipeline_layout, &pipeline);
if (!r.IsSuccess())
return r;
// Note that a command updating a descriptor set and a command using
// it must be submitted separately, because using a descriptor set
// while updating it is not safe.
UpdateDescriptorSetsIfNeeded();
{
CommandBufferGuard cmd_buf_guard(GetCommandBuffer());
if (!cmd_buf_guard.IsRecording())
return cmd_buf_guard.GetResult();
r = SendVertexBufferDataIfNeeded(vertex_buffer);
if (!r.IsSuccess())
return r;
frame_->ChangeFrameToWriteLayout(GetCommandBuffer());
frame_->CopyBuffersToImages();
frame_->TransferImagesToDevice(GetCommandBuffer());
{
RenderPassGuard render_pass_guard(this);
BindVkDescriptorSets(pipeline_layout);
r = RecordPushConstant(pipeline_layout);
if (!r.IsSuccess())
return r;
device_->GetPtrs()->vkCmdBindPipeline(command_->GetVkCommandBuffer(),
VK_PIPELINE_BIND_POINT_GRAPHICS,
pipeline);
if (vertex_buffer != nullptr)
vertex_buffer->BindToCommandBuffer(command_.get());
if (command->IsIndexed()) {
if (!index_buffer_)
return Result("Vulkan: Draw indexed is used without given indices");
r = index_buffer_->BindToCommandBuffer(command_.get());
if (!r.IsSuccess())
return r;
// VkRunner spec says
// "vertexCount will be used as the index count, firstVertex
// becomes the vertex offset and firstIndex will always be zero."
device_->GetPtrs()->vkCmdDrawIndexed(
command_->GetVkCommandBuffer(),
command->GetVertexCount(), /* indexCount */
command->GetInstanceCount(), /* instanceCount */
0, /* firstIndex */
static_cast<int32_t>(
command->GetFirstVertexIndex()), /* vertexOffset */
command->GetFirstInstance()); /* firstInstance */
} else {
device_->GetPtrs()->vkCmdDraw(
command_->GetVkCommandBuffer(), command->GetVertexCount(),
command->GetInstanceCount(), command->GetFirstVertexIndex(),
command->GetFirstInstance());
}
}
frame_->TransferImagesToHost(command_.get());
r = cmd_buf_guard.Submit(GetFenceTimeout(),
GetPipelineRuntimeLayerEnabled());
if (!r.IsSuccess())
return r;
}
r = ReadbackDescriptorsToHostDataQueue();
if (!r.IsSuccess())
return r;
frame_->CopyImagesToBuffers();
device_->GetPtrs()->vkDestroyPipeline(device_->GetVkDevice(), pipeline,
nullptr);
device_->GetPtrs()->vkDestroyPipelineLayout(device_->GetVkDevice(),
pipeline_layout, nullptr);
return {};
}
} // namespace vulkan
} // namespace amber