xenia/src/xenia/ui/vulkan/blitter.cc
2017-12-16 17:15:49 -06:00

572 lines
20 KiB
C++

/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2016 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/ui/vulkan/blitter.h"
#include "xenia/base/math.h"
#include "xenia/ui/vulkan/fenced_pools.h"
namespace xe {
namespace ui {
namespace vulkan {
// Generated with `xenia-build genspirv`.
#include "xenia/ui/vulkan/shaders/bin/blit_color_frag.h"
#include "xenia/ui/vulkan/shaders/bin/blit_depth_frag.h"
#include "xenia/ui/vulkan/shaders/bin/blit_vert.h"
Blitter::Blitter() {}
Blitter::~Blitter() { Shutdown(); }
VkResult Blitter::Initialize(VulkanDevice* device) {
device_ = device;
VkResult status = VK_SUCCESS;
// Shaders
VkShaderModuleCreateInfo shader_create_info;
std::memset(&shader_create_info, 0, sizeof(shader_create_info));
shader_create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
shader_create_info.codeSize = sizeof(blit_vert);
shader_create_info.pCode = reinterpret_cast<const uint32_t*>(blit_vert);
status = vkCreateShaderModule(*device_, &shader_create_info, nullptr,
&blit_vertex_);
CheckResult(status, "vkCreateShaderModule");
if (status != VK_SUCCESS) {
return status;
}
shader_create_info.codeSize = sizeof(blit_color_frag);
shader_create_info.pCode = reinterpret_cast<const uint32_t*>(blit_color_frag);
status = vkCreateShaderModule(*device_, &shader_create_info, nullptr,
&blit_color_);
CheckResult(status, "vkCreateShaderModule");
if (status != VK_SUCCESS) {
return status;
}
shader_create_info.codeSize = sizeof(blit_depth_frag);
shader_create_info.pCode = reinterpret_cast<const uint32_t*>(blit_depth_frag);
status = vkCreateShaderModule(*device_, &shader_create_info, nullptr,
&blit_depth_);
CheckResult(status, "vkCreateShaderModule");
if (status != VK_SUCCESS) {
return status;
}
// Create the descriptor set layout used for our texture sampler.
// As it changes almost every draw we cache it per texture.
VkDescriptorSetLayoutCreateInfo texture_set_layout_info;
texture_set_layout_info.sType =
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
texture_set_layout_info.pNext = nullptr;
texture_set_layout_info.flags = 0;
texture_set_layout_info.bindingCount = 1;
VkDescriptorSetLayoutBinding texture_binding;
texture_binding.binding = 0;
texture_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
texture_binding.descriptorCount = 1;
texture_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
texture_binding.pImmutableSamplers = nullptr;
texture_set_layout_info.pBindings = &texture_binding;
status = vkCreateDescriptorSetLayout(*device_, &texture_set_layout_info,
nullptr, &descriptor_set_layout_);
CheckResult(status, "vkCreateDescriptorSetLayout");
if (status != VK_SUCCESS) {
return status;
}
// Create a descriptor pool
VkDescriptorPoolSize pool_sizes[1];
pool_sizes[0].descriptorCount = 4096;
pool_sizes[0].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descriptor_pool_ = std::make_unique<DescriptorPool>(
*device_, 4096,
std::vector<VkDescriptorPoolSize>(pool_sizes, std::end(pool_sizes)));
// Create the pipeline layout used for our pipeline.
VkPipelineLayoutCreateInfo pipeline_layout_info;
pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipeline_layout_info.pNext = nullptr;
pipeline_layout_info.flags = 0;
VkDescriptorSetLayout set_layouts[] = {descriptor_set_layout_};
pipeline_layout_info.setLayoutCount =
static_cast<uint32_t>(xe::countof(set_layouts));
pipeline_layout_info.pSetLayouts = set_layouts;
VkPushConstantRange push_constant_ranges[2];
// vec4 src_uv
push_constant_ranges[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
push_constant_ranges[0].offset = 0;
push_constant_ranges[0].size = sizeof(float) * 4;
// bool swap_channels
push_constant_ranges[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
push_constant_ranges[1].offset = sizeof(float) * 4;
push_constant_ranges[1].size = sizeof(int) * 4;
pipeline_layout_info.pushConstantRangeCount =
static_cast<uint32_t>(xe::countof(push_constant_ranges));
pipeline_layout_info.pPushConstantRanges = push_constant_ranges;
status = vkCreatePipelineLayout(*device_, &pipeline_layout_info, nullptr,
&pipeline_layout_);
CheckResult(status, "vkCreatePipelineLayout");
if (status != VK_SUCCESS) {
return status;
}
// Create two samplers.
VkSamplerCreateInfo sampler_create_info = {
VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
nullptr,
0,
VK_FILTER_NEAREST,
VK_FILTER_NEAREST,
VK_SAMPLER_MIPMAP_MODE_NEAREST,
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
0.f,
VK_FALSE,
1.f,
VK_FALSE,
VK_COMPARE_OP_NEVER,
0.f,
0.f,
VK_BORDER_COLOR_INT_TRANSPARENT_BLACK,
VK_FALSE,
};
status =
vkCreateSampler(*device_, &sampler_create_info, nullptr, &samp_nearest_);
CheckResult(status, "vkCreateSampler");
if (status != VK_SUCCESS) {
return status;
}
sampler_create_info.minFilter = VK_FILTER_LINEAR;
sampler_create_info.magFilter = VK_FILTER_LINEAR;
sampler_create_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
status =
vkCreateSampler(*device_, &sampler_create_info, nullptr, &samp_linear_);
CheckResult(status, "vkCreateSampler");
if (status != VK_SUCCESS) {
return status;
}
return VK_SUCCESS;
}
void Blitter::Shutdown() {
if (samp_nearest_) {
vkDestroySampler(*device_, samp_nearest_, nullptr);
samp_nearest_ = nullptr;
}
if (samp_linear_) {
vkDestroySampler(*device_, samp_linear_, nullptr);
samp_linear_ = nullptr;
}
if (blit_vertex_) {
vkDestroyShaderModule(*device_, blit_vertex_, nullptr);
blit_vertex_ = nullptr;
}
if (blit_color_) {
vkDestroyShaderModule(*device_, blit_color_, nullptr);
blit_color_ = nullptr;
}
if (blit_depth_) {
vkDestroyShaderModule(*device_, blit_depth_, nullptr);
blit_depth_ = nullptr;
}
if (pipeline_color_) {
vkDestroyPipeline(*device_, pipeline_color_, nullptr);
pipeline_color_ = nullptr;
}
if (pipeline_depth_) {
vkDestroyPipeline(*device_, pipeline_depth_, nullptr);
pipeline_depth_ = nullptr;
}
if (pipeline_layout_) {
vkDestroyPipelineLayout(*device_, pipeline_layout_, nullptr);
pipeline_layout_ = nullptr;
}
if (descriptor_set_layout_) {
vkDestroyDescriptorSetLayout(*device_, descriptor_set_layout_, nullptr);
descriptor_set_layout_ = nullptr;
}
for (auto& pipeline : pipelines_) {
vkDestroyPipeline(*device_, pipeline.second, nullptr);
}
pipelines_.clear();
for (auto& pass : render_passes_) {
vkDestroyRenderPass(*device_, pass.second, nullptr);
}
render_passes_.clear();
}
void Blitter::Scavenge() {
if (descriptor_pool_->has_open_batch()) {
descriptor_pool_->EndBatch();
}
descriptor_pool_->Scavenge();
}
void Blitter::BlitTexture2D(VkCommandBuffer command_buffer, VkFence fence,
VkImageView src_image_view, VkRect2D src_rect,
VkExtent2D src_extents, VkFormat dst_image_format,
VkOffset2D dst_offset, VkExtent2D dst_extents,
VkFramebuffer dst_framebuffer, VkFilter filter,
bool color_or_depth, bool swap_channels) {
// Do we need a full draw, or can we cheap out with a blit command?
bool full_draw = swap_channels || true;
if (full_draw) {
if (!descriptor_pool_->has_open_batch()) {
descriptor_pool_->BeginBatch(fence);
}
// Acquire a render pass.
auto render_pass = GetRenderPass(dst_image_format, color_or_depth);
VkRenderPassBeginInfo render_pass_info = {
VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
nullptr,
render_pass,
dst_framebuffer,
{{dst_offset.x, dst_offset.y}, {dst_extents.width, dst_extents.height}},
0,
nullptr,
};
vkCmdBeginRenderPass(command_buffer, &render_pass_info,
VK_SUBPASS_CONTENTS_INLINE);
VkViewport viewport = {
float(dst_offset.x),
float(dst_offset.y),
float(dst_extents.width),
float(dst_extents.height),
0.f,
1.f,
};
vkCmdSetViewport(command_buffer, 0, 1, &viewport);
VkRect2D scissor = {
dst_offset.x,
dst_offset.y,
dst_extents.width,
dst_extents.height,
};
vkCmdSetScissor(command_buffer, 0, 1, &scissor);
// Acquire a pipeline.
auto pipeline =
GetPipeline(render_pass, color_or_depth ? blit_color_ : blit_depth_);
vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
pipeline);
// Acquire and update a descriptor set for this image.
auto set = descriptor_pool_->AcquireEntry(descriptor_set_layout_);
assert_not_null(set);
VkWriteDescriptorSet write;
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write.pNext = nullptr;
write.dstSet = set;
write.dstBinding = 0;
write.dstArrayElement = 0;
write.descriptorCount = 1;
write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
VkDescriptorImageInfo image;
image.sampler = filter == VK_FILTER_NEAREST ? samp_nearest_ : samp_linear_;
image.imageView = src_image_view;
image.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
write.pImageInfo = &image;
write.pBufferInfo = nullptr;
write.pTexelBufferView = nullptr;
vkUpdateDescriptorSets(*device_, 1, &write, 0, nullptr);
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
pipeline_layout_, 0, 1, &set, 0, nullptr);
VtxPushConstants vtx_constants = {
{
float(src_rect.offset.x) / src_extents.width,
float(src_rect.offset.y) / src_extents.height,
float(src_rect.extent.width) / src_extents.width,
float(src_rect.extent.height) / src_extents.height,
},
};
vkCmdPushConstants(command_buffer, pipeline_layout_,
VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(VtxPushConstants),
&vtx_constants);
PixPushConstants pix_constants = {
0,
0,
0,
swap_channels ? 1 : 0,
};
vkCmdPushConstants(command_buffer, pipeline_layout_,
VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(VtxPushConstants),
sizeof(PixPushConstants), &pix_constants);
vkCmdDraw(command_buffer, 4, 1, 0, 0);
vkCmdEndRenderPass(command_buffer);
}
}
void Blitter::CopyColorTexture2D(VkCommandBuffer command_buffer, VkFence fence,
VkImage src_image, VkImageView src_image_view,
VkOffset2D src_offset, VkImage dst_image,
VkImageView dst_image_view, VkExtent2D extents,
VkFilter filter, bool swap_channels) {}
void Blitter::CopyDepthTexture(VkCommandBuffer command_buffer, VkFence fence,
VkImage src_image, VkImageView src_image_view,
VkOffset2D src_offset, VkImage dst_image,
VkImageView dst_image_view, VkExtent2D extents) {
}
VkRenderPass Blitter::GetRenderPass(VkFormat format, bool color_or_depth) {
auto pass = render_passes_.find(format);
if (pass != render_passes_.end()) {
return pass->second;
}
// Create and cache the render pass.
VkRenderPass render_pass = CreateRenderPass(format, color_or_depth);
if (render_pass) {
render_passes_[format] = render_pass;
}
return render_pass;
}
VkPipeline Blitter::GetPipeline(VkRenderPass render_pass,
VkShaderModule frag_shader) {
auto it = pipelines_.find(std::make_pair(render_pass, frag_shader));
if (it != pipelines_.end()) {
return it->second;
}
// Create and cache the pipeline.
VkPipeline pipeline = CreatePipeline(render_pass, frag_shader);
if (pipeline) {
pipelines_[std::make_pair(render_pass, frag_shader)] = pipeline;
}
return pipeline;
}
VkRenderPass Blitter::CreateRenderPass(VkFormat output_format,
bool color_or_depth) {
VkAttachmentDescription attachments[1];
std::memset(attachments, 0, sizeof(attachments));
// Output attachment
attachments[0].flags = 0;
attachments[0].format = output_format;
attachments[0].samples = VK_SAMPLE_COUNT_1_BIT;
attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
attachments[0].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkAttachmentReference attach_refs[1];
attach_refs[0].attachment = 0;
attach_refs[0].layout =
color_or_depth ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
: VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpass = {
0, VK_PIPELINE_BIND_POINT_GRAPHICS,
0, nullptr,
0, nullptr,
nullptr, nullptr,
0, nullptr,
};
if (color_or_depth) {
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = attach_refs;
} else {
subpass.pDepthStencilAttachment = attach_refs;
}
VkRenderPassCreateInfo renderpass_info = {
VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
nullptr,
0,
1,
attachments,
1,
&subpass,
0,
nullptr,
};
VkRenderPass renderpass = nullptr;
VkResult result =
vkCreateRenderPass(*device_, &renderpass_info, nullptr, &renderpass);
CheckResult(result, "vkCreateRenderPass");
return renderpass;
}
VkPipeline Blitter::CreatePipeline(VkRenderPass render_pass,
VkShaderModule frag_shader) {
VkResult result = VK_SUCCESS;
// Pipeline
VkGraphicsPipelineCreateInfo pipeline_info;
std::memset(&pipeline_info, 0, sizeof(VkGraphicsPipelineCreateInfo));
pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
// Shaders
pipeline_info.stageCount = 2;
VkPipelineShaderStageCreateInfo stages[2];
std::memset(stages, 0, sizeof(stages));
stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
stages[0].module = blit_vertex_;
stages[0].pName = "main";
stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
stages[1].module = frag_shader;
stages[1].pName = "main";
pipeline_info.pStages = stages;
// Vertex input
VkPipelineVertexInputStateCreateInfo vtx_state;
std::memset(&vtx_state, 0, sizeof(vtx_state));
vtx_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vtx_state.flags = 0;
vtx_state.vertexAttributeDescriptionCount = 0;
vtx_state.pVertexAttributeDescriptions = nullptr;
vtx_state.vertexBindingDescriptionCount = 0;
vtx_state.pVertexBindingDescriptions = nullptr;
pipeline_info.pVertexInputState = &vtx_state;
// Input Assembly
VkPipelineInputAssemblyStateCreateInfo input_info;
input_info.sType =
VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
input_info.pNext = nullptr;
input_info.flags = 0;
input_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
input_info.primitiveRestartEnable = VK_FALSE;
pipeline_info.pInputAssemblyState = &input_info;
pipeline_info.pTessellationState = nullptr;
VkPipelineViewportStateCreateInfo viewport_state_info;
viewport_state_info.sType =
VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewport_state_info.pNext = nullptr;
viewport_state_info.flags = 0;
viewport_state_info.viewportCount = 1;
viewport_state_info.pViewports = nullptr;
viewport_state_info.scissorCount = 1;
viewport_state_info.pScissors = nullptr;
pipeline_info.pViewportState = &viewport_state_info;
VkPipelineRasterizationStateCreateInfo rasterization_info;
rasterization_info.sType =
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterization_info.pNext = nullptr;
rasterization_info.flags = 0;
rasterization_info.depthClampEnable = VK_FALSE;
rasterization_info.rasterizerDiscardEnable = VK_FALSE;
rasterization_info.polygonMode = VK_POLYGON_MODE_FILL;
rasterization_info.cullMode = VK_CULL_MODE_NONE;
rasterization_info.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rasterization_info.depthBiasEnable = VK_FALSE;
rasterization_info.depthBiasConstantFactor = 0;
rasterization_info.depthBiasClamp = 0;
rasterization_info.depthBiasSlopeFactor = 0;
rasterization_info.lineWidth = 1.0f;
pipeline_info.pRasterizationState = &rasterization_info;
VkPipelineMultisampleStateCreateInfo multisample_info;
multisample_info.sType =
VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisample_info.pNext = nullptr;
multisample_info.flags = 0;
multisample_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
multisample_info.sampleShadingEnable = VK_FALSE;
multisample_info.minSampleShading = 0;
multisample_info.pSampleMask = nullptr;
multisample_info.alphaToCoverageEnable = VK_FALSE;
multisample_info.alphaToOneEnable = VK_FALSE;
pipeline_info.pMultisampleState = &multisample_info;
VkPipelineDepthStencilStateCreateInfo depth_info = {
VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
nullptr,
0,
VK_TRUE,
VK_TRUE,
VK_COMPARE_OP_ALWAYS,
VK_FALSE,
VK_FALSE,
{},
{},
0.f,
1.f,
};
pipeline_info.pDepthStencilState = &depth_info;
VkPipelineColorBlendStateCreateInfo blend_info;
blend_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
blend_info.pNext = nullptr;
blend_info.flags = 0;
blend_info.logicOpEnable = VK_FALSE;
blend_info.logicOp = VK_LOGIC_OP_NO_OP;
VkPipelineColorBlendAttachmentState blend_attachments[1];
blend_attachments[0].blendEnable = VK_FALSE;
blend_attachments[0].srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
blend_attachments[0].dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
blend_attachments[0].colorBlendOp = VK_BLEND_OP_ADD;
blend_attachments[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
blend_attachments[0].dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
blend_attachments[0].alphaBlendOp = VK_BLEND_OP_ADD;
blend_attachments[0].colorWriteMask = 0xF;
blend_info.attachmentCount =
static_cast<uint32_t>(xe::countof(blend_attachments));
blend_info.pAttachments = blend_attachments;
std::memset(blend_info.blendConstants, 0, sizeof(blend_info.blendConstants));
pipeline_info.pColorBlendState = &blend_info;
VkPipelineDynamicStateCreateInfo dynamic_state_info;
dynamic_state_info.sType =
VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamic_state_info.pNext = nullptr;
dynamic_state_info.flags = 0;
VkDynamicState dynamic_states[] = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR,
};
dynamic_state_info.dynamicStateCount =
static_cast<uint32_t>(xe::countof(dynamic_states));
dynamic_state_info.pDynamicStates = dynamic_states;
pipeline_info.pDynamicState = &dynamic_state_info;
pipeline_info.layout = pipeline_layout_;
pipeline_info.renderPass = render_pass;
pipeline_info.subpass = 0;
pipeline_info.basePipelineHandle = nullptr;
pipeline_info.basePipelineIndex = -1;
VkPipeline pipeline = nullptr;
result = vkCreateGraphicsPipelines(*device_, nullptr, 1, &pipeline_info,
nullptr, &pipeline);
CheckResult(result, "vkCreateGraphicsPipelines");
return pipeline;
}
} // namespace vulkan
} // namespace ui
} // namespace xe