Files
Xenia-Canary/src/xenia/ui/vk/vulkan_immediate_drawer.cc
2020-04-07 16:09:41 -05:00

430 lines
18 KiB
C++

/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2019 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "xenia/ui/vk/vulkan_immediate_drawer.h"
#include "xenia/base/assert.h"
#include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/ui/vk/vulkan_context.h"
#include "xenia/ui/vk/vulkan_util.h"
namespace xe {
namespace ui {
namespace vk {
// Generated with `xb genspirv`.
#include "xenia/ui/vk/shaders/bin/immediate_frag.h"
#include "xenia/ui/vk/shaders/bin/immediate_vert.h"
class VulkanImmediateTexture : public ImmediateTexture {
public:
VulkanImmediateTexture(uint32_t width, uint32_t height,
ImmediateTextureFilter filter, bool repeat);
~VulkanImmediateTexture() override;
};
VulkanImmediateTexture::VulkanImmediateTexture(uint32_t width, uint32_t height,
ImmediateTextureFilter filter,
bool repeat)
: ImmediateTexture(width, height) {
handle = reinterpret_cast<uintptr_t>(this);
}
VulkanImmediateTexture::~VulkanImmediateTexture() {}
VulkanImmediateDrawer::VulkanImmediateDrawer(VulkanContext* graphics_context)
: ImmediateDrawer(graphics_context), context_(graphics_context) {}
VulkanImmediateDrawer::~VulkanImmediateDrawer() { Shutdown(); }
bool VulkanImmediateDrawer::Initialize() {
auto device = context_->GetVulkanProvider()->GetDevice();
// Create the pipeline layout.
// TODO(Triang3l): Texture descriptor set layout.
VkPushConstantRange push_constant_ranges[2];
push_constant_ranges[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
push_constant_ranges[0].offset = offsetof(PushConstants, vertex);
push_constant_ranges[0].size = sizeof(PushConstants::vertex);
push_constant_ranges[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
push_constant_ranges[1].offset = offsetof(PushConstants, fragment);
push_constant_ranges[1].size = sizeof(PushConstants::fragment);
VkPipelineLayoutCreateInfo pipeline_layout_create_info;
pipeline_layout_create_info.sType =
VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipeline_layout_create_info.pNext = nullptr;
pipeline_layout_create_info.flags = 0;
pipeline_layout_create_info.setLayoutCount = 0;
pipeline_layout_create_info.pSetLayouts = nullptr;
pipeline_layout_create_info.pushConstantRangeCount =
uint32_t(xe::countof(push_constant_ranges));
pipeline_layout_create_info.pPushConstantRanges = push_constant_ranges;
if (vkCreatePipelineLayout(device, &pipeline_layout_create_info, nullptr,
&pipeline_layout_) != VK_SUCCESS) {
XELOGE("Failed to create the Vulkan immediate drawer pipeline layout");
return false;
}
// Load the shaders.
VkShaderModuleCreateInfo shader_module_create_info;
shader_module_create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
shader_module_create_info.pNext = nullptr;
shader_module_create_info.flags = 0;
shader_module_create_info.codeSize = sizeof(immediate_vert);
shader_module_create_info.pCode =
reinterpret_cast<const uint32_t*>(immediate_vert);
VkShaderModule shader_module_vertex;
if (vkCreateShaderModule(device, &shader_module_create_info, nullptr,
&shader_module_vertex) != VK_SUCCESS) {
XELOGE("Failed to create the Vulkan immediate drawer vertex shader module");
return false;
}
shader_module_create_info.codeSize = sizeof(immediate_frag);
shader_module_create_info.pCode =
reinterpret_cast<const uint32_t*>(immediate_frag);
VkShaderModule shader_module_fragment;
if (vkCreateShaderModule(device, &shader_module_create_info, nullptr,
&shader_module_fragment) != VK_SUCCESS) {
XELOGE(
"Failed to create the Vulkan immediate drawer fragment shader module");
vkDestroyShaderModule(device, shader_module_vertex, nullptr);
return false;
}
// Create the pipelines for triangles and lines.
VkGraphicsPipelineCreateInfo pipeline_create_info;
pipeline_create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipeline_create_info.pNext = nullptr;
pipeline_create_info.flags = VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT;
VkPipelineShaderStageCreateInfo pipeline_stages[2];
pipeline_stages[0].sType = pipeline_stages[1].sType =
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
pipeline_stages[0].pNext = pipeline_stages[1].pNext = nullptr;
pipeline_stages[0].flags = pipeline_stages[1].flags = 0;
pipeline_stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
pipeline_stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
pipeline_stages[0].module = shader_module_vertex;
pipeline_stages[1].module = shader_module_fragment;
pipeline_stages[0].pName = pipeline_stages[1].pName = "main";
pipeline_stages[0].pSpecializationInfo = nullptr;
pipeline_stages[1].pSpecializationInfo = nullptr;
pipeline_create_info.stageCount = uint32_t(xe::countof(pipeline_stages));
pipeline_create_info.pStages = pipeline_stages;
VkPipelineVertexInputStateCreateInfo pipeline_vertex_input;
pipeline_vertex_input.sType =
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
pipeline_vertex_input.pNext = nullptr;
pipeline_vertex_input.flags = 0;
VkVertexInputBindingDescription pipeline_vertex_bindings[1];
pipeline_vertex_bindings[0].binding = 0;
pipeline_vertex_bindings[0].stride = sizeof(ImmediateVertex);
pipeline_vertex_bindings[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
pipeline_vertex_input.vertexBindingDescriptionCount =
uint32_t(xe::countof(pipeline_vertex_bindings));
pipeline_vertex_input.pVertexBindingDescriptions = pipeline_vertex_bindings;
VkVertexInputAttributeDescription pipeline_vertex_attributes[3];
pipeline_vertex_attributes[0].location = 0;
pipeline_vertex_attributes[0].binding = 0;
pipeline_vertex_attributes[0].format = VK_FORMAT_R32G32_SFLOAT;
pipeline_vertex_attributes[0].offset = offsetof(ImmediateVertex, x);
pipeline_vertex_attributes[1].location = 1;
pipeline_vertex_attributes[1].binding = 0;
pipeline_vertex_attributes[1].format = VK_FORMAT_R32G32_SFLOAT;
pipeline_vertex_attributes[1].offset = offsetof(ImmediateVertex, u);
pipeline_vertex_attributes[2].location = 2;
pipeline_vertex_attributes[2].binding = 0;
pipeline_vertex_attributes[2].format = VK_FORMAT_R8G8B8A8_UNORM;
pipeline_vertex_attributes[2].offset = offsetof(ImmediateVertex, color);
pipeline_vertex_input.vertexAttributeDescriptionCount =
uint32_t(xe::countof(pipeline_vertex_attributes));
pipeline_vertex_input.pVertexAttributeDescriptions =
pipeline_vertex_attributes;
pipeline_create_info.pVertexInputState = &pipeline_vertex_input;
VkPipelineInputAssemblyStateCreateInfo pipeline_input_assembly;
pipeline_input_assembly.sType =
VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
pipeline_input_assembly.pNext = nullptr;
pipeline_input_assembly.flags = 0;
pipeline_input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
pipeline_create_info.pInputAssemblyState = &pipeline_input_assembly;
pipeline_create_info.pTessellationState = nullptr;
VkPipelineViewportStateCreateInfo pipeline_viewport;
pipeline_viewport.sType =
VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
pipeline_viewport.pNext = nullptr;
pipeline_viewport.flags = 0;
pipeline_viewport.viewportCount = 1;
pipeline_viewport.pViewports = nullptr;
pipeline_viewport.scissorCount = 1;
pipeline_viewport.pScissors = nullptr;
pipeline_create_info.pViewportState = &pipeline_viewport;
VkPipelineRasterizationStateCreateInfo pipeline_rasterization = {};
pipeline_rasterization.sType =
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
pipeline_rasterization.polygonMode = VK_POLYGON_MODE_FILL;
pipeline_rasterization.cullMode = VK_CULL_MODE_BACK_BIT;
pipeline_rasterization.frontFace = VK_FRONT_FACE_CLOCKWISE;
pipeline_rasterization.lineWidth = 1.0f;
pipeline_create_info.pRasterizationState = &pipeline_rasterization;
VkPipelineMultisampleStateCreateInfo pipeline_multisample = {};
pipeline_multisample.sType =
VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
pipeline_multisample.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
pipeline_create_info.pMultisampleState = &pipeline_multisample;
pipeline_create_info.pDepthStencilState = nullptr;
VkPipelineColorBlendStateCreateInfo pipeline_color_blend;
pipeline_color_blend.sType =
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
pipeline_color_blend.pNext = nullptr;
pipeline_color_blend.flags = 0;
pipeline_color_blend.logicOpEnable = VK_FALSE;
pipeline_color_blend.logicOp = VK_LOGIC_OP_NO_OP;
VkPipelineColorBlendAttachmentState pipeline_color_blend_attachment = {};
pipeline_color_blend_attachment.blendEnable = VK_TRUE;
pipeline_color_blend_attachment.srcColorBlendFactor =
VK_BLEND_FACTOR_SRC_ALPHA;
pipeline_color_blend_attachment.dstColorBlendFactor =
VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
pipeline_color_blend_attachment.colorBlendOp = VK_BLEND_OP_ADD;
pipeline_color_blend_attachment.srcAlphaBlendFactor =
VK_BLEND_FACTOR_SRC_ALPHA;
pipeline_color_blend_attachment.dstAlphaBlendFactor =
VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
pipeline_color_blend_attachment.alphaBlendOp = VK_BLEND_OP_ADD;
pipeline_color_blend_attachment.colorWriteMask =
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
pipeline_color_blend.attachmentCount = 1;
pipeline_color_blend.pAttachments = &pipeline_color_blend_attachment;
pipeline_create_info.pColorBlendState = &pipeline_color_blend;
static const VkDynamicState pipeline_dynamic_states[] = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR,
};
VkPipelineDynamicStateCreateInfo pipeline_dynamic_state;
pipeline_dynamic_state.sType =
VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
pipeline_dynamic_state.pNext = nullptr;
pipeline_dynamic_state.flags = 0;
pipeline_dynamic_state.dynamicStateCount =
uint32_t(xe::countof(pipeline_dynamic_states));
pipeline_dynamic_state.pDynamicStates = pipeline_dynamic_states;
pipeline_create_info.pDynamicState = &pipeline_dynamic_state;
pipeline_create_info.layout = pipeline_layout_;
pipeline_create_info.renderPass = context_->GetPresentRenderPass();
pipeline_create_info.subpass = 0;
pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE;
pipeline_create_info.basePipelineIndex = -1;
if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1,
&pipeline_create_info, nullptr,
&pipeline_triangle_) != VK_SUCCESS) {
XELOGE("Failed to create the Vulkan immediate drawer triangle pipeline");
vkDestroyShaderModule(device, shader_module_fragment, nullptr);
vkDestroyShaderModule(device, shader_module_vertex, nullptr);
return false;
}
pipeline_create_info.flags = VK_PIPELINE_CREATE_DERIVATIVE_BIT;
pipeline_input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
pipeline_create_info.basePipelineHandle = pipeline_triangle_;
if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1,
&pipeline_create_info, nullptr,
&pipeline_line_) != VK_SUCCESS) {
XELOGE("Failed to create the Vulkan immediate drawer triangle pipeline");
vkDestroyShaderModule(device, shader_module_fragment, nullptr);
vkDestroyShaderModule(device, shader_module_vertex, nullptr);
return false;
}
vkDestroyShaderModule(device, shader_module_fragment, nullptr);
vkDestroyShaderModule(device, shader_module_vertex, nullptr);
// Create transient resource pools for draws.
vertex_buffer_chain_ = std::make_unique<UploadBufferChain>(
context_, 2 * 1024 * 2014,
VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
// Reset the current state.
current_command_buffer_ = VK_NULL_HANDLE;
return true;
}
void VulkanImmediateDrawer::Shutdown() {
auto device = context_->GetVulkanProvider()->GetDevice();
vertex_buffer_chain_.reset();
util::DestroyAndNullHandle(vkDestroyPipeline, device, pipeline_line_);
util::DestroyAndNullHandle(vkDestroyPipeline, device, pipeline_triangle_);
util::DestroyAndNullHandle(vkDestroyPipelineLayout, device, pipeline_layout_);
}
std::unique_ptr<ImmediateTexture> VulkanImmediateDrawer::CreateTexture(
uint32_t width, uint32_t height, ImmediateTextureFilter filter, bool repeat,
const uint8_t* data) {
auto texture =
std::make_unique<VulkanImmediateTexture>(width, height, filter, repeat);
if (data) {
UpdateTexture(texture.get(), data);
}
return std::unique_ptr<ImmediateTexture>(texture.release());
}
void VulkanImmediateDrawer::UpdateTexture(ImmediateTexture* texture,
const uint8_t* data) {}
void VulkanImmediateDrawer::Begin(int render_target_width,
int render_target_height) {
current_command_buffer_ = context_->GetPresentCommandBuffer();
current_render_target_extent_.width = render_target_width;
current_render_target_extent_.height = render_target_height;
VkViewport viewport;
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = float(render_target_width);
viewport.height = float(render_target_height);
viewport.minDepth = 0.0f;
viewport.maxDepth = 0.0f;
vkCmdSetViewport(current_command_buffer_, 0, 1, &viewport);
float viewport_inv_size[2];
viewport_inv_size[0] = 1.0f / viewport.width;
viewport_inv_size[1] = 1.0f / viewport.height;
vkCmdPushConstants(current_command_buffer_, pipeline_layout_,
VK_SHADER_STAGE_VERTEX_BIT,
offsetof(PushConstants, vertex.viewport_inv_size),
sizeof(viewport_inv_size), viewport_inv_size);
current_pipeline_ = VK_NULL_HANDLE;
}
void VulkanImmediateDrawer::BeginDrawBatch(const ImmediateDrawBatch& batch) {
assert_true(current_command_buffer_ != VK_NULL_HANDLE);
if (current_command_buffer_ == VK_NULL_HANDLE) {
return;
}
batch_open_ = false;
// Bind the vertices.
size_t vertex_buffer_size = batch.vertex_count * sizeof(ImmediateVertex);
VkBuffer vertex_buffer;
VkDeviceSize vertex_buffer_offset;
void* vertex_buffer_mapping = vertex_buffer_chain_->RequestFull(
vertex_buffer_size, vertex_buffer, vertex_buffer_offset);
if (!vertex_buffer_mapping) {
XELOGE(
"Failed to get a Vulkan buffer for {} vertices in the immediate drawer",
batch.vertex_count);
return;
}
std::memcpy(vertex_buffer_mapping, batch.vertices, vertex_buffer_size);
vkCmdBindVertexBuffers(current_command_buffer_, 0, 1, &vertex_buffer,
&vertex_buffer_offset);
// Bind the indices.
batch_has_index_buffer_ = batch.indices != nullptr;
if (batch_has_index_buffer_) {
size_t index_buffer_size = batch.index_count * sizeof(uint16_t);
VkBuffer index_buffer;
VkDeviceSize index_buffer_offset;
void* index_buffer_mapping = vertex_buffer_chain_->RequestFull(
xe::align(index_buffer_size, VkDeviceSize(sizeof(uint32_t))),
index_buffer, index_buffer_offset);
if (!index_buffer_mapping) {
XELOGE(
"Failed to get a Vulkan buffer for {} indices in the immediate "
"drawer",
batch.index_count);
return;
}
std::memcpy(index_buffer_mapping, batch.indices, index_buffer_size);
vkCmdBindIndexBuffer(current_command_buffer_, index_buffer,
index_buffer_offset, VK_INDEX_TYPE_UINT16);
}
batch_open_ = true;
}
void VulkanImmediateDrawer::Draw(const ImmediateDraw& draw) {
assert_true(current_command_buffer_ != VK_NULL_HANDLE);
if (current_command_buffer_ == VK_NULL_HANDLE) {
return;
}
if (!batch_open_) {
// Could be an error while obtaining the vertex and index buffers.
return;
}
// TODO(Triang3l): Bind the texture.
// Set whether texture coordinates need to be restricted.
uint32_t restrict_texture_samples = draw.restrict_texture_samples ? 1 : 0;
vkCmdPushConstants(current_command_buffer_, pipeline_layout_,
VK_SHADER_STAGE_FRAGMENT_BIT,
offsetof(PushConstants, fragment.restrict_texture_samples),
sizeof(uint32_t), &restrict_texture_samples);
// Bind the pipeline for the primitive type.
VkPipeline pipeline;
switch (draw.primitive_type) {
case ImmediatePrimitiveType::kLines:
pipeline = pipeline_line_;
break;
case ImmediatePrimitiveType::kTriangles:
pipeline = pipeline_triangle_;
break;
default:
assert_unhandled_case(draw.primitive_type);
return;
}
if (current_pipeline_ != pipeline) {
vkCmdBindPipeline(current_command_buffer_, VK_PIPELINE_BIND_POINT_GRAPHICS,
pipeline);
current_pipeline_ = pipeline;
}
// Set the scissor rectangle if enabled.
VkRect2D scissor;
if (draw.scissor) {
scissor.offset.x = draw.scissor_rect[0];
scissor.offset.y = draw.scissor_rect[1];
scissor.extent.width = draw.scissor_rect[2];
scissor.extent.height = draw.scissor_rect[3];
} else {
scissor.offset.x = scissor.offset.y = 0;
scissor.extent = current_render_target_extent_;
}
vkCmdSetScissor(current_command_buffer_, 0, 1, &scissor);
// Draw.
if (batch_has_index_buffer_) {
vkCmdDrawIndexed(current_command_buffer_, draw.count, 1, draw.index_offset,
draw.base_vertex, 0);
} else {
vkCmdDraw(current_command_buffer_, draw.count, 1, draw.base_vertex, 0);
}
}
void VulkanImmediateDrawer::EndDrawBatch() { batch_open_ = false; }
void VulkanImmediateDrawer::End() {
vertex_buffer_chain_->EndFrame();
current_command_buffer_ = VK_NULL_HANDLE;
}
} // namespace vk
} // namespace ui
} // namespace xe