[Vulkan v2] Upload buffer chain, immediate drawer without textures

This commit is contained in:
Triang3l 2019-08-26 22:57:14 +03:00
parent e9802a9f3b
commit b3382f3de1
12 changed files with 778 additions and 31 deletions

View File

@ -203,13 +203,14 @@ bool D3D12ImmediateDrawer::Initialize() {
D3D12_INPUT_ELEMENT_DESC pipeline_input_elements[3] = {}; D3D12_INPUT_ELEMENT_DESC pipeline_input_elements[3] = {};
pipeline_input_elements[0].SemanticName = "POSITION"; pipeline_input_elements[0].SemanticName = "POSITION";
pipeline_input_elements[0].Format = DXGI_FORMAT_R32G32_FLOAT; pipeline_input_elements[0].Format = DXGI_FORMAT_R32G32_FLOAT;
pipeline_input_elements[0].AlignedByteOffset = 0; pipeline_input_elements[0].AlignedByteOffset = offsetof(ImmediateVertex, x);
pipeline_input_elements[1].SemanticName = "TEXCOORD"; pipeline_input_elements[1].SemanticName = "TEXCOORD";
pipeline_input_elements[1].Format = DXGI_FORMAT_R32G32_FLOAT; pipeline_input_elements[1].Format = DXGI_FORMAT_R32G32_FLOAT;
pipeline_input_elements[1].AlignedByteOffset = 8; pipeline_input_elements[1].AlignedByteOffset = offsetof(ImmediateVertex, u);
pipeline_input_elements[2].SemanticName = "COLOR"; pipeline_input_elements[2].SemanticName = "COLOR";
pipeline_input_elements[2].Format = DXGI_FORMAT_R8G8B8A8_UNORM; pipeline_input_elements[2].Format = DXGI_FORMAT_R8G8B8A8_UNORM;
pipeline_input_elements[2].AlignedByteOffset = 16; pipeline_input_elements[2].AlignedByteOffset =
offsetof(ImmediateVertex, color);
pipeline_desc.InputLayout.pInputElementDescs = pipeline_input_elements; pipeline_desc.InputLayout.pInputElementDescs = pipeline_input_elements;
pipeline_desc.InputLayout.NumElements = pipeline_desc.InputLayout.NumElements =
UINT(xe::countof(pipeline_input_elements)); UINT(xe::countof(pipeline_input_elements));
@ -475,11 +476,15 @@ void D3D12ImmediateDrawer::Begin(int render_target_width,
current_command_list_->RSSetViewports(1, &viewport); current_command_list_->RSSetViewports(1, &viewport);
current_command_list_->SetGraphicsRootSignature(root_signature_); current_command_list_->SetGraphicsRootSignature(root_signature_);
float viewport_inv_scale[2]; float viewport_inv_size[2];
viewport_inv_scale[0] = 1.0f / viewport.Width; viewport_inv_size[0] = 1.0f / viewport.Width;
viewport_inv_scale[1] = 1.0f / viewport.Height; viewport_inv_size[1] = 1.0f / viewport.Height;
current_command_list_->SetGraphicsRoot32BitConstants( current_command_list_->SetGraphicsRoot32BitConstants(
UINT(RootParameter::kViewportInvSize), 2, viewport_inv_scale, 0); UINT(RootParameter::kViewportInvSize), 2, viewport_inv_size, 0);
current_primitive_topology_ = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED;
current_texture_ = nullptr;
current_sampler_index_ = SamplerIndex::kInvalid;
} }
void D3D12ImmediateDrawer::BeginDrawBatch(const ImmediateDrawBatch& batch) { void D3D12ImmediateDrawer::BeginDrawBatch(const ImmediateDrawBatch& batch) {
@ -514,8 +519,8 @@ void D3D12ImmediateDrawer::BeginDrawBatch(const ImmediateDrawBatch& batch) {
index_buffer_view.SizeInBytes = batch.index_count * sizeof(uint16_t); index_buffer_view.SizeInBytes = batch.index_count * sizeof(uint16_t);
index_buffer_view.Format = DXGI_FORMAT_R16_UINT; index_buffer_view.Format = DXGI_FORMAT_R16_UINT;
void* index_buffer_mapping = vertex_buffer_pool_->RequestFull( void* index_buffer_mapping = vertex_buffer_pool_->RequestFull(
index_buffer_view.SizeInBytes, nullptr, nullptr, xe::align(index_buffer_view.SizeInBytes, UINT(sizeof(uint32_t))),
&index_buffer_view.BufferLocation); nullptr, nullptr, &index_buffer_view.BufferLocation);
if (index_buffer_mapping == nullptr) { if (index_buffer_mapping == nullptr) {
XELOGE("Failed to get a buffer for %u indices in the immediate drawer", XELOGE("Failed to get a buffer for %u indices in the immediate drawer",
batch.index_count); batch.index_count);
@ -527,9 +532,6 @@ void D3D12ImmediateDrawer::BeginDrawBatch(const ImmediateDrawBatch& batch) {
} }
batch_open_ = true; batch_open_ = true;
current_primitive_topology_ = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED;
current_texture_ = nullptr;
current_sampler_index_ = SamplerIndex::kInvalid;
} }
void D3D12ImmediateDrawer::Draw(const ImmediateDraw& draw) { void D3D12ImmediateDrawer::Draw(const ImmediateDraw& draw) {

View File

@ -9,8 +9,9 @@ struct XePixelShaderInput {
float4 main(XePixelShaderInput input) : SV_Target { float4 main(XePixelShaderInput input) : SV_Target {
float4 output = input.color; float4 output = input.color;
if (!xe_restrict_texture_samples || input.texcoord.x <= 1.0) { if (!xe_restrict_texture_samples || input.texcoord.x <= 1.0f) {
output *= xe_immediate_texture.Sample(xe_immediate_sampler, input.texcoord); output *= xe_immediate_texture.SampleLevel(xe_immediate_sampler,
input.texcoord, 0.0f);
} }
return output; return output;
} }

View File

@ -0,0 +1,22 @@
#version 450 core
precision highp float;
layout(location = 0) in vec2 xe_in_texcoord;
layout(location = 1) in vec4 xe_in_color;
layout(location = 0) out vec4 xe_out_color;
layout(push_constant) uniform XePushConstants {
layout(offset = 8) uint restrict_texture_samples;
} xe_push_constants;
// layout(set = 0, binding = 0) uniform sampler2D xe_immediate_texture_sampler;
void main() {
xe_out_color = xe_in_color;
/* if (xe_push_constants.restrict_texture_samples == 0u ||
xe_in_texcoord.x <= 1.0) {
xe_out_color *=
textureLod(xe_immediate_texture_sampler, xe_in_texcoord, 0.0f);
} */
}

View File

@ -0,0 +1,21 @@
#version 450 core
precision highp float;
layout(location = 0) in vec2 xe_in_position;
layout(location = 1) in vec2 xe_in_texcoord;
layout(location = 2) in vec4 xe_in_color;
layout(location = 0) out vec2 xe_out_texcoord;
layout(location = 1) out vec4 xe_out_color;
layout(push_constant) uniform XePushConstants {
layout(offset = 0) vec2 viewport_inv_size;
} xe_push_constants;
void main() {
gl_Position = vec4(
xe_in_position * xe_push_constants.viewport_inv_size * 2.0 - 1.0, 0.0,
1.0);
xe_out_texcoord = xe_in_texcoord;
xe_out_color = xe_in_color;
}

View File

@ -0,0 +1,211 @@
/**
******************************************************************************
* 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/transient_objects.h"
#include <algorithm>
#include "xenia/base/assert.h"
#include "xenia/base/logging.h"
#include "xenia/ui/vk/vulkan_context.h"
namespace xe {
namespace ui {
namespace vk {
UploadBufferChain::UploadBufferChain(VulkanContext* context,
VkDeviceSize frame_page_size,
VkBufferUsageFlags usage_flags)
: context_(context),
frame_page_size_(frame_page_size),
usage_flags_(usage_flags) {}
UploadBufferChain::~UploadBufferChain() {
// Allow mid-frame destruction in cases like device loss.
EndFrame();
ClearCache();
}
void UploadBufferChain::ClearCache() {
assert_true(current_frame_buffer_ == 0 && current_frame_buffer_bytes_ == 0);
auto device = context_->GetVulkanProvider()->GetDevice();
for (UploadBuffer& upload_buffer : upload_buffers_) {
vkDestroyBuffer(device, upload_buffer.buffer, nullptr);
vkUnmapMemory(device, upload_buffer.memory);
vkFreeMemory(device, upload_buffer.memory, nullptr);
}
}
void UploadBufferChain::EndFrame() {
EndPage();
current_frame_buffer_ = 0;
buffer_creation_failed_ = false;
}
void UploadBufferChain::EndPage() {
if (current_frame_buffer_bytes_ == 0) {
return;
}
if (!memory_host_coherent_) {
auto device = context_->GetVulkanProvider()->GetDevice();
VkMappedMemoryRange flush_range;
flush_range.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
flush_range.pNext = nullptr;
flush_range.memory = upload_buffers_[current_frame_buffer_].memory;
flush_range.offset = frame_page_size_ * context_->GetCurrentQueueFrame();
flush_range.size = current_frame_buffer_bytes_;
vkFlushMappedMemoryRanges(device, 1, &flush_range);
}
++current_frame_buffer_;
current_frame_buffer_bytes_ = 0;
}
bool UploadBufferChain::EnsureCurrentBufferAllocated() {
if (current_frame_buffer_ < upload_buffers_.size()) {
return true;
}
assert_true(current_frame_buffer_ == upload_buffers_.size());
assert_true(current_frame_buffer_bytes_ == 0);
if (buffer_creation_failed_) {
return false;
}
UploadBuffer upload_buffer;
auto provider = context_->GetVulkanProvider();
auto device = provider->GetDevice();
VkBufferCreateInfo buffer_create_info;
buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
buffer_create_info.pNext = nullptr;
buffer_create_info.flags = 0;
buffer_create_info.size = frame_page_size_ * VulkanContext::kQueuedFrames;
buffer_create_info.usage = usage_flags_;
buffer_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
buffer_create_info.queueFamilyIndexCount = 0;
buffer_create_info.pQueueFamilyIndices = nullptr;
if (vkCreateBuffer(device, &buffer_create_info, nullptr,
&upload_buffer.buffer) != VK_SUCCESS) {
XELOGE(
"Failed to create a Vulkan upload buffer with %ull x %u bytes and "
"0x%.8X usage",
buffer_create_info.size, VulkanContext::kQueuedFrames, usage_flags_);
buffer_creation_failed_ = true;
return false;
}
if (memory_type_ == UINT32_MAX) {
// Page memory requirements not known yet.
VkMemoryRequirements buffer_memory_requirements;
vkGetBufferMemoryRequirements(device, upload_buffer.buffer,
&buffer_memory_requirements);
memory_type_ =
provider->FindMemoryType(buffer_memory_requirements.memoryTypeBits,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
if (memory_type_ == UINT32_MAX) {
XELOGE(
"Failed to find a memory type for an upload buffer with %ull bytes "
"and 0x%.8X usage",
buffer_memory_requirements.size, usage_flags_);
vkDestroyBuffer(device, upload_buffer.buffer, nullptr);
buffer_creation_failed_ = true;
return false;
}
const VkPhysicalDeviceMemoryProperties& device_memory_properties =
provider->GetPhysicalDeviceMemoryProperties();
memory_host_coherent_ =
(device_memory_properties.memoryTypes[memory_type_].propertyFlags &
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0;
memory_page_size_ = buffer_memory_requirements.size;
}
VkMemoryAllocateInfo memory_allocate_info;
memory_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
memory_allocate_info.pNext = nullptr;
memory_allocate_info.allocationSize = memory_page_size_;
memory_allocate_info.memoryTypeIndex = memory_type_;
if (vkAllocateMemory(device, &memory_allocate_info, nullptr,
&upload_buffer.memory) != VK_SUCCESS) {
XELOGE("Failed to allocate %ull for a Vulkan upload buffer",
memory_page_size_);
vkDestroyBuffer(device, upload_buffer.buffer, nullptr);
buffer_creation_failed_ = true;
return false;
}
if (vkBindBufferMemory(device, upload_buffer.buffer, upload_buffer.memory,
0) != VK_SUCCESS) {
XELOGE("Failed to bind a %ull-byte memory object to a Vulkan upload buffer",
memory_page_size_);
vkDestroyBuffer(device, upload_buffer.buffer, nullptr);
vkFreeMemory(device, upload_buffer.memory, nullptr);
buffer_creation_failed_ = true;
return false;
}
if (vkMapMemory(device, upload_buffer.memory, 0, memory_page_size_, 0,
&upload_buffer.mapping) != VK_SUCCESS) {
XELOGE("Failed to map a %ull-byte memory object of a Vulkan upload buffer",
memory_page_size_);
vkDestroyBuffer(device, upload_buffer.buffer, nullptr);
vkFreeMemory(device, upload_buffer.memory, nullptr);
buffer_creation_failed_ = true;
return false;
}
upload_buffers_.push_back(upload_buffer);
return true;
}
uint8_t* UploadBufferChain::RequestFull(VkDeviceSize size, VkBuffer& buffer_out,
VkDeviceSize& offset_out) {
assert_true(size <= frame_page_size_);
if (size > frame_page_size_) {
return nullptr;
}
if (frame_page_size_ - current_frame_buffer_bytes_ < size) {
EndPage();
}
if (!EnsureCurrentBufferAllocated()) {
return nullptr;
}
VkDeviceSize offset = current_frame_buffer_bytes_ +
context_->GetCurrentQueueFrame() * frame_page_size_;
current_frame_buffer_bytes_ += size;
UploadBuffer& upload_buffer = upload_buffers_[current_frame_buffer_];
buffer_out = upload_buffer.buffer;
offset_out = offset;
return reinterpret_cast<uint8_t*>(upload_buffer.mapping) + offset;
}
uint8_t* UploadBufferChain::RequestPartial(VkDeviceSize size,
VkBuffer& buffer_out,
VkDeviceSize& offset_out,
VkDeviceSize& size_out) {
if (current_frame_buffer_bytes_ >= frame_page_size_) {
EndPage();
}
if (!EnsureCurrentBufferAllocated()) {
return nullptr;
}
size = std::min(size, frame_page_size_ - current_frame_buffer_bytes_);
size_out = size;
VkDeviceSize offset = current_frame_buffer_bytes_ +
context_->GetCurrentQueueFrame() * frame_page_size_;
current_frame_buffer_bytes_ += size;
UploadBuffer& upload_buffer = upload_buffers_[current_frame_buffer_];
buffer_out = upload_buffer.buffer;
offset_out = offset;
return reinterpret_cast<uint8_t*>(upload_buffer.mapping) + offset;
}
} // namespace vk
} // namespace ui
} // namespace xe

View File

@ -0,0 +1,72 @@
/**
******************************************************************************
* 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. *
******************************************************************************
*/
#ifndef XENIA_UI_VK_TRANSIENT_OBJECTS_H_
#define XENIA_UI_VK_TRANSIENT_OBJECTS_H_
#include <vector>
#include "xenia/ui/vk/vulkan_provider.h"
namespace xe {
namespace ui {
namespace vk {
class VulkanContext;
class UploadBufferChain {
public:
UploadBufferChain(VulkanContext* context, VkDeviceSize frame_page_size,
VkBufferUsageFlags usage_flags);
~UploadBufferChain();
void EndFrame();
void ClearCache();
// Request to write data in a single piece, creating a new page if the current
// one doesn't have enough free space.
uint8_t* RequestFull(VkDeviceSize size, VkBuffer& buffer_out,
VkDeviceSize& offset_out);
// Request to write data in multiple parts, filling the buffer entirely.
uint8_t* RequestPartial(VkDeviceSize size, VkBuffer& buffer_out,
VkDeviceSize& offset_out, VkDeviceSize& size_out);
private:
VulkanContext* context_;
VkBufferUsageFlags usage_flags_;
VkDeviceSize frame_page_size_;
void EndPage();
bool EnsureCurrentBufferAllocated();
VkDeviceSize memory_page_size_ = 0;
uint32_t memory_type_ = UINT32_MAX;
bool memory_host_coherent_ = false;
struct UploadBuffer {
// frame_page_size_ * VulkanContext::kQueuedFrames bytes.
// Single allocation for VulkanContext::kQueuedFrames pages so there are
// less different memory objects.
VkDeviceMemory memory;
void* mapping;
VkBuffer buffer;
};
std::vector<UploadBuffer> upload_buffers_;
size_t current_frame_buffer_ = 0;
VkDeviceSize current_frame_buffer_bytes_ = 0;
bool buffer_creation_failed_ = false;
};
} // namespace vk
} // namespace ui
} // namespace xe
#endif // XENIA_UI_VK_TRANSIENT_OBJECTS_H_

View File

@ -321,6 +321,7 @@ bool VulkanContext::Initialize() {
swapchain_ = VK_NULL_HANDLE; swapchain_ = VK_NULL_HANDLE;
} }
initialized_fully_ = true;
return true; return true;
} }
@ -608,9 +609,11 @@ void VulkanContext::EndSwap() {
VkSubmitInfo submit_info; VkSubmitInfo submit_info;
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submit_info.pNext = nullptr; submit_info.pNext = nullptr;
submit_info.waitSemaphoreCount = 0; submit_info.waitSemaphoreCount = 1;
submit_info.pWaitSemaphores = nullptr; submit_info.pWaitSemaphores = &semaphore_present_complete_;
submit_info.pWaitDstStageMask = nullptr; VkPipelineStageFlags present_complete_wait_dst_stage_mask =
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
submit_info.pWaitDstStageMask = &present_complete_wait_dst_stage_mask;
submit_info.commandBufferCount = 1; submit_info.commandBufferCount = 1;
submit_info.pCommandBuffers = &command_buffer; submit_info.pCommandBuffers = &command_buffer;
submit_info.signalSemaphoreCount = 1; submit_info.signalSemaphoreCount = 1;

View File

@ -56,6 +56,7 @@ class VulkanContext : public GraphicsContext {
const VkSurfaceFormatKHR& GetSurfaceFormat() const { return surface_format_; } const VkSurfaceFormatKHR& GetSurfaceFormat() const { return surface_format_; }
VkRenderPass GetPresentRenderPass() const { return present_render_pass_; }
VkCommandBuffer GetPresentCommandBuffer() const { VkCommandBuffer GetPresentCommandBuffer() const {
return present_command_buffers_[current_queue_frame_]; return present_command_buffers_[current_queue_frame_];
} }

View File

@ -9,12 +9,20 @@
#include "xenia/ui/vk/vulkan_immediate_drawer.h" #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_context.h"
#include "xenia/ui/vk/vulkan_util.h"
namespace xe { namespace xe {
namespace ui { namespace ui {
namespace vk { 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 { class VulkanImmediateTexture : public ImmediateTexture {
public: public:
VulkanImmediateTexture(uint32_t width, uint32_t height, VulkanImmediateTexture(uint32_t width, uint32_t height,
@ -36,16 +44,235 @@ VulkanImmediateDrawer::VulkanImmediateDrawer(VulkanContext* graphics_context)
VulkanImmediateDrawer::~VulkanImmediateDrawer() { Shutdown(); } VulkanImmediateDrawer::~VulkanImmediateDrawer() { Shutdown(); }
bool VulkanImmediateDrawer::Initialize() { return true; } bool VulkanImmediateDrawer::Initialize() {
auto device = context_->GetVulkanProvider()->GetDevice();
void VulkanImmediateDrawer::Shutdown() {} // 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( std::unique_ptr<ImmediateTexture> VulkanImmediateDrawer::CreateTexture(
uint32_t width, uint32_t height, ImmediateTextureFilter filter, bool repeat, uint32_t width, uint32_t height, ImmediateTextureFilter filter, bool repeat,
const uint8_t* data) { const uint8_t* data) {
auto texture = auto texture =
std::make_unique<VulkanImmediateTexture>(width, height, filter, repeat); std::make_unique<VulkanImmediateTexture>(width, height, filter, repeat);
if (data != nullptr) { if (data) {
UpdateTexture(texture.get(), data); UpdateTexture(texture.get(), data);
} }
return std::unique_ptr<ImmediateTexture>(texture.release()); return std::unique_ptr<ImmediateTexture>(texture.release());
@ -55,15 +282,147 @@ void VulkanImmediateDrawer::UpdateTexture(ImmediateTexture* texture,
const uint8_t* data) {} const uint8_t* data) {}
void VulkanImmediateDrawer::Begin(int render_target_width, void VulkanImmediateDrawer::Begin(int render_target_width,
int render_target_height) {} int render_target_height) {
current_command_buffer_ = context_->GetPresentCommandBuffer();
void VulkanImmediateDrawer::BeginDrawBatch(const ImmediateDrawBatch& batch) {} current_render_target_extent_.width = render_target_width;
current_render_target_extent_.height = render_target_height;
void VulkanImmediateDrawer::Draw(const ImmediateDraw& draw) {} 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);
void VulkanImmediateDrawer::EndDrawBatch() {} 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);
void VulkanImmediateDrawer::End() {} 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 %u 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 %u 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 vk
} // namespace ui } // namespace ui

View File

@ -13,6 +13,7 @@
#include <memory> #include <memory>
#include "xenia/ui/immediate_drawer.h" #include "xenia/ui/immediate_drawer.h"
#include "xenia/ui/vk/transient_objects.h"
namespace xe { namespace xe {
namespace ui { namespace ui {
@ -43,6 +44,27 @@ class VulkanImmediateDrawer : public ImmediateDrawer {
private: private:
VulkanContext* context_ = nullptr; VulkanContext* context_ = nullptr;
struct PushConstants {
struct {
float viewport_inv_size[2];
} vertex;
struct {
uint32_t restrict_texture_samples;
} fragment;
};
VkPipelineLayout pipeline_layout_ = VK_NULL_HANDLE;
VkPipeline pipeline_triangle_ = VK_NULL_HANDLE;
VkPipeline pipeline_line_ = VK_NULL_HANDLE;
std::unique_ptr<UploadBufferChain> vertex_buffer_chain_ = nullptr;
VkCommandBuffer current_command_buffer_ = VK_NULL_HANDLE;
VkExtent2D current_render_target_extent_;
bool batch_open_ = false;
bool batch_has_index_buffer_;
VkPipeline current_pipeline_ = VK_NULL_HANDLE;
}; };
} // namespace vk } // namespace vk

View File

@ -14,6 +14,7 @@
#include "xenia/base/cvar.h" #include "xenia/base/cvar.h"
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
#include "xenia/base/math.h"
#include "xenia/base/platform.h" #include "xenia/base/platform.h"
#include "xenia/ui/vk/vulkan_context.h" #include "xenia/ui/vk/vulkan_context.h"
#include "xenia/ui/vk/vulkan_util.h" #include "xenia/ui/vk/vulkan_util.h"
@ -57,6 +58,22 @@ VulkanProvider::~VulkanProvider() {
} }
} }
uint32_t VulkanProvider::FindMemoryType(
uint32_t memory_type_bits_requirement,
VkMemoryPropertyFlags required_properties) const {
uint32_t memory_index;
while (xe::bit_scan_forward(memory_type_bits_requirement, &memory_index)) {
memory_type_bits_requirement &= ~(uint32_t(1) << memory_index);
VkMemoryPropertyFlags properties =
physical_device_memory_properties_.memoryTypes[memory_index]
.propertyFlags;
if ((properties & required_properties) == required_properties) {
return memory_index;
}
}
return UINT32_MAX;
}
bool VulkanProvider::Initialize() { bool VulkanProvider::Initialize() {
if (volkInitialize() != VK_SUCCESS) { if (volkInitialize() != VK_SUCCESS) {
XELOGE("Failed to initialize the Vulkan loader volk"); XELOGE("Failed to initialize the Vulkan loader volk");
@ -130,15 +147,15 @@ bool VulkanProvider::Initialize() {
physical_device_index = 0; physical_device_index = 0;
physical_device_index_end = physical_device_count; physical_device_index_end = physical_device_count;
} }
VkPhysicalDeviceProperties physical_device_properties;
std::vector<VkExtensionProperties> physical_device_extensions; std::vector<VkExtensionProperties> physical_device_extensions;
std::vector<VkQueueFamilyProperties> queue_families; std::vector<VkQueueFamilyProperties> queue_families;
bool sparse_residency_buffer = false; bool sparse_residency_buffer = false;
for (; physical_device_index < physical_device_index_end; for (; physical_device_index < physical_device_index_end;
++physical_device_index) { ++physical_device_index) {
VkPhysicalDevice physical_device = physical_devices[physical_device_index]; VkPhysicalDevice physical_device = physical_devices[physical_device_index];
vkGetPhysicalDeviceProperties(physical_device, &physical_device_properties); vkGetPhysicalDeviceProperties(physical_device,
if (physical_device_properties.apiVersion < api_version) { &physical_device_properties_);
if (physical_device_properties_.apiVersion < api_version) {
continue; continue;
} }
vkGetPhysicalDeviceFeatures(physical_device, &physical_device_features_); vkGetPhysicalDeviceFeatures(physical_device, &physical_device_features_);
@ -219,11 +236,15 @@ bool VulkanProvider::Initialize() {
// TODO(Triang3l): Check if VK_EXT_fragment_shader_interlock and // TODO(Triang3l): Check if VK_EXT_fragment_shader_interlock and
// fragmentShaderSampleInterlock are supported. // fragmentShaderSampleInterlock are supported.
// Get the needed info about the physical device.
vkGetPhysicalDeviceMemoryProperties(physical_device_,
&physical_device_memory_properties_);
// Log physical device properties. // Log physical device properties.
XELOGVK("Vulkan physical device: %s (vendor %.4X, device %.4X)", XELOGVK("Vulkan physical device: %s (vendor %.4X, device %.4X)",
physical_device_properties.deviceName, physical_device_properties_.deviceName,
physical_device_properties.vendorID, physical_device_properties_.vendorID,
physical_device_properties.deviceID); physical_device_properties_.deviceID);
// Create a logical device and a queue. // Create a logical device and a queue.
float queue_priority = 1.0f; float queue_priority = 1.0f;

View File

@ -48,13 +48,23 @@ class VulkanProvider : public GraphicsProvider {
VkInstance GetInstance() const { return instance_; } VkInstance GetInstance() const { return instance_; }
VkPhysicalDevice GetPhysicalDevice() const { return physical_device_; } VkPhysicalDevice GetPhysicalDevice() const { return physical_device_; }
const VkPhysicalDeviceProperties& GetPhysicalDeviceProperties() const {
return physical_device_properties_;
}
const VkPhysicalDeviceFeatures& GetPhysicalDeviceFeatures() const { const VkPhysicalDeviceFeatures& GetPhysicalDeviceFeatures() const {
return physical_device_features_; return physical_device_features_;
} }
const VkPhysicalDeviceMemoryProperties& GetPhysicalDeviceMemoryProperties()
const {
return physical_device_memory_properties_;
}
VkDevice GetDevice() const { return device_; } VkDevice GetDevice() const { return device_; }
uint32_t GetGraphicsQueueFamily() const { return graphics_queue_family_; } uint32_t GetGraphicsQueueFamily() const { return graphics_queue_family_; }
VkQueue GetGraphicsQueue() const { return graphics_queue_; } VkQueue GetGraphicsQueue() const { return graphics_queue_; }
uint32_t FindMemoryType(uint32_t memory_type_bits_requirement,
VkMemoryPropertyFlags required_properties) const;
private: private:
explicit VulkanProvider(Window* main_window); explicit VulkanProvider(Window* main_window);
@ -62,7 +72,9 @@ class VulkanProvider : public GraphicsProvider {
VkInstance instance_ = VK_NULL_HANDLE; VkInstance instance_ = VK_NULL_HANDLE;
VkPhysicalDevice physical_device_ = VK_NULL_HANDLE; VkPhysicalDevice physical_device_ = VK_NULL_HANDLE;
VkPhysicalDeviceProperties physical_device_properties_;
VkPhysicalDeviceFeatures physical_device_features_; VkPhysicalDeviceFeatures physical_device_features_;
VkPhysicalDeviceMemoryProperties physical_device_memory_properties_;
VkDevice device_ = VK_NULL_HANDLE; VkDevice device_ = VK_NULL_HANDLE;
uint32_t graphics_queue_family_ = UINT32_MAX; uint32_t graphics_queue_family_ = UINT32_MAX;
VkQueue graphics_queue_ = VK_NULL_HANDLE; VkQueue graphics_queue_ = VK_NULL_HANDLE;