[Vulkan v2] Upload buffer chain, immediate drawer without textures
This commit is contained in:
parent
e9802a9f3b
commit
b3382f3de1
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
} */
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
|
@ -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_
|
|
@ -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;
|
||||||
|
|
|
@ -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_];
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue