307 lines
18 KiB
C++
307 lines
18 KiB
C++
// VulkanWindow: render passes, pipeline layout, shader modules and the pipeline cache keyed by D3D8 state.
|
|
#include "vulkan_window.h"
|
|
|
|
#include "host_util.h"
|
|
#include "blend_state.h"
|
|
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <stdexcept>
|
|
|
|
namespace mt_host {
|
|
|
|
namespace {
|
|
|
|
std::vector<std::uint32_t> read_spirv(const char* name) {
|
|
std::size_t size = 0;
|
|
void* bytes = SDL_LoadFile(bundled_file_path(name).c_str(), &size);
|
|
#ifdef __ANDROID__
|
|
if (!bytes) bytes = SDL_LoadFile((std::string("assets://") + name).c_str(), &size);
|
|
if (!bytes) bytes = SDL_LoadFile(name, &size);
|
|
#endif
|
|
if (!bytes) throw std::runtime_error(std::string("cannot open bundled shader: ") + name);
|
|
if (size == 0 || size % 4) {
|
|
SDL_free(bytes);
|
|
throw std::runtime_error(std::string("invalid SPIR-V size: ") + name);
|
|
}
|
|
std::vector<std::uint32_t> words(size / 4);
|
|
std::memcpy(words.data(), bytes, size);
|
|
SDL_free(bytes);
|
|
return words;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void VulkanWindow::create_render_pass_and_layout() {
|
|
const bool msaa = samples_ != VK_SAMPLE_COUNT_1_BIT;
|
|
VkAttachmentDescription attachments[3]{};
|
|
attachments[0].format = swapchain_format_; attachments[0].samples = samples_;
|
|
attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
|
attachments[0].storeOp = msaa ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE;
|
|
attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
attachments[0].finalLayout = msaa ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
|
// MSAA resolve target: the swapchain image.
|
|
attachments[2].format = swapchain_format_; attachments[2].samples = VK_SAMPLE_COUNT_1_BIT;
|
|
attachments[2].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachments[2].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
|
attachments[2].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; attachments[2].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
|
attachments[1].format = VK_FORMAT_D32_SFLOAT; attachments[1].samples = samples_;
|
|
attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
|
attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
|
|
|
VkAttachmentReference color_ref{0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
|
|
VkAttachmentReference depth_ref{1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL};
|
|
VkAttachmentReference resolve_ref{2, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
|
|
VkSubpassDescription subpass{};
|
|
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
|
subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &color_ref;
|
|
if (msaa) subpass.pResolveAttachments = &resolve_ref;
|
|
subpass.pDepthStencilAttachment = &depth_ref;
|
|
|
|
VkSubpassDependency dependency{};
|
|
dependency.srcSubpass = VK_SUBPASS_EXTERNAL; dependency.dstSubpass = 0;
|
|
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
|
|
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
|
|
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
|
|
|
VkRenderPassCreateInfo pass_info{VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO};
|
|
pass_info.attachmentCount = msaa ? 3 : 2; pass_info.pAttachments = attachments;
|
|
pass_info.subpassCount = 1; pass_info.pSubpasses = &subpass;
|
|
pass_info.dependencyCount = 1; pass_info.pDependencies = &dependency;
|
|
check(vkCreateRenderPass(device_, &pass_info, nullptr, &pass_), "vkCreateRenderPass");
|
|
create_framebuffers();
|
|
|
|
const auto vert = read_spirv("native.vert.spv");
|
|
const auto frag = read_spirv("native.frag.spv");
|
|
auto module = [&](const std::vector<std::uint32_t>& code) {
|
|
VkShaderModuleCreateInfo info{VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO};
|
|
info.codeSize = code.size() * sizeof(std::uint32_t); info.pCode = code.data();
|
|
VkShaderModule handle = VK_NULL_HANDLE;
|
|
check(vkCreateShaderModule(device_, &info, nullptr, &handle), "vkCreateShaderModule");
|
|
return handle;
|
|
};
|
|
vertex_module_ = module(vert);
|
|
fragment_module_ = module(frag);
|
|
|
|
VkDescriptorSetLayout set_layouts[2] = {descriptor_layout_, bone_descriptor_layout_};
|
|
VkPushConstantRange push_constants{};
|
|
push_constants.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
|
|
push_constants.size = sizeof(PushConstants);
|
|
VkPipelineLayoutCreateInfo layout_info{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
|
|
layout_info.setLayoutCount = 2; layout_info.pSetLayouts = set_layouts;
|
|
layout_info.pushConstantRangeCount = 1; layout_info.pPushConstantRanges = &push_constants;
|
|
check(vkCreatePipelineLayout(device_, &layout_info, nullptr, &layout_), "vkCreatePipelineLayout");
|
|
|
|
get_pipeline(0, 2, 0);
|
|
create_offscreen_pass();
|
|
create_scene_pass();
|
|
}
|
|
|
|
void VulkanWindow::create_offscreen_pass() {
|
|
VkFormatProperties properties{};
|
|
vkGetPhysicalDeviceFormatProperties(physical_, VK_FORMAT_R5G6B5_UNORM_PACK16, &properties);
|
|
const VkFormatFeatureFlags wanted =
|
|
VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT;
|
|
offscreen_format_ = (properties.optimalTilingFeatures & wanted) == wanted
|
|
? VK_FORMAT_R5G6B5_UNORM_PACK16 : VK_FORMAT_R8G8B8A8_UNORM;
|
|
VkAttachmentDescription attachments[2]{};
|
|
attachments[0].format = offscreen_format_; attachments[0].samples = VK_SAMPLE_COUNT_1_BIT;
|
|
attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
|
attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
|
attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
|
attachments[0].initialLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
attachments[0].finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
attachments[1].format = VK_FORMAT_D32_SFLOAT; attachments[1].samples = VK_SAMPLE_COUNT_1_BIT;
|
|
attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
|
attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
|
attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
|
attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
|
attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
|
VkAttachmentReference color_ref{0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
|
|
VkAttachmentReference depth_ref{1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL};
|
|
VkSubpassDescription subpass{};
|
|
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
|
subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &color_ref;
|
|
subpass.pDepthStencilAttachment = &depth_ref;
|
|
VkSubpassDependency dependencies[2]{};
|
|
// Earlier work on the queue -- the previous frame sampling the texture, the previous
|
|
// offscreen pass, the first-use clear -- completes before this pass writes it.
|
|
dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; dependencies[0].dstSubpass = 0;
|
|
dependencies[0].srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
|
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
|
|
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT;
|
|
dependencies[0].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
|
|
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
|
|
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
|
|
dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
|
|
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
|
// The back-buffer pass samples the result.
|
|
dependencies[1].srcSubpass = 0; dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL;
|
|
dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
|
dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
|
dependencies[1].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
|
dependencies[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
VkRenderPassCreateInfo pass_info{VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO};
|
|
pass_info.attachmentCount = 2; pass_info.pAttachments = attachments;
|
|
pass_info.subpassCount = 1; pass_info.pSubpasses = &subpass;
|
|
pass_info.dependencyCount = 2; pass_info.pDependencies = dependencies;
|
|
check(vkCreateRenderPass(device_, &pass_info, nullptr, &offscreen_pass_), "vkCreateRenderPass offscreen");
|
|
}
|
|
|
|
VkPipeline VulkanWindow::get_pipeline(std::uint8_t cull, std::uint8_t depth, std::uint8_t blend,
|
|
bool lines, std::uint8_t z_func, bool offscreen, const Render3DDraw* draw) {
|
|
std::array<std::uint32_t, 17> key{};
|
|
key[0] = std::uint32_t(cull) | (std::uint32_t(depth) << 8) |
|
|
(std::uint32_t(blend) << 16) | (lines ? (1u << 24) : 0u) |
|
|
(std::uint32_t(z_func & 0xfu) << 25) | (offscreen ? (1u << 29) : 0u);
|
|
static const bool specialize = std::getenv("MT_GENERIC_SHADERS") == nullptr;
|
|
key[1] = specialize ? 1u : 0u;
|
|
if (specialize && draw) {
|
|
key[2] = 1u;
|
|
for (int stage = 0; stage < 2; ++stage) {
|
|
const int base = 3 + stage * 6;
|
|
key[base] = draw->color_op[stage];
|
|
key[base + 1] = draw->color_arg1[stage];
|
|
key[base + 2] = draw->color_arg2[stage];
|
|
key[base + 3] = draw->alpha_op[stage];
|
|
key[base + 4] = draw->alpha_arg1[stage];
|
|
key[base + 5] = draw->alpha_arg2[stage];
|
|
}
|
|
key[15] = draw->alpha_test;
|
|
key[16] = draw->alpha_test ? draw->alpha_func : 0u;
|
|
}
|
|
if (auto it = pipelines_.find(key); it != pipelines_.end()) return it->second;
|
|
|
|
VkPipelineShaderStageCreateInfo stages[2]{};
|
|
stages[0].sType = stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
|
stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; stages[0].module = vertex_module_; stages[0].pName = "main";
|
|
stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; stages[1].module = fragment_module_; stages[1].pName = "main";
|
|
std::array<VkSpecializationMapEntry, 16> entries{};
|
|
for (std::uint32_t i = 0; i < entries.size(); ++i)
|
|
entries[i] = {i, i * std::uint32_t(sizeof(std::uint32_t)), sizeof(std::uint32_t)};
|
|
VkSpecializationInfo specialization{};
|
|
specialization.mapEntryCount = entries.size();
|
|
specialization.pMapEntries = entries.data();
|
|
specialization.dataSize = 16 * sizeof(std::uint32_t);
|
|
specialization.pData = key.data() + 1;
|
|
stages[1].pSpecializationInfo = &specialization;
|
|
|
|
VkVertexInputBindingDescription binding{0, sizeof(Vertex), VK_VERTEX_INPUT_RATE_VERTEX};
|
|
VkVertexInputAttributeDescription attributes[9] = {
|
|
{0, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, position)},
|
|
{1, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, normal)},
|
|
{2, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(Vertex, uv)},
|
|
{3, 0, VK_FORMAT_R32G32B32A32_SFLOAT, offsetof(Vertex, color)},
|
|
{4, 0, VK_FORMAT_R8G8B8A8_UINT, offsetof(Vertex, joints)},
|
|
{5, 0, VK_FORMAT_R32G32B32A32_SFLOAT, offsetof(Vertex, weights)},
|
|
{6, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(Vertex, mask_uv)},
|
|
{7, 0, VK_FORMAT_R32_SFLOAT, offsetof(Vertex, rhw)},
|
|
{8, 0, VK_FORMAT_R32_SFLOAT, offsetof(Vertex, vertex_fog)}};
|
|
VkPipelineVertexInputStateCreateInfo input{VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO};
|
|
input.vertexBindingDescriptionCount = 1; input.pVertexBindingDescriptions = &binding;
|
|
input.vertexAttributeDescriptionCount = 9; input.pVertexAttributeDescriptions = attributes;
|
|
|
|
VkPipelineInputAssemblyStateCreateInfo assembly{VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO};
|
|
assembly.topology = lines ? VK_PRIMITIVE_TOPOLOGY_LINE_LIST : VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
|
|
|
VkViewport viewport{0, 0, float(extent_.width), float(extent_.height), 0, 1};
|
|
// An offscreen target is at most the device's 4096 (GetDeviceCaps); the viewport clips inside it.
|
|
VkRect2D scissor{{0, 0}, offscreen ? VkExtent2D{4096, 4096} : extent_};
|
|
VkPipelineViewportStateCreateInfo viewport_info{VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO};
|
|
viewport_info.viewportCount = 1; viewport_info.pViewports = &viewport;
|
|
viewport_info.scissorCount = 1; viewport_info.pScissors = &scissor;
|
|
// D3DVIEWPORT8 changes per draw (SetViewport), so the viewport is dynamic state.
|
|
const VkDynamicState dynamic_states[] = {VK_DYNAMIC_STATE_VIEWPORT};
|
|
VkPipelineDynamicStateCreateInfo dynamic_info{VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO};
|
|
dynamic_info.dynamicStateCount = 1; dynamic_info.pDynamicStates = dynamic_states;
|
|
|
|
VkPipelineRasterizationStateCreateInfo raster{VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO};
|
|
raster.polygonMode = VK_POLYGON_MODE_FILL;
|
|
raster.cullMode = cull == 1 ? VK_CULL_MODE_BACK_BIT : (cull == 2 ? VK_CULL_MODE_FRONT_BIT : VK_CULL_MODE_NONE);
|
|
raster.frontFace = VK_FRONT_FACE_CLOCKWISE;
|
|
raster.lineWidth = 1;
|
|
|
|
VkPipelineMultisampleStateCreateInfo multisample{VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO};
|
|
multisample.rasterizationSamples = offscreen ? VK_SAMPLE_COUNT_1_BIT : samples_;
|
|
|
|
VkPipelineDepthStencilStateCreateInfo depth_stencil{VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO};
|
|
depth_stencil.depthTestEnable = depth > 0 ? VK_TRUE : VK_FALSE;
|
|
depth_stencil.depthWriteEnable = depth > 1 ? VK_TRUE : VK_FALSE;
|
|
switch (z_func) {
|
|
case 1: depth_stencil.depthCompareOp = VK_COMPARE_OP_NEVER; break;
|
|
case 2: depth_stencil.depthCompareOp = VK_COMPARE_OP_LESS; break;
|
|
case 3: depth_stencil.depthCompareOp = VK_COMPARE_OP_EQUAL; break;
|
|
case 5: depth_stencil.depthCompareOp = VK_COMPARE_OP_GREATER; break;
|
|
case 6: depth_stencil.depthCompareOp = VK_COMPARE_OP_NOT_EQUAL; break;
|
|
case 7: depth_stencil.depthCompareOp = VK_COMPARE_OP_GREATER_OR_EQUAL; break;
|
|
case 8: depth_stencil.depthCompareOp = VK_COMPARE_OP_ALWAYS; break;
|
|
default: depth_stencil.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; break;
|
|
}
|
|
|
|
auto d3d_to_vk_blend = [](std::uint8_t d3d, VkBlendFactor fallback) -> VkBlendFactor {
|
|
switch (d3d_blend_for_rgb_target(d3d)) {
|
|
case 1: return VK_BLEND_FACTOR_ZERO;
|
|
case 2: return VK_BLEND_FACTOR_ONE;
|
|
case 3: return VK_BLEND_FACTOR_SRC_COLOR;
|
|
case 4: return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
|
|
case 5: return VK_BLEND_FACTOR_SRC_ALPHA;
|
|
case 6: return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
|
case 7: return VK_BLEND_FACTOR_DST_ALPHA;
|
|
case 8: return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
|
|
case 9: return VK_BLEND_FACTOR_DST_COLOR;
|
|
case 10: return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;
|
|
case 11: return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE;
|
|
default: return fallback;
|
|
}
|
|
};
|
|
|
|
VkPipelineColorBlendAttachmentState blend_attachment{};
|
|
blend_attachment.colorWriteMask = 0xf;
|
|
if (blend > 0) {
|
|
blend_attachment.blendEnable = VK_TRUE;
|
|
if (blend >= 16) {
|
|
blend_attachment.srcColorBlendFactor =
|
|
d3d_to_vk_blend(blend & 0xFu, VK_BLEND_FACTOR_SRC_ALPHA);
|
|
blend_attachment.dstColorBlendFactor =
|
|
d3d_to_vk_blend((blend >> 4) & 0xFu, VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA);
|
|
} else {
|
|
blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
|
blend_attachment.dstColorBlendFactor =
|
|
blend == 2 ? VK_BLEND_FACTOR_ONE : VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
|
}
|
|
blend_attachment.colorBlendOp = VK_BLEND_OP_ADD;
|
|
// D3D8 has no separate alpha blend: SRCBLEND/DESTBLEND apply to alpha as well.
|
|
auto alpha_factor = [](VkBlendFactor factor) {
|
|
switch (factor) {
|
|
case VK_BLEND_FACTOR_SRC_COLOR: return VK_BLEND_FACTOR_SRC_ALPHA;
|
|
case VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR: return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
|
case VK_BLEND_FACTOR_DST_COLOR: return VK_BLEND_FACTOR_DST_ALPHA;
|
|
case VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR: return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
|
|
case VK_BLEND_FACTOR_SRC_ALPHA_SATURATE: return VK_BLEND_FACTOR_ONE;
|
|
default: return factor;
|
|
}
|
|
};
|
|
blend_attachment.srcAlphaBlendFactor = alpha_factor(blend_attachment.srcColorBlendFactor);
|
|
blend_attachment.dstAlphaBlendFactor = alpha_factor(blend_attachment.dstColorBlendFactor);
|
|
blend_attachment.alphaBlendOp = VK_BLEND_OP_ADD;
|
|
}
|
|
VkPipelineColorBlendStateCreateInfo blending{VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO};
|
|
blending.attachmentCount = 1; blending.pAttachments = &blend_attachment;
|
|
|
|
VkGraphicsPipelineCreateInfo pipeline_info{VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO};
|
|
pipeline_info.stageCount = 2; pipeline_info.pStages = stages;
|
|
pipeline_info.pVertexInputState = &input; pipeline_info.pInputAssemblyState = &assembly;
|
|
pipeline_info.pViewportState = &viewport_info; pipeline_info.pRasterizationState = &raster;
|
|
pipeline_info.pMultisampleState = &multisample; pipeline_info.pDepthStencilState = &depth_stencil;
|
|
pipeline_info.pColorBlendState = &blending;
|
|
pipeline_info.pDynamicState = &dynamic_info;
|
|
pipeline_info.layout = layout_; pipeline_info.renderPass = offscreen ? offscreen_pass_ : pass_;
|
|
VkPipeline handle = VK_NULL_HANDLE;
|
|
check(vkCreateGraphicsPipelines(device_, VK_NULL_HANDLE, 1, &pipeline_info, nullptr, &handle), "vkCreateGraphicsPipelines");
|
|
pipelines_.emplace(key, handle);
|
|
return handle;
|
|
}
|
|
|
|
} // namespace mt_host
|